RICADO Gen 4 API JS Client

RICADO Gen 4 API JS Client

npm (scoped)

RICADO Gen 4 API JS Client

RICADO Gen 4 API client for NodeJS and Browsers

  • Written in modern JavaScript. Tested for NodeJS 16+ and Browsers
  • Includes Typings for Typescript
  • Complete support for the RICADO Gen 4 API - OpenAPI Documentation
  • Perfect symmetry: JS method signatures match the Web API Docs
  • All methods return a Promise
  • The Authentication Token is automatically handled on all methods

NOTE: All examples written in this README use the NodeJS (ES5) Specification.

Documentation

Documentation for the latest Stable Release can be found here: ricado-group.github.io/js-api-client

Install

npm install @ricado/api-client

The src folder contains ESNext code.

The dist folder contains ES6 transpiled code and is the default export for this module.

Usage

@ricado/api-client mirrors the published API Docs. All interaction is through stateless functions in Controllers.

const api = require('@ricado/api-client');

api.initialize();

api.userAccountLogin('user@ricado.co.nz', 'abcd1234')
.then(jwt => console.log(jwt))
.catch(error => console.log(error));

api.Controllers.UserAccountController.getCurrent()
.then(userAccount => console.log("Logged in as `" + userAccount.firstName + " " + userAccount.lastName))
.catch(error => console.log(error));

Using async/await in Node 8.x or newer:

const api = require('@ricado/api-client');

;(async function() {
    var userAccount = await api.Controllers.UserAccountController.getCurrent();

    console.log("Hello! I'm " + userAccount.firstName);
})();

NOTE: A Promise will only resolve if there was no issue that needs dealing with. See Error Handling below for examples.

Importing and Initializing

Your first step will be to import the API Client module and Initialize the API before any use.

const api = require('@ricado/api-client');

api.initialize();

The api.initialize() method can also support an existing JSON Web Token. This is useful if you are persisting the JSON Web Token in your app and would like to skip the Login (Generate a new JWT) process.

const api = require('@ricado/api-client');

let jwt = myFunctionThatGetsTheJWT();

api.initialize(jwt);

Performing a Login

You'll need to Login (Generate a new JWT) before you can make any API Calls (except for /ping of course!)

To Login a User Account:

let email = 'name@ricado.co.nz';
let password = 'abcd1234';

api.userAccountLogin(email, password)
.then((jwt) => {
    console.log("Signed In!");
    // NOTE: You may wish to persist this jwt somewhere in your app for future use!
})
.catch((error) => {
    console.log("Failed to Sign In!");
    console.log(error);
});

To Login an API Account:

let key = 'myapikey';
let secret = 'myapisecret';

api.apiAccountLogin(key, secret)
.then((jwt) => {
    console.log("Ready to go!");
    // NOTE: You may wish to persist this jwt somewhere in your app for future use!
})
.catch((error) => {
    console.log("Failed to Sign In!");
    console.log(error);
});

Performing a Logout

If you wish to Logout (Destroy existing JWT) there's one simple method to call.

NOTE: Calling this method will Destroy the Token on the API Servers and will clear the locally stored JWT (api.JWT)

api.logout()
.then(result => console.log("Done!"))
.catch(error => console.log(error));

Locking and Unlocking the JSON Web Token

The API makes use of a concept called "Token Locking" to reduce the risk of a JSON Web Token being copied and used by another party. The API will (at it's own discretion) automatically Lock the JSON Web Token after a period of inactivity. It is then required to Unlock the JSON Web Token before any API calls can be successfully resumed. If you make an API call without first unlocking the token, an UnauthorizedError will be thrown with a message about the Token being Locked.

NOTE: These methods can only be used on Tokens for a User Account. Tokens for an API Account will never be locked.

If the User has a Pin Code set, unlock using that Pin Code

let pinCode = 12345;

api.pinCodeUnlock(pinCode)
.then(result => console.log("Unlocked!")) // Remember when the return type is `Promise<boolean>` it will always be `true`!
.catch(error => console.log(error));

If the User doesn't have a Pin Code set, unlock using a Password

let password = "abcd1234";

api.passwordUnlock(password)
.then(result => console.log("Unlocked!"))
.catch(error => console.log(error));

Lock is called without any parameters

api.lock()
.then(result => console.log("Locked!"))
.catch(error => console.log(error));

Retrieving a Single Model

A Single Model can be retrieved by calling the stateless getOne(id) function on a Controller. The Single Model is passed back in the Promise.then method.

let siteId = 1;

api.Controllers.SiteController.getOne(siteId)
.then(site => console.log("Got My Site: " + site.name))
.catch(error => console.log(error));

Updating a Single Model

Models can be updated by calling the stateless update(id, properties) function on a Controller. When you Update a Single Model, the Updated version of that same Model is passed back in the Promise.then method.

let siteId = 1;

api.Controllers.SiteController.update(siteId, {name: "My New Site Name"})
.then(site => console.log("The Name is now: " + site.name))
.catch(error => console.log(error));

Deleting a Single Model

Models can be deleted by calling the stateless delete(id) function on a Controller. When you Delete a Single Model, the Promise.then method will be called if successful, otherwise the Promise.catch method will be called with the appropriate Error.

let siteId = 1;

api.Controllers.SiteController.delete(siteId)
.then(result => console.log("Deleted!"))
.catch(error => console.log(error));

Retrieving a Collection of Models

A Collection of Models (all) can be retrieved by calling the stateless getAll() function on a Controller. The Model Collection is passed back in the Promise.then method.

api.Controllers.SiteController.getAll()
.then((sites) => {
    sites.forEach((site) => {
        console.log("Got another Site: " + site.name);
    });
})
.catch(error => console.log(error));

Creating a new Models Object

A new Model Object can be created by calling the stateless create(json) function on a Controller. The newly created Model Object is passed back in the Promise.then method.

api.Controllers.SiteController.create({name: "My Site Name", companyId: "xyz-xyz-xyz"})
.then(site => console.log("Got my new Site ID: " + site.id))
.catch(error => console.log(error));

NOTE: Refer to the API Documentation for the structures required to create each type of Model.

Error Handling

This API Client provides additional Error Classes that extend the built-in Error type. All Errors will at minimum define Error.message. You can also make use of Error.stack in Node and on most modern browsers.

Errors:

  • BadRequestError Used for Response Code 400
  • UnauthorizedError Used for Response Code 401
  • ForbiddenError Used for Response Code 403
  • NotFoundError Used for Response Code 404
  • NotAllowedError Used for Response Code 405
  • ServerError Used for Response Code 500
  • NetworkError Wraps up Errors thrown by fetch

Promise Example:

api.Controllers.UserAccountController.deleteCurrent()
.then(result => result ? console.log("Success!") : console.log("Uh-oh!"))
.catch((error) => {
    // This Error should be one of the Above Errors or a generic `Error` type.
    console.log("Error Message: " + error.message);
    console.log("Error Stack: " + error.stack);
});

async/await Example:

async function deleteApiAccount(id) {
    try
    {
        var result = await api.Controllers.ApiAccountController.delete(id);

        result ? console.log("Success!") : console.log("Uh-oh!");
    }
    catch (error)
    {
        // This Error should be one of the Above Errors or a generic `Error` type.
        console.log("Error Message: " + error.message);
        console.log("Error Stack: " + error.stack);
    }
}

Example: Retrieving a Collection of Users

api.Controllers.UserAccountController.getAll()
.then((users) => {
    // The `users` variable will always be set and will at minimum be an empty Array `[]`. There isn't a need to check that users is defined and that is is in-fact an Array.
    users.forEach(user => console.log("Got " + user.firstName + " " + user.lastName));
})
.catch((error) => {
    /**
     * `.catch(error)` will always be called if something went wrong
     * 
     * - A network error occured
     * - You might not be logged in or have a locked token
     * - The server had an *issue*
     */
    console.log(error);
});

Example: Attempting to Retrieve a User that doesn't exist

api.Controllers.UserAccountController.getOne('an-id-that-does-not-exist')
.then((user) => {
    // You'll never make it to here as the User does not exist. If the user did exist however, you can always be confident that the `user` variable will be set and will at bare minimum be a `UserAccountModel` object.
    console.log("His name is " + user.firstName + " " + user.lastName);
})
.catch((error) => {
    /**
     * In this example, apart from something like a `NetworkError` we would expect to see a `NotFoundError` to be passed back.
     */
    console.log(error);
});

Example: Attempting to Delete a User that doesn't exist

api.Controllers.UserAccountController.delete('an-id-that-does-not-exist')
.then((result) => {
    // You'll never make it to here as the User does not exist. If the user did exist however, you can always be confident that the `result` variable will be set and will always be `true` meaning `result === true` will always be the case!
})
.catch((error) => {
    /**
     * In this example, apart from something like a `NetworkError` we would expect to see a `NotFoundError` to be passed back.
     */
    console.log(error);
});

Testing

For Node:

npm test

For Browsers:

Yet to be supported!!

Contributing

Currently this code-base is mostly generated from the PHP API and then hand-edited as required. In the future, we hope this process of generation will happen automatically with the use of a local npm script!

In the mean-time, if you wish to contribute, please either create a new branch (if you have permission) or fork this repository and submit a pull request.

TODO

  • Support generating this README.md from a Template so we can inject some code examples and types automatically!