-
-
Notifications
You must be signed in to change notification settings - Fork 453
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(connector): add vonage connector (#6768)
- Loading branch information
Showing
10 changed files
with
662 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Vonage SMS connector | ||
|
||
The official Logto connector for Vonage SMS. | ||
|
||
## Get started | ||
|
||
Vonage is a global communications provider, offering robust cloud-based communication services, including SMS (short message service). The Vonage SMS Connector is a plugin provided by the Logto team to enable Logto end-users to register and sign in to their Logto account via SMS verification codes. | ||
|
||
## Set up in Vonage | ||
|
||
> 💡 **Tip** | ||
> | ||
> You can skip this step if you have already completed them. | ||
To work with this connector, you will need to [sign up for an account](https://developer.vonage.com/en/account/guides/dashboard-management#create-and-configure-a-vonage-account) in Vonage. This will give you an API key and secret that you can use to access the APIs through this connector. | ||
|
||
Once you have an account, you can find your API key and API secret at the top of the Vonage API Dashboard. | ||
|
||
And you may need to [rant a virtual number](https://developer.vonage.com/en/numbers/guides/number-management#rent-a-virtual-number) to send SMS messages. | ||
|
||
See the [Vonage SMS API](https://developer.vonage.com/en/messaging/sms/overview) for more information. | ||
|
||
## Set up in Logto | ||
|
||
1. **API Key**: Your Vonage API key. | ||
2. **API Secret**: Your Vonage API secret. | ||
3. **Brand Name**: The brand name you want to use to send the SMS, this is also called the `from` field, see the [Sender Identity](https://developer.vonage.com/en/messaging/sms/guides/custom-sender-id) for more information. | ||
4. **Templates**: The templates you want to use to send the SMS, you can use the default templates or modify them as needed. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
{ | ||
"name": "@logto/connector-vonage-sms", | ||
"version": "0.0.0", | ||
"description": "Vonage SMS connector implementation.", | ||
"author": "Silverhand Inc. <contact@silverhand.io>", | ||
"dependencies": { | ||
"@logto/connector-kit": "workspace:^4.0.0", | ||
"@silverhand/essentials": "^2.9.1", | ||
"@vonage/auth": "^1.12.0", | ||
"@vonage/server-sdk": "^3.19.0", | ||
"zod": "^3.23.8" | ||
}, | ||
"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", | ||
"check": "tsc --noEmit", | ||
"build": "tsup", | ||
"dev": "tsup --watch", | ||
"lint": "eslint --ext .ts src", | ||
"lint:report": "pnpm lint --format json --output-file report.json", | ||
"test": "vitest src", | ||
"test:ci": "pnpm run test --silent --coverage", | ||
"prepublishOnly": "pnpm build" | ||
}, | ||
"engines": { | ||
"node": "^20.9.0" | ||
}, | ||
"eslintConfig": { | ||
"extends": "@silverhand", | ||
"settings": { | ||
"import/core-modules": [ | ||
"@silverhand/essentials", | ||
"got", | ||
"nock", | ||
"snakecase-keys", | ||
"zod" | ||
] | ||
} | ||
}, | ||
"prettier": "@silverhand/eslint-config/.prettierrc", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"devDependencies": { | ||
"@silverhand/eslint-config": "6.0.1", | ||
"@silverhand/ts-config": "6.0.0", | ||
"@types/node": "^20.11.20", | ||
"@types/supertest": "^6.0.2", | ||
"@vitest/coverage-v8": "^2.0.0", | ||
"eslint": "^8.56.0", | ||
"lint-staged": "^15.0.2", | ||
"prettier": "^3.0.0", | ||
"supertest": "^7.0.0", | ||
"tsup": "^8.3.0", | ||
"typescript": "^5.5.3", | ||
"vitest": "^2.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { ConnectorMetadata } from '@logto/connector-kit'; | ||
import { ConnectorConfigFormItemType } from '@logto/connector-kit'; | ||
|
||
export const defaultMetadata: ConnectorMetadata = { | ||
id: 'vonage-sms', | ||
target: 'vonage-sms', | ||
platform: null, | ||
name: { | ||
en: 'Vonage SMS Service', | ||
}, | ||
logo: './logo.svg', | ||
logoDark: null, | ||
description: { | ||
en: 'Communications APIs to connect the world', | ||
}, | ||
readme: './README.md', | ||
formItems: [ | ||
{ | ||
key: 'apiKey', | ||
label: 'API Key', | ||
type: ConnectorConfigFormItemType.Text, | ||
required: true, | ||
}, | ||
{ | ||
key: 'apiSecret', | ||
label: 'API Secret', | ||
type: ConnectorConfigFormItemType.Text, | ||
required: true, | ||
}, | ||
{ | ||
key: 'brandName', | ||
label: 'Brand Name', | ||
type: ConnectorConfigFormItemType.Text, | ||
required: true, | ||
}, | ||
{ | ||
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.', | ||
}, | ||
], | ||
}, | ||
], | ||
}; |
10 changes: 10 additions & 0 deletions
10
packages/connectors/connector-vonage-sms/src/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import createConnector from './index.js'; | ||
import { mockedConfig } from './mock.js'; | ||
|
||
const getConfig = vi.fn().mockResolvedValue(mockedConfig); | ||
|
||
describe('Vonage SMS connector', () => { | ||
it('init without throwing errors', async () => { | ||
await expect(createConnector({ getConfig })).resolves.not.toThrow(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { assert } from '@silverhand/essentials'; | ||
|
||
import type { | ||
GetConnectorConfig, | ||
SendMessageFunction, | ||
CreateConnector, | ||
SmsConnector, | ||
} from '@logto/connector-kit'; | ||
import { | ||
ConnectorError, | ||
ConnectorErrorCodes, | ||
validateConfig, | ||
ConnectorType, | ||
replaceSendMessageHandlebars, | ||
} from '@logto/connector-kit'; | ||
import { Auth } from '@vonage/auth'; | ||
import { Vonage } from '@vonage/server-sdk'; | ||
|
||
import { defaultMetadata } from './constant.js'; | ||
import { vonageSmsConfigGuard } from './types.js'; | ||
|
||
const sendMessage = | ||
(getConfig: GetConnectorConfig): SendMessageFunction => | ||
async (data, inputConfig) => { | ||
const { to, type, payload } = data; | ||
const config = inputConfig ?? (await getConfig(defaultMetadata.id)); | ||
validateConfig(config, vonageSmsConfigGuard); | ||
const { apiKey, apiSecret, brandName, templates } = config; | ||
const template = templates.find((template) => template.usageType === type); | ||
|
||
assert( | ||
template, | ||
new ConnectorError( | ||
ConnectorErrorCodes.TemplateNotFound, | ||
`Cannot find template for type: ${type}` | ||
) | ||
); | ||
|
||
const vonageAuth = new Auth({ | ||
apiKey, | ||
apiSecret, | ||
}); | ||
const vonage = new Vonage(vonageAuth); | ||
|
||
try { | ||
return await vonage.sms.send({ | ||
from: brandName, | ||
to, | ||
text: replaceSendMessageHandlebars(template.content, payload), | ||
}); | ||
} catch (error: unknown) { | ||
if (error instanceof Error) { | ||
throw new ConnectorError(ConnectorErrorCodes.General, error.message); | ||
} | ||
|
||
throw error; | ||
} | ||
}; | ||
|
||
const createVonageSmsConnector: CreateConnector<SmsConnector> = async ({ getConfig }) => { | ||
return { | ||
metadata: defaultMetadata, | ||
type: ConnectorType.Sms, | ||
configGuard: vonageSmsConfigGuard, | ||
sendMessage: sendMessage(getConfig), | ||
}; | ||
}; | ||
|
||
export default createVonageSmsConnector; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import type { VonageSmsConfig } from './types.js'; | ||
|
||
const mockedApiKey = 'api-key'; | ||
const mockedApiSecret = 'api-secret'; | ||
const mockedBrandName = 'brand name'; | ||
|
||
export const mockedConfig: VonageSmsConfig = { | ||
apiKey: mockedApiKey, | ||
apiSecret: mockedApiSecret, | ||
brandName: mockedBrandName, | ||
templates: [ | ||
{ | ||
usageType: 'Generic', | ||
content: 'This is for testing purposes only. Your verification code is {{code}}.', | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { z } from 'zod'; | ||
|
||
/** | ||
* UsageType here is used to specify the use case of the template, can be either | ||
* 'Register', 'SignIn', 'ForgotPassword', 'Generic'. | ||
*/ | ||
const requiredTemplateUsageTypes = ['Register', 'SignIn', 'ForgotPassword', 'Generic']; | ||
|
||
const templateGuard = z.object({ | ||
usageType: z.string(), | ||
content: z.string(), | ||
}); | ||
|
||
export const vonageSmsConfigGuard = z.object({ | ||
apiKey: z.string(), | ||
apiSecret: z.string(), | ||
brandName: 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 VonageSmsConfig = z.infer<typeof vonageSmsConfigGuard>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.