1.0.3 • Published 2 years ago

@service-exchange/node-sdk v1.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

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: npm.io

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

ParameterDescriptionTypeRequiredDefault
appIdapp idstringyes-
appSecretapp secretstringyes-
domainThe 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 | stringnoDomain.Feishu
loggerLevelLog LevelLoggerLevelNoinfo
logger-LoggerNo-
cacheCacheCacheNo-
disableTokenCacheWhether to disable the cache, if disabled, the token will not be cached, and it will be re-pulled every time it needs to be usedbooleanNofalse
appTypeThe type of application, divided into store application or self-built applicationAppTypeNoAppType.SelfBuild
helpDeskIdhelpdesk idstringno-
helpDeskTokenhelpdesk tokenstringno-

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:

MethodDescription
withTenantKeySet tenant key
withTenantTokenSet tenant token
withHelpDeskCredentialWhether to bring in the Service Desk token
withUserAccessTokenSet access token
withAllCombines 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:

  1. Construct an instance of the event handler EventDispatcher;
  2. Register the events to be monitored and their handler functions on the instance;
  3. 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

ParameterDescriptionTypeRequiredDefault
encryptKeyPush data encryption key, required when enabling encrypted push use for data decryptionstringno-
loggerLevellog levelLoggerLevelnolark.LoggerLevel.info
logger-LoggerNo-
cacheCacheCacheNo-

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: npm.io

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

ParameterDescriptionTypeRequiredDefault
encryptKeyPush data encryption key, required when enabling encrypted push use for data decryptionstringno-
verificationTokenSecurity verification, it needs to be used when enabling message security verificationstringNo-
loggerLevelLog LevelLoggerLevelNoLoggerLevel.info
logger-LoggerNo-
cacheCacheCacheNo-

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