Coder Social home page Coder Social logo

ringcentral-extensible's Introduction

RingCentral Extensible SDK

Build Status

RingCentral Extensible is a SDK with a tiny core and lots of extensions. It is an endeavour to get rid of bloated SDK. You install extensions on demand.

Getting help and support

If you are having difficulty using this SDK, or working with the RingCentral API, please visit our developer community forums for help and to get quick answers to your questions. If you wish to contact the RingCentral Developer Support team directly, please submit a help ticket from our developer website.

Installation

yarn add @rc-ex/core

Then you should be able to import the SDK like this:

import RingCentral from '@rc-ex/core';

Usage

You can also find lots of useful code snippets from test cases.

This SDK supports extensions. You can enable features by installing extensions.

If you want to add features to this SDK, create an extension.

Logging

The logging implementation copies AWS SDK logging.

To enable logging:

RingCentral.config.logger = console;

Or you could use a third-party logger:

import winston from 'winston';

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      format: winston.format.simple(),
    }),
  ],
});
RingCentral.config.logger = logger;

Sample log entries:

[3/16/2022, 9:58:47 AM HTTP GET 200 OK] https://platform.devtest.ringcentral.com /restapi/v1.0/account/~/extension/~/call-log
[3/16/2022, 5:47:53 PM HTTP POST 401 Unauthorized] https://platform.ringcentral.com /restapi/oauth/token

A demo application printing logs to console.

Binary content downloading

Some sample code for binary content downloading may not work.

Because RingCentral is gradually migrating binary content to CDN such as media.ringcentral.com.

For example, to download the attachment of a fax:

// `message` is the fax message object
const r = await rc.get(message.attachments[0].uri, undefined, { responseType: 'arraybuffer' });
const content = r.data;

The following does NOT work:

// `message` is the fax message object
const content = await rc
  .restapi()
  .account()
  .extension()
  .messageStore(message.id)
  .content(message.attachments[0].id)
  .get();

Rule of thumb

But not all binary content has been migrated to CDN. If the resource to download provides you with a CDN uri, use that CDN uri. If there is no CDN uri provided, construct the uri as sample code shows.

For maintainers

Regenerate code using latest swagger spec

Please refer to the RingCentral Code Generator project.

Test

yarn reset && yarn compile && yarn test

Test one test case

t=auto-recover yarn test

Publish

Update version number in packages/core/src/Rest.ts

yarn lerna publish from-package

By default lerna check git tag to determine which packages to publish. from-package will make lerna check npmjs.com instead.

As I just tried, it works without from-package option.

NPM 2FA

I don't know how to make it work with lerna and I have to disable it via npmjs.com GUI: I disabled "Require two-factor authentication for write actions".

Add dependency

yarn workspace @rc-ex/debug add ramda

Todo

  • Extension to refresh token
  • Extension to do pagination

ringcentral-extensible's People

Contributors

brutalbeard avatar grokify avatar ruleechen avatar tylerlong avatar u9520107 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ringcentral-extensible's Issues

Unable to login with email

So it would seem that logging in to RC with email instead of phone number will not work. Login is successful via Postman using the exact same credentials.

The code

      const token = await this.client.authorize({
        username: '[email protected]',
        password: 'somepass',
      });

Request

{
  "method": "post",
  "baseURL": "https://platform.devtest.ringcentral.com",
  "url": "/restapi/oauth/token",
  "data": "grant_type=password&username=test.user%40domain.com&extension=&password=somepass&client_id=123abc",
  "headers": {
    "Accept": "application/json, text/plain, */*",
    "Content-Type": "application/x-www-form-urlencoded",
    "X-User-Agent": "Unknown/0.0.1 ringcentral-extensible/core/0.6.0",
    "User-Agent": "axios/0.20.0",
    "Content-Length": 126
  }
}

Response

