UNPKG

@dataswift/hat-js

Version:

HAT JavaScript SDK for web and Node.js

894 lines (665 loc) 20 kB
# HAT JS ## Overview This library contains all the API calls needed to communicate with the HAT - Authentication. - Fetch Applications and Data Plugs. - Fetch Data Debits. - Read and write data to endpoints. - File API. ## Table of Contents - [Supported environments](#supported-environments) - [Installation](#installation) - [Usage](#usage) - [Importing](#importing) - [Initialisation](#Initialisation) - [Authentication](#authentication) - [Check if HAT exists](#check-if-hat-exists) - [Sign out](#sign-out) - [Get HAT domain](#get-hat-domain) - [Get auth token](#get-auth-token) - [Check if token has expired](#check-if-token-has-expired) - [Generate HAT login URL](#generate-hat-login-url) - [Applications](#applications) - [Get all the apps](#get-all-the-apps) - [Get an application with a specific ID](#get-an-application-with-a-specific-id) - [Get an application status](#get-an-application-status) - [Generate a Data Plug setup URL](#generate-a-data-plug-setup-url) - [HAT Data](#hat-data) - [Write Data](#write-data) - [Update Data](#update-data) - [Delete Data](#delete-data) - [Read Data](#read-data) - [Data Debits](#data-debits) - [Get all the Data Debits](#get-all-the-data-debits) - [Get a Data Debit with a specific ID](#get-a-data-debit-with-a-specific-id) - [Get Data Debit values](#get-data-debit-values) - [File API](#file-api) - [Upload a File](#upload-a-file) - [Mark File as public](#mark-a-file-as-public) - [Mark File as private](#mark-a-file-as-private) - [Update File metadata](#update-file-metadata) - [Search Files](#search-files) - [Delete File](#delete-file) - [Generate content URL](#generate-file-content-url) - [Handling errors](#handling-errors) - [FAQ](#faq) - [License](#license) ## Supported Environments HAT JS supports all the modern browsers. Some older browsers do not support all the features required by HAT JS. If you want to support these browsers you need to load polyfill for [Promise][3]. ## Installation HAT JS is available as **@dataswift/hat-js** package on [npm][4]. **NPM installation** ``` npm install @dataswift/hat-js ``` **CDN Link** Check the latest released version on [npmjs.com][4]. ``` <script src="https://cdn.dataswift.io/libs/hatjs/hat-vX.X.X.min.js"></script> ``` ## Usage ### Importing **ES6** ```javascript import { HatClient } from '@dataswift/hat-js'; ``` **ES5 Modules** ```javascript var HatClient = require("@dataswift/hat-js").HatClient; ``` ### Initialisation **Configuration type:** ```typescript interface HatClientConfig { apiVersion?: string; // Api Version for the HAT. eg. v2.6 hatDomain?: string; // The HAT domain of the user. eg. testing.hubat.net token?: string; // The Application token. secure?: boolean; // If you want to run the HAT locally, you have to modify this field to 'false'. onTokenChange?: Function; } ``` **Example:** ```javascript const config = { token: '<access-token>', apiVersion: 'v2.6', secure: true, onTokenChange: (newToken) => storeNewToken(newToken), }; const hat = new HatClient(config); ``` ## Authentication ### Check if HAT exists | Parameters | Type | | ---------- | ------ | | hatDomain | string | **Example:** ```typescript const hatDomain = 'testing.hubat.net'; try { const res = hat.auth().isDomainRegistered(hatDomain); if (res) { // HAT domain is registered } } catch (e) { // HAT domain doesn't exist } ``` ### Sign out **Example:** ```typescript hat.auth().signOut(); ``` ### Get HAT domain **Example:** ```typescript const res = hat.auth().getHatDomain(); if (res) { // Returns the HAT Domain // eg. testing.hubat.net } ``` ### Get auth token **Example:** ```typescript const res = hat.auth().getToken(); if (res) { // Returns the auth token } ``` ### Check if token has expired **Example:** ```typescript const token = '<auth-token>'; if (hat.auth().isTokenExpired(token)) { // Token has expired } ``` ### Generate HAT login URL **Example:** ```typescript const hatDomain = 'testing.hubat.net'; const appId = 'our-app-id'; const redirect = 'callback-url'; const fallback = 'fallback-url'; const url = hat.auth().generateHatLoginUrl(hatDomain, appId, redirect, fallback); if (url) { window.location.href = url; } ``` ## Applications ### Get all the apps The are 2 ways to fetch applications: With default options: **Example:** ```typescript try { const result = await hat.applications().getAllDefault(); if (result.parsedBody) { // result.parsedBody contains an array of Applications. } } catch (error) { // The request failed... } ``` With custom options: **Example:** ```typescript // In custom options you can specify the following parameters: "ordering", "orderBy", "take", "skip" const options = { ordering: 'descending', orderBy: 'name', }; try { const result = await hat.applications().getAll(options); if (result.parsedBody) { // result.parsedBody contains an array of Applications. } } catch (error) { // The request failed... } ``` ### Get an application with a specific ID **Example:** ```typescript try { const result = await hat.applications().getById('testhatapp'); if (result.parsedBody) { // result.parsedBody contains the application. } } catch (error) { // The request failed... } ``` ### Get an application status | Parameters | Type | | ----------- | ------------------- | | application | [HatApplication][1] | **Example:** ```typescript const status = hat.applications().getAppStatus(application); console.log(status); // Will print one of 'goto' | 'running' | 'fetching' | 'failing' | 'untouched' | 'update'; ``` ### Generate a Data Plug setup URL You can generate a URL in order to redirect the user to setup a Data Plug. | Parameters | Type | | ----------- | ------------------- | | application | [HatApplication][1] | | redirect | string | | fallback | string | **Example:** ```typescript const redirectUrl = 'https://myapp.dataswift.io/app/redirect'; const fallbackUrl = 'https://myapp.dataswift.io/app/fallback'; const generatedUrl = hat.applications().getSetupUrl(application, redirectUrl, fallbackUrl); console.log(generatedUrl); // Will print: https://testing.hubat.net/#/hatlogin?name=facebook&fallback=https://myapp.dataswift.io/app/fallback&redirect=https://facebook.hubofallthings.com%3Fredirect=https://myapp.dataswift.io/app/redirect window.location.href = generatedUrl; // Navigate the user to setup the Data Plug. ``` ## HAT Data **Response type:** ```typescript interface HatRecord<T> { endpoint: string; // eg. locations recordId: string; // A unique record ID of the record. data: T; // The Data } ``` ### Write Data | Parameters | Type | | ---------- | ------ | | namespace | string | | endpoint | string | | body | object | Response - HatRecord\<T> **Example:** ```typescript const namespace = 'testhatapp'; const endpoint = 'locations'; const newLocation = { latitude: '51.01', longitude: '52.12', }; try { const result = await hat.hatData().create(namespace, endpoint, newLocation); if (result.parsedBody) { // result.parsedBody contains a HatRecord with the new data. } } catch (error) { // Failed to write data... } ``` ### Update Data | Parameters | Type | | ---------- | -------------------- | | body | Array<HatRecord\<T>> | Response: - Array<HatRecord\<T>> **Example:** ```typescript const location = { endpoint: 'testhatapp/locations', recordId: 'c14b8f73-157f-40f1-9c77-985d9dea50d2', data: { latitude: '51.01', longitude: '52.12', }, }; location.data.latitude = '49.01'; const records = [location]; // Array of records to update. In this case we have only one. try { const result = await hat.hatData().update(records); if (result.parsedBody) { // result.parsedBody contains an array of HatRecords<T> with the updated location data. } } catch (error) { // Failed to update the records... } ``` ### Delete Data | Parameters | Type | | ---------- | ------------- | | recordIds | Array<string> | Response: - A successful response message **Example:** ```typescript const recordIds = ['c14b8f73-157f-40f1-9c77-985d9dea50d2', 'd52b8f73-151f-40f1-9c77-932d9dea5daa']; try { const result = await hat.hatData().delete(recordIds); if (result.parsedBody) { // result.parsedBody contains a successful response message. // In this case the result will be {message: "All records deleted"}. } } catch (error) { // Failed to delete records... } ``` ### Read Data The are 2 ways to fetch data: With default options: | Parameters | Type | | ---------- | ------ | | namespace | string | | endpoint | string | Response - Array<HatRecord\<T>> **Example:** ```typescript const namespace = 'testhatapp'; const endpoint = 'locations'; try { const result = await hat.hatData().getAllDefault(namespace, endpoint); if (result.parsedBody) { // result.parsedBody contains an Array of HatRecord<Location>. } } catch (error) { // Failed to fetch the data } ``` With custom options: **Example:** ```typescript const options = { ordering: 'descending', orderBy: 'lastUpdated', }; const namespace = 'testhatapp'; const endpoint = 'locations'; try { const result = await hat.hatData().getAll(namespace, endpoint, options); if (result.parsedBody) { // result.parsedBody contains an Array of HatRecords<Location>, ordered by the last updated date. } } catch (error) { // Failed to fetch the data } ``` ## Data Debits ### Get all the Data Debits The are 2 ways to fetch data debits: With default options: Response: - Array<[DataDebit][2]>: An array of Data Debits. **Example:** ```typescript try { const result = await hat.dataDebits().getAllDefault(); if (result.parsedBody) { // result.parsedBody contains an array of Data Debits. } } catch (error) { // Failed to fetch Data Debits. } ``` With custom options: | Parameters | Type | | ---------- | ------ | | options | Object | Response: - Array<[DataDebit][2]>: An array of Data Debits. **Example:** ```typescript // In custom options you can specify the following parameters: "ordering", "orderBy", "take", "skip" const options = { ordering: 'descending', orderBy: 'name', }; try { const result = await hat.dataDebits().getAll(options); if (result.parsedBody) { // result.parsedBody contains an array of Data Debits. } } catch (error) { // Failed to fetch Data Debits. } ``` ### Get a Data Debit with a specific ID | Parameters | Type | | ----------- | ------ | | DataDebitId | string | Response: [DataDebit][2]: The Data Debit object. **Example:** ```typescript try { const result = await hat.dataDebits().getById('your-data-debit-id'); if (result.parsedBody) { // result.parsedBody contains the data debit. } } catch (e) { // Failed to get Data Debit by ID... } ``` ### Get Data Debit values | Parameters | Type | | ----------- | ------ | | DataDebitId | string | Response: - DataDebitValues<T>: The Data Debit values bundle. **Response type:** ```typescript interface DataDebitValues<T> { bundle: { [endpoint: string]: Array<HatRecord<T>> }; } ``` **Example:** ```typescript try { const result = await hat.dataDebits().getDataDebitValues('your-data-debit-id'); if (result.parsedBody) { // result.parsedBody contains the bundle. } } catch (error) { // Failed to get Data Debit values... } ``` ## File API ### Upload a File | Parameters | Type | | ---------- | ------------------------- | | metadata | FileMetadataReq | | file | ArrayBuffer &#124; Buffer | | fileType | string | Response: - FileMetadataRes: The File metadata. **FileMetadataReq type:** ```typescript interface FileMetadataReq { name: string; source: string; tags?: string[]; title?: string; description?: string; dateCreated?: number; lastUpdated?: number; } ``` **Response type:** ```typescript interface FileMetadataRes { fileId: string; name: string; source: string; tags?: string[]; title?: string; description?: string; dateCreated: number; lastUpdated: number; status: FileStatus; contentUrl?: string; contentPublic: boolean; permissions: FilePermissions; } interface FileStatus { status: string; size?: number; } interface FilePermissions { userId: string; contentReadable: boolean; } ``` **Example:** ```typescript try { const meta = { name: 'testFileName', source: 'applicationName', tags: ['iphone', 'photo'], }; const file = []; // ArrayBuffer or Buffer const result = await hat.files().uploadFile(meta, file, 'image/png'); if (result.parsedBody) { // result.parsedBody contains the File metadata. } } catch (error) { // Failed to upload a file... } ``` **Example response:** ```typescript { "fileId": "applicationnametestfilename-5", "name": "testFileName", "source": "applicationName", "dateCreated": "2020-03-04T13:13:37.041Z", "lastUpdated": "2020-03-04T13:13:37.042Z", "tags": [ "iphone", "photo" ], "status": { "status": "New" }, "contentUrl": "https://hubat-net-hatservice-v3ztbxc9civz-storages3bucket-m0gs7co0oyi2.s3.eu-west-1.amazonaws.com/testing.hubat.net/applicationnametestfilename-5?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20190404T131337Z&X-Amz-SignedHeaders=host%3Bx-amz-server-side-encryption&X-Amz-Expires=299&X-Amz-Credential=AKIAICFRCZUZIP4PQ64A%2F20190404%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=6e7b112fca980dafe9935e8c849dfdb7e4e49abb658947909ec35c12370247b2", "contentPublic": false, "permissions": [ { "userId": "de35e18d-147f-4664-8de7-409abf881754", "contentReadable": true } ] } ``` ### Mark a file as public | Parameters | Type | | ---------- | ------ | | fileId | string | Response: - FileMetadataRes: The File metadata. **Example:** ```typescript try { const result = await hat.files().markFileAsPublic('file-id'); if (result.parsedBody) { // result.parsedBody contains the File metadata. } } catch (error) { // Failed to mark a file as public... } ``` ### Mark a file as private | Parameters | Type | | ---------- | ------ | | fileId | string | Response: - FileMetadataRes: The File metadata. **Example:** ```typescript try { const result = await hat.files().markFileAsPrivate('file-id'); if (result.parsedBody) { // result.parsedBody contains the File metadata. } } catch (error) { // Failed to mark a file as private... } ``` ### Update File metadata | Parameters | Type | | ---------- | ------ | | fileId | string | Response: - FileMetadataRes: The File metadata. **Example:** ```typescript try { const meta = { name: 'differentFileName', source: 'applicationName', tags: ['iphone', 'photo', 'extra tag'], }; const result = await hat.files().updateFileParameters('file-id', meta); if (result.parsedBody) { // result.parsedBody contains the File metadata. } } catch (error) { // Failed to update file's metadata... } ``` ### Search Files | Parameters | Type | | ---------- | --------------- | | metadata | FileMetadataReq | Response: - Array<FileMetadataRes>: An array of File metadata. **Example:** ```typescript try { const meta = { name: '', source: 'applicationName', tags: ['iphone', 'photo'], }; const result = await hat.files().searchFiles(meta); if (result.parsedBody) { // result.parsedBody contains an array of File metadata. // The result contains all the files with the tags "iphone" and "photo" with the same source. } } catch (error) { // Failed to search for Files... } ``` ### Delete File | Parameters | Type | | ---------- | ------ | | fileId | string | Response: - FileMetadataRes: The File metadata. **Example:** ```typescript try { const result = await hat.files().deleteFile('file-id'); if (result.parsedBody) { // result.parsedBody contains the File metadata. } } catch (error) { // Failed to delete the File... } ``` ### Generate File content URL | Parameters | Type | | ---------- | ------ | | fileId | string | Response: - string: The URL to the content. **Example:** ```typescript const url = hat.files().getFileContentUrl('file-id'); console.log(url); // will print: // https://<hat-url>/api/v2.6/files/content/<file-id>> // The file will be available in this URL. // Note: if the file is private, will need to pass the authentication token as // a header to access the file. ``` ## Handling Errors HAT JS error response: ```javascript { cause: string, // The cause of the error. message: string, // The message of the error. status: number // When the cause is HAT_API_EXCEPTION you can handle the HTTP response code. } ``` **Error Codes:** | Cause | Message | Error definitions | | :--------------------- | :-------------------------------- | :----------------------------------------------- | | HAT_CLIENT_VALIDATION | HAT_DOMAIN_IS_UNDEFINED | `hatDomain` parameter is required | | HAT_CLIENT_VALIDATION | RECORD_IDS_MISSING | `recordsIds` array parameter is empty or null | | HAT_CLIENT_VALIDATION | APPLICATION_NOT_SUPPORTED | application is not supported | | HAT_CLIENT_VALIDATION | NOT_AUTHENTICATED | client is not authenticated | | HAT_TOKEN_VALIDATION | HAT_TOKEN_IS_NOT_VALID_OR_EXPIRED | Authentication token has expired or is not valid | | HAT_TOKEN_VALIDATION | HAT_TOKEN_IS_UNDEFINED | Authentication token is undefined | | HAT_TOKEN_VALIDATION | HAT_TOKEN_FAILED_TO_DECODE | Authentication token has failed to decode | | HAT_API_EXCEPTION | HTTP_RESPONSE_FAILED | HTTP response failed | | HAT_FILE_API_EXCEPTION | FILE_IS_UNDEFINED | File parameter is undefined | | HAT_FILE_API_EXCEPTION | FAILED_TO_FETCH_METADATA | Failed to fetch HAT File metadata | | HAT_FILE_API_EXCEPTION | FILE_FAILED_TO_UPLOAD | Failed to upload the file to AWS | ## FAQ ### Why do I get CORS errors? - Make sure that you set the localhost port to 3000, and you use a testing HAT. eg. testing.hubat.net - You are using a production HAT. Localhost is not allowed origin for the production HATs. - The secure property in the configuration is set to `false`. Only if you want to run the HAT locally, you have to set this field to `false`. Otherwise, for the test environment it must be `true`. ### I'm getting a 403: Access Denied, how do I fix it? - Make sure you use the correct App ID and namespace. Note: The testing App ID has a `-dev` suffix even if it's not displayed correctly in the Developer Portal. - In order for an application to fetch the list of applications or a specific application, it needs an extra permission. - You are trying to write data to a different namespace. ### Why do I get 400: Bad request? - Make sure you use the `secure` configuration correctly. Only if you want to run the HAT locally, you have to set this field to `false`. - The HAT doesn't allow to create duplicate data. ## License This work is licensed under the Mozilla Public License Version 2.0. Please read the [LICENSE][5] file for further details. [1]: https://docs.dataswift.io/guides/application-management/01-application-information-format [2]: https://docs.dataswift.io/guides/data-debits/01-data-debit-proposal [3]: https://github.com/taylorhakes/promise-polyfill [4]: https://www.npmjs.com/package/@dataswift/hat-js [5]: https://github.com/Hub-of-all-Things/hat-js-sdk/master/dev/LICENSE.md