@service-exchange/node-sdk v1.0.3
Service Exchange interface SDK
Overview
Service Exchange Platform provides a series of atomic APIs on the server side to realize diversified functions, but the actual coding process is not very smooth: when using these APIs to complete operation, a lot of extra work needs to be considered, such as token acquisition and maintenance, data encryption and decryption, request signature verification, etc.; in the actual coding process, the semantics of function calls are missing, cause mental burden.
All of these make the overall development experience poor. In order to make the open capability easy to use, we have written this SDK, which integrates all the verbose logic into built-in processing, provides a complete type hints, and provides external semantics. Programming interface to improve coding experience.
Concept
Development documentation: A reference to the open interface of the open platform, a must-see for developers, you can use the search function to query documents efficiently. More introduction instructions .
Developer background: The management background of the developer's development application, more introduction.
Enterprise self-built Application: The application can only be installed and used within the enterprise, more introduction.
ISV Application: The app will be listed in the ISV Application Display, each enterprise can choose to install, more introduction instructions.
Installation
npm
npm install @service-exchange/node-sdk
yarn
yarn add @service-exchange/node-sdk
How to use
Provides two versions of ECMAScript and CommonJS, and supports the use of native Javascript and Typescript. The examples are all taking Typescript as an example.
Typescript
import * as lark from '@service-exchange/node-sdk';
CommonJS
const lark = require('@service-exchange/node-sdk');
ECMAScript
import * as lark from '@service-exchange/node-sdk';
API Call
The list of all APIs on Service Exchange Platform: click here.
The SDK provides a semantic calling method. You only need to construct a client instance according to the relevant parameters, and then use the semantic method (client.business domain.resource.method) on it to complete the API call, the calling process and the calling result. There are complete types for prompting, such as sending a message to a group chat:
import * as lark from '@service-exchange/node-sdk';
const client = new lark. Client({
appId: 'app id',
appSecret: 'app secret',
appType: lark.AppType.SelfBuild,
domain: lark.Domain.Feishu,
});
const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: 'receive_id',
content: 'hello world',
msg_type: 'text',
},
});
tips: If you want to debug an API, you can click the link in the comment to enter the API debugging platform for debugging:
Create Client
For self-built applications, you can use the following code to create a client:
import * as lark from '@service-exchange/node-sdk';
const client = new lark. Client({
appId: 'app id',
appSecret: 'app secret'
});
For store apps, the specified appType that needs to be displayed is lark.AppType.ISV:
import * as lark from '@service-exchange/node-sdk';
const client = new lark.Client({
appId: 'app id',
appSecret: 'app secret',
appType: lark.AppType.ISV,
});
When using the client of the created store application to initiate an API call, you need to manually pass the tenant_key, you can Use lark.withTenantKey
to do it:
client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: 'chat_id',
content: 'hello world',
msg_type: 'text'
},
}, lark.withTenantKey('tenant key'));
Client
construction parameters
Parameter | Description | Type | Required | Default |
---|---|---|---|---|
appId | app id | string | yes | - |
appSecret | app secret | string | yes | - |
domain | The domain of the application, divided into Feishu (https://open.service.exchange), lark (https://open.service.exchange), others (the complete domain name needs to be passed) | Domain | string | no | Domain.Feishu |
loggerLevel | Log Level | LoggerLevel | No | info |
logger | - | Logger | No | - |
cache | Cache | Cache | No | - |
disableTokenCache | Whether to disable the cache, if disabled, the token will not be cached, and it will be re-pulled every time it needs to be used | boolean | No | false |
appType | The type of application, divided into store application or self-built application | AppType | No | AppType.SelfBuild |
helpDeskId | helpdesk id | string | no | - |
helpDeskToken | helpdesk token | string | no | - |
Pagination
For the interface whose return value is presented in the form of pagination, it provides iterator encapsulation (the method name suffix is WithIterator), which improves the usability and eliminates the tedious operation of repeatedly obtaining data according to page_token, such as obtaining the user list:
// Process 20 pieces of data each time
for await (const items of await client.contact.user.listWithIterator({
params: {
department_id: '0',
page_size: 20,
},
})) {
console.log(items);
}
// You can also use next to manually control the iteration, fetching 20 pieces of data each time
const listIterator = await SDKClient.contact.user.listWithIterator({
params: {
department_id: '0',
page_size: 20,
},
});
const { value } = await listIterator[Symbol.asyncIterator]().next();
console.log(value);
- Of course, you can also use the version without iterator encapsulation. In this case, you need to manually perform paging calls each time according to the returned page_token. *
File upload
In the same way as calling ordinary API, you can pass the parameters according to the type prompt, and the processing of file upload is encapsulated inside, such as:
const res = await client.im.file.create({
data: {
file_type: 'mp4',
file_name: 'test.mp4',
file: fs.readFileSync('file path'),
},
});
File download
The returned binary stream is encapsulated, eliminating the processing of the stream itself, just call the writeFile method to write the data to the file, such as:
const resp = await client.im.file.get({
path: {
file_key: 'file key',
},
});
await resp.writeFile(`filepath.suffix`);
Normal call
Some old versions of the open interface cannot generate corresponding semantic calling methods, and you need to use the request method on the client to make manual calls:
import * as lark from '@service-exchange/node-sdk';
const client = new lark. Client({
appId: 'app id',
appSecret: 'app secret',
appType: lark.AppType.SelfBuild,
domain: lark.Domain.Feishu,
});
const res = await client. request({
method: 'POST',
url: 'xxx',
data: {},
params: {},
});
Configure request options
If you want to modify the parameters of the request during the API call, such as carrying some headers, custom tenantToken, etc., you can use the second parameter of the request method to modify:
await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: 'receive_id',
content: 'hello world',
msg_type: 'text',
},
}, {
headers: {
customizedHeaderKey: 'customizedHeaderValue'
}
});
The SDK also encapsulates commonly used modification operations into methods, which can be used:
Method | Description |
---|---|
withTenantKey | Set tenant key |
withTenantToken | Set tenant token |
withHelpDeskCredential | Whether to bring in the Service Desk token |
withUserAccessToken | Set access token |
withAll | Combines the results of the above methods |
await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: 'receive_id',
content: 'hello world',
msg_type: 'text',
},
}, lark.withTenantToken('tenant token'));
await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: 'receive_id',
content: 'hello world',
msg_type: 'text',
},
}, lark.withAll([
lark.withTenantToken('tenant token'),
lark.withTenantKey('tenant key')
]));
Events handling
For a list of all events opened on Service Exchange Platform, please click here.
For the event processing scenario, we care about is only what kind of events to listen for, and what we do after the event occurs. other work such as data decryption we don't want to care about. The SDK provides an intuitive way to describe this part of the logic:
- Construct an instance of the event handler
EventDispatcher
; - Register the events to be monitored and their handler functions on the instance;
- Bind the instance to the service;
EventDispatcher
will perform operations such as data decryption internally. If no relevant parameters are passed, it will be automatically ignored.
import http from 'http';
import * as lark from '@service-exchange/node-sdk';
const eventDispatcher = new lark.EventDispatcher({
encryptKey: 'encrypt key'
}).register({
'im.message.receive_v1': async (data) => {
const chatId = data.message.chat_id;
const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: chatId,
content: 'hello world',
msg_type: 'text'
},
});
return res;
}
});
const server = http.createServer();
server.on('request', lark.adaptDefault('/webhook/event', eventDispatcher));
server.listen(3000);
EventDispatcher
constructor parameters
Parameter | Description | Type | Required | Default |
---|---|---|---|---|
encryptKey | Push data encryption key, required when enabling encrypted push use for data decryption | string | no | - |
loggerLevel | log level | LoggerLevel | no | lark.LoggerLevel.info |
logger | - | Logger | No | - |
cache | Cache | Cache | No | - |
Note: Some events are v1.0 version and are no longer maintained. The SDK retains support for them. It is strongly recommended to use new versions of events that are consistent with their functions. Move the mouse to the corresponding event subscription function to see the relevant documents:
Combined with express
The SDK provides an adapter for experss to convert eventDispatcher into express middleware, which can be seamlessly combined with services written using express (The use of bodyParser in the example is not necessary, but the community mostly uses it to format body data):
import * as lark from '@service-exchange/node-sdk';
import express from 'express';
import bodyParser from 'body-parser';
const server = express();
server.use(bodyParser.json());
const eventDispatcher = new lark.EventDispatcher({
encryptKey: 'encryptKey',
}).register({
'im.message.receive_v1': async (data) => {
const chatId = data.message.chat_id;
const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: chatId,
content: 'hello world',
msg_type: 'text'
},
});
return res;
}
});
server.use('/webhook/event', lark.adaptExpress(eventDispatcher));
server.listen(3000);
Combined with Koa
The SDK provides an adapter for Koa to convert eventDispatcher into Koa middleware, which can be seamlessly combined with services written using Koa (The use of koa-body in the example is not necessary, but the community mostly uses it to format body data):
import * as lark from '@service-exchange/node-sdk';
import Koa from 'koa';
import koaBody from 'koa-body';
const server = new Koa();
server.use(koaBody());
const eventDispatcher = new lark.EventDispatcher({
encryptKey: 'encryptKey',
}).register({
'im.message.receive_v1': async (data) => {
const open_chat_id = data.message.chat_id;
const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: open_chat_id,
content: 'hello world',
msg_type: 'text'
},
});
return res;
},
});
server.use(nodeSdk.adaptKoa('/webhook/event', eventDispatcher));
server.listen(3000);
Combined with koa-router
When using Koa to write services, in most cases, koa-router is used to process routing, so the SDK also provides adaptations for this situation:
import * as nodeSdk from '@service-exchange/node-sdk';
import Koa from 'koa';
import Router from '@koa/router';
import koaBody from 'koa-body';
const server = new Koa();
const router = new Router();
server.use(koaBody());
onst eventDispatcher = new lark.EventDispatcher({
encryptKey: 'encryptKey',
}).register({
'im.message.receive_v1': async (data) => {
const open_chat_id = data.message.chat_id;
const res = await client.im.message.create({
params: {
receive_id_type: 'chat_id',
},
data: {
receive_id: open_chat_id,
content: 'hello world',
msg_type: 'text'
},
});
return res;
},
});
router.post('/webhook/event', lark.adaptKoaRouter(eventDispatcher));
server.use(router.routes());
server.listen(3000);
Custom adapter
If you want to adapt to services written by other libraries, you currently need to encapsulate the corresponding adapter yourself. Pass the received event data to the invoke method of the instantiated eventDispatcher
for event processing:
const data = server.getData();
const resule = await dispatcher.invoke(data);
server.sendResult(result);
Message Card
The processing of the Message Card is also a kind of Event processing. The only difference between the two is that the processor of the Message Card is used to respond to the events generated by the interaction between the user and the Message Card. If the processor has a return value (the value structure should be in line with the structure defined by Message Card Structure), then the return value is used to update the responded message card:
import http from 'http';
import * as lark from '@service-exchange/node-sdk';
const cardDispatcher = new lark.CardActionHandler(
{
encryptKey: 'encrypt key',
verificationToken: 'verification token'
},
async(data) => {
console.log(data);
return newCard;
}
);
const server = http.createServer();
server.on('request', lark.adaptDefault('/webhook/card', cardDispatcher));
server.listen(3000);
CardActionHandler
construction parameters
Parameter | Description | Type | Required | Default |
---|---|---|---|---|
encryptKey | Push data encryption key, required when enabling encrypted push use for data decryption | string | no | - |
verificationToken | Security verification, it needs to be used when enabling message security verification | string | No | - |
loggerLevel | Log Level | LoggerLevel | No | LoggerLevel.info |
logger | - | Logger | No | - |
cache | Cache | Cache | No | - |
Tool method
AESCipher
Decrypt. If Encrypted Push is configured, the open platform will push encrypted data, At this time, the data needs to be decrypted, and this method can be called for convenient decryption. (In general, the decryption logic is built into the SDK, and no manual processing is required).
import * as lark from '@service-exchange/node-sdk';
new lark.AESCipher('encrypt key').decrypt('content');
LICENSE
MIT
Contact Us
Click Server SDK in the upper right corner of the page 【Is this document helpful to you? 】Submit feedback
2 years ago