fiftyone.pipeline.cloudrequestengine
Version:
Cloud request engine for the 51Degrees Pipeline API
228 lines (205 loc) • 7.68 kB
JavaScript
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2025 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */
const path = require('path');
const http = require('http');
const MockRequestClient = require('./classes/mockRequestClient');
const CloudRequestEngine = require('../cloudRequestEngine');
const CloudRequestError = require('../cloudRequestError');
// CloudEngine does not use relative path to import module so update the module
// lookups path here via setting of NODE_PATH environment variable.
const parentDir = path.resolve(__dirname, '../..');
process.env.NODE_PATH = parentDir + path.delimiter + process.env.NODE_PATH;
require('module').Module._initPaths();
const pipelineBuilderPath = path.resolve(__dirname, '../../fiftyone.pipeline.core/pipelineBuilder');
const PipelineBuilder = require(pipelineBuilderPath);
// Invalid resource key
const testResourceKey = 'AAAAAAAAAAAA';
/**
* Test cloud request engine adds correct information to post request
* and returns the response in the ElementData
*/
test('process', done => {
const jsonResponse = { device: { value: 51 } };
const client = new MockRequestClient({ json: jsonResponse });
const engine = new CloudRequestEngine({
resourceKey: testResourceKey,
requestClient: client
});
const pipeline = new PipelineBuilder()
.add(engine)
.build();
const data = pipeline.createFlowData();
data.process().then((processedData) => {
const result = processedData.getFromElement(engine).cloud;
expect(result).toBe(JSON.stringify(jsonResponse));
const jsonObj = JSON.parse(result);
expect(jsonObj.device.value).toBe(51);
done();
});
});
/**
* Verify that the CloudRequestEngine can correctly parse a
* response from the accessible properties endpoint that contains
* meta-data for sub-properties.
*/
test('sub properties', () => {
const properties = {
Products: {
device: {
DataTier: 'CloudV4TAC',
Properties: [
{
Name: 'IsMobile',
Type: 'Boolean',
Category: 'Device'
},
{
Name: 'IsTablet',
Type: 'Boolean',
Category: 'Device'
}
]
},
devices: {
DataTier: 'CloudV4TAC',
Properties: [
{
Name: 'Devices',
Type: 'Array',
Category: 'Unspecified',
ItemProperties: [
{
Name: 'IsMobile',
Type: 'Boolean',
Category: 'Device'
},
{
Name: 'IsTablet',
Type: 'Boolean',
Category: 'Device'
}
]
}
]
}
}
};
const client = new MockRequestClient({ properties });
const engine = new CloudRequestEngine({
resourceKey: testResourceKey,
requestClient: client
});
return engine.ready().then(engine => {
expect(Object.entries(engine.flowElementProperties).length).toBe(2);
const deviceProperties = engine.flowElementProperties.device;
expect(Object.entries(deviceProperties).length).toBe(2);
propertiesContainName(deviceProperties, 'IsMobile');
propertiesContainName(deviceProperties, 'IsTablet');
const devicesProperties = engine.flowElementProperties.devices;
expect(devicesProperties).not.toBeUndefined();
expect(devicesProperties).not.toBeNull();
expect(Object.entries(devicesProperties).length).toBe(1);
propertiesContainName(devicesProperties.devices.itemproperties, 'IsMobile');
propertiesContainName(devicesProperties.devices.itemproperties, 'IsTablet');
});
});
/**
* Test cloud request engine handles JSON errors from the cloud service
* as expected.
* An exception should be thrown by the cloud request engine
* containing the errors from the cloud service in the JSON object.
*/
test('validate error handling JSON errors', async () => {
const errorMessage = 'an error message returned by the cloud service';
const client = new MockRequestClient({
resourceKey: 'resourceKey',
error: '{ "status":"400", "errors": ["' + errorMessage + '"] }'
});
const e = () => {
return new CloudRequestEngine({
resourceKey: testResourceKey,
requestClient: client
}).ready();
};
await expect(e()).rejects.toEqual([new CloudRequestError(errorMessage)]);
});
/**
* Test cloud request engine handles unavailability of cloud service
* as expected.
* An exception should be thrown by the cloud request engine
* containing the errors from the cloud service in the JSON object.
*/
test('validate case when cloud unavailable', (done) => {
const SERVER_PORT = 3000;
const errorMessage = 'Cloud server internal error';
const server = http.createServer((req, res) => {
const client = new MockRequestClient({
resourceKey: 'resourceKey',
error: '{ "status":"500", "errors": ["' + errorMessage + '"] }'
});
const requestEngine = new CloudRequestEngine({
resourceKey: testResourceKey,
requestClient: client
});
requestEngine.ready().then(engine => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(engine.device));
}).catch(error => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(error));
});
}).listen(SERVER_PORT, () => {
const options = {
hostname: 'localhost',
port: SERVER_PORT,
path: '/',
method: 'GET'
};
const req = http.request(options, (res) => {
let response = '';
res.on('data', (chunk) => {
response += chunk;
});
res.on('end', () => {
const error = JSON.parse(response)[0];
expect(error.errorMessage).toBe(errorMessage);
expect(error.name).toBe('CloudRequestError');
server.close(); // Close the server after the request is complete.
done();
});
});
req.end();
});
});
/**
* Check if Properties contains name
*
* @param {object} properties Object of properties
* @param {string} name Name of the property
*/
function propertiesContainName (properties, name) {
expect(properties[name.toLowerCase()]).not.toBeUndefined();
expect(properties[name.toLowerCase()]).not.toBeNull();
expect(properties[name.toLowerCase()].name).toBe(name);
}