{
  "data": {
    "error": "invalid_grant",
    "errors": [
      {
        "errorCode": "OAU-140",
        "message": "Invalid resource owner credentials"
      }
    ],
    "error_description": "Invalid resource owner credentials"
  },
  "status": 400,
  "statusText": "Bad Request",
  "headers": {
    "server": "nginx",
    "date": "Wed, 18 Nov 2020 12:50:15 GMT",
    "content-type": "application/json;charset=utf-8",
    "content-length": "199",
    "connection": "keep-alive",
    "x-application-context": "application:8080",
    "content-language": "en",
    "rcrequestid": "9b190bec-299c-11eb-982d-005056bb86e2",
    "pragma": "no-cache",
    "cache-control": "no-store",
    "aceroutingkey": "sjc11-c01-ace11.cddc5192-27f9-11eb-af02-005056bb6e9b",
    "routingkey": "SJC11P01"
  }
}

@rc-ex/core 0.6.0
Node 12.16.1

Install WebSocketExtension

It costs about 1 minute to install WebSocketExtension when the relevant web socket server is not reachable. As part of the instructure that is too long for the init phase.

As investigated. The key point is this line which waits for the response of ws server with 60s timeout.

PubNub JSON structure issue

Cannot convert a Subscription object to JSON:

setup PubNub
TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Subscription'
    |     property 'pne' -> object with constructor 'PubNubExtension'
    |     property 'subscriptions' -> object with constructor 'Array'
    --- index 0 closes the circle
    at JSON.stringify (<anonymous>)
    at /Users/tyler.liu/src/ts/glip-bot-webhook-verification/src/setup-pubnub.ts:29:20
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

POST without body doesn't work

POST without body doesn't work

rc.restapi().account().telephony().conference().post();

example: https://github.com/ringcentral/ringcentral-extensible/blob/master/packages/core/samples.md#createconferencecallsession

UnhandledPromiseRejectionWarning: Error: HTTP 415 Unsupported Media Type

    Response:
    {
  "data": {
    "errors": [
      {
        "errorCode": "TAS-114",
        "message": "Unsupported content-type. Accept: application/json"
      }
    ]
  },
  "status": 415,
  "statusText": "Unsupported Media Type",
  "headers": {
    "server": "nginx",
    "date": "Fri, 13 May 2022 22:29:06 GMT",
    "content-type": "application/json",
    "content-length": "149",
    "connection": "close",
    "x-rate-limit-group": "heavy",
    "x-rate-limit-limit": "30",
    "x-rate-limit-remaining": "29",
    "x-rate-limit-window": "60",
    "routingkey": "SJC01P07",
    "rcrequestid": "19aa5b54-d30c-11ec-bcb7-0050568cbe06"
  }
}

    Request:
    {
  "method": "post",
  "baseURL": "https://platform.ringcentral.com",
  "url": "/restapi/v1.0/account/~/telephony/conference",
  "headers": {
    "Accept": "application/json, text/plain, */*",
    "Content-Type": "application/x-www-form-urlencoded",
    "X-User-Agent": "Unknown/0.0.1 ringcentral-extensible/core/0.12.4",
    "Authorization": "Bearer U0pDMDFQMDdQQVMwMHxBQUJrOUVTWlV2c2hvY0dkYTVSbm1HbUh6RV90cS1udTBUb0FUYXNvdTNCQjdUaHZRMnJHMnJja21BZ0dIdTZERkJGdDk0ZDZBOVFxTEhOclpuYWR3WFI5ZzA1MlM1RkpkU3hlZHBRTThPQ1ZONjYyUU55LTBVSXozQ2Y0Y25HWktPZGdhMDdrVVdrdDVtZ09KUXBTckhEb3NSbkNyMUY1Y00wLVd3bWtNckk3ZU9DY1dnN1ZlNkR1T05HVXh6YmhYMmI4R1hhVnxGOHhrc2d8RDBreVI3aXdiNUpSM241SUlSemJqQXxBUQ",
    "User-Agent": "axios/0.26.1"
  }
}

The following works:

rc.post('/restapi/v1.0/account/~/telephony/conference', {});

Feedbacks about WS extension

  • the current implementation supports "wsc" section only for messages of the ConnectionDetails type. But it should support it for any incoming message types.
  • the reconnect() is not executed automatically and has to be executed by clients after receiving Events.autoRecoverError. I suppose it is suitable to add some default logic in case of failed automatic recovery.
  • recovery should not be retried for 60 seconds if there is no saved wsc token. From my point of view, It should fail asap and initiate reconnect().
  • "autoRecovery" stops working after 60 seconds of client disconnection. And the new connection doesn't support auto-recovery. I could miss something but looks like this. Please, correct me if I'm wrong.
  • there is a lack of something like Events.autoRecoverError for the case when we tried to recovered and received "recoveryState: Failed" in the ConnectionDetails message. Events.autoRecoverError could mean any problem including network problems. So it could be retried (e.g. 60 seconds) and doesn't require any special actions. But if the client received "recoveryState : Failed", it should not retry recovery and must reconnect, re-read all possibly stale info, and re-subscribe.

WebSocket connection lost when recover with cached token

The feature of recover from cached token works well for single page. When we open two or more pages, each page try to recover from the cached token. The pages will try to launch their own websocket connection.

Then it comes to this issue:

Each page will call the below api, but calling this api will disconnect other pages worksocket connection as my test.

const r = await this.rc.post('/restapi/oauth/wstoken');

Version 0.9.9 errors on console

Found some new errors on console log after upgrade to version 0.9.9

TypeError: Cannot read property 'readyState' of undefined
TypeError: Cannot read property 'close' of undefined


image
image
image
image
image

Feature request: Reconnect after lost connection

The ws connection will lost such as when system resume from sleep. It's ok since the network is changed. But we need a mechanism to recover the connection when disconnected. It's better to have the mechanism integration with @rc-ex/ws module. Just like what we have in webphone sdk.

Fix CI

Travis CI is closing its service.

Cannot read properties of undefined (reading 'addEventListener')

TypeError: Cannot read properties of undefined (reading 'addEventListener')
at Subscription.setupWsEventListener (/Users/alexpower/Code/Projects/teleingest/node_modules/@rc-ex/ws/lib/subscription.js:22:21)
at new Subscription (/Users/alexpower/Code/Projects/teleingest/node_modules/@rc-ex/ws/lib/subscription.js:19:14)
at WebSocketExtension.subscribe (/Users/alexpower/Code/Projects/teleingest/node_modules/@rc-ex/ws/lib/index.js:315:30)
at Subscription. (/Users/alexpower/Code/Projects/teleingest/node_modules/@ringcentral/subscriptions/lib/Subscriptions.js:89:58)
at step (/Users/alexpower/Code/Projects/teleingest/node_modules/@ringcentral/subscriptions/lib/Subscriptions.js:48:23)
at Object.next (/Users/alexpower/Code/Projects/teleingest/node_modules/@ringcentral/subscriptions/lib/Subscriptions.js:29:53)
at fulfilled (/Users/alexpower/Code/Projects/teleingest/node_modules/@ringcentral/subscriptions/lib/Subscriptions.js:20:58)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

I have the above error when running my subscription... This was something that was working just over a week ago and has only recently arose.

below is my implementation

const { SDK } = require("@ringcentral/sdk");
const { Subscriptions } = require("@ringcentral/subscriptions");

const logger = require('pino')(
    {
        messageKey: "message",
        formatters: {
            level: (label) => {
                return { level: label };
            },
        }
    }
);

const sdk = new SDK({
    server: process.env.RC_ENV !== "production" ? SDK.server.sandbox : SDK.server.production,
    clientId: process.env.RC_CLIENT,
    clientSecret: process.env.RC_SECRET
});

const platform = sdk.platform();
const subscriptions = new Subscriptions({
    sdk: sdk,
});

const main = async () => {
    // login
    await platform.login({
        jwt: process.env.RC_JWT,
    });

    // subscribe
    const subscription = subscriptions.createSubscription();
    subscription.on(subscription.events.notification, (evt) => {
        console.log(JSON.stringify(evt, null, 2));
    });
    await subscription.setEventFilters(['/restapi/v1.0/account/~/telephony/sessions']).register();

    // trigger events
    subscription
        .register()
        .then(() => logger.info("Subscribed to events."))
        .catch((err) => {
            logger.error({ err }, err.message)
        });
};

main();

Provide util method / extension for pagination

Implementation 1

let page = 1;
  let totalPage = 1;
  while (page <= totalPage) {
    const r = await rc
      .restapi()
      .account()
      .phoneNumber()
      .list({perPage: 1000, page});
    phoneNumbers = [
      ...phoneNumbers,
      ...r.records!.filter(record => numbers.has(record.phoneNumber ?? '')),
    ];
    totalPage = r.paging!.totalPages!;
    page += 1;
  }

Implementation 2

let result: GetMessageInfoResponse[] = [];
  let page = 1;
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const r = await messageStore.list({
      messageType: ['SMS'],
      perPage: 1000,
      dateFrom: date.toISOString(),
      page,
    });
    result = [...result, ...r.records!];
    if (r.navigation?.nextPage === undefined) {
      break;
    }
    page += 1;
  }
  return result;

Websocket error on console log

rest.js:40
Uncaught (in promise) DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
at WebSocketExtension.request (file:///D:/rc/git/integration-apps/packages/lyncintegration/app/views/main/index.js:85407:13)
at Subscription.subscribe (file:///D:/rc/git/integration-apps/packages/lyncintegration/app/views/main/index.js:85485:49)
at WebSocketExtension.connect (file:///D:/rc/git/integration-apps/packages/lyncintegration/app/views/main/index.js:85319:36)
at async WebSocketExtension.recover (file:///D:/rc/git/integration-apps/packages/lyncintegration/app/views/main/index.js:85250:13)

image

Attaching images does not work as expected

Calling any endpoint that needs certain parameters (for instance image) does not work as currently FormData is hardcoded to use the name of 'attachement'. This change was made for a test case of IVR audio upload, however this will now break certain endpoints such as updating a profile picture which is looking for form-data with the name of 'image'.

https://github.com/ringcentral/ringcentral-extensible/blob/master/packages/core/src/FormData.ts#L40 Is the incorrect line; Taking the commit from Jan 9th fixes the issue with the form data using a hardcoded name.

Maybe this should be a new field under the Attachment type to control what type the formdata is;

I got around this issue by taking a copy of the Utils code to create the formdata

ExtensionInfoEvents.ExtensionInfoEventsBody.hints outdated

There are some issues with this definition:

  1. Link to the current spec in README.md is outdated
  2. According to the spec:
hints:
        type: array
        description: Returned for 'Update' event type only
        items:
          type: string
          enum:
            - AccountSettings
            - AccountStatus
            - AnsweringRules
            - CompanyNumbers
            - DialingPlan
            - ExtensionInfo
            - Features
            - Limits
            - Permissions
            - ProfileImage
            - VideoConfiguration

However, the in the current release (0.8.6 of ringcentral-extensible):

hints?: ('LimitsFeatures' | 'AccountSettings' | 'CompanyNumbers' | 'AccountStatus' | 'DialingPlan' | 'Permissions' | 'ProfileImage' | 'ExtensionInfo' | 'VideoConfiguration')[];

Limits and Features are combined in the current release, but are separated in the spec.

monorepo setup

Currently the ability to use the default ringcentral sdk is built as an extension in this lib. I find the management of dependency packages a bit problematic.

I'd suggest converting this into a monorepo setup and potentially releasing these extensions as separate packages under the same namespace.
This way these extension packages can have stronger control over defining dependencies.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.