mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(connector): create SMSAero connector (#4096)
* feat(connector): create SMSAero connector * fix(connector): smsaero connector code review fixes * chore: add changeset for sms aero --------- Co-authored-by: wangsijie <wangsijie94@gmail.com>
This commit is contained in:
parent
97fc519ac1
commit
3e72b6d56d
10 changed files with 436 additions and 2 deletions
5
.changeset/dull-starfishes-scream.md
Normal file
5
.changeset/dull-starfishes-scream.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@logto/connector-smsaero": minor
|
||||
---
|
||||
|
||||
Add SMSAero connector
|
64
packages/connectors/connector-smsaero/README.md
Normal file
64
packages/connectors/connector-smsaero/README.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# SMSAero short message service connector
|
||||
|
||||
The official Logto connector for SMSAero short message service.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
- [SMSAero short message service connector](#smsaero-short-message-service-connector)
|
||||
- [Register account](#register-account)
|
||||
- [Set up senders' phone numbers](#set-up-senders-phone-numbers)
|
||||
- [Get account credentials](#get-account-credentials)
|
||||
- [Compose the connector JSON](#compose-the-connector-json)
|
||||
- [Test SMSAero connector](#test-smsaero-connector)
|
||||
- [Config types](#config-types)
|
||||
- [Reference](#reference)
|
||||
|
||||
## Register account
|
||||
|
||||
Create a new account on [SMSAero](https://smsaero.ru/). (Jump to the next step if you already have one.)
|
||||
|
||||
## Get account credentials
|
||||
|
||||
We will need the API credentials to make the connector work. Let's begin from
|
||||
the [API and SMPP](https://smsaero.ru/cabinet/settings/apikey/).
|
||||
|
||||
Copy "API-key" or generate new one.
|
||||
|
||||
## Compose the connector JSON
|
||||
|
||||
Fill out the _email_, _apiKey_ and _senderName_ fields with your email, API key and sender name.
|
||||
|
||||
You can fill sender name with "SMSAero" to use default sender name provided by SMSAero.
|
||||
|
||||
You can add multiple SMS connector templates for different cases. Here is an example of adding a single template:
|
||||
|
||||
- Fill out the `content` field with arbitrary string-typed contents. Do not forget to leave `{{code}}` placeholder for
|
||||
random verification code.
|
||||
- Fill out the `usageType` field with either `Register`, `SignIn`, `ForgotPassword`, `Generic` for different use cases.
|
||||
In order to enable full user flows, templates with usageType `Register`, `SignIn`, `ForgotPassword` and `Generic` are
|
||||
required.
|
||||
|
||||
### Test SMSAero connector
|
||||
|
||||
You can enter a phone number and click on "Send" to see whether the settings can work before "Save and Done".
|
||||
|
||||
That's it. Don't forget
|
||||
to [Enable connector in sign-in experience](https://docs.logto.io/docs/tutorials/get-started/passwordless-sign-in-by-adding-connectors#enable-sms-or-email-passwordless-sign-in).
|
||||
|
||||
### Config types
|
||||
|
||||
| Name | Type |
|
||||
|------------|-------------|
|
||||
| email | string |
|
||||
| apiKey | string |
|
||||
| senderName | string |
|
||||
| templates | Templates[] |
|
||||
|
||||
| Template Properties | Type | Enum values |
|
||||
|---------------------|-------------|---------------------------------------------------------|
|
||||
| content | string | N/A |
|
||||
| usageType | enum string | 'Register' \| 'SignIn' \| 'ForgotPassword' \| 'Generic' |
|
||||
|
||||
## Reference
|
||||
|
||||
- [SMSAero API Documentation](https://smsaero.ru/integration/documentation/api/)
|
8
packages/connectors/connector-smsaero/logo.svg
Normal file
8
packages/connectors/connector-smsaero/logo.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body_1" width="192" height="192">
|
||||
|
||||
<g transform="matrix(1.3333334 0 0 1.3333334 0 0)">
|
||||
<image x="0" y="0" xlink:href="data:image/png;base64,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" preserveAspectRatio="none" width="144" height="144"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
51
packages/connectors/connector-smsaero/package.json
Normal file
51
packages/connectors/connector-smsaero/package.json
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "@logto/connector-smsaero",
|
||||
"version": "1.0.0",
|
||||
"description": "SMSAero connector implementation.",
|
||||
"author": "Danil Tankov <danil.tankoff@yandex.ru>",
|
||||
"dependencies": {
|
||||
"@logto/connector-kit": "workspace:^1.1.0"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"module": "./lib/index.js",
|
||||
"exports": "./lib/index.js",
|
||||
"license": "MPL-2.0",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"lib",
|
||||
"docs",
|
||||
"logo.svg",
|
||||
"logo-dark.svg"
|
||||
],
|
||||
"scripts": {
|
||||
"precommit": "lint-staged",
|
||||
"build:test": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap",
|
||||
"build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c",
|
||||
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
|
||||
"lint": "eslint --ext .ts src",
|
||||
"lint:report": "pnpm lint --format json --output-file report.json",
|
||||
"test:only": "NODE_OPTIONS=--experimental-vm-modules jest",
|
||||
"test": "pnpm build:test && pnpm test:only",
|
||||
"test:ci": "pnpm test:only --silent --coverage",
|
||||
"prepublishOnly": "pnpm build"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.12.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@silverhand",
|
||||
"settings": {
|
||||
"import/core-modules": [
|
||||
"@silverhand/essentials",
|
||||
"got",
|
||||
"nock",
|
||||
"snakecase-keys",
|
||||
"zod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"prettier": "@silverhand/eslint-config/.prettierrc",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
70
packages/connectors/connector-smsaero/src/constant.ts
Normal file
70
packages/connectors/connector-smsaero/src/constant.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import type { ConnectorMetadata } from '@logto/connector-kit';
|
||||
import { ConnectorConfigFormItemType } from '@logto/connector-kit';
|
||||
|
||||
export const endpoint = `https://gate.smsaero.ru/v2/sms/send`;
|
||||
|
||||
export const defaultMetadata: ConnectorMetadata = {
|
||||
id: 'smsaero-short-message-service',
|
||||
target: 'smsaero-sms',
|
||||
platform: null,
|
||||
name: {
|
||||
en: 'SMS Aero service',
|
||||
},
|
||||
logo: './logo.svg',
|
||||
logoDark: null,
|
||||
description: {
|
||||
en: 'SMS Aero offers users to use SMS-mailing in 5 minutes without viewing the contract. Developers are offered a convenient API with accessible classes and 24x7 chat support.',
|
||||
},
|
||||
readme: './README.md',
|
||||
formItems: [
|
||||
{
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
type: ConnectorConfigFormItemType.Text,
|
||||
required: true,
|
||||
placeholder: '<account-email>',
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: ConnectorConfigFormItemType.Text,
|
||||
required: true,
|
||||
placeholder: '<api-key>',
|
||||
},
|
||||
{
|
||||
key: 'senderName',
|
||||
label: 'Sender Name',
|
||||
type: ConnectorConfigFormItemType.Text,
|
||||
required: true,
|
||||
placeholder: 'SMSAero',
|
||||
},
|
||||
{
|
||||
key: 'templates',
|
||||
label: 'Templates',
|
||||
type: ConnectorConfigFormItemType.Json,
|
||||
required: true,
|
||||
defaultValue: [
|
||||
{
|
||||
usageType: 'SignIn',
|
||||
content:
|
||||
'Your Logto sign-in verification code is {{code}}. The code will remain active for 10 minutes.',
|
||||
},
|
||||
{
|
||||
usageType: 'Register',
|
||||
content:
|
||||
'Your Logto sign-up verification code is {{code}}. The code will remain active for 10 minutes.',
|
||||
},
|
||||
{
|
||||
usageType: 'ForgotPassword',
|
||||
content:
|
||||
'Your Logto password change verification code is {{code}}. The code will remain active for 10 minutes.',
|
||||
},
|
||||
{
|
||||
usageType: 'Generic',
|
||||
content:
|
||||
'Your Logto verification code is {{code}}. The code will remain active for 10 minutes.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
12
packages/connectors/connector-smsaero/src/index.test.ts
Normal file
12
packages/connectors/connector-smsaero/src/index.test.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import createConnector from './index.js';
|
||||
import { mockedConfig } from './mock.js';
|
||||
|
||||
const { jest } = import.meta;
|
||||
|
||||
const getConfig = jest.fn().mockResolvedValue(mockedConfig);
|
||||
|
||||
describe('SMSAero SMS connector', () => {
|
||||
it('init without throwing errors', async () => {
|
||||
await expect(createConnector({ getConfig })).resolves.not.toThrow();
|
||||
});
|
||||
});
|
85
packages/connectors/connector-smsaero/src/index.ts
Normal file
85
packages/connectors/connector-smsaero/src/index.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import { assert } from '@silverhand/essentials';
|
||||
import { got, HTTPError } from 'got';
|
||||
|
||||
import type {
|
||||
CreateConnector,
|
||||
GetConnectorConfig,
|
||||
SendMessageFunction,
|
||||
SmsConnector,
|
||||
} from '@logto/connector-kit';
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
ConnectorType,
|
||||
validateConfig,
|
||||
} from '@logto/connector-kit';
|
||||
|
||||
import { defaultMetadata, endpoint } from './constant.js';
|
||||
import type { PublicParameters, SmsAeroConfig } from './types.js';
|
||||
import { smsAeroConfigGuard } from './types.js';
|
||||
|
||||
function sendMessage(getConfig: GetConnectorConfig): SendMessageFunction {
|
||||
return async (data, inputConfig) => {
|
||||
const { to, type, payload } = data;
|
||||
|
||||
const config = inputConfig ?? (await getConfig(defaultMetadata.id));
|
||||
validateConfig<SmsAeroConfig>(config, smsAeroConfigGuard);
|
||||
|
||||
const { email, apiKey, senderName, templates } = config;
|
||||
|
||||
const template = templates.find((template) => template.usageType === type);
|
||||
|
||||
assert(
|
||||
template,
|
||||
new ConnectorError(
|
||||
ConnectorErrorCodes.TemplateNotFound,
|
||||
`Cannot find template for type: ${type}`
|
||||
)
|
||||
);
|
||||
|
||||
const parameters: PublicParameters = {
|
||||
number: to,
|
||||
sign: senderName,
|
||||
text: template.content.replace(/{{code}}/g, payload.code),
|
||||
};
|
||||
|
||||
const auth = Buffer.from(`${email}:${apiKey}`).toString('base64');
|
||||
|
||||
try {
|
||||
return await got.post(endpoint, {
|
||||
headers: {
|
||||
Authorization: `Basic ${auth}`,
|
||||
},
|
||||
json: parameters,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof HTTPError) {
|
||||
const {
|
||||
response: { body: rawBody },
|
||||
} = error;
|
||||
assert(
|
||||
typeof rawBody === 'string',
|
||||
new ConnectorError(
|
||||
ConnectorErrorCodes.InvalidResponse,
|
||||
`Invalid response raw body type: ${typeof rawBody}`
|
||||
)
|
||||
);
|
||||
|
||||
throw new ConnectorError(ConnectorErrorCodes.General, rawBody);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const createSmsAeroConnector: CreateConnector<SmsConnector> = async ({ getConfig }) => {
|
||||
return {
|
||||
metadata: defaultMetadata,
|
||||
type: ConnectorType.Sms,
|
||||
configGuard: smsAeroConfigGuard,
|
||||
sendMessage: sendMessage(getConfig),
|
||||
};
|
||||
};
|
||||
|
||||
export default createSmsAeroConnector;
|
17
packages/connectors/connector-smsaero/src/mock.ts
Normal file
17
packages/connectors/connector-smsaero/src/mock.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { SmsAeroConfig } from './types.js';
|
||||
|
||||
const mockedEmail = 'test@test.com';
|
||||
const mockedApiKey = 'api-key';
|
||||
const mockedSenderName = 'sender-name';
|
||||
|
||||
export const mockedConfig: SmsAeroConfig = {
|
||||
email: mockedEmail,
|
||||
apiKey: mockedApiKey,
|
||||
senderName: mockedSenderName,
|
||||
templates: [
|
||||
{
|
||||
usageType: 'Test',
|
||||
content: 'This is for testing purposes only. Your verification code is {{code}}.',
|
||||
},
|
||||
],
|
||||
};
|
43
packages/connectors/connector-smsaero/src/types.ts
Normal file
43
packages/connectors/connector-smsaero/src/types.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* @doc https://smsaero.ru/integration/documentation/api/
|
||||
*/
|
||||
|
||||
export type PublicParameters = {
|
||||
number: string;
|
||||
sign: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* UsageType here is used to specify the use case of the template, can be either
|
||||
* 'Register', 'SignIn', 'ForgotPassword', 'Generic' or 'Test'.
|
||||
*/
|
||||
const requiredTemplateUsageTypes = ['Register', 'SignIn', 'ForgotPassword', 'Generic'];
|
||||
|
||||
const templateGuard = z.object({
|
||||
usageType: z.string(),
|
||||
content: z.string(),
|
||||
});
|
||||
|
||||
export const smsAeroConfigGuard = z.object({
|
||||
email: z.string().email(),
|
||||
apiKey: z.string(),
|
||||
senderName: z.string(),
|
||||
templates: z.array(templateGuard).refine(
|
||||
(templates) =>
|
||||
requiredTemplateUsageTypes.every((requiredType) =>
|
||||
templates.map((template) => template.usageType).includes(requiredType)
|
||||
),
|
||||
(templates) => ({
|
||||
message: `Template with UsageType (${requiredTemplateUsageTypes
|
||||
.filter(
|
||||
(requiredType) => !templates.map((template) => template.usageType).includes(requiredType)
|
||||
)
|
||||
.join(', ')}) should be provided!`,
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export type SmsAeroConfig = z.infer<typeof smsAeroConfigGuard>;
|
|
@ -2263,6 +2263,85 @@ importers:
|
|||
specifier: ^5.0.0
|
||||
version: 5.0.2
|
||||
|
||||
packages/connectors/connector-smsaero:
|
||||
dependencies:
|
||||
'@logto/connector-kit':
|
||||
specifier: workspace:^1.1.0
|
||||
version: link:../../toolkit/connector-kit
|
||||
'@silverhand/essentials':
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
got:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.0
|
||||
snakecase-keys:
|
||||
specifier: ^5.4.4
|
||||
version: 5.4.4
|
||||
zod:
|
||||
specifier: ^3.20.2
|
||||
version: 3.20.2
|
||||
devDependencies:
|
||||
'@jest/types':
|
||||
specifier: ^29.5.0
|
||||
version: 29.5.0
|
||||
'@rollup/plugin-commonjs':
|
||||
specifier: ^25.0.0
|
||||
version: 25.0.0(rollup@3.8.0)
|
||||
'@rollup/plugin-json':
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0(rollup@3.8.0)
|
||||
'@rollup/plugin-node-resolve':
|
||||
specifier: ^15.0.1
|
||||
version: 15.0.1(rollup@3.8.0)
|
||||
'@rollup/plugin-typescript':
|
||||
specifier: ^11.0.0
|
||||
version: 11.0.0(rollup@3.8.0)(typescript@5.0.2)
|
||||
'@silverhand/eslint-config':
|
||||
specifier: 3.0.1
|
||||
version: 3.0.1(eslint@8.34.0)(prettier@2.8.4)(typescript@5.0.2)
|
||||
'@silverhand/ts-config':
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0(typescript@5.0.2)
|
||||
'@types/jest':
|
||||
specifier: ^29.4.0
|
||||
version: 29.4.0
|
||||
'@types/node':
|
||||
specifier: ^18.11.18
|
||||
version: 18.11.18
|
||||
'@types/supertest':
|
||||
specifier: ^2.0.11
|
||||
version: 2.0.11
|
||||
eslint:
|
||||
specifier: ^8.34.0
|
||||
version: 8.34.0
|
||||
jest:
|
||||
specifier: ^29.5.0
|
||||
version: 29.5.0(@types/node@18.11.18)
|
||||
jest-matcher-specific-error:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
lint-staged:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.0
|
||||
nock:
|
||||
specifier: ^13.2.2
|
||||
version: 13.2.2
|
||||
prettier:
|
||||
specifier: ^2.8.2
|
||||
version: 2.8.4
|
||||
rollup:
|
||||
specifier: ^3.8.0
|
||||
version: 3.8.0
|
||||
rollup-plugin-summary:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(rollup@3.8.0)
|
||||
supertest:
|
||||
specifier: ^6.2.2
|
||||
version: 6.2.2
|
||||
typescript:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.2
|
||||
|
||||
packages/connectors/connector-smtp:
|
||||
dependencies:
|
||||
'@logto/connector-kit':
|
||||
|
@ -10745,7 +10824,7 @@ packages:
|
|||
dev: false
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: true
|
||||
|
||||
/concat-stream@2.0.0:
|
||||
|
@ -11328,7 +11407,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/dezalgo@1.0.3:
|
||||
resolution: {integrity: sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=}
|
||||
resolution: {integrity: sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==}
|
||||
dependencies:
|
||||
asap: 2.0.6
|
||||
wrappy: 1.0.2
|
||||
|
|
Loading…
Reference in a new issue