/* * Copyright (C) 2018 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import { SCBulkResponse, SCBulkRoute, SCLicensePlate, SCNamespaces, SCThings, SCThingType, SCThingUpdateResponse, SCThingUpdateRoute, } from '@openstapps/core'; import {asyncPool} from 'async-pool-native/dist/async-pool'; import * as moment from 'moment'; import {Bulk} from './bulk'; import {Client} from './client'; import {EmptyBulkError, NamespaceNotDefinedError} from './errors'; /* tslint:disable:no-var-requires */ /** * The package @types/uuid unfortunately doesn't expose the browser versions of the hashing functions. * That's why we need to use a little trickery to get to it. */ const v35 = require('uuid/lib/v35'); const sha1Browser = require('uuid/lib/sha1-browser'); const v5 = v35('v5', 0x50, sha1Browser); /* tslint:enable */ export class ConnectorClient extends Client { /** * Instance of multi search request route */ private readonly bulkRoute = new SCBulkRoute(); /** * Instance of multi search request route */ private readonly thingUpdateRoute = new SCThingUpdateRoute(); /** * Make a UUID from a UID and a namespace ID * * *Note: valid namespace IDs are license plates of StApps universities. * See documentation of `NAMESPACES` for valid namespace IDs.* * * @param uid UID to make UUID from * @param namespaceId Namespace ID to use to make UUID */ static makeUUID(uid: string, namespaceId: SCLicensePlate): string { if (typeof SCNamespaces[namespaceId] === 'undefined') { throw new NamespaceNotDefinedError(namespaceId); } return v5(uid.toString(), SCNamespaces[namespaceId]); } /** * Request a bulk transfer to the backend * * This uses the Bulk API supplied by the backend and returns an object that can be used * just like the client itself, while handling the information necessary in bulk transfers. * * @param type StAppsCore thing type * @param source Source identifier (should be unique per actual data source) * @param timeout Timeout in seconds when the bulk should expire */ async bulk(type: SCThingType, source: string, timeout?: number): Promise> { // set default value for timeout to one hour if (typeof timeout !== 'number') { timeout = 3600; } const bulkData = await this.invokeRoute(this.bulkRoute, undefined, { expiration: moment().add(timeout, 'seconds').format(), source: source, type: type, }); return new Bulk(type, this, bulkData); } /** * Index a list of things * * Note that source is optional but is set to `'stapps-api'` in that case. * This will override any previous bulk that you indexed with that source. * * @param things List of things to index * @param source Source of the things * @param timeout Timeout of the bulk in seconds * @see ConnectorClient.bulk */ async index(things: T[], source?: string, timeout?: number): Promise { // check that number of things is not zero if (things.length === 0) { throw new EmptyBulkError(); } // set default source if none is given if (typeof source === 'undefined') { source = 'stapps-api'; } // request a new bulk const bulk = await this.bulk(things[0].type, source, timeout); // add items to the bulk - 5 concurrently await asyncPool(5, things, (thing) => bulk.add(thing)); // close bulk await bulk.done(); } /** * Update an existing StAppsCore thing * * @param thing StAppsCore thing to update */ async update(thing: SCThings): Promise { return this.invokeRoute(this.thingUpdateRoute, { TYPE: encodeURIComponent(thing.type), UID: encodeURIComponent(thing.uid), }, thing); } }