@openeo/js-commons
Advanced tools
Comparing version 0.5.0-alpha.1 to 1.0.0-rc.1
2345
dist/main.js
(function webpackUniversalModuleDefinition(root, factory) { | ||
if(typeof exports === 'object' && typeof module === 'object') | ||
module.exports = factory((function webpackLoadOptionalExternalModule() { try { return require("ajv"); } catch(e) {} }())); | ||
module.exports = factory(); | ||
else if(typeof define === 'function' && define.amd) | ||
define(["ajv"], factory); | ||
define([], factory); | ||
else { | ||
var a = typeof exports === 'object' ? factory((function webpackLoadOptionalExternalModule() { try { return require("ajv"); } catch(e) {} }())) : factory(root["ajv"]); | ||
var a = factory(); | ||
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; | ||
} | ||
})(window, function(__WEBPACK_EXTERNAL_MODULE__12__) { | ||
})(window, function() { | ||
return /******/ (function(modules) { // webpackBootstrap | ||
@@ -94,3 +94,3 @@ /******/ // The module cache | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 7); | ||
/******/ return __webpack_require__(__webpack_require__.s = 2); | ||
/******/ }) | ||
@@ -100,42 +100,61 @@ /************************************************************************/ | ||
/* 0 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
/***/ (function(module, exports) { | ||
const compareVersions = __webpack_require__(9); | ||
class Utils { | ||
var Utils = { | ||
/** | ||
* Checks whether a variable is a real object or not. | ||
* | ||
* This is a more strict version of `typeof x === 'object'` as this example would also succeeds for arrays and `null`. | ||
* This function only returns `true` for real objects and not for arrays, `null` or any other data types. | ||
* | ||
* @param {*} obj - A variable to check. | ||
* @returns {boolean} - `true` is the given variable is an object, `false` otherwise. | ||
*/ | ||
static isObject(obj) { | ||
return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj)); | ||
} | ||
compareVersion(v1, v2) { | ||
try { | ||
return compareVersions(v1, v2); | ||
} catch (error) { | ||
return null; | ||
} | ||
}, | ||
/** | ||
* Checks whether a variable is numeric. | ||
* | ||
* Numeric is every string with numeric data or a number, excluding NaN and finite numbers. | ||
* | ||
* @param {*} n - A variable to check. | ||
* @returns {boolean} - `true` is the given variable is numeric, `false` otherwise. | ||
*/ | ||
static isNumeric(n) { | ||
return !isNaN(parseFloat(n)) && isFinite(n); | ||
} | ||
/** | ||
* Deep clone for JSON-compatible data. | ||
* | ||
* @param {*} x | ||
* @returns {*} | ||
*/ | ||
static deepClone(x) { | ||
return JSON.parse(JSON.stringify(x)); | ||
} | ||
isObject(obj) { | ||
return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj)); | ||
}, | ||
size(obj) { | ||
if (typeof obj === 'object' && obj !== null) { | ||
if (Array.isArray(obj)) { | ||
return obj.length; | ||
/** | ||
* Normalize a URL (mostly handling slashes). | ||
* | ||
* @static | ||
* @param {string} baseUrl - The URL to normalize | ||
* @param {string} path - An optional path to add to the URL | ||
* @returns {string} Normalized URL. | ||
*/ | ||
static normalizeUrl(baseUrl, path = null) { | ||
let url = baseUrl.replace(/\/$/, ""); // Remove trailing slash from base URL | ||
if (typeof path === 'string') { | ||
if (path.substr(0, 1) !== '/') { | ||
path = '/' + path; // Add leading slash to path | ||
} | ||
else { | ||
return Object.keys(obj).length; | ||
} | ||
url = url + path.replace(/\/$/, ""); // Remove trailing slash from path | ||
} | ||
return 0; | ||
}, | ||
replacePlaceholders(message, variables = {}) { | ||
if (typeof message === 'string' && this.isObject(variables)) { | ||
for(var placeholder in variables) { | ||
message = message.replace('{' + placeholder + '}', variables[placeholder]); | ||
} | ||
} | ||
return message; | ||
return url; | ||
} | ||
}; | ||
} | ||
@@ -148,1236 +167,100 @@ module.exports = Utils; | ||
const Utils = __webpack_require__(0); | ||
const VersionCompare = __webpack_require__(4); | ||
const MESSAGES = { | ||
"MultipleResultNodes": "Multiple result nodes specified for process graph.", | ||
"StartNodeMissing": "No start nodes found for process graph.", | ||
"ResultNodeMissing": "No result node found for process graph.", | ||
"MultipleResultNodesCallback": "Multiple result nodes specified for the callback in the process '{process_id}' (node: '{node_id}').", | ||
"StartNodeMissingCallback": "No start nodes found for the callback in the process '{process_id}' (node: '{node_id}')'.", | ||
"ResultNodeMissingCallback": "No result node found for the callback in the process '{process_id}' (node: '{node_id}').", | ||
"ReferencedNodeMissing": "Referenced node '{node_id}' doesn't exist.", | ||
"NodeIdInvalid": "Invalid node id specified in process graph.", | ||
"NodeInvalid": "Process graph node '{node_id}' is not a valid object.", | ||
"ProcessIdMissing": "Process graph node '{node_id}' doesn't contain a process id.", | ||
"CallbackArgumentInvalid": "Invalid callback argument '{argument}' requested in the process '{process_id}' (node: '{node_id}').", | ||
"ProcessUnsupported": "Process '{process}' is not supported.", | ||
"ProcessArgumentUnsupported": "Process '{process}' does not support argument '{argument}'.", | ||
"ProcessArgumentRequired": "Process '{process}' requires argument '{argument}'.", | ||
"ProcessArgumentInvalid": "The argument '{argument}' in process '{process}' is invalid: {reason}", | ||
"VariableValueMissing": "No value specified for process graph variable '{variable_id}'.", | ||
"VariableDefaultValueTypeInvalid": "The default value specified for the process graph variable '{variable_id}' is not of type '{type}'.", | ||
"VariableValueTypeInvalid": "The value specified for the process graph variable '{variable_id}' is not of type '{type}'.", | ||
"VariableIdInvalid": "A specified variable ID is not valid.", | ||
"VariableTypeInvalid": "The data type specified for the process graph variable '{variable_id}' is invalid. Must be one of: string, boolean, number, array or object.", | ||
}; | ||
const semver = /^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+))?(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i; | ||
module.exports = class ProcessGraphError extends Error { | ||
class Versions { | ||
constructor(codeOrMsg, variables = {}) { | ||
super(); | ||
this.variables = variables; | ||
if (typeof MESSAGES[codeOrMsg] === 'string') { | ||
this.code = codeOrMsg; | ||
this.message = Utils.replacePlaceholders(MESSAGES[codeOrMsg], variables); | ||
static compare(v1, v2, operator = null) { | ||
if (operator !== null) { | ||
return VersionCompare.compare(v1, v2, operator); | ||
} | ||
else { | ||
this.code = codeOrMsg.replace(/[^\w\d]+/g, ''); | ||
this.message = codeOrMsg; | ||
return VersionCompare(v1, v2); | ||
} | ||
} | ||
toJSON() { | ||
return { | ||
code: this.code, | ||
message: this.message | ||
}; | ||
} | ||
}; | ||
/***/ }), | ||
/* 2 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const ProcessGraphError = __webpack_require__(1); | ||
const Utils = __webpack_require__(0); | ||
module.exports = class ProcessGraphNode { | ||
constructor(json, id, parent) { | ||
if (typeof id !== 'string' || id.length === 0) { | ||
throw new ProcessGraphError('NodeIdInvalid'); | ||
// Function or regexp is coming from compare-version, but not exposed so copied it here. | ||
// See also https://github.com/omichelsen/compare-versions/issues/37 | ||
static validate(version) { | ||
if (typeof version !== 'string') { | ||
return false; | ||
} | ||
if (!Utils.isObject(json)) { | ||
throw new ProcessGraphError('NodeInvalid', {node_id: id}); | ||
} | ||
if (typeof json.process_id !== 'string') { | ||
throw new ProcessGraphError('ProcessIdMissing', {node_id: id}); | ||
} | ||
this.id = id; | ||
this.processGraph = parent; | ||
this.process_id = json.process_id; | ||
this.arguments = Utils.isObject(json.arguments) ? JSON.parse(JSON.stringify(json.arguments)) : {}; | ||
this.description = json.description || null; | ||
this.isResultNode = json.result || false; | ||
this.expectsFrom = []; | ||
this.passesTo = []; | ||
this.result = null; | ||
this.resultsAvailableFrom = []; | ||
return semver.test(version); | ||
} | ||
getProcessGraph() { | ||
return this.processGraph; | ||
} | ||
getArgumentNames() { | ||
return Object.keys(this.arguments); | ||
} | ||
hasArgument(name) { | ||
return (name in this.arguments); | ||
} | ||
getArgumentType(name) { | ||
return ProcessGraphNode.getType(this.arguments[name]); | ||
} | ||
getRawArgument(name) { | ||
return this.arguments[name]; | ||
} | ||
getRawArgumentValue(name) { | ||
var arg = this.arguments[name]; | ||
switch(ProcessGraphNode.getType(arg)) { | ||
case 'result': | ||
return arg.from_node; | ||
case 'callback': | ||
return arg.callback; | ||
case 'callback-argument': | ||
return arg.from_argument; | ||
default: | ||
return arg; | ||
} | ||
} | ||
getArgument(name, defaultValue = undefined) { | ||
if (typeof this.arguments[name] === 'undefined') { | ||
return defaultValue; | ||
} | ||
return this.processArgument(this.arguments[name]); | ||
} | ||
processArgument(arg) { | ||
var type = ProcessGraphNode.getType(arg); | ||
switch(type) { | ||
case 'result': | ||
return this.processGraph.getNode(arg.from_node).getResult(); | ||
case 'callback': | ||
return arg.callback; | ||
case 'callback-argument': | ||
return this.processGraph.getParameter(arg.from_argument); | ||
case 'variable': | ||
return this.processGraph.getVariableValue(arg.variable_id); | ||
case 'array': | ||
case 'object': | ||
for(var i in arg) { | ||
arg[i] = this.processArgument(arg[i]); | ||
} | ||
return arg; | ||
default: | ||
return arg; | ||
} | ||
} | ||
static getType(obj, reportNullAs = 'null') { | ||
if (typeof obj === 'object') { | ||
if (obj === null) { | ||
return reportNullAs; | ||
} | ||
else if (Array.isArray(obj)) { | ||
return 'array'; | ||
} | ||
else if(obj.hasOwnProperty("callback")) { | ||
return 'callback'; | ||
} | ||
else if(obj.hasOwnProperty("variable_id")) { | ||
return 'variable'; | ||
} | ||
else if(obj.hasOwnProperty("from_node")) { | ||
return 'result'; | ||
} | ||
else if(obj.hasOwnProperty("from_argument")) { | ||
return 'callback-argument'; | ||
} | ||
else { | ||
return 'object'; | ||
} | ||
} | ||
return (typeof obj); | ||
} | ||
isStartNode() { | ||
return (this.expectsFrom.length === 0); | ||
} | ||
addPreviousNode(node) { | ||
this.expectsFrom.push(node); | ||
} | ||
getPreviousNodes() { | ||
return this.expectsFrom; | ||
} | ||
addNextNode(node) { | ||
this.passesTo.push(node); | ||
} | ||
getNextNodes() { | ||
return this.passesTo; | ||
} | ||
reset() { | ||
this.result = null; | ||
this.resultsAvailableFrom = []; | ||
} | ||
setDescription(description) { | ||
if (typeof description === 'string') { | ||
this.description = description; | ||
} | ||
else { | ||
this.description = null; | ||
} | ||
} | ||
setResult(result) { | ||
this.result = result; | ||
} | ||
getResult() { | ||
return this.result; | ||
} | ||
solveDependency(dependencyNode) { | ||
if (dependencyNode !== null && this.expectsFrom.includes(dependencyNode)) { | ||
this.resultsAvailableFrom.push(dependencyNode); | ||
} | ||
return (this.expectsFrom.length === this.resultsAvailableFrom.length); // all dependencies solved? | ||
} | ||
}; | ||
/***/ }), | ||
/* 3 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const JsonSchemaValidator = __webpack_require__(4); | ||
const ProcessGraphError = __webpack_require__(1); | ||
const ProcessGraphNode = __webpack_require__(2); | ||
const ProcessGraph = __webpack_require__(5); | ||
module.exports = class BaseProcess { | ||
constructor(schema, validator = null) { | ||
this.schema = schema; | ||
if (validator === null) { | ||
this.jsonSchema = new JsonSchemaValidator(); | ||
} | ||
else { | ||
this.jsonSchema = validator; | ||
} | ||
} | ||
async validate(node) { | ||
// Check for arguments we don't support and throw error | ||
var unsupportedArgs = node.getArgumentNames().filter(name => (typeof this.schema.parameters[name] === 'undefined')); | ||
if (unsupportedArgs.length > 0) { | ||
throw new ProcessGraphError('ProcessArgumentUnsupported', { | ||
process: this.schema.id, | ||
argument: unsupportedArgs[0] | ||
}); | ||
} | ||
// Validate against JSON Schema | ||
for(let name in this.schema.parameters) { | ||
let param = this.schema.parameters[name]; | ||
let arg = node.getRawArgument(name); | ||
if (await this.validateArgument(arg, node, name, param)) { | ||
continue; | ||
} | ||
// Validate against JSON schema | ||
let errors = await this.jsonSchema.validateJson(arg, param.schema); | ||
if (errors.length > 0) { | ||
throw new ProcessGraphError('ProcessArgumentInvalid', { | ||
process: this.schema.id, | ||
argument: name, | ||
reason: errors.join("; ") | ||
}); | ||
} | ||
} | ||
} | ||
async validateArgument(arg, node, parameterName, param) { | ||
let argType = ProcessGraphNode.getType(arg); | ||
if (arg instanceof ProcessGraph) { | ||
await arg.validate(true); | ||
return true; | ||
} | ||
switch(argType) { | ||
// Check whether parameter is required | ||
case 'undefined': | ||
if (param.required) { | ||
throw new ProcessGraphError('ProcessArgumentRequired', { | ||
process: this.schema.id, | ||
argument: parameterName | ||
}); | ||
} | ||
return true; // Parameter not set, nothing to validate against | ||
case 'callback-argument': | ||
var cbParams = node.getProcessGraph().getCallbackParameters(); | ||
// No need for further checks, callback argument is validated in processgraph.js: see parseCallbackArgument() | ||
return JsonSchemaValidator.isSchemaCompatible(param.schema, cbParams[arg.from_argument]); | ||
case 'variable': | ||
var variableSchema = { | ||
type: arg.type || 'string' | ||
}; | ||
return JsonSchemaValidator.isSchemaCompatible(param.schema, variableSchema); | ||
case 'result': | ||
try { | ||
var pg = node.getProcessGraph(); | ||
var process_id = pg.getNode(arg.from_node).process_id; | ||
var process = pg.getProcess(process_id); | ||
return JsonSchemaValidator.isSchemaCompatible(param.schema, process.schema.returns.schema); | ||
} catch (e) {} | ||
break; | ||
case 'array': | ||
case 'object': | ||
// ToDo: Check how we can validate arrays and objects that have references to callback arguments, variables and node results in them... | ||
// See issue https://github.com/Open-EO/openeo-js-commons/issues/5 | ||
// for(var i in arg) { | ||
// await this.validateArgument(arg[i], node, parameterName, param); | ||
// } | ||
return true; | ||
} | ||
return false; | ||
} | ||
/* istanbul ignore next */ | ||
async execute(/*node*/) { | ||
throw "execute not implemented yet"; | ||
} | ||
/* istanbul ignore next */ | ||
test() { | ||
// Run the tests from the examples | ||
throw "test not implemented yet"; | ||
} | ||
}; | ||
/***/ }), | ||
/* 4 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
var ajv; | ||
try { | ||
ajv = __webpack_require__(12); | ||
} catch(err) {} | ||
const Utils = __webpack_require__(0); | ||
module.exports = class JsonSchemaValidator { | ||
constructor() { | ||
this.typeHints = { | ||
'band-name': {type: 'string', validate: 'validateBandName'}, | ||
'bounding-box': {type: 'object', validate: 'validateBoundingBox'}, | ||
'callback': {type: 'object', validate: 'validateCallback'}, | ||
'collection-id': {type: 'string', validate: 'validateCollectionId'}, | ||
'epsg-code': {type: 'integer', validate: 'validateEpsgCode'}, | ||
'geojson': {type: 'object', validate: 'validateGeoJson'}, | ||
'job-id': {type: 'string', validate: 'validateJobId'}, | ||
'kernel': {type: 'array', validate: 'validateKernel'}, | ||
'output-format': {type: 'string', validate: 'validateOutputFormat'}, | ||
'output-format-options': {type: 'object', validate: 'validateOutputFormatOptions'}, | ||
'process-graph-id': {type: 'string', validate: 'validateProcessGraphId'}, | ||
'process-graph-variables': {type: 'object', validate: 'validateProcessGraphVariables'}, | ||
'proj-definition': {type: 'string', validate: 'validateProjDefinition'}, // Proj is deprecated. Implement projjson and wkt2 instead | ||
'raster-cube': {type: 'object', validate: 'validateRasterCube'}, | ||
'temporal-interval': {type: 'array', validate: 'validateTemporalInterval'}, | ||
'temporal-intervals': {type: 'array', validate: 'validateTemporalIntervals'}, | ||
'vector-cube': {type: 'object', validate: 'validateVectorCube'} | ||
}; | ||
var ajvOptions = { | ||
schemaId: 'auto', | ||
format: 'full', | ||
unknownFormats: Object.keys(this.typeHints) | ||
}; | ||
if (!ajv) { | ||
throw "ajv not installed"; | ||
} | ||
this.ajv = new ajv(ajvOptions); | ||
this.ajv.addKeyword('parameters', { | ||
dependencies: [ | ||
"type", | ||
"format" | ||
], | ||
metaSchema: { | ||
type: "object", | ||
additionalProperties: { | ||
type: "object" | ||
} | ||
}, | ||
valid: true, | ||
errors: true | ||
}); | ||
this.ajv.addKeyword('typehint', { | ||
dependencies: [ | ||
"type" | ||
], | ||
validate: async (typehint, data, schema) => { | ||
if (typeof this.typeHints[typehint] === 'object') { | ||
var th = this.typeHints[typehint]; | ||
if (th.type === schema.type || (Array.isArray(schema.type) && schema.type.includes(th.type))) { | ||
return await this[th.validate](data); | ||
} | ||
} | ||
return false; | ||
}, | ||
async: true, | ||
errors: true | ||
}); | ||
this.outputFormats = null; | ||
this.geoJsonValidator = null; | ||
} | ||
/* This is a temporary workaround for the following issues: | ||
- https://github.com/epoberezkin/ajv/issues/1039 | ||
- https://github.com/Open-EO/openeo-processes/issues/67 | ||
Once one of the issues is solved, fixSchema can be removed. | ||
*/ | ||
fixSchemaFormat(s) { | ||
for(var i in s) { | ||
if (i === 'format' && typeof s[i] === 'string' && Object.keys(this.typeHints).includes(s[i])) { | ||
s.typehint = s[i]; | ||
} | ||
if (s[i] && typeof s[i] === 'object') { | ||
s[i] = this.fixSchemaFormat(s[i]); | ||
} | ||
} | ||
return s; | ||
} | ||
fixSchema(s) { | ||
s = JSON.parse(JSON.stringify(s)); | ||
// Set applicable JSON Schema draft version if not already set | ||
if (typeof s.$schema === 'undefined') { | ||
s.$schema = "http://json-schema.org/draft-07/schema#"; | ||
} | ||
// format => typehint (see above) | ||
s = this.fixSchemaFormat(s); | ||
return s; | ||
} | ||
async validateJson(json, schema) { | ||
schema = this.fixSchema(schema); | ||
schema.$async = true; | ||
try { | ||
await this.ajv.validate(schema, json); | ||
/** | ||
* Tries to determine the most suitable version from a well-known discovery document that software is compatible to. | ||
* | ||
* @static | ||
* @param {array} wkVersions - A well-known discovery document compliant to the API specification. | ||
* @returns {object[]} - Gives a list that lists all compatible versions (as still API compliant objects) ordered from the most suitable to the least suitable. | ||
*/ | ||
static findCompatible(wkVersions, preferProduction = true, minVersion = null, maxVersion = null) { | ||
if (!Array.isArray(wkVersions) || wkVersions.length === 0) { | ||
return []; | ||
} catch (e) { | ||
if (Array.isArray(e.errors)) { | ||
return e.errors.map(e => e.message); | ||
} | ||
else { | ||
throw e; | ||
} | ||
} | ||
} | ||
validateJsonSchema(schema) { | ||
schema = JSON.parse(JSON.stringify(schema)); | ||
schema = this.fixSchema(schema); | ||
let result = this.ajv.compile(schema); | ||
return result.errors || []; | ||
} | ||
// Pass the content of https://geojson.org/schema/GeoJSON.json | ||
setGeoJsonSchema(schema) { | ||
var gjv = new ajv(); | ||
this.geoJsonValidator = gjv.compile(schema); | ||
} | ||
// Expects API compatible output formats (see GET /output_formats). | ||
setOutputFormats(outputFormats) { | ||
this.outputFormats = {}; | ||
for (var key in outputFormats) { | ||
this.outputFormats[key.toUpperCase()] = outputFormats[key]; | ||
} | ||
} | ||
/* istanbul ignore next */ | ||
async validateBandName(/*data*/) { | ||
// Can't validate band name without knowing/loading the data. | ||
// => To be overridden by end-user application. | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateBoundingBox(/*data*/) { | ||
// Nothing to validate, schema is (usually) delivered by processes. | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateCallback(/*data*/) { | ||
// This should be checked by process graph parsing automatically. | ||
// Otherwise to be overridden by end-user application. | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateCollectionId(/*data*/) { | ||
// To be overridden by end-user application. | ||
return true; | ||
} | ||
async validateEpsgCode(data) { | ||
// Rough check for valid numbers as we don't want to maintain a full epsg code list in this repo. | ||
// Fully validation to be implemented by end-user application by overriding this method. | ||
if (data >= 2000) { | ||
return true; | ||
} | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid EPSG code specified." | ||
}]); | ||
} | ||
// A very rough GeoJSON validation if no GeoJSON schema is available. | ||
validateGeoJsonSimple(data) { | ||
if (!Utils.isObject(data)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (not an object)." | ||
}]); | ||
} | ||
else if (typeof data.type !== 'string') { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (no type property)." | ||
}]); | ||
} | ||
switch(data.type) { | ||
case "Point": | ||
case "MultiPoint": | ||
case "LineString": | ||
case "MultiLineString": | ||
case "Polygon": | ||
case "MultiPolygon": | ||
if (!Array.isArray(data.coordinates)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (Geometry has no valid coordinates member)." | ||
}]); | ||
let compatible = wkVersions.filter(c => { | ||
if (typeof c.url === 'string' && Versions.validate(c.api_version)) { | ||
let hasMinVer = Versions.validate(minVersion); | ||
let hasMaxVer = Versions.validate(maxVersion); | ||
if (hasMinVer && hasMaxVer) { | ||
return Versions.compare(c.api_version, minVersion, ">=") && Versions.compare(c.api_version, maxVersion, "<="); | ||
} | ||
return true; | ||
case "GeometryCollection": | ||
if (!Array.isArray(data.geometries)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (GeometryCollection has no valid geometries member)." | ||
}]); | ||
else if (hasMinVer) { | ||
return Versions.compare(c.api_version, minVersion, ">="); | ||
} | ||
return true; | ||
case "Feature": | ||
if (data.geometry !== null && !Utils.isObject(data.geometry)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (Feature has no valid geometry member)." | ||
}]); | ||
else if (hasMaxVer) { | ||
return Versions.compare(c.api_version, maxVersion, "<="); | ||
} | ||
if (data.properties !== null && !Utils.isObject(data.properties)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (Feature has no valid properties member)." | ||
}]); | ||
} | ||
return true; | ||
case "FeatureCollection": | ||
if (!Array.isArray(data.features)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON specified (FeatureCollection has no valid features member)." | ||
}]); | ||
} | ||
return true; | ||
default: | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid GeoJSON type specified." | ||
}]); | ||
} | ||
} | ||
async validateGeoJson(data) { | ||
if (this.geoJsonValidator !== null) { | ||
if (!this.geoJsonValidator(data)) { | ||
throw new ajv.ValidationError(this.geoJsonValidator.errors); | ||
} | ||
return true; | ||
} | ||
else { | ||
return this.validateGeoJsonSimple(data); | ||
} | ||
} | ||
/* istanbul ignore next */ | ||
async validateJobId(/*data*/) { | ||
// To be overridden by end-user application | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateKernel(/*data*/) { | ||
// ToDo? / To be overridden by end-user application | ||
return true; | ||
} | ||
async validateOutputFormat(data) { | ||
if (Utils.isObject(this.outputFormats) && !(data.toUpperCase() in this.outputFormats)) { | ||
throw new ajv.ValidationError([{ | ||
message: "Output format not supported." | ||
}]); | ||
} | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateOutputFormatOptions(/*data*/) { | ||
// This depends on the output format specified and can't be fully validated without knowning the chosen output format. | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateProcessGraphId(/*data*/) { | ||
// To be overridden by end-user application | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateProcessGraphVariables(/*data*/) { | ||
// Nothing to validate against... | ||
return true; | ||
} | ||
async validateProjDefinition(data) { | ||
// To be overridden by end-user application, just doing a very basic check here. | ||
if (!data.toLowerCase().includes("+proj")) { | ||
throw new ajv.ValidationError([{ | ||
message: "Invalid PROJ string specified (doesn't contain '+proj')." | ||
}]); | ||
} | ||
return true; | ||
} | ||
/* istanbul ignore next */ | ||
async validateRasterCube(/*data*/) { | ||
// This is usually a reference to a process result as we haven't specified any JSON encoding for raster cubes. | ||
return true; | ||
} | ||
async validateTemporalInterval(/*data*/) { | ||
// ToDo: Fully check against schema, most is already checked by JSON Schemas itself, but check for example that | ||
// both can't be null at the same time or the first element is > the second element. | ||
return true; | ||
} | ||
async validateTemporalIntervals(data) { | ||
var invalid = data.filter(x => !this.validateTemporalInterval(x)); | ||
return invalid.length === 0; | ||
} | ||
/* istanbul ignore next */ | ||
async validateVectorCube(/*data*/) { | ||
// This is usually a reference to a process result as we haven't specified any JSON encoding for raster cubes. | ||
return true; | ||
} | ||
// Checks whether the valueSchema is compatible to the paramSchema. | ||
// So would a value compatible with valueSchema be accepted by paramSchema? | ||
// allowValueAsElements: If true, it checks whether the valueSchema would be allowed as part of an array or object. For example number could be allowed as part of an array of numbers. | ||
static isSchemaCompatible(paramSchema, valueSchema, strict = false, allowValueAsElements = false) { | ||
var paramSchemas = this._convertSchemaToArray(paramSchema); | ||
var valueSchemas = this._convertSchemaToArray(valueSchema); | ||
var compatible = paramSchemas.filter(ps => { | ||
for(var i in valueSchemas) { | ||
var vs = valueSchemas[i]; | ||
if (typeof ps.type !== 'string' || (!strict && typeof vs.type !== 'string')) { // "any" type is always compatible | ||
else { | ||
return true; | ||
} | ||
else if (ps.type === vs.type || (allowValueAsElements && (ps.type === 'array' || ps.type === 'object')) || (ps.type === 'number' && vs.type === 'integer') || (!strict && ps.type === 'integer' && vs.type === 'number')) { | ||
if (ps.type === 'array' && Utils.isObject(ps.items) && Utils.isObject(vs.items)) { | ||
if (allowValueAsElements && JsonSchemaValidator.isSchemaCompatible(ps.items, vs, strict)) { | ||
return true; | ||
} | ||
else if (JsonSchemaValidator.isSchemaCompatible(ps.items, vs.items, strict)) { | ||
return true; | ||
} | ||
} | ||
else if (ps.type === 'object' && Utils.isObject(ps.properties) && Utils.isObject(vs.properties)) { | ||
// ToDo: Check properties, required properties etc. | ||
// If allowValueAsElements is true, all types are allowed to be part of the object. | ||
return true; | ||
} | ||
else if (!strict && (typeof ps.format !== 'string' || typeof vs.format !== 'string')) { | ||
return true; | ||
} | ||
else if (typeof ps.format !== 'string') { // types without format always accepts the same type with a format | ||
return true; | ||
} | ||
else if (ps.format === vs.format) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
}); | ||
return compatible.length > 0; | ||
} | ||
static _convertSchemaToArray(schema) { | ||
var schemas = []; | ||
// ToDo: schema.not and schema.allOf is not supported - see also class constructor of ProcessSchema in processSchema.js of openeo-web-editor. | ||
if (schema.oneOf || schema.anyOf) { | ||
schemas = (schema.oneOf || schema.anyOf); | ||
}); | ||
if (compatible.length === 0) { | ||
return []; | ||
} | ||
else if (Array.isArray(schema.type)) { | ||
schemas = schema.type.map(t => Object.assign({}, schema, {type: t})); | ||
} | ||
else { | ||
schemas = [schema]; | ||
} | ||
return schemas; | ||
} | ||
/** | ||
* Returns the indices of provided JSON Schemas that the provided values matches against. | ||
* | ||
* Returns a single index if a single type is mathcing. | ||
* Returns undefined if no valid type is found. | ||
* Returns an array of indices if multiple types are found. | ||
* | ||
* @param {Array} types - Array of JSON schemas | ||
* @param {*} value - A value | ||
* @return {(string[]|string|undefined)} - Returns matching indices, see description. | ||
*/ | ||
static async getTypeForValue(types, value) { | ||
var validator = new JsonSchemaValidator(); | ||
var potentialTypes = []; | ||
for(var i in types) { | ||
var errors = await validator.validateJson(value, types[i]); | ||
if (errors.length === 0) { | ||
potentialTypes.push(String(i)); | ||
return compatible.sort((c1, c2) => { | ||
let p1 = c1.production !== false; | ||
let p2 = c2.production !== false; | ||
if (!preferProduction || p1 === p2) { | ||
return Versions.compare(c1.api_version, c2.api_version) * -1; // `* -1` to sort in descending order. | ||
} | ||
} | ||
return potentialTypes.length > 1 ? potentialTypes : potentialTypes[0]; | ||
} | ||
}; | ||
/***/ }), | ||
/* 5 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const ErrorList = __webpack_require__(6); | ||
const ProcessGraphError = __webpack_require__(1); | ||
const ProcessGraphNode = __webpack_require__(2); | ||
const Utils = __webpack_require__(0); | ||
const VARIABLE_TYPES = ['string', 'number', 'boolean', 'array', 'object']; | ||
module.exports = class ProcessGraph { | ||
constructor(jsonProcessGraph, processRegistry) { | ||
this.json = jsonProcessGraph; | ||
this.processRegistry = processRegistry; | ||
this.nodes = {}; | ||
this.startNodes = {}; | ||
this.resultNode = null; | ||
this.childrenProcessGraphs = []; | ||
this.parentNode = null; | ||
this.parentProcessId = null; | ||
this.parentParameterName = null; | ||
this.variables = {}; | ||
this.parsed = false; | ||
this.validated = false; | ||
this.errors = new ErrorList(); | ||
this.parameters = {}; | ||
} | ||
toJSON() { | ||
return this.json; | ||
} | ||
createNodeInstance(json, id, parent) { | ||
return new ProcessGraphNode(json, id, parent); | ||
} | ||
createProcessGraphInstance(json) { | ||
return new ProcessGraph(json, this.processRegistry); | ||
} | ||
setParent(parent, parameterName) { | ||
if (parent instanceof ProcessGraphNode) { | ||
this.parentNode = parent; | ||
this.parentProcessId = parent.process_id; | ||
} | ||
else { | ||
this.parentNode = null; | ||
this.parentProcessId = parent; | ||
} | ||
this.parentParameterName = parameterName; | ||
} | ||
isValid() { | ||
return this.validated && this.errors.count() === 0; | ||
} | ||
addError(error) { | ||
this.errors.add(error); | ||
} | ||
parse() { | ||
if (this.parsed) { | ||
return; | ||
} | ||
for(let id in this.json) { | ||
this.nodes[id] = this.createNodeInstance(this.json[id], id, this); | ||
} | ||
var makeError = (errorId) => { | ||
if (this.parentProcessId) { | ||
return new ProcessGraphError( | ||
errorId + 'Callback', | ||
{ | ||
process_id: this.parentProcessId, | ||
node_id: this.parentNode ? this.parentNode.id : 'N/A' | ||
} | ||
); | ||
else if (p1) { | ||
return -1; | ||
} | ||
else { | ||
return new ProcessGraphError(errorId); | ||
return 1; | ||
} | ||
}; | ||
for(let id in this.nodes) { | ||
var node = this.nodes[id]; | ||
if (node.isResultNode) { | ||
if (this.resultNode !== null) { | ||
throw makeError('MultipleResultNodes'); | ||
} | ||
this.resultNode = node; | ||
} | ||
this.parseArguments(id, node); | ||
} | ||
if (!this.findStartNodes()) { | ||
throw makeError('StartNodeMissing'); | ||
} | ||
if (this.resultNode === null) { | ||
throw makeError('ResultNodeMissing'); | ||
} | ||
this.parsed = true; | ||
} | ||
async validate(throwOnErrors = true) { | ||
if (this.validated) { | ||
return null; | ||
} | ||
this.validated = true; | ||
// Parse | ||
try { | ||
this.parse(); | ||
} catch (error) { | ||
this.addError(error); | ||
if (throwOnErrors) { | ||
throw error; | ||
} | ||
} | ||
// Validate | ||
await this.validateNodes(this.getStartNodes(), throwOnErrors); | ||
return this.errors; | ||
} | ||
async execute(parameters = null) { | ||
await this.validate(); | ||
this.reset(); | ||
this.setParameters(parameters); | ||
await this.executeNodes(this.getStartNodes()); | ||
return this.getResultNode(); | ||
} | ||
async validateNodes(nodes, throwOnErrors, previousNode = null) { | ||
if (nodes.length === 0) { | ||
return; | ||
} | ||
var promises = nodes.map(async (node) => { | ||
// Validate this node after all dependencies are available | ||
if (!node.solveDependency(previousNode)) { | ||
return; | ||
} | ||
// Get process and validate | ||
try { | ||
await this.validateNode(node); | ||
} catch (e) { | ||
if (e instanceof ErrorList) { | ||
this.errors.merge(e); | ||
if (throwOnErrors) { | ||
throw e.first(); | ||
} | ||
} | ||
else { | ||
this.addError(e); | ||
if (throwOnErrors) { | ||
throw e; | ||
} | ||
} | ||
} | ||
await this.validateNodes(node.getNextNodes(), throwOnErrors, node); | ||
}); | ||
await Promise.all(promises); | ||
} | ||
async validateNode(node) { | ||
var process = this.getProcess(node); | ||
return await process.validate(node); | ||
} | ||
async executeNodes(nodes, previousNode = null) { | ||
if (nodes.length === 0) { | ||
return; | ||
static findLatest(wkVersions, preferProduction = true, minVersion = null, maxVersion = null) { | ||
let versions = Versions.findCompatible(wkVersions, preferProduction, minVersion, maxVersion); | ||
if (versions.length > 0) { | ||
return versions[0]; | ||
} | ||
var promises = nodes.map(async (node) => { | ||
// Execute this node after all dependencies are available | ||
if (!node.solveDependency(previousNode)) { | ||
return; | ||
} | ||
var result = await this.executeNode(node); | ||
node.setResult(result); | ||
// Execute next nodes in chain | ||
await this.executeNodes(node.getNextNodes(), node); | ||
}); | ||
return Promise.all(promises); | ||
} | ||
async executeNode(node) { | ||
var process = this.getProcess(node); | ||
return await process.execute(node); | ||
} | ||
parseArguments(nodeId, node, args) { | ||
if (typeof args === 'undefined') { | ||
args = node.arguments; | ||
else { | ||
return null; | ||
} | ||
for(var argumentName in args) { | ||
var arg = args[argumentName]; | ||
var type = ProcessGraphNode.getType(arg); | ||
switch(type) { | ||
case 'result': | ||
this.connectNodes(node, arg.from_node); | ||
break; | ||
case 'variable': | ||
this.parseVariable(arg); | ||
break; | ||
case 'callback': | ||
arg.callback = this.createProcessGraph(arg.callback, node, argumentName); | ||
break; | ||
case 'callback-argument': | ||
this.parseCallbackArgument(node, arg.from_argument); | ||
break; | ||
case 'array': | ||
case 'object': | ||
this.parseArguments(nodeId, node, arg); | ||
break; | ||
} | ||
} | ||
} | ||
parseCallbackArgument(node, name) { | ||
var cbParams = this.getCallbackParameters(); | ||
if (!Utils.isObject(cbParams) || !cbParams.hasOwnProperty(name)) { | ||
throw new ProcessGraphError('CallbackArgumentInvalid', { | ||
argument: name, | ||
node_id: node.id, | ||
process_id: node.process_id | ||
}); | ||
} | ||
} | ||
createProcessGraph(json, node, argumentName) { | ||
var pg = this.createProcessGraphInstance(json); | ||
pg.setParent(node, argumentName); | ||
pg.parse(); | ||
this.childrenProcessGraphs.push(pg); | ||
return pg; | ||
} | ||
parseVariable(variable) { | ||
// Check whether the variable id is valid | ||
if (typeof variable.variable_id !== 'string') { | ||
throw new ProcessGraphError('VariableIdInvalid'); | ||
} | ||
var obj = {}; | ||
// Check whether the data type is valid | ||
if (typeof variable.type !== 'undefined' && !VARIABLE_TYPES.includes(variable.type)) { | ||
throw new ProcessGraphError('VariableTypeInvalid', variable); | ||
} | ||
obj.type = typeof variable.type !== 'undefined' ? variable.type : 'string'; | ||
// Check whether the defult value has the correct data type | ||
var defaultType = ProcessGraphNode.getType(variable.default); | ||
if (defaultType !== 'undefined') { | ||
if (defaultType !== obj.type) { | ||
throw new ProcessGraphError('VariableDefaultValueTypeInvalid', variable); | ||
} | ||
else { | ||
obj.value = variable.default; | ||
} | ||
} | ||
} | ||
setParameters(parameters) { | ||
if (typeof parameters === 'object' && parameters !== null) { | ||
this.parameters = parameters; | ||
} | ||
} | ||
getParameter(name) { | ||
return this.parameters[name]; | ||
} | ||
setVariableValues(variables) { | ||
for(var i in variables) { | ||
this.setVariable(i, variables[i]); | ||
} | ||
} | ||
setVariableValue(id, value) { | ||
if (typeof this.variables[id] !== 'object') { | ||
this.variables[id] = {}; | ||
} | ||
this.variables[id].value = value; | ||
} | ||
getVariableValue(id) { | ||
var variable = this.variables[id]; | ||
if (typeof variable !== 'object' || typeof variable.value === 'undefined') { | ||
throw new ProcessGraphError('VariableValueMissing', {variable_id: id}); | ||
} | ||
var type = ProcessGraphNode.getType(variable.value); | ||
if (type !== variable.type) { | ||
throw new ProcessGraphError('VariableValueTypeInvalid', {variable_id: id, type: variable.type}); | ||
} | ||
return this.variables[id].value; | ||
} | ||
connectNodes(node, prevNodeId) { | ||
var prevNode = this.nodes[prevNodeId]; | ||
if (typeof prevNode === 'undefined') { | ||
throw new ProcessGraphError('ReferencedNodeMissing', {node_id: prevNodeId}); | ||
} | ||
node.addPreviousNode(prevNode); | ||
prevNode.addNextNode(node); | ||
} | ||
findStartNodes() { | ||
var found = false; | ||
for(var id in this.nodes) { | ||
var node = this.nodes[id]; | ||
if (node.isStartNode()) { | ||
this.startNodes[id] = node; | ||
found = true; | ||
} | ||
} | ||
return found; | ||
} | ||
reset() { | ||
for(var id in this.nodes) { | ||
this.nodes[id].reset(); | ||
} | ||
this.childrenProcessGraphs.forEach(child => child.reset()); | ||
} | ||
getResultNode() { | ||
return this.resultNode; | ||
} | ||
getStartNodes() { | ||
return Object.values(this.startNodes); | ||
} | ||
getStartNodeIds() { | ||
return Object.keys(this.startNodes); | ||
} | ||
getNode(nodeId) { | ||
return this.nodes[nodeId]; | ||
} | ||
getNodeCount() { | ||
return Utils.size(this.nodes); | ||
} | ||
getNodes() { | ||
return this.nodes; | ||
} | ||
getErrors() { | ||
return this.errors; | ||
} | ||
getProcess(node) { | ||
var process = this.processRegistry.get(node.process_id); | ||
if (process === null) { | ||
throw new ProcessGraphError('ProcessUnsupported', {process: node.process_id}); | ||
} | ||
return process; | ||
} | ||
getParentProcess() { | ||
return this.processRegistry.get(this.parentProcessId); | ||
} | ||
getCallbackParameters() { | ||
var process = this.getParentProcess(); | ||
if (!this.parentParameterName || !process) { | ||
return {}; | ||
} | ||
var schema = process.schema.parameters[this.parentParameterName].schema; | ||
if (Utils.isObject(schema.parameters)) { | ||
return schema.parameters; | ||
} | ||
// ToDo: If a process parameter supports multiple different callbacks, i.e. reduce with either an array of two separate values, this | ||
// can't be separated accordingly and we just return all potential values. So it might happen that people get a successful validation | ||
// but they used the wrong callback parameters. | ||
// See issue https://github.com/Open-EO/openeo-js-commons/issues/6 | ||
var cbParams = {}; | ||
var choice = schema.anyOf || schema.oneOf || schema.allOf; | ||
if (Array.isArray(choice)) { | ||
for(let i in choice) { | ||
var p = choice[i]; | ||
if (Utils.isObject(p.parameters)) { | ||
Object.assign(cbParams, p.parameters); | ||
} | ||
} | ||
} | ||
return cbParams; | ||
} | ||
}; | ||
/***/ }), | ||
/* 6 */ | ||
/***/ (function(module, exports) { | ||
module.exports = class ErrorList { | ||
constructor() { | ||
this.errors = []; | ||
} | ||
first() { | ||
return this.errors[0] || null; | ||
} | ||
last() { | ||
return this.errors[this.errors.length-1] || null; | ||
} | ||
merge(errorList) { | ||
this.errors = this.errors.concat(errorList.getAll()); | ||
} | ||
add(error) { | ||
this.errors.push(error); | ||
} | ||
} | ||
count() { | ||
return this.errors.length; | ||
} | ||
module.exports = Versions; | ||
toJSON() { | ||
return this.errors.map(e => { | ||
if (typeof e.toJSON === 'function') { | ||
return e.toJSON(); | ||
} | ||
else { | ||
return { | ||
code: 'InternalError', | ||
message: e.message | ||
}; | ||
} | ||
}); | ||
} | ||
getMessage() { | ||
var msg = ''; | ||
for (var i in this.errors) { | ||
msg += (parseInt(i, 10)+1) + ". " + this.errors[i].message + "\r\n"; | ||
} | ||
return msg.trim(); | ||
} | ||
getAll() { | ||
return this.errors; | ||
} | ||
}; | ||
/***/ }), | ||
/* 7 */ | ||
/* 2 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
// Migrations | ||
const MigrateCapabilities = __webpack_require__(8); | ||
const MigrateCollections = __webpack_require__(10); | ||
const MigrateProcesses = __webpack_require__(11); | ||
// Process graphs | ||
const BaseProcess = __webpack_require__(3); | ||
const JsonSchemaValidator = __webpack_require__(4); | ||
const ProcessGraph = __webpack_require__(5); | ||
const ProcessGraphError = __webpack_require__(1); | ||
const ProcessGraphNode = __webpack_require__(2); | ||
const ProcessRegistry = __webpack_require__(13); | ||
const MigrateCapabilities = __webpack_require__(3); | ||
const MigrateCollections = __webpack_require__(5); | ||
const MigrateProcesses = __webpack_require__(6); | ||
// Others | ||
const ErrorList = __webpack_require__(6); | ||
const FeatureList = __webpack_require__(14); | ||
const Versions = __webpack_require__(1); | ||
const Utils = __webpack_require__(0); | ||
@@ -1389,56 +272,78 @@ | ||
MigrateProcesses, | ||
BaseProcess, | ||
JsonSchemaValidator, | ||
ProcessGraph, | ||
ProcessGraphError, | ||
ProcessGraphNode, | ||
ProcessRegistry, | ||
ErrorList, | ||
FeatureList, | ||
Utils | ||
Versions, | ||
Utils, | ||
}; | ||
/***/ }), | ||
/* 8 */ | ||
/* 3 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const Utils = __webpack_require__(0); | ||
const Versions = __webpack_require__(1); | ||
var MigrateCapabilities = { | ||
const NO_VERSION = "0.0.0"; | ||
guessApiVersion(capabilities) { | ||
if (typeof capabilities.api_version === 'string') { | ||
return capabilities.api_version; | ||
} | ||
else if (typeof capabilities.version === 'string') { | ||
return capabilities.version; | ||
} | ||
class MigrateCapabilities { | ||
/** | ||
* Tries to determine the API version from the capabilities object. | ||
* | ||
* Returns the version number, e.g. "0.4.2", "1.0.0" or "0.0.0" (if unknown). | ||
* | ||
* @param {object} capabilities | ||
* @returns {string} | ||
*/ | ||
static guessApiVersion(capabilities) { | ||
// No object passed | ||
if (!Utils.isObject(capabilities)) { | ||
return NO_VERSION; | ||
} | ||
// Get exact info from version fields | ||
if (Versions.validate(capabilities.api_version)) { | ||
return capabilities.api_version; | ||
} | ||
else if (Versions.validate(capabilities.version)) { | ||
return capabilities.version; | ||
} | ||
// Now we are really guessing | ||
else if (Array.isArray(capabilities.endpoints) && capabilities.endpoints.filter(e => e.path === '/output_formats').length > 0) { | ||
return "0.4"; | ||
} | ||
else if (!capabilities.backend_version && !capabilities.title && !capabilities.description && !capabilities.links) { | ||
return "0.3"; | ||
} | ||
else { // Latest version | ||
return "1.0"; | ||
} | ||
}, | ||
else if (Array.isArray(capabilities.endpoints)) { | ||
if (capabilities.endpoints.filter(e => e.path === '/file_formats' || e.path === '/conformance' || e.path === '/files').length > 0) { | ||
return "1.0.0"; | ||
} | ||
else if (capabilities.endpoints.filter(e => e.path === '/output_formats' || e.path === '/files/{user_id}').length > 0) { | ||
return "0.4.2"; | ||
} | ||
else if (!capabilities.backend_version && !capabilities.title && !capabilities.description && !capabilities.links) { | ||
return "0.3.1"; | ||
} | ||
} | ||
// Can't determine version | ||
return NO_VERSION; | ||
} | ||
// Always returns a copy of the input object | ||
convertCapabilitiesToLatestSpec(originalCapabilities, version = null, updateVersionNumber = true, title = "Unknown", backend_version = "Unknown") { | ||
var capabilities = Object.assign({}, originalCapabilities); | ||
static convertCapabilitiesToLatestSpec(originalCapabilities, version = null, updateVersionNumbers = true, updateEndpointPaths = true, id = "unknown", title = "Unknown", backend_version = "Unknown") { | ||
if (version === null) { | ||
version = this.guessApiVersion(capabilities); | ||
version = this.guessApiVersion(originalCapabilities); | ||
} | ||
// convert v0.3 capabilities to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// version => api_version | ||
if (typeof capabilities.version !== 'undefined') { | ||
delete capabilities.version; | ||
} | ||
// Return empty if version number is not available | ||
if (version === NO_VERSION) { | ||
return {}; | ||
} | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let capabilities = Utils.deepClone(originalCapabilities); | ||
// Fill & Update version number | ||
if (!updateVersionNumbers) { | ||
capabilities.api_version = version; | ||
} | ||
else { | ||
capabilities.api_version = "1.0.0"; | ||
} | ||
// Convert billing plans | ||
@@ -1453,11 +358,19 @@ if (Utils.isObject(capabilities.billing)) { | ||
// Convert endpoints | ||
capabilities.endpoints = this.convertEndpointsToLatestSpec(capabilities.endpoints, version); | ||
capabilities.endpoints = this.convertEndpointsToLatestSpec(capabilities.endpoints, version, updateEndpointPaths); | ||
// Fill STAC Version field | ||
if (!updateVersionNumbers && Versions.compare(version, "0.4.x", "=")) { | ||
capabilities.stac_version = "0.6.2"; | ||
} | ||
else if (updateVersionNumbers || typeof capabilities.stac_version !== 'string') { | ||
capabilities.stac_version = "0.9.0"; | ||
} | ||
// Add missing fields with somewhat useful data | ||
if (updateVersionNumber || typeof capabilities.api_version !== 'string') { | ||
capabilities.api_version = "1.0.0"; | ||
} | ||
if (typeof capabilities.backend_version !== 'string') { | ||
capabilities.backend_version = backend_version; | ||
} | ||
if (typeof capabilities.id !== 'string') { | ||
capabilities.id = id; | ||
} | ||
if (typeof capabilities.title !== 'string') { | ||
@@ -1474,53 +387,68 @@ capabilities.title = title; | ||
return capabilities; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertBillingToLatestSpec(originalBilling, version) { | ||
var billing = Object.assign({}, originalBilling); | ||
// convert v0.3 billing info to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// Add paid flag to billing plans | ||
if (Array.isArray(billing.plans)) { | ||
billing.plans = billing.plans.map(plan => { | ||
if (typeof plan.paid !== 'boolean') { | ||
plan.paid = true; | ||
if (typeof plan.name === 'string' && plan.name.toLowerCase().includes('free')) { | ||
plan.paid = false; | ||
} | ||
} | ||
return plan; | ||
}); | ||
} | ||
static convertBillingToLatestSpec(originalBilling, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let billing = {}; | ||
if (Utils.isObject(originalBilling)) { | ||
billing = Utils.deepClone(originalBilling); | ||
} | ||
if (typeof billing.currency !== 'string') { | ||
billing.currency = null; | ||
} | ||
return billing; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertEndpointsToLatestSpec(originalEndpoints, version) { | ||
var endpoints = []; | ||
if (Array.isArray(originalEndpoints)) { | ||
endpoints = originalEndpoints.slice(0); | ||
static convertEndpointsToLatestSpec(originalEndpoints, version, updatePaths = false) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
// convert v0.3 service types to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// Nothing to do as nothing has changed. | ||
if (!Array.isArray(originalEndpoints)) { | ||
return []; | ||
} | ||
let endpoints = Utils.deepClone(originalEndpoints); | ||
// convert v0.4 endpoints to v1.0 | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
if (updatePaths) { | ||
endpoints = endpoints.map(e => { | ||
switch (e.path) { | ||
case '/output_formats': | ||
e.path = '/file_formats'; | ||
break; | ||
case '/files/{user_id}': | ||
e.path = '/files'; | ||
break; | ||
case '/files/{user_id}/{path}': | ||
e.path = '/files/{path}'; | ||
break; | ||
} | ||
return e; | ||
}); | ||
} | ||
else { | ||
// Nothing to do as structure has not changed. | ||
} | ||
} | ||
return endpoints; | ||
}, | ||
} | ||
// Alias for convertFileFormatsToLatestSpec | ||
convertOutputFormatsToLatestSpec(originalFormats, version) { | ||
static convertOutputFormatsToLatestSpec(originalFormats, version) { | ||
return this.convertFileFormatsToLatestSpec(originalFormats, version); | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertFileFormatsToLatestSpec(originalFormats, version) { | ||
var formats = Object.assign({}, originalFormats); | ||
if (Utils.compareVersion(version, "0.3.x") === 0 && Utils.isObject(formats.formats)) { | ||
formats = formats.formats; | ||
static convertFileFormatsToLatestSpec(originalFormats, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let formats = Utils.deepClone(originalFormats); | ||
if (Utils.compareVersion(version, "0.4.x") <= 0 && Utils.isObject(formats)) { | ||
if (Versions.compare(version, "0.4.x", "=") && Utils.isObject(formats)) { | ||
formats = { | ||
@@ -1539,20 +467,71 @@ output: formats | ||
return formats; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertServiceTypesToLatestSpec(originalTypes, version) { | ||
var types = Object.assign({}, originalTypes); | ||
// Nothing to do as nothing has changed in 0.3 and 0.4. | ||
if (Utils.compareVersion(version, "0.4.x") > 0) { | ||
// Add future changes here. | ||
static convertServiceTypesToLatestSpec(originalTypes, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let types = Utils.deepClone(originalTypes); | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
for(let t in types) { | ||
if (!Utils.isObject(types[t])) { | ||
types[t] = {}; | ||
continue; | ||
} | ||
// Remove attributes | ||
delete types[t].attributes; | ||
// Rename parameters to configuration | ||
if (Utils.isObject(types[t].parameters)) { | ||
types[t].configuration = types[t].parameters; | ||
} | ||
delete types[t].parameters; | ||
// Rename variables to process_parameters | ||
if (Array.isArray(types[t].variables)) { | ||
types[t].process_parameters = types[t].variables.map(v => { | ||
let param = { | ||
name: v.variable_id, | ||
description: typeof v.description === 'string' ? v.description : "", | ||
schema: { | ||
type: [ | ||
typeof v.type === 'string' ? v.type : "string", | ||
"null" | ||
] | ||
} | ||
}; | ||
if (typeof v.default !== 'undefined') { | ||
param.default = v.default; | ||
} | ||
return param; | ||
}); | ||
} | ||
delete types[t].variables; | ||
} | ||
} | ||
return types; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertUdfRuntimesToLatestSpec(originalRuntimes, version) { | ||
var runtimes = Object.assign({}, originalRuntimes); | ||
static convertUdfRuntimesToLatestSpec(originalRuntimes, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let runtimes = Utils.deepClone(originalRuntimes); | ||
// Nothing to do, was not supported in 0.3 and nothing changed in 0.4. | ||
if (Utils.compareVersion(version, "0.4.x") > 0) { | ||
// Add future changes here. | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
for(let r in runtimes) { | ||
if (!Utils.isObject(runtimes[r])) { | ||
delete runtimes[r]; | ||
continue; | ||
} | ||
// null is not allowed any longer, replace with empty string | ||
if (runtimes[r].description === null) { | ||
runtimes[r].description = ""; | ||
} | ||
} | ||
} | ||
@@ -1562,3 +541,3 @@ return runtimes; | ||
}; | ||
} | ||
@@ -1568,3 +547,3 @@ module.exports = MigrateCapabilities; | ||
/***/ }), | ||
/* 9 */ | ||
/* 4 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
@@ -1687,67 +666,244 @@ | ||
/***/ }), | ||
/* 10 */ | ||
/* 5 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const Utils = __webpack_require__(0); | ||
const Versions = __webpack_require__(1); | ||
var MigrateCollections = { | ||
const extMap = { | ||
"cube": "datacube", | ||
"eo": "eo", | ||
"label": "label", | ||
"pc": "pointcloud", | ||
"proj": "projection", | ||
"sar": "sar", | ||
"sat": "sat", | ||
"sci": "scientific" | ||
}; | ||
const fieldMap = { | ||
// Item to core | ||
'item:license': 'license', | ||
'item:providers': 'providers', | ||
// EO to core | ||
'eo:instrument': 'instruments', | ||
'eo:platform': 'platform', | ||
'eo:constellation': 'constellation', | ||
// EO to proj | ||
'eo:epsg': 'proj:epsg', | ||
// EO to sat | ||
'eo:off_nadir': 'sat:off_nadir_angle', | ||
'eo:azimuth': 'sat:azimuth_angle', | ||
'eo:sun_azimuth': 'sat:sun_azimuth_angle', | ||
'eo:sun_elevation': 'sat:sun_elevation_angle', | ||
// Datetime Range to core | ||
'dtr:start_datetime': 'start_datetime', | ||
'dtr:end_datetime': 'end_datetime', | ||
// Point Cloud | ||
'pc:schema': 'pc:schemas', | ||
// SAR rename | ||
'sar:type': 'sar:product_type', | ||
'sar:polarization': 'sar:polarizations', | ||
// SAR to core | ||
'sar:instrument': 'instruments', | ||
'sar:platform': 'platform', | ||
'sar:constellation': 'constellation', | ||
// SAR to sat | ||
'sar:off_nadir': 'sat:off_nadir_angle', | ||
'sar:relative_orbit': 'sat:relative_orbit', | ||
// The following four fields don't translate directly, see code below | ||
'sar:pass_direction': 'sat:orbit_state', | ||
// sar:resolution => sar:resolution_range, sar:resolution_azimuth | ||
// sar:pixel_spacing => sar:pixel_spacing_range, sar:pixel_spacing_azimuth | ||
// sar:looks => sar:looks_range, sar:looks_azimuth, sar:looks_equivalent_number (opt) | ||
}; | ||
const moveToRoot = [ | ||
'cube:dimensions', | ||
'sci:publications', | ||
'sci:doi', | ||
'sci:citation' | ||
]; | ||
class MigrateCollections { | ||
// Always returns a copy of the input collection object | ||
convertCollectionToLatestSpec(originalCollection, version) { | ||
if (!version || typeof version !== 'string') { | ||
throw new Error("No version specified"); | ||
static convertCollectionToLatestSpec(originalCollection, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
if (Utils.compareVersion(version, "0.5.x") >= 0) { | ||
throw "Migrating collections from API version 0.4.0 is not supported yet"; | ||
// Make sure we don't alter the original object | ||
let collection = Utils.deepClone(originalCollection); | ||
// If collection has no id => seems to be an invalid collection => abort | ||
if (typeof collection.id !== 'string' || collection.id.length === 0) { | ||
return {}; | ||
} | ||
var collection = Object.assign({}, originalCollection); | ||
if (!Object.keys(collection).length) { | ||
return collection; | ||
// Update stac_version | ||
if (!Versions.validate(collection.stac_version) || Versions.compare(collection.stac_version, "0.9.0", "<")) { | ||
collection.stac_version = "0.9.0"; | ||
} | ||
// convert v0.3 processes to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// name => id | ||
collection.id = collection.name; | ||
delete collection.name; | ||
// Add stac_version | ||
collection.stac_version = '0.6.1'; | ||
// Rename provider => providers | ||
if (Array.isArray(collection.provider)) { | ||
collection.providers = collection.provider; | ||
delete collection.provider; | ||
// Add missing extent upfront. Makes the following code simpler as it works on the object. | ||
if (!Utils.isObject(collection.extent)) { | ||
collection.extent = {}; | ||
} | ||
// convert v0.4 collections to latest version | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
// Restructure spatial extent | ||
if (Array.isArray(collection.extent.spatial)) { | ||
collection.extent.spatial = { | ||
bbox: [ | ||
collection.extent.spatial | ||
] | ||
}; | ||
} | ||
if (typeof collection.properties !== 'object') { | ||
collection.properties = {}; | ||
// Restructure temporal extent | ||
if (Array.isArray(collection.extent.temporal)) { | ||
collection.extent.temporal = { | ||
interval: [ | ||
collection.extent.temporal | ||
] | ||
}; | ||
} | ||
// Migrate eo:bands | ||
if (collection['eo:bands'] !== null && typeof collection['eo:bands'] === 'object' && !Array.isArray(collection['eo:bands'])) { | ||
var bands = []; | ||
for(let key in collection['eo:bands']) { | ||
var band = Object.assign({}, collection['eo:bands'][key]); | ||
band.name = key; | ||
if (typeof band.resolution !== 'undefined' && typeof band.gsd === 'undefined') { | ||
band.gsd = band.resolution; | ||
delete band.resolution; | ||
// move properties to other_properties | ||
if (Utils.isObject(collection.properties)) { | ||
if (!Utils.isObject(collection.other_properties)) { | ||
collection.other_properties = {}; | ||
} | ||
for(let key in collection.properties) { | ||
collection.other_properties[key] = { | ||
values: [ | ||
collection.properties[key] | ||
] | ||
}; | ||
} | ||
} | ||
delete collection.properties; | ||
// now we can work on all properties and migrate to summaries | ||
let props = Utils.isObject(collection.other_properties) ? collection.other_properties : {}; | ||
for(let key in props) { | ||
let val = props[key]; | ||
if (Utils.isObject(val) && (Array.isArray(val.extent) || Array.isArray(val.values))) { | ||
if (Array.isArray(val.extent)) { | ||
props[key] = { | ||
min: val.extent[0], | ||
max: val.extent[1] | ||
}; | ||
} | ||
if (typeof band.wavelength !== 'undefined' && typeof band.center_wavelength === 'undefined') { | ||
band.center_wavelength = band.wavelength; | ||
delete band.wavelength; | ||
else { // val.value is an array | ||
let is2dArray = val.values.filter(v => !Array.isArray(v)).length === 0; | ||
if (is2dArray) { | ||
if (val.values.length < 2) { | ||
props[key] = val.values[0]; | ||
} | ||
else { | ||
props[key] = val.values.reduce((a, b) => a.concat(b)); | ||
} | ||
} | ||
else { | ||
props[key] = val.values; | ||
} | ||
} | ||
bands.push(band); | ||
} | ||
collection['eo:bands'] = bands; | ||
else { | ||
// If not valid, move to top-level | ||
if (typeof collection[key] === 'undefined') { | ||
collection[key] = val; | ||
} | ||
delete props[key]; | ||
} | ||
} | ||
// Move all other properties into properties. | ||
for (let key in collection) { | ||
if (key.includes(':')) { | ||
collection.properties[key] = collection[key]; | ||
delete collection[key]; | ||
delete collection.other_properties; | ||
collection.summaries = Utils.isObject(collection.summaries) ? collection.summaries : {}; | ||
for(let key in props) { | ||
let val = props[key]; | ||
if (key === 'sar:pass_direction') { | ||
// Convert null to geostationary | ||
val = val.map(v => v === null ? 'geostationary' : v); | ||
} | ||
// Convert arrays into separate fields as needed for some SAR fields | ||
if ((key === 'sar:resolution' || key === 'sar:pixel_spacing' || key === 'sar:looks') && Array.isArray(val) && val.length >= 2) { | ||
collection.summaries[key + '_range'] = val.slice(0,1); | ||
collection.summaries[key + '_azimuth'] = val.slice(1,2); | ||
if (val.length > 2) { | ||
collection.summaries[key + '_equivalent_number'] = val.slice(2,3); | ||
} | ||
} | ||
// Do the renaming of fields | ||
else if (typeof fieldMap[key] === 'string') { | ||
collection.summaries[fieldMap[key]] = val; | ||
} | ||
// Move invalid summaries to the top level | ||
else if (moveToRoot.includes(key) && Array.isArray(val) && val.length === 1) { | ||
collection[key] = val[0]; | ||
} | ||
// Do the general conversion | ||
else { | ||
collection.summaries[key] = val; | ||
} | ||
} | ||
} | ||
// Add missing required fields | ||
if (typeof collection.description !== 'string') { | ||
collection.description = ""; | ||
} | ||
if (!Utils.isObject(collection.extent.spatial)) { | ||
collection.extent.spatial = {}; | ||
} | ||
if (!Utils.isObject(collection.extent.temporal)) { | ||
collection.extent.temporal = {}; | ||
} | ||
if (typeof collection.license !== 'string') { | ||
collection.license = "proprietary"; | ||
} | ||
if (!Utils.isObject(collection.summaries)) { | ||
collection.summaries = {}; | ||
} | ||
if (!Utils.isObject(collection['cube:dimensions'])) { | ||
collection['cube:dimensions'] = {}; | ||
} | ||
// Fix links | ||
if (!Array.isArray(collection.links)) { | ||
collection.links = []; | ||
} | ||
// Add missing rel type | ||
collection.links = collection.links.map(l => { | ||
l.rel = typeof l.rel === 'string' ? l.rel : "related"; | ||
return l; | ||
}); | ||
// Fix stac_extensions | ||
var extensions = Array.isArray(collection.stac_extensions) ? collection.stac_extensions : []; | ||
for(var key in collection) { | ||
let ext = null; | ||
let prefix = key.split(':', 1); | ||
if (key === 'deprecated' || key === 'version') { | ||
ext = 'version'; | ||
} | ||
else if (typeof extMap[prefix] === 'string') { | ||
ext = extMap[prefix]; | ||
} | ||
if (ext !== null && !extensions.includes(ext)) { | ||
extensions.push(ext); | ||
} | ||
} | ||
extensions.sort(); | ||
collection.stac_extensions = extensions; | ||
return collection; | ||
} | ||
}; | ||
} | ||
@@ -1757,26 +913,20 @@ module.exports = MigrateCollections; | ||
/***/ }), | ||
/* 11 */ | ||
/* 6 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const Utils = __webpack_require__(0); | ||
const Versions = __webpack_require__(1); | ||
var MigrateProcesses = { | ||
class MigrateProcesses { | ||
// Always returns a copy of the input process object | ||
convertProcessToLatestSpec(originalProcess, version) { | ||
if (!version || typeof version !== 'string') { | ||
throw new Error("No version specified"); | ||
static convertProcessToLatestSpec(originalProcess, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
// Make sure we don't alter the original object | ||
var process = Object.assign({}, originalProcess); | ||
let process = Utils.deepClone(originalProcess); | ||
let isVersion03 = Utils.compareVersion(version, "0.3.x") === 0; | ||
// name => id | ||
if (isVersion03) { | ||
process.id = process.name; | ||
delete process.name; | ||
} | ||
// If process has no id => seems to be an invalid process, abort | ||
// If process has no id => seems to be an invalid process => abort | ||
if (typeof process.id !== 'string' || process.id.length === 0) { | ||
@@ -1786,2 +936,34 @@ return {}; | ||
// Convert the parameters from object to array | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
// Determine the parameter order | ||
if (!Array.isArray(process.parameter_order) || process.parameter_order.length === 0) { | ||
process.parameter_order = []; | ||
for(let param in process.parameters) { | ||
process.parameter_order.push(param); | ||
} | ||
} | ||
// Upgrade parameters and convert from array to object | ||
let params = []; | ||
for(let name of process.parameter_order) { | ||
// Add name | ||
let obj = {name: name}; | ||
if (Utils.isObject(process.parameters[name])) { | ||
Object.assign(obj, process.parameters[name]); | ||
} | ||
// Migrate from required to optional | ||
if (!obj.required) { | ||
obj.optional = true; | ||
} | ||
delete obj.required; | ||
// Add to list of ordered params | ||
params.push(obj); | ||
} | ||
delete process.parameter_order; | ||
process.parameters = params; | ||
} | ||
// Set required field description if not a string | ||
@@ -1792,52 +974,28 @@ if (typeof process.description !== 'string') { | ||
// Parameters | ||
if (Utils.isObject(process.parameters)) { | ||
for(var key in process.parameters) { | ||
process.parameters[key] = upgradeParamAndReturn(process.parameters[key], version); | ||
// Update parameters | ||
if (Array.isArray(process.parameters)) { | ||
for(let i in process.parameters) { | ||
let param = process.parameters[i]; | ||
if (!Utils.isObject(param)) { | ||
continue; | ||
} | ||
// Set required field description if not a string | ||
if (typeof param.description !== 'string') { | ||
param.description = ""; | ||
} | ||
// Upgrade parameter schema | ||
process.parameters[i] = upgradeSchema(param, version); | ||
} | ||
} | ||
else { | ||
process.parameters = {}; | ||
process.parameters = []; | ||
} | ||
// Return value | ||
process.returns = upgradeParamAndReturn(process.returns, version); | ||
if (isVersion03) { | ||
// exception object | ||
if (Utils.isObject(process.exceptions)) { | ||
for(let key in process.exceptions) { | ||
var e = process.exceptions[key]; | ||
if (typeof e.message === 'undefined') { | ||
process.exceptions[key] = Object.assign({}, e, { | ||
message: e.description | ||
}); | ||
} | ||
} | ||
} | ||
// examples object | ||
if (Utils.isObject(process.examples)) { | ||
var examples = []; | ||
for(let key in process.examples) { | ||
var old = process.examples[key]; | ||
var example = { | ||
title: old.summary || key, | ||
description: old.description | ||
}; | ||
if (old.process_graph) { | ||
example.process_graph = old.process_graph; | ||
} | ||
examples.push(example); | ||
} | ||
process.examples = examples; | ||
} | ||
// Fill parameter order | ||
if (typeof process.parameters === 'object' && !Array.isArray(process.parameter_order)) { | ||
var parameter_order = Object.keys(process.parameters); | ||
if (parameter_order.length > 1) { | ||
process.parameter_order = parameter_order; | ||
} | ||
} | ||
// Update return value | ||
if (!Utils.isObject(process.returns)) { | ||
process.returns = {}; | ||
} | ||
process.returns = upgradeSchema(process.returns, version, false); | ||
@@ -1847,37 +1005,20 @@ return process; | ||
}; | ||
} | ||
function upgradeParamAndReturn(obj, version) { | ||
// Not an object => return minimum required fields | ||
if (!Utils.isObject(obj)) { | ||
return { | ||
description: "", | ||
schema: {} | ||
}; | ||
function upgradeSchema(obj, version, isParam = true) { | ||
var schema = {}; | ||
if (obj.schema && typeof obj.schema === 'object') { // array or object? | ||
schema = obj.schema; | ||
} | ||
var param = Object.assign({}, obj); | ||
// v0.3 => v0.4: mime_type => media_type | ||
if (Utils.compareVersion(version, "0.3.x") === 0 && typeof param.mime_type !== 'undefined') { | ||
param.media_type = param.mime_type; | ||
delete param.mime_type; | ||
} | ||
// Set required fields if not valid yet | ||
if (typeof param.description !== 'string') { | ||
param.description = ""; | ||
} | ||
if (typeof param.schema !== 'object' || !param.schema) { | ||
param.schema = {}; | ||
} | ||
if (Utils.compareVersion(version, "0.4.x") <= 0) { | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
// Remove anyOf/oneOf wrapper | ||
for(var type in {anyOf: null, oneOf: null}) { | ||
if (Array.isArray(param.schema[type])) { | ||
if (typeof param.schema.default !== 'undefined') { | ||
param.default = param.schema.default; | ||
for(let type of ['anyOf', 'oneOf']) { | ||
if (Array.isArray(schema[type])) { | ||
// Parameters only: Move default value to parameter-level | ||
if (isParam && typeof schema.default !== 'undefined') { | ||
obj.default = schema.default; | ||
} | ||
param.schema = param.schema[type]; | ||
// Move array one level up, removing anyOf and oneOf | ||
schema = schema[type]; | ||
break; | ||
@@ -1887,37 +1028,69 @@ } | ||
// Remove default value from schema, add on parameter-level instead | ||
var moveMediaType = (Utils.compareVersion(version, "0.4.x") <= 0 && typeof param.media_type !== 'undefined'); | ||
var schemas = Array.isArray(param.schema) ? param.schema : [param.schema]; | ||
for(var i in schemas) { | ||
if (typeof schemas[i].default !== 'undefined') { | ||
param.default = schemas[i].default; | ||
delete schemas[i].default; | ||
let moveMediaType = (Versions.compare(version, "0.4.x") <= 0 && typeof obj.media_type !== 'undefined'); | ||
let schemas = Array.isArray(schema) ? schema : [schema]; | ||
for(let subSchema of schemas) { | ||
// Rename format to subtype recursively | ||
subSchema = renameFormat(subSchema); | ||
// Parameters only: Move default value to parameter-level | ||
if (isParam && typeof subSchema.default !== 'undefined') { | ||
obj.default = subSchema.default; | ||
delete subSchema.default; | ||
} | ||
// v0.3 => v0.4: mime_type => media_type | ||
// Replace media_type field with contentMediaType from JSON Schemas | ||
if (moveMediaType) { | ||
schemas[i].contentMediaType = param.media_type; | ||
subSchema.contentMediaType = obj.media_type; | ||
} | ||
renameFormat(schemas[i]); | ||
} | ||
// Remove the media type, has been moved to JSON Schema above. | ||
// Remove the media type | ||
if (moveMediaType) { | ||
delete param.media_type; | ||
delete obj.media_type; | ||
} | ||
} | ||
return param; | ||
obj.schema = schema; | ||
return obj; | ||
} | ||
function renameFormat(schema) { | ||
for(var i in schema) { | ||
if (i === 'format') { | ||
schema.subtype = schema.format; | ||
if (!['date-time', 'time', 'date', 'uri'].includes(schema.format)) { | ||
delete schema.format; | ||
} | ||
if (Utils.isObject(schema) && typeof schema.type !== 'undefined' && typeof schema.format === 'string') { | ||
switch(schema.format) { | ||
case 'url': | ||
schema.format = 'uri'; | ||
break; | ||
case 'proj-definition': | ||
schema.deprecated = true; | ||
break; | ||
case 'callback': | ||
schema.format = 'process-graph'; | ||
if (Utils.isObject(schema.parameters)) { | ||
let params = []; | ||
for(let name in schema.parameters) { | ||
let paramSchema = schema.parameters[name]; | ||
let param = { | ||
name: name, | ||
description: typeof paramSchema.description === 'string' ? paramSchema.description : "", | ||
schema: paramSchema | ||
}; | ||
params.push(param); | ||
} | ||
schema.parameters = params; | ||
} | ||
break; | ||
} | ||
else if (schema[i] && typeof schema[i] === 'object') { | ||
renameFormat(schema[i]); | ||
schema.subtype = schema.format; | ||
// Leave format for "well-known" formats defined in JSON Schema | ||
if (!['date-time', 'time', 'date', 'uri'].includes(schema.format)) { | ||
delete schema.format; | ||
} | ||
} | ||
for(let i in schema) { | ||
if (schema[i] && typeof schema[i] === 'object') { | ||
schema[i] = renameFormat(schema[i]); | ||
} | ||
} | ||
return schema; | ||
} | ||
@@ -1927,232 +1100,4 @@ | ||
/***/ }), | ||
/* 12 */ | ||
/***/ (function(module, exports) { | ||
if(typeof __WEBPACK_EXTERNAL_MODULE__12__ === 'undefined') {var e = new Error("Cannot find module 'ajv'"); e.code = 'MODULE_NOT_FOUND'; throw e;} | ||
module.exports = __WEBPACK_EXTERNAL_MODULE__12__; | ||
/***/ }), | ||
/* 13 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const BaseProcess = __webpack_require__(3); | ||
const Utils = __webpack_require__(0); | ||
module.exports = class ProcessRegistry { | ||
constructor() { | ||
// Keys added to this object must be lowercase! | ||
this.processes = {}; | ||
} | ||
addFromResponse(response) { | ||
for(var i in response.processes) { | ||
this.add(response.processes[i]); | ||
} | ||
} | ||
add(process) { | ||
this.processes[process.id] = new BaseProcess(process); | ||
} | ||
count() { | ||
return Utils.size(this.processes); | ||
} | ||
get(id) { | ||
if (typeof id === 'string') { | ||
var pid = id.toLowerCase(); | ||
if (typeof this.processes[pid] !== 'undefined') { | ||
return this.processes[pid]; | ||
} | ||
} | ||
return null; | ||
} | ||
getSchema(id) { | ||
var p = this.get(id); | ||
return p !== null ? p.schema : null; | ||
} | ||
getProcessSchemas() { | ||
return Object.values(this.processes).map(impl => impl.schema); | ||
} | ||
}; | ||
/***/ }), | ||
/* 14 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
const Utils = __webpack_require__(0); | ||
var FeatureList = { | ||
// Manual assignment of the endpoints above to individual features. | ||
// A functionality is considered supported when ALL of the corresponding endpoints are supported. | ||
features: { | ||
'Basic functionality': [ | ||
'get /collections', | ||
'get /collections/{}', | ||
'get /processes', | ||
'get /file_formats' | ||
], | ||
'Authenticate with HTTP Basic': [ // TODO: Remove later because this auth method should not be used | ||
'get /credentials/basic', | ||
// 'get /me' // not necessarily needed (just outputs metadata) | ||
], | ||
'Authenticate with OpenID Connect': [ // TODO: Remove later because the user doesn't care HOW the auth works | ||
'get /credentials/oidc', | ||
// 'get /me' // not necessarily needed (just outputs metadata) | ||
], | ||
'Batch processing': [ | ||
'get /jobs', | ||
'post /jobs', | ||
'get /jobs/{}', | ||
// 'patch /jobs/{}', // not necessarily needed (can be achieved by deleting and re-creating) | ||
'delete /jobs/{}', | ||
'get /jobs/{}/logs', | ||
'get /jobs/{}/results', | ||
'post /jobs/{}/results', | ||
// 'delete /jobs/{}/results' // not necessarily needed (can be deleted by deleting the entire job) | ||
], | ||
'Estimate processing costs': [ | ||
'get /jobs/{}/estimate' | ||
], | ||
'Preview processing results': [ | ||
'post /result' | ||
], | ||
'Secondary web services': [ | ||
'get /service_types', | ||
'get /services', | ||
'post /services', | ||
'get /services/{}', | ||
// 'patch /services/{}', // not necessarily needed (can be achieved by deleting and re-creating) | ||
'delete /services/{}', | ||
'get /services/{}/logs' | ||
], | ||
'File storage': [ | ||
'get /files/{}', | ||
'get /files/{}/{}', | ||
'put /files/{}/{}', | ||
'delete /files/{}/{}' | ||
], | ||
'Stored process graphs': [ | ||
'get /process_graphs', | ||
'post /process_graphs', | ||
'get /process_graphs/{}', | ||
// 'patch /process_graphs/{}', // not necessarily needed (can be achieved by deleting and re-creating) | ||
'delete /process_graphs/{}' | ||
], | ||
'Validate process graphs': [ | ||
'post /validation', | ||
], | ||
'Notifications and monitoring': [ | ||
'get /subscription' | ||
], | ||
'User defined functions (UDF)': [ | ||
'get /udf_runtimes' | ||
] | ||
}, | ||
legacyFeatures: { | ||
'post /result': { | ||
'post /preview': ["0.3.*"] | ||
}, | ||
'get /file_formats': { | ||
'get /output_formats': ["0.3.*", "0.4.*"] | ||
} | ||
}, | ||
getListForVersion(version) { | ||
var list = {}; | ||
for(var feature in this.features) { | ||
list[feature] = []; | ||
for(var i in this.features[feature]) { | ||
var endpoint = this.findLegacyEndpoint(version, this.features[feature][i]); | ||
list[feature].push(endpoint); | ||
} | ||
} | ||
return list; | ||
}, | ||
findLegacyEndpoint(version, endpoint, method = null) { | ||
if (method !== null) { | ||
endpoint = this.endpointToString(method, endpoint); | ||
} | ||
if (typeof this.legacyFeatures[endpoint] === 'object') { | ||
var legacy = this.legacyFeatures[endpoint]; | ||
for(var legacyEndpoint in legacy) { | ||
for(var i in legacy[legacyEndpoint]) { | ||
var legacyVersion = legacy[legacyEndpoint][i]; | ||
if (Utils.compareVersion(version, legacyVersion) === 0) { | ||
return legacyEndpoint; | ||
} | ||
} | ||
} | ||
} | ||
return endpoint; | ||
}, | ||
getFeatures() { | ||
return Object.keys(this.features); | ||
}, | ||
getFeatureCount() { | ||
return Object.keys(this.features).length; | ||
}, | ||
endpointsToStringList(endpoints) { | ||
var list = []; | ||
for(let i in endpoints) { | ||
for(let j in endpoints[i].methods) { | ||
list.push(this.endpointToString(endpoints[i].methods[j], endpoints[i].path)); | ||
} | ||
} | ||
return list; | ||
}, | ||
endpointToString(method, path) { | ||
// allow arbitrary parameter names => don't care about content in curly brackets | ||
let request = method + ' ' + path.replace(/{[^}]+}/g, '{}'); | ||
return request.toLowerCase(); | ||
}, | ||
getReport(endpoints, version, convert = true) { | ||
var supportedFeatureCount = 0; | ||
var supportedEndpoints = convert ? this.endpointsToStringList(endpoints) : endpoints; | ||
var status = this.getListForVersion(version); | ||
// Assign each functionality a supported flag (0 = none, 1 = partially, 2 = fully) | ||
Object.keys(status).forEach(key => { | ||
let requiredEndpoints = status[key]; | ||
// Get a list of unsupported, but required endpoints | ||
let unsupported = requiredEndpoints.filter(requiredEndpoint => !supportedEndpoints.includes(requiredEndpoint)); | ||
switch(unsupported.length) { | ||
// No unsupported endpoints => fully supported | ||
case 0: | ||
status[key] = 2; | ||
supportedFeatureCount++; | ||
break; | ||
// All endpoints are unsupported | ||
case requiredEndpoints.length: | ||
status[key] = 0; | ||
break; | ||
// Some endpoints are supported => partially supported | ||
default: | ||
status[key] = 1; | ||
} | ||
}); | ||
return { | ||
count: supportedFeatureCount, | ||
list: status | ||
}; | ||
} | ||
}; | ||
module.exports = FeatureList; | ||
/***/ }) | ||
/******/ ]); | ||
}); |
@@ -1,1 +0,1 @@ | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(function(){try{return require("ajv")}catch(e){}}());else if("function"==typeof define&&define.amd)define(["ajv"],t);else{var r="object"==typeof exports?t(function(){try{return require("ajv")}catch(e){}}()):t(e.ajv);for(var s in r)("object"==typeof exports?exports:e)[s]=r[s]}}(window,function(e){return function(e){var t={};function r(s){if(t[s])return t[s].exports;var a=t[s]={i:s,l:!1,exports:{}};return e[s].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=e,r.c=t,r.d=function(e,t,s){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(r.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)r.d(s,a,function(t){return e[t]}.bind(null,a));return s},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=7)}([function(e,t,r){const s=r(9);var a={compareVersion(e,t){try{return s(e,t)}catch(e){return null}},isObject:e=>"object"==typeof e&&e===Object(e)&&!Array.isArray(e),size:e=>"object"==typeof e&&null!==e?Array.isArray(e)?e.length:Object.keys(e).length:0,replacePlaceholders(e,t={}){if("string"==typeof e&&this.isObject(t))for(var r in t)e=e.replace("{"+r+"}",t[r]);return e}};e.exports=a},function(e,t,r){const s=r(0),a={MultipleResultNodes:"Multiple result nodes specified for process graph.",StartNodeMissing:"No start nodes found for process graph.",ResultNodeMissing:"No result node found for process graph.",MultipleResultNodesCallback:"Multiple result nodes specified for the callback in the process '{process_id}' (node: '{node_id}').",StartNodeMissingCallback:"No start nodes found for the callback in the process '{process_id}' (node: '{node_id}')'.",ResultNodeMissingCallback:"No result node found for the callback in the process '{process_id}' (node: '{node_id}').",ReferencedNodeMissing:"Referenced node '{node_id}' doesn't exist.",NodeIdInvalid:"Invalid node id specified in process graph.",NodeInvalid:"Process graph node '{node_id}' is not a valid object.",ProcessIdMissing:"Process graph node '{node_id}' doesn't contain a process id.",CallbackArgumentInvalid:"Invalid callback argument '{argument}' requested in the process '{process_id}' (node: '{node_id}').",ProcessUnsupported:"Process '{process}' is not supported.",ProcessArgumentUnsupported:"Process '{process}' does not support argument '{argument}'.",ProcessArgumentRequired:"Process '{process}' requires argument '{argument}'.",ProcessArgumentInvalid:"The argument '{argument}' in process '{process}' is invalid: {reason}",VariableValueMissing:"No value specified for process graph variable '{variable_id}'.",VariableDefaultValueTypeInvalid:"The default value specified for the process graph variable '{variable_id}' is not of type '{type}'.",VariableValueTypeInvalid:"The value specified for the process graph variable '{variable_id}' is not of type '{type}'.",VariableIdInvalid:"A specified variable ID is not valid.",VariableTypeInvalid:"The data type specified for the process graph variable '{variable_id}' is invalid. Must be one of: string, boolean, number, array or object."};e.exports=class extends Error{constructor(e,t={}){super(),this.variables=t,"string"==typeof a[e]?(this.code=e,this.message=s.replacePlaceholders(a[e],t)):(this.code=e.replace(/[^\w\d]+/g,""),this.message=e)}toJSON(){return{code:this.code,message:this.message}}}},function(e,t,r){const s=r(1),a=r(0);e.exports=class e{constructor(e,t,r){if("string"!=typeof t||0===t.length)throw new s("NodeIdInvalid");if(!a.isObject(e))throw new s("NodeInvalid",{node_id:t});if("string"!=typeof e.process_id)throw new s("ProcessIdMissing",{node_id:t});this.id=t,this.processGraph=r,this.process_id=e.process_id,this.arguments=a.isObject(e.arguments)?JSON.parse(JSON.stringify(e.arguments)):{},this.description=e.description||null,this.isResultNode=e.result||!1,this.expectsFrom=[],this.passesTo=[],this.result=null,this.resultsAvailableFrom=[]}getProcessGraph(){return this.processGraph}getArgumentNames(){return Object.keys(this.arguments)}hasArgument(e){return e in this.arguments}getArgumentType(t){return e.getType(this.arguments[t])}getRawArgument(e){return this.arguments[e]}getRawArgumentValue(t){var r=this.arguments[t];switch(e.getType(r)){case"result":return r.from_node;case"callback":return r.callback;case"callback-argument":return r.from_argument;default:return r}}getArgument(e,t){return void 0===this.arguments[e]?t:this.processArgument(this.arguments[e])}processArgument(t){switch(e.getType(t)){case"result":return this.processGraph.getNode(t.from_node).getResult();case"callback":return t.callback;case"callback-argument":return this.processGraph.getParameter(t.from_argument);case"variable":return this.processGraph.getVariableValue(t.variable_id);case"array":case"object":for(var r in t)t[r]=this.processArgument(t[r]);return t;default:return t}}static getType(e,t="null"){return"object"==typeof e?null===e?t:Array.isArray(e)?"array":e.hasOwnProperty("callback")?"callback":e.hasOwnProperty("variable_id")?"variable":e.hasOwnProperty("from_node")?"result":e.hasOwnProperty("from_argument")?"callback-argument":"object":typeof e}isStartNode(){return 0===this.expectsFrom.length}addPreviousNode(e){this.expectsFrom.push(e)}getPreviousNodes(){return this.expectsFrom}addNextNode(e){this.passesTo.push(e)}getNextNodes(){return this.passesTo}reset(){this.result=null,this.resultsAvailableFrom=[]}setDescription(e){this.description="string"==typeof e?e:null}setResult(e){this.result=e}getResult(){return this.result}solveDependency(e){return null!==e&&this.expectsFrom.includes(e)&&this.resultsAvailableFrom.push(e),this.expectsFrom.length===this.resultsAvailableFrom.length}}},function(e,t,r){const s=r(4),a=r(1),i=r(2),o=r(5);e.exports=class{constructor(e,t=null){this.schema=e,this.jsonSchema=null===t?new s:t}async validate(e){var t=e.getArgumentNames().filter(e=>void 0===this.schema.parameters[e]);if(t.length>0)throw new a("ProcessArgumentUnsupported",{process:this.schema.id,argument:t[0]});for(let t in this.schema.parameters){let r=this.schema.parameters[t],s=e.getRawArgument(t);if(await this.validateArgument(s,e,t,r))continue;let i=await this.jsonSchema.validateJson(s,r.schema);if(i.length>0)throw new a("ProcessArgumentInvalid",{process:this.schema.id,argument:t,reason:i.join("; ")})}}async validateArgument(e,t,r,n){let c=i.getType(e);if(e instanceof o)return await e.validate(!0),!0;switch(c){case"undefined":if(n.required)throw new a("ProcessArgumentRequired",{process:this.schema.id,argument:r});return!0;case"callback-argument":var l=t.getProcessGraph().getCallbackParameters();return s.isSchemaCompatible(n.schema,l[e.from_argument]);case"variable":var p={type:e.type||"string"};return s.isSchemaCompatible(n.schema,p);case"result":try{var d=t.getProcessGraph(),u=d.getNode(e.from_node).process_id,h=d.getProcess(u);return s.isSchemaCompatible(n.schema,h.schema.returns.schema)}catch(e){}break;case"array":case"object":return!0}return!1}async execute(){throw"execute not implemented yet"}test(){throw"test not implemented yet"}}},function(e,t,r){var s;try{s=r(12)}catch(e){}const a=r(0);e.exports=class e{constructor(){this.typeHints={"band-name":{type:"string",validate:"validateBandName"},"bounding-box":{type:"object",validate:"validateBoundingBox"},callback:{type:"object",validate:"validateCallback"},"collection-id":{type:"string",validate:"validateCollectionId"},"epsg-code":{type:"integer",validate:"validateEpsgCode"},geojson:{type:"object",validate:"validateGeoJson"},"job-id":{type:"string",validate:"validateJobId"},kernel:{type:"array",validate:"validateKernel"},"output-format":{type:"string",validate:"validateOutputFormat"},"output-format-options":{type:"object",validate:"validateOutputFormatOptions"},"process-graph-id":{type:"string",validate:"validateProcessGraphId"},"process-graph-variables":{type:"object",validate:"validateProcessGraphVariables"},"proj-definition":{type:"string",validate:"validateProjDefinition"},"raster-cube":{type:"object",validate:"validateRasterCube"},"temporal-interval":{type:"array",validate:"validateTemporalInterval"},"temporal-intervals":{type:"array",validate:"validateTemporalIntervals"},"vector-cube":{type:"object",validate:"validateVectorCube"}};var e={schemaId:"auto",format:"full",unknownFormats:Object.keys(this.typeHints)};if(!s)throw"ajv not installed";this.ajv=new s(e),this.ajv.addKeyword("parameters",{dependencies:["type","format"],metaSchema:{type:"object",additionalProperties:{type:"object"}},valid:!0,errors:!0}),this.ajv.addKeyword("typehint",{dependencies:["type"],validate:async(e,t,r)=>{if("object"==typeof this.typeHints[e]){var s=this.typeHints[e];if(s.type===r.type||Array.isArray(r.type)&&r.type.includes(s.type))return await this[s.validate](t)}return!1},async:!0,errors:!0}),this.outputFormats=null,this.geoJsonValidator=null}fixSchemaFormat(e){for(var t in e)"format"===t&&"string"==typeof e[t]&&Object.keys(this.typeHints).includes(e[t])&&(e.typehint=e[t]),e[t]&&"object"==typeof e[t]&&(e[t]=this.fixSchemaFormat(e[t]));return e}fixSchema(e){return void 0===(e=JSON.parse(JSON.stringify(e))).$schema&&(e.$schema="http://json-schema.org/draft-07/schema#"),e=this.fixSchemaFormat(e)}async validateJson(e,t){(t=this.fixSchema(t)).$async=!0;try{return await this.ajv.validate(t,e),[]}catch(e){if(Array.isArray(e.errors))return e.errors.map(e=>e.message);throw e}}validateJsonSchema(e){return e=JSON.parse(JSON.stringify(e)),e=this.fixSchema(e),this.ajv.compile(e).errors||[]}setGeoJsonSchema(e){var t=new s;this.geoJsonValidator=t.compile(e)}setOutputFormats(e){for(var t in this.outputFormats={},e)this.outputFormats[t.toUpperCase()]=e[t]}async validateBandName(){return!0}async validateBoundingBox(){return!0}async validateCallback(){return!0}async validateCollectionId(){return!0}async validateEpsgCode(e){if(e>=2e3)return!0;throw new s.ValidationError([{message:"Invalid EPSG code specified."}])}validateGeoJsonSimple(e){if(!a.isObject(e))throw new s.ValidationError([{message:"Invalid GeoJSON specified (not an object)."}]);if("string"!=typeof e.type)throw new s.ValidationError([{message:"Invalid GeoJSON specified (no type property)."}]);switch(e.type){case"Point":case"MultiPoint":case"LineString":case"MultiLineString":case"Polygon":case"MultiPolygon":if(!Array.isArray(e.coordinates))throw new s.ValidationError([{message:"Invalid GeoJSON specified (Geometry has no valid coordinates member)."}]);return!0;case"GeometryCollection":if(!Array.isArray(e.geometries))throw new s.ValidationError([{message:"Invalid GeoJSON specified (GeometryCollection has no valid geometries member)."}]);return!0;case"Feature":if(null!==e.geometry&&!a.isObject(e.geometry))throw new s.ValidationError([{message:"Invalid GeoJSON specified (Feature has no valid geometry member)."}]);if(null!==e.properties&&!a.isObject(e.properties))throw new s.ValidationError([{message:"Invalid GeoJSON specified (Feature has no valid properties member)."}]);return!0;case"FeatureCollection":if(!Array.isArray(e.features))throw new s.ValidationError([{message:"Invalid GeoJSON specified (FeatureCollection has no valid features member)."}]);return!0;default:throw new s.ValidationError([{message:"Invalid GeoJSON type specified."}])}}async validateGeoJson(e){if(null!==this.geoJsonValidator){if(!this.geoJsonValidator(e))throw new s.ValidationError(this.geoJsonValidator.errors);return!0}return this.validateGeoJsonSimple(e)}async validateJobId(){return!0}async validateKernel(){return!0}async validateOutputFormat(e){if(a.isObject(this.outputFormats)&&!(e.toUpperCase()in this.outputFormats))throw new s.ValidationError([{message:"Output format not supported."}]);return!0}async validateOutputFormatOptions(){return!0}async validateProcessGraphId(){return!0}async validateProcessGraphVariables(){return!0}async validateProjDefinition(e){if(!e.toLowerCase().includes("+proj"))throw new s.ValidationError([{message:"Invalid PROJ string specified (doesn't contain '+proj')."}]);return!0}async validateRasterCube(){return!0}async validateTemporalInterval(){return!0}async validateTemporalIntervals(e){return 0===e.filter(e=>!this.validateTemporalInterval(e)).length}async validateVectorCube(){return!0}static isSchemaCompatible(t,r,s=!1,i=!1){var o=this._convertSchemaToArray(t),n=this._convertSchemaToArray(r);return o.filter(t=>{for(var r in n){var o=n[r];if("string"!=typeof t.type||!s&&"string"!=typeof o.type)return!0;if(t.type===o.type||i&&("array"===t.type||"object"===t.type)||"number"===t.type&&"integer"===o.type||!s&&"integer"===t.type&&"number"===o.type)if("array"===t.type&&a.isObject(t.items)&&a.isObject(o.items)){if(i&&e.isSchemaCompatible(t.items,o,s))return!0;if(e.isSchemaCompatible(t.items,o.items,s))return!0}else{if("object"===t.type&&a.isObject(t.properties)&&a.isObject(o.properties))return!0;if(!(s||"string"==typeof t.format&&"string"==typeof o.format))return!0;if("string"!=typeof t.format)return!0;if(t.format===o.format)return!0}}return!1}).length>0}static _convertSchemaToArray(e){return e.oneOf||e.anyOf?e.oneOf||e.anyOf:Array.isArray(e.type)?e.type.map(t=>Object.assign({},e,{type:t})):[e]}static async getTypeForValue(t,r){var s=new e,a=[];for(var i in t){0===(await s.validateJson(r,t[i])).length&&a.push(String(i))}return a.length>1?a:a[0]}}},function(e,t,r){const s=r(6),a=r(1),i=r(2),o=r(0),n=["string","number","boolean","array","object"];e.exports=class e{constructor(e,t){this.json=e,this.processRegistry=t,this.nodes={},this.startNodes={},this.resultNode=null,this.childrenProcessGraphs=[],this.parentNode=null,this.parentProcessId=null,this.parentParameterName=null,this.variables={},this.parsed=!1,this.validated=!1,this.errors=new s,this.parameters={}}toJSON(){return this.json}createNodeInstance(e,t,r){return new i(e,t,r)}createProcessGraphInstance(t){return new e(t,this.processRegistry)}setParent(e,t){e instanceof i?(this.parentNode=e,this.parentProcessId=e.process_id):(this.parentNode=null,this.parentProcessId=e),this.parentParameterName=t}isValid(){return this.validated&&0===this.errors.count()}addError(e){this.errors.add(e)}parse(){if(!this.parsed){for(let e in this.json)this.nodes[e]=this.createNodeInstance(this.json[e],e,this);var e=e=>this.parentProcessId?new a(e+"Callback",{process_id:this.parentProcessId,node_id:this.parentNode?this.parentNode.id:"N/A"}):new a(e);for(let r in this.nodes){var t=this.nodes[r];if(t.isResultNode){if(null!==this.resultNode)throw e("MultipleResultNodes");this.resultNode=t}this.parseArguments(r,t)}if(!this.findStartNodes())throw e("StartNodeMissing");if(null===this.resultNode)throw e("ResultNodeMissing");this.parsed=!0}}async validate(e=!0){if(this.validated)return null;this.validated=!0;try{this.parse()}catch(t){if(this.addError(t),e)throw t}return await this.validateNodes(this.getStartNodes(),e),this.errors}async execute(e=null){return await this.validate(),this.reset(),this.setParameters(e),await this.executeNodes(this.getStartNodes()),this.getResultNode()}async validateNodes(e,t,r=null){if(0!==e.length){var a=e.map(async e=>{if(e.solveDependency(r)){try{await this.validateNode(e)}catch(e){if(e instanceof s){if(this.errors.merge(e),t)throw e.first()}else if(this.addError(e),t)throw e}await this.validateNodes(e.getNextNodes(),t,e)}});await Promise.all(a)}}async validateNode(e){var t=this.getProcess(e);return await t.validate(e)}async executeNodes(e,t=null){if(0!==e.length){var r=e.map(async e=>{if(e.solveDependency(t)){var r=await this.executeNode(e);e.setResult(r),await this.executeNodes(e.getNextNodes(),e)}});return Promise.all(r)}}async executeNode(e){var t=this.getProcess(e);return await t.execute(e)}parseArguments(e,t,r){for(var s in void 0===r&&(r=t.arguments),r){var a=r[s];switch(i.getType(a)){case"result":this.connectNodes(t,a.from_node);break;case"variable":this.parseVariable(a);break;case"callback":a.callback=this.createProcessGraph(a.callback,t,s);break;case"callback-argument":this.parseCallbackArgument(t,a.from_argument);break;case"array":case"object":this.parseArguments(e,t,a)}}}parseCallbackArgument(e,t){var r=this.getCallbackParameters();if(!o.isObject(r)||!r.hasOwnProperty(t))throw new a("CallbackArgumentInvalid",{argument:t,node_id:e.id,process_id:e.process_id})}createProcessGraph(e,t,r){var s=this.createProcessGraphInstance(e);return s.setParent(t,r),s.parse(),this.childrenProcessGraphs.push(s),s}parseVariable(e){if("string"!=typeof e.variable_id)throw new a("VariableIdInvalid");var t={};if(void 0!==e.type&&!n.includes(e.type))throw new a("VariableTypeInvalid",e);t.type=void 0!==e.type?e.type:"string";var r=i.getType(e.default);if("undefined"!==r){if(r!==t.type)throw new a("VariableDefaultValueTypeInvalid",e);t.value=e.default}}setParameters(e){"object"==typeof e&&null!==e&&(this.parameters=e)}getParameter(e){return this.parameters[e]}setVariableValues(e){for(var t in e)this.setVariable(t,e[t])}setVariableValue(e,t){"object"!=typeof this.variables[e]&&(this.variables[e]={}),this.variables[e].value=t}getVariableValue(e){var t=this.variables[e];if("object"!=typeof t||void 0===t.value)throw new a("VariableValueMissing",{variable_id:e});if(i.getType(t.value)!==t.type)throw new a("VariableValueTypeInvalid",{variable_id:e,type:t.type});return this.variables[e].value}connectNodes(e,t){var r=this.nodes[t];if(void 0===r)throw new a("ReferencedNodeMissing",{node_id:t});e.addPreviousNode(r),r.addNextNode(e)}findStartNodes(){var e=!1;for(var t in this.nodes){var r=this.nodes[t];r.isStartNode()&&(this.startNodes[t]=r,e=!0)}return e}reset(){for(var e in this.nodes)this.nodes[e].reset();this.childrenProcessGraphs.forEach(e=>e.reset())}getResultNode(){return this.resultNode}getStartNodes(){return Object.values(this.startNodes)}getStartNodeIds(){return Object.keys(this.startNodes)}getNode(e){return this.nodes[e]}getNodeCount(){return o.size(this.nodes)}getNodes(){return this.nodes}getErrors(){return this.errors}getProcess(e){var t=this.processRegistry.get(e.process_id);if(null===t)throw new a("ProcessUnsupported",{process:e.process_id});return t}getParentProcess(){return this.processRegistry.get(this.parentProcessId)}getCallbackParameters(){var e=this.getParentProcess();if(!this.parentParameterName||!e)return{};var t=e.schema.parameters[this.parentParameterName].schema;if(o.isObject(t.parameters))return t.parameters;var r={},s=t.anyOf||t.oneOf||t.allOf;if(Array.isArray(s))for(let e in s){var a=s[e];o.isObject(a.parameters)&&Object.assign(r,a.parameters)}return r}}},function(e,t){e.exports=class{constructor(){this.errors=[]}first(){return this.errors[0]||null}last(){return this.errors[this.errors.length-1]||null}merge(e){this.errors=this.errors.concat(e.getAll())}add(e){this.errors.push(e)}count(){return this.errors.length}toJSON(){return this.errors.map(e=>"function"==typeof e.toJSON?e.toJSON():{code:"InternalError",message:e.message})}getMessage(){var e="";for(var t in this.errors)e+=parseInt(t,10)+1+". "+this.errors[t].message+"\r\n";return e.trim()}getAll(){return this.errors}}},function(e,t,r){const s=r(8),a=r(10),i=r(11),o=r(3),n=r(4),c=r(5),l=r(1),p=r(2),d=r(13),u=r(6),h=r(14),f=r(0);e.exports={MigrateCapabilities:s,MigrateCollections:a,MigrateProcesses:i,BaseProcess:o,JsonSchemaValidator:n,ProcessGraph:c,ProcessGraphError:l,ProcessGraphNode:p,ProcessRegistry:d,ErrorList:u,FeatureList:h,Utils:f}},function(e,t,r){const s=r(0);var a={guessApiVersion:e=>"string"==typeof e.api_version?e.api_version:"string"==typeof e.version?e.version:Array.isArray(e.endpoints)&&e.endpoints.filter(e=>"/output_formats"===e.path).length>0?"0.4":e.backend_version||e.title||e.description||e.links?"1.0":"0.3",convertCapabilitiesToLatestSpec(e,t=null,r=!0,a="Unknown",i="Unknown"){var o=Object.assign({},e);return null===t&&(t=this.guessApiVersion(o)),0===s.compareVersion(t,"0.3.x")&&void 0!==o.version&&delete o.version,s.isObject(o.billing)?o.billing=this.convertBillingToLatestSpec(o.billing,t):delete o.billing,o.endpoints=this.convertEndpointsToLatestSpec(o.endpoints,t),(r||"string"!=typeof o.api_version)&&(o.api_version="1.0.0"),"string"!=typeof o.backend_version&&(o.backend_version=i),"string"!=typeof o.title&&(o.title=a),"string"!=typeof o.description&&(o.description=""),Array.isArray(o.links)||(o.links=[]),o},convertBillingToLatestSpec(e,t){var r=Object.assign({},e);return 0===s.compareVersion(t,"0.3.x")&&Array.isArray(r.plans)&&(r.plans=r.plans.map(e=>("boolean"!=typeof e.paid&&(e.paid=!0,"string"==typeof e.name&&e.name.toLowerCase().includes("free")&&(e.paid=!1)),e))),r},convertEndpointsToLatestSpec(e,t){var r=[];return Array.isArray(e)&&(r=e.slice(0)),s.compareVersion(t,"0.3.x"),r},convertOutputFormatsToLatestSpec(e,t){return this.convertFileFormatsToLatestSpec(e,t)},convertFileFormatsToLatestSpec(e,t){var r=Object.assign({},e);return 0===s.compareVersion(t,"0.3.x")&&s.isObject(r.formats)&&(r=r.formats),s.compareVersion(t,"0.4.x")<=0&&s.isObject(r)&&(r={output:r}),s.isObject(r.input)||(r.input={}),s.isObject(r.output)||(r.output={}),r},convertServiceTypesToLatestSpec(e,t){var r=Object.assign({},e);return s.compareVersion(t,"0.4.x"),r},convertUdfRuntimesToLatestSpec(e,t){var r=Object.assign({},e);return s.compareVersion(t,"0.4.x"),r}};e.exports=a},function(e,t,r){var s,a,i;a=[],void 0===(i="function"==typeof(s=function(){var e=/^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+))?(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;function t(e){var t,r,s=e.replace(/^v/,"").replace(/\+.*$/,""),a=(r="-",-1===(t=s).indexOf(r)?t.length:t.indexOf(r)),i=s.substring(0,a).split(".");return i.push(s.substring(a+1)),i}function r(e){return isNaN(Number(e))?e:Number(e)}function s(t){if("string"!=typeof t)throw new TypeError("Invalid argument expected string");if(!e.test(t))throw new Error("Invalid argument not valid semver ('"+t+"' received)")}function a(e,a){[e,a].forEach(s);for(var i=t(e),o=t(a),n=0;n<Math.max(i.length-1,o.length-1);n++){var c=parseInt(i[n]||0,10),l=parseInt(o[n]||0,10);if(c>l)return 1;if(l>c)return-1}var p=i[i.length-1],d=o[o.length-1];if(p&&d){var u=p.split(".").map(r),h=d.split(".").map(r);for(n=0;n<Math.max(u.length,h.length);n++){if(void 0===u[n]||"string"==typeof h[n]&&"number"==typeof u[n])return-1;if(void 0===h[n]||"string"==typeof u[n]&&"number"==typeof h[n])return 1;if(u[n]>h[n])return 1;if(h[n]>u[n])return-1}}else if(p||d)return p?-1:1;return 0}var i=[">",">=","=","<","<="];return a.compare=function(e,t,r){switch(function(e){if("string"!=typeof e)throw new TypeError("Invalid operator type, expected string but got "+typeof e);if(-1===i.indexOf(e))throw new TypeError("Invalid operator, expected one of "+i.join("|"))}(r),r){case">":return a(e,t)>0;case">=":return a(e,t)>=0;case"<":return a(e,t)<0;case"<=":return a(e,t)<=0;default:return 0===a(e,t)}},a})?s.apply(t,a):s)||(e.exports=i)},function(e,t,r){const s=r(0);var a={convertCollectionToLatestSpec(e,t){if(!t||"string"!=typeof t)throw new Error("No version specified");if(s.compareVersion(t,"0.5.x")>=0)throw"Migrating collections from API version 0.4.0 is not supported yet";var r=Object.assign({},e);if(!Object.keys(r).length)return r;if(0===s.compareVersion(t,"0.3.x")){if(r.id=r.name,delete r.name,r.stac_version="0.6.1",Array.isArray(r.provider)&&(r.providers=r.provider,delete r.provider),"object"!=typeof r.properties&&(r.properties={}),null!==r["eo:bands"]&&"object"==typeof r["eo:bands"]&&!Array.isArray(r["eo:bands"])){var a=[];for(let e in r["eo:bands"]){var i=Object.assign({},r["eo:bands"][e]);i.name=e,void 0!==i.resolution&&void 0===i.gsd&&(i.gsd=i.resolution,delete i.resolution),void 0!==i.wavelength&&void 0===i.center_wavelength&&(i.center_wavelength=i.wavelength,delete i.wavelength),a.push(i)}r["eo:bands"]=a}for(let e in r)e.includes(":")&&(r.properties[e]=r[e],delete r[e])}return r}};e.exports=a},function(e,t,r){const s=r(0);var a={convertProcessToLatestSpec(e,t){if(!t||"string"!=typeof t)throw new Error("No version specified");var r=Object.assign({},e);let a=0===s.compareVersion(t,"0.3.x");if(a&&(r.id=r.name,delete r.name),"string"!=typeof r.id||0===r.id.length)return{};if("string"!=typeof r.description&&(r.description=""),s.isObject(r.parameters))for(var o in r.parameters)r.parameters[o]=i(r.parameters[o],t);else r.parameters={};if(r.returns=i(r.returns,t),a){if(s.isObject(r.exceptions))for(let e in r.exceptions){var n=r.exceptions[e];void 0===n.message&&(r.exceptions[e]=Object.assign({},n,{message:n.description}))}if(s.isObject(r.examples)){var c=[];for(let e in r.examples){var l=r.examples[e],p={title:l.summary||e,description:l.description};l.process_graph&&(p.process_graph=l.process_graph),c.push(p)}r.examples=c}if("object"==typeof r.parameters&&!Array.isArray(r.parameter_order)){var d=Object.keys(r.parameters);d.length>1&&(r.parameter_order=d)}}return r}};function i(e,t){if(!s.isObject(e))return{description:"",schema:{}};var r=Object.assign({},e);if(0===s.compareVersion(t,"0.3.x")&&void 0!==r.mime_type&&(r.media_type=r.mime_type,delete r.mime_type),"string"!=typeof r.description&&(r.description=""),"object"==typeof r.schema&&r.schema||(r.schema={}),s.compareVersion(t,"0.4.x")<=0){for(var a in{anyOf:null,oneOf:null})if(Array.isArray(r.schema[a])){void 0!==r.schema.default&&(r.default=r.schema.default),r.schema=r.schema[a];break}var i=s.compareVersion(t,"0.4.x")<=0&&void 0!==r.media_type,n=Array.isArray(r.schema)?r.schema:[r.schema];for(var c in n)void 0!==n[c].default&&(r.default=n[c].default,delete n[c].default),i&&(n[c].contentMediaType=r.media_type),o(n[c]);i&&delete r.media_type}return r}function o(e){for(var t in e)"format"===t?(e.subtype=e.format,["date-time","time","date","uri"].includes(e.format)||delete e.format):e[t]&&"object"==typeof e[t]&&o(e[t])}e.exports=a},function(t,r){if(void 0===e){var s=new Error("Cannot find module 'ajv'");throw s.code="MODULE_NOT_FOUND",s}t.exports=e},function(e,t,r){const s=r(3),a=r(0);e.exports=class{constructor(){this.processes={}}addFromResponse(e){for(var t in e.processes)this.add(e.processes[t])}add(e){this.processes[e.id]=new s(e)}count(){return a.size(this.processes)}get(e){if("string"==typeof e){var t=e.toLowerCase();if(void 0!==this.processes[t])return this.processes[t]}return null}getSchema(e){var t=this.get(e);return null!==t?t.schema:null}getProcessSchemas(){return Object.values(this.processes).map(e=>e.schema)}}},function(e,t,r){const s=r(0);var a={features:{"Basic functionality":["get /collections","get /collections/{}","get /processes","get /file_formats"],"Authenticate with HTTP Basic":["get /credentials/basic"],"Authenticate with OpenID Connect":["get /credentials/oidc"],"Batch processing":["get /jobs","post /jobs","get /jobs/{}","delete /jobs/{}","get /jobs/{}/logs","get /jobs/{}/results","post /jobs/{}/results"],"Estimate processing costs":["get /jobs/{}/estimate"],"Preview processing results":["post /result"],"Secondary web services":["get /service_types","get /services","post /services","get /services/{}","delete /services/{}","get /services/{}/logs"],"File storage":["get /files/{}","get /files/{}/{}","put /files/{}/{}","delete /files/{}/{}"],"Stored process graphs":["get /process_graphs","post /process_graphs","get /process_graphs/{}","delete /process_graphs/{}"],"Validate process graphs":["post /validation"],"Notifications and monitoring":["get /subscription"],"User defined functions (UDF)":["get /udf_runtimes"]},legacyFeatures:{"post /result":{"post /preview":["0.3.*"]},"get /file_formats":{"get /output_formats":["0.3.*","0.4.*"]}},getListForVersion(e){var t={};for(var r in this.features)for(var s in t[r]=[],this.features[r]){var a=this.findLegacyEndpoint(e,this.features[r][s]);t[r].push(a)}return t},findLegacyEndpoint(e,t,r=null){if(null!==r&&(t=this.endpointToString(r,t)),"object"==typeof this.legacyFeatures[t]){var a=this.legacyFeatures[t];for(var i in a)for(var o in a[i]){var n=a[i][o];if(0===s.compareVersion(e,n))return i}}return t},getFeatures(){return Object.keys(this.features)},getFeatureCount(){return Object.keys(this.features).length},endpointsToStringList(e){var t=[];for(let r in e)for(let s in e[r].methods)t.push(this.endpointToString(e[r].methods[s],e[r].path));return t},endpointToString:(e,t)=>(e+" "+t.replace(/{[^}]+}/g,"{}")).toLowerCase(),getReport(e,t,r=!0){var s=0,a=r?this.endpointsToStringList(e):e,i=this.getListForVersion(t);return Object.keys(i).forEach(e=>{let t=i[e];switch(t.filter(e=>!a.includes(e)).length){case 0:i[e]=2,s++;break;case t.length:i[e]=0;break;default:i[e]=1}}),{count:s,list:i}}};e.exports=a}])}); | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var r=t();for(var i in r)("object"==typeof exports?exports:e)[i]=r[i]}}(window,(function(){return function(e){var t={};function r(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{}};return e[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(i,n,function(t){return e[t]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=2)}([function(e,t){e.exports=class{static isObject(e){return"object"==typeof e&&e===Object(e)&&!Array.isArray(e)}static isNumeric(e){return!isNaN(parseFloat(e))&&isFinite(e)}static deepClone(e){return JSON.parse(JSON.stringify(e))}static normalizeUrl(e,t=null){let r=e.replace(/\/$/,"");return"string"==typeof t&&("/"!==t.substr(0,1)&&(t="/"+t),r+=t.replace(/\/$/,"")),r}}},function(e,t,r){const i=r(4),n=/^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+))?(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;class a{static compare(e,t,r=null){return null!==r?i.compare(e,t,r):i(e,t)}static validate(e){return"string"==typeof e&&n.test(e)}static findCompatible(e,t=!0,r=null,i=null){if(!Array.isArray(e)||0===e.length)return[];let n=e.filter(e=>{if("string"==typeof e.url&&a.validate(e.api_version)){let t=a.validate(r),n=a.validate(i);return t&&n?a.compare(e.api_version,r,">=")&&a.compare(e.api_version,i,"<="):t?a.compare(e.api_version,r,">="):!n||a.compare(e.api_version,i,"<=")}return!1});return 0===n.length?[]:n.sort((e,r)=>{let i=!1!==e.production,n=!1!==r.production;return t&&i!==n?i?-1:1:-1*a.compare(e.api_version,r.api_version)})}static findLatest(e,t=!0,r=null,i=null){let n=a.findCompatible(e,t,r,i);return n.length>0?n[0]:null}}e.exports=a},function(e,t,r){const i=r(3),n=r(5),a=r(6),s=r(1),o=r(0);e.exports={MigrateCapabilities:i,MigrateCollections:n,MigrateProcesses:a,Versions:s,Utils:o}},function(e,t,r){const i=r(0),n=r(1);e.exports=class{static guessApiVersion(e){if(!i.isObject(e))return"0.0.0";if(n.validate(e.api_version))return e.api_version;if(n.validate(e.version))return e.version;if(Array.isArray(e.endpoints)){if(e.endpoints.filter(e=>"/file_formats"===e.path||"/conformance"===e.path||"/files"===e.path).length>0)return"1.0.0";if(e.endpoints.filter(e=>"/output_formats"===e.path||"/files/{user_id}"===e.path).length>0)return"0.4.2";if(!(e.backend_version||e.title||e.description||e.links))return"0.3.1"}return"0.0.0"}static convertCapabilitiesToLatestSpec(e,t=null,r=!0,a=!0,s="unknown",o="Unknown",p="Unknown"){if(null===t&&(t=this.guessApiVersion(e)),"0.0.0"===t)return{};if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let l=i.deepClone(e);return l.api_version=r?"1.0.0":t,i.isObject(l.billing)?l.billing=this.convertBillingToLatestSpec(l.billing,t):delete l.billing,l.endpoints=this.convertEndpointsToLatestSpec(l.endpoints,t,a),!r&&n.compare(t,"0.4.x","=")?l.stac_version="0.6.2":(r||"string"!=typeof l.stac_version)&&(l.stac_version="0.9.0"),"string"!=typeof l.backend_version&&(l.backend_version=p),"string"!=typeof l.id&&(l.id=s),"string"!=typeof l.title&&(l.title=o),"string"!=typeof l.description&&(l.description=""),Array.isArray(l.links)||(l.links=[]),l}static convertBillingToLatestSpec(e,t){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let r={};return i.isObject(e)&&(r=i.deepClone(e)),"string"!=typeof r.currency&&(r.currency=null),r}static convertEndpointsToLatestSpec(e,t,r=!1){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";if(!Array.isArray(e))return[];let a=i.deepClone(e);return n.compare(t,"0.4.x","=")&&r&&(a=a.map(e=>{switch(e.path){case"/output_formats":e.path="/file_formats";break;case"/files/{user_id}":e.path="/files";break;case"/files/{user_id}/{path}":e.path="/files/{path}"}return e})),a}static convertOutputFormatsToLatestSpec(e,t){return this.convertFileFormatsToLatestSpec(e,t)}static convertFileFormatsToLatestSpec(e,t){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let r=i.deepClone(e);return n.compare(t,"0.4.x","=")&&i.isObject(r)&&(r={output:r}),i.isObject(r.input)||(r.input={}),i.isObject(r.output)||(r.output={}),r}static convertServiceTypesToLatestSpec(e,t){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let r=i.deepClone(e);if(n.compare(t,"0.4.x","="))for(let e in r)i.isObject(r[e])?(delete r[e].attributes,i.isObject(r[e].parameters)&&(r[e].configuration=r[e].parameters),delete r[e].parameters,Array.isArray(r[e].variables)&&(r[e].process_parameters=r[e].variables.map(e=>{let t={name:e.variable_id,description:"string"==typeof e.description?e.description:"",schema:{type:["string"==typeof e.type?e.type:"string","null"]}};return void 0!==e.default&&(t.default=e.default),t})),delete r[e].variables):r[e]={};return r}static convertUdfRuntimesToLatestSpec(e,t){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let r=i.deepClone(e);if(n.compare(t,"0.4.x","="))for(let e in r)i.isObject(r[e])?null===r[e].description&&(r[e].description=""):delete r[e];return r}}},function(e,t,r){var i,n,a;n=[],void 0===(a="function"==typeof(i=function(){var e=/^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+))?(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;function t(e){var t,r,i=e.replace(/^v/,"").replace(/\+.*$/,""),n=(r="-",-1===(t=i).indexOf(r)?t.length:t.indexOf(r)),a=i.substring(0,n).split(".");return a.push(i.substring(n+1)),a}function r(e){return isNaN(Number(e))?e:Number(e)}function i(t){if("string"!=typeof t)throw new TypeError("Invalid argument expected string");if(!e.test(t))throw new Error("Invalid argument not valid semver ('"+t+"' received)")}function n(e,n){[e,n].forEach(i);for(var a=t(e),s=t(n),o=0;o<Math.max(a.length-1,s.length-1);o++){var p=parseInt(a[o]||0,10),l=parseInt(s[o]||0,10);if(p>l)return 1;if(l>p)return-1}var c=a[a.length-1],u=s[s.length-1];if(c&&u){var f=c.split(".").map(r),d=u.split(".").map(r);for(o=0;o<Math.max(f.length,d.length);o++){if(void 0===f[o]||"string"==typeof d[o]&&"number"==typeof f[o])return-1;if(void 0===d[o]||"string"==typeof f[o]&&"number"==typeof d[o])return 1;if(f[o]>d[o])return 1;if(d[o]>f[o])return-1}}else if(c||u)return c?-1:1;return 0}var a=[">",">=","=","<","<="];return n.compare=function(e,t,r){switch(function(e){if("string"!=typeof e)throw new TypeError("Invalid operator type, expected string but got "+typeof e);if(-1===a.indexOf(e))throw new TypeError("Invalid operator, expected one of "+a.join("|"))}(r),r){case">":return n(e,t)>0;case">=":return n(e,t)>=0;case"<":return n(e,t)<0;case"<=":return n(e,t)<=0;default:return 0===n(e,t)}},n})?i.apply(t,n):i)||(e.exports=a)},function(e,t,r){const i=r(0),n=r(1),a={cube:"datacube",eo:"eo",label:"label",pc:"pointcloud",proj:"projection",sar:"sar",sat:"sat",sci:"scientific"},s={"item:license":"license","item:providers":"providers","eo:instrument":"instruments","eo:platform":"platform","eo:constellation":"constellation","eo:epsg":"proj:epsg","eo:off_nadir":"sat:off_nadir_angle","eo:azimuth":"sat:azimuth_angle","eo:sun_azimuth":"sat:sun_azimuth_angle","eo:sun_elevation":"sat:sun_elevation_angle","dtr:start_datetime":"start_datetime","dtr:end_datetime":"end_datetime","pc:schema":"pc:schemas","sar:type":"sar:product_type","sar:polarization":"sar:polarizations","sar:instrument":"instruments","sar:platform":"platform","sar:constellation":"constellation","sar:off_nadir":"sat:off_nadir_angle","sar:relative_orbit":"sat:relative_orbit","sar:pass_direction":"sat:orbit_state"},o=["cube:dimensions","sci:publications","sci:doi","sci:citation"];e.exports=class{static convertCollectionToLatestSpec(e,t){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let r=i.deepClone(e);if("string"!=typeof r.id||0===r.id.length)return{};if(n.validate(r.stac_version)&&!n.compare(r.stac_version,"0.9.0","<")||(r.stac_version="0.9.0"),i.isObject(r.extent)||(r.extent={}),n.compare(t,"0.4.x","=")){if(Array.isArray(r.extent.spatial)&&(r.extent.spatial={bbox:[r.extent.spatial]}),Array.isArray(r.extent.temporal)&&(r.extent.temporal={interval:[r.extent.temporal]}),i.isObject(r.properties)){i.isObject(r.other_properties)||(r.other_properties={});for(let e in r.properties)r.other_properties[e]={values:[r.properties[e]]}}delete r.properties;let e=i.isObject(r.other_properties)?r.other_properties:{};for(let t in e){let n=e[t];if(i.isObject(n)&&(Array.isArray(n.extent)||Array.isArray(n.values)))if(Array.isArray(n.extent))e[t]={min:n.extent[0],max:n.extent[1]};else{0===n.values.filter(e=>!Array.isArray(e)).length?n.values.length<2?e[t]=n.values[0]:e[t]=n.values.reduce((e,t)=>e.concat(t)):e[t]=n.values}else void 0===r[t]&&(r[t]=n),delete e[t]}delete r.other_properties,r.summaries=i.isObject(r.summaries)?r.summaries:{};for(let t in e){let i=e[t];"sar:pass_direction"===t&&(i=i.map(e=>null===e?"geostationary":e)),("sar:resolution"===t||"sar:pixel_spacing"===t||"sar:looks"===t)&&Array.isArray(i)&&i.length>=2?(r.summaries[t+"_range"]=i.slice(0,1),r.summaries[t+"_azimuth"]=i.slice(1,2),i.length>2&&(r.summaries[t+"_equivalent_number"]=i.slice(2,3))):"string"==typeof s[t]?r.summaries[s[t]]=i:o.includes(t)&&Array.isArray(i)&&1===i.length?r[t]=i[0]:r.summaries[t]=i}}"string"!=typeof r.description&&(r.description=""),i.isObject(r.extent.spatial)||(r.extent.spatial={}),i.isObject(r.extent.temporal)||(r.extent.temporal={}),"string"!=typeof r.license&&(r.license="proprietary"),i.isObject(r.summaries)||(r.summaries={}),i.isObject(r["cube:dimensions"])||(r["cube:dimensions"]={}),Array.isArray(r.links)||(r.links=[]),r.links=r.links.map(e=>(e.rel="string"==typeof e.rel?e.rel:"related",e));var p=Array.isArray(r.stac_extensions)?r.stac_extensions:[];for(var l in r){let e=null,t=l.split(":",1);"deprecated"===l||"version"===l?e="version":"string"==typeof a[t]&&(e=a[t]),null===e||p.includes(e)||p.push(e)}return p.sort(),r.stac_extensions=p,r}}},function(e,t,r){const i=r(0),n=r(1);function a(e,t,r=!0){var i={};if(e.schema&&"object"==typeof e.schema&&(i=e.schema),n.compare(t,"0.4.x","=")){for(let t of["anyOf","oneOf"])if(Array.isArray(i[t])){r&&void 0!==i.default&&(e.default=i.default),i=i[t];break}let a=n.compare(t,"0.4.x")<=0&&void 0!==e.media_type,o=Array.isArray(i)?i:[i];for(let t of o)t=s(t),r&&void 0!==t.default&&(e.default=t.default,delete t.default),a&&(t.contentMediaType=e.media_type);a&&delete e.media_type}return e.schema=i,e}function s(e){if(i.isObject(e)&&void 0!==e.type&&"string"==typeof e.format){switch(e.format){case"url":e.format="uri";break;case"proj-definition":e.deprecated=!0;break;case"callback":if(e.format="process-graph",i.isObject(e.parameters)){let t=[];for(let r in e.parameters){let i=e.parameters[r],n={name:r,description:"string"==typeof i.description?i.description:"",schema:i};t.push(n)}e.parameters=t}}e.subtype=e.format,["date-time","time","date","uri"].includes(e.format)||delete e.format}for(let t in e)e[t]&&"object"==typeof e[t]&&(e[t]=s(e[t]));return e}e.exports=class{static convertProcessToLatestSpec(e,t){if(n.compare(t,"0.3.x","<="))throw"Migrating from API version 0.3.0 and older is not supported.";let r=i.deepClone(e);if("string"!=typeof r.id||0===r.id.length)return{};if(n.compare(t,"0.4.x","=")){if(!Array.isArray(r.parameter_order)||0===r.parameter_order.length){r.parameter_order=[];for(let e in r.parameters)r.parameter_order.push(e)}let e=[];for(let t of r.parameter_order){let n={name:t};i.isObject(r.parameters[t])&&Object.assign(n,r.parameters[t]),n.required||(n.optional=!0),delete n.required,e.push(n)}delete r.parameter_order,r.parameters=e}if("string"!=typeof r.description&&(r.description=""),Array.isArray(r.parameters))for(let e in r.parameters){let n=r.parameters[e];i.isObject(n)&&("string"!=typeof n.description&&(n.description=""),r.parameters[e]=a(n,t))}else r.parameters=[];return i.isObject(r.returns)||(r.returns={}),r.returns=a(r.returns,t,!1),r}}}])})); |
{ | ||
"name": "@openeo/js-commons", | ||
"version": "0.5.0-alpha.1", | ||
"version": "1.0.0-rc.1", | ||
"author": "openEO Consortium", | ||
@@ -25,5 +25,2 @@ "contributors": [ | ||
], | ||
"peerDependencies": { | ||
"ajv": "^6.10.0" | ||
}, | ||
"devDependencies": { | ||
@@ -30,0 +27,0 @@ "@babel/core": "^7.0.0", |
@@ -6,6 +6,6 @@ # openeo-js-commons | ||
This library's version is **0.5.0-alpha.1** and supports **openEO API version 1.0.x**. Legacy versions are available as releases. | ||
This library's version is **1.0.0-rc.1** and supports **openEO API version 1.0.x**. Legacy versions are available as releases. | ||
## Features | ||
- Converting responses to the latest API version is supported for: | ||
- Converting responses from API version 0.4 to the latest API version is supported for: | ||
- Capabilities | ||
@@ -16,9 +16,10 @@ - Collections | ||
- Service Types | ||
- UDF Runtimes | ||
- Back-end feature detection | ||
- Process graph handling: | ||
- Parsing a process graph | ||
- Validation based on the JSON Schemas | ||
- Framework to implement process graph execution | ||
- JSON Schema validation for Process parameters and return values | ||
- Validate, compare and prioritize version numbers (e.g. for well-known discovery) | ||
**Note:** | ||
- Process graph parsing has been moved to [openeo-js-processgraphs](https://github.com/Open-EO/openeo-js-processgraphs). | ||
- Support for migrating from API 0.3 to the latest API version has been dropped. Use the library in version 0.4 instead. | ||
## Usage | ||
@@ -28,3 +29,3 @@ | ||
You can then require the parts of the library you want to use. For example: `const { FeatureList } = require('@openeo/js-commons');` | ||
You can then require the parts of the library you want to use. For example: `const { MigrateProcesses } = require('@openeo/js-commons');` | ||
@@ -34,11 +35,6 @@ In a web environment you can include the library as follows: | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/@openeo/js-commons@0.4/dist/main.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/@openeo/js-commons@1.0.0-rc.1/dist/main.min.js"></script> | ||
``` | ||
<!-- When releasing a stable release, change the version to @1 instead of 1.0.0 to allow backward-compatible upgrades --> | ||
This library has a peer dependency to `ajv`, so if you'd like to use process graph validation or execution you need to include `ajv` (v6.10) in your package.json or include it in your web page: | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/ajv@6.10/lib/ajv.min.js"></script> | ||
``` | ||
More information can be found in the [**JS commons documentation**](https://open-eo.github.io/openeo-js-commons/0.5.0-alpha.1/). | ||
More information can be found in the [**JS commons documentation**](https://open-eo.github.io/openeo-js-commons/1.0.0-rc.1/). |
@@ -5,14 +5,4 @@ // Migrations | ||
const MigrateProcesses = require('./migrate/processes'); | ||
// Process graphs | ||
const BaseProcess = require('./processgraph/process'); | ||
const JsonSchemaValidator = require('./processgraph/jsonschema'); | ||
const ProcessGraph = require('./processgraph/processgraph'); | ||
const ProcessGraphError = require('./processgraph/error'); | ||
const ProcessGraphNode = require('./processgraph/node'); | ||
const ProcessRegistry = require('./processgraph/registry'); | ||
// Others | ||
const ErrorList = require('./errorlist'); | ||
const FeatureList = require('./featurelist'); | ||
const Versions = require('./versions'); | ||
const Utils = require('./utils'); | ||
@@ -24,13 +14,4 @@ | ||
MigrateProcesses, | ||
BaseProcess, | ||
JsonSchemaValidator, | ||
ProcessGraph, | ||
ProcessGraphError, | ||
ProcessGraphNode, | ||
ProcessRegistry, | ||
ErrorList, | ||
FeatureList, | ||
Utils | ||
Versions, | ||
Utils, | ||
}; |
const Utils = require('../utils.js'); | ||
const Versions = require('../versions.js'); | ||
var MigrateCapabilities = { | ||
const NO_VERSION = "0.0.0"; | ||
guessApiVersion(capabilities) { | ||
if (typeof capabilities.api_version === 'string') { | ||
return capabilities.api_version; | ||
} | ||
else if (typeof capabilities.version === 'string') { | ||
return capabilities.version; | ||
} | ||
class MigrateCapabilities { | ||
/** | ||
* Tries to determine the API version from the capabilities object. | ||
* | ||
* Returns the version number, e.g. "0.4.2", "1.0.0" or "0.0.0" (if unknown). | ||
* | ||
* @param {object} capabilities | ||
* @returns {string} | ||
*/ | ||
static guessApiVersion(capabilities) { | ||
// No object passed | ||
if (!Utils.isObject(capabilities)) { | ||
return NO_VERSION; | ||
} | ||
// Get exact info from version fields | ||
if (Versions.validate(capabilities.api_version)) { | ||
return capabilities.api_version; | ||
} | ||
else if (Versions.validate(capabilities.version)) { | ||
return capabilities.version; | ||
} | ||
// Now we are really guessing | ||
else if (Array.isArray(capabilities.endpoints) && capabilities.endpoints.filter(e => e.path === '/output_formats').length > 0) { | ||
return "0.4"; | ||
} | ||
else if (!capabilities.backend_version && !capabilities.title && !capabilities.description && !capabilities.links) { | ||
return "0.3"; | ||
} | ||
else { // Latest version | ||
return "1.0"; | ||
} | ||
}, | ||
else if (Array.isArray(capabilities.endpoints)) { | ||
if (capabilities.endpoints.filter(e => e.path === '/file_formats' || e.path === '/conformance' || e.path === '/files').length > 0) { | ||
return "1.0.0"; | ||
} | ||
else if (capabilities.endpoints.filter(e => e.path === '/output_formats' || e.path === '/files/{user_id}').length > 0) { | ||
return "0.4.2"; | ||
} | ||
else if (!capabilities.backend_version && !capabilities.title && !capabilities.description && !capabilities.links) { | ||
return "0.3.1"; | ||
} | ||
} | ||
// Can't determine version | ||
return NO_VERSION; | ||
} | ||
// Always returns a copy of the input object | ||
convertCapabilitiesToLatestSpec(originalCapabilities, version = null, updateVersionNumber = true, title = "Unknown", backend_version = "Unknown") { | ||
var capabilities = Object.assign({}, originalCapabilities); | ||
static convertCapabilitiesToLatestSpec(originalCapabilities, version = null, updateVersionNumbers = true, updateEndpointPaths = true, id = "unknown", title = "Unknown", backend_version = "Unknown") { | ||
if (version === null) { | ||
version = this.guessApiVersion(capabilities); | ||
version = this.guessApiVersion(originalCapabilities); | ||
} | ||
// convert v0.3 capabilities to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// version => api_version | ||
if (typeof capabilities.version !== 'undefined') { | ||
delete capabilities.version; | ||
} | ||
// Return empty if version number is not available | ||
if (version === NO_VERSION) { | ||
return {}; | ||
} | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let capabilities = Utils.deepClone(originalCapabilities); | ||
// Fill & Update version number | ||
if (!updateVersionNumbers) { | ||
capabilities.api_version = version; | ||
} | ||
else { | ||
capabilities.api_version = "1.0.0"; | ||
} | ||
// Convert billing plans | ||
@@ -47,11 +78,19 @@ if (Utils.isObject(capabilities.billing)) { | ||
// Convert endpoints | ||
capabilities.endpoints = this.convertEndpointsToLatestSpec(capabilities.endpoints, version); | ||
capabilities.endpoints = this.convertEndpointsToLatestSpec(capabilities.endpoints, version, updateEndpointPaths); | ||
// Fill STAC Version field | ||
if (!updateVersionNumbers && Versions.compare(version, "0.4.x", "=")) { | ||
capabilities.stac_version = "0.6.2"; | ||
} | ||
else if (updateVersionNumbers || typeof capabilities.stac_version !== 'string') { | ||
capabilities.stac_version = "0.9.0"; | ||
} | ||
// Add missing fields with somewhat useful data | ||
if (updateVersionNumber || typeof capabilities.api_version !== 'string') { | ||
capabilities.api_version = "1.0.0"; | ||
} | ||
if (typeof capabilities.backend_version !== 'string') { | ||
capabilities.backend_version = backend_version; | ||
} | ||
if (typeof capabilities.id !== 'string') { | ||
capabilities.id = id; | ||
} | ||
if (typeof capabilities.title !== 'string') { | ||
@@ -68,53 +107,68 @@ capabilities.title = title; | ||
return capabilities; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertBillingToLatestSpec(originalBilling, version) { | ||
var billing = Object.assign({}, originalBilling); | ||
// convert v0.3 billing info to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// Add paid flag to billing plans | ||
if (Array.isArray(billing.plans)) { | ||
billing.plans = billing.plans.map(plan => { | ||
if (typeof plan.paid !== 'boolean') { | ||
plan.paid = true; | ||
if (typeof plan.name === 'string' && plan.name.toLowerCase().includes('free')) { | ||
plan.paid = false; | ||
} | ||
} | ||
return plan; | ||
}); | ||
} | ||
static convertBillingToLatestSpec(originalBilling, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let billing = {}; | ||
if (Utils.isObject(originalBilling)) { | ||
billing = Utils.deepClone(originalBilling); | ||
} | ||
if (typeof billing.currency !== 'string') { | ||
billing.currency = null; | ||
} | ||
return billing; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertEndpointsToLatestSpec(originalEndpoints, version) { | ||
var endpoints = []; | ||
if (Array.isArray(originalEndpoints)) { | ||
endpoints = originalEndpoints.slice(0); | ||
static convertEndpointsToLatestSpec(originalEndpoints, version, updatePaths = false) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
// convert v0.3 service types to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// Nothing to do as nothing has changed. | ||
if (!Array.isArray(originalEndpoints)) { | ||
return []; | ||
} | ||
let endpoints = Utils.deepClone(originalEndpoints); | ||
// convert v0.4 endpoints to v1.0 | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
if (updatePaths) { | ||
endpoints = endpoints.map(e => { | ||
switch (e.path) { | ||
case '/output_formats': | ||
e.path = '/file_formats'; | ||
break; | ||
case '/files/{user_id}': | ||
e.path = '/files'; | ||
break; | ||
case '/files/{user_id}/{path}': | ||
e.path = '/files/{path}'; | ||
break; | ||
} | ||
return e; | ||
}); | ||
} | ||
else { | ||
// Nothing to do as structure has not changed. | ||
} | ||
} | ||
return endpoints; | ||
}, | ||
} | ||
// Alias for convertFileFormatsToLatestSpec | ||
convertOutputFormatsToLatestSpec(originalFormats, version) { | ||
static convertOutputFormatsToLatestSpec(originalFormats, version) { | ||
return this.convertFileFormatsToLatestSpec(originalFormats, version); | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertFileFormatsToLatestSpec(originalFormats, version) { | ||
var formats = Object.assign({}, originalFormats); | ||
if (Utils.compareVersion(version, "0.3.x") === 0 && Utils.isObject(formats.formats)) { | ||
formats = formats.formats; | ||
static convertFileFormatsToLatestSpec(originalFormats, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let formats = Utils.deepClone(originalFormats); | ||
if (Utils.compareVersion(version, "0.4.x") <= 0 && Utils.isObject(formats)) { | ||
if (Versions.compare(version, "0.4.x", "=") && Utils.isObject(formats)) { | ||
formats = { | ||
@@ -133,20 +187,71 @@ output: formats | ||
return formats; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertServiceTypesToLatestSpec(originalTypes, version) { | ||
var types = Object.assign({}, originalTypes); | ||
// Nothing to do as nothing has changed in 0.3 and 0.4. | ||
if (Utils.compareVersion(version, "0.4.x") > 0) { | ||
// Add future changes here. | ||
static convertServiceTypesToLatestSpec(originalTypes, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let types = Utils.deepClone(originalTypes); | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
for(let t in types) { | ||
if (!Utils.isObject(types[t])) { | ||
types[t] = {}; | ||
continue; | ||
} | ||
// Remove attributes | ||
delete types[t].attributes; | ||
// Rename parameters to configuration | ||
if (Utils.isObject(types[t].parameters)) { | ||
types[t].configuration = types[t].parameters; | ||
} | ||
delete types[t].parameters; | ||
// Rename variables to process_parameters | ||
if (Array.isArray(types[t].variables)) { | ||
types[t].process_parameters = types[t].variables.map(v => { | ||
let param = { | ||
name: v.variable_id, | ||
description: typeof v.description === 'string' ? v.description : "", | ||
schema: { | ||
type: [ | ||
typeof v.type === 'string' ? v.type : "string", | ||
"null" | ||
] | ||
} | ||
}; | ||
if (typeof v.default !== 'undefined') { | ||
param.default = v.default; | ||
} | ||
return param; | ||
}); | ||
} | ||
delete types[t].variables; | ||
} | ||
} | ||
return types; | ||
}, | ||
} | ||
// Always returns a copy of the input object | ||
convertUdfRuntimesToLatestSpec(originalRuntimes, version) { | ||
var runtimes = Object.assign({}, originalRuntimes); | ||
static convertUdfRuntimesToLatestSpec(originalRuntimes, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
let runtimes = Utils.deepClone(originalRuntimes); | ||
// Nothing to do, was not supported in 0.3 and nothing changed in 0.4. | ||
if (Utils.compareVersion(version, "0.4.x") > 0) { | ||
// Add future changes here. | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
for(let r in runtimes) { | ||
if (!Utils.isObject(runtimes[r])) { | ||
delete runtimes[r]; | ||
continue; | ||
} | ||
// null is not allowed any longer, replace with empty string | ||
if (runtimes[r].description === null) { | ||
runtimes[r].description = ""; | ||
} | ||
} | ||
} | ||
@@ -156,4 +261,4 @@ return runtimes; | ||
}; | ||
} | ||
module.exports = MigrateCapabilities; |
const Utils = require('../utils.js'); | ||
const Versions = require('../versions.js'); | ||
var MigrateCollections = { | ||
const extMap = { | ||
"cube": "datacube", | ||
"eo": "eo", | ||
"label": "label", | ||
"pc": "pointcloud", | ||
"proj": "projection", | ||
"sar": "sar", | ||
"sat": "sat", | ||
"sci": "scientific" | ||
}; | ||
const fieldMap = { | ||
// Item to core | ||
'item:license': 'license', | ||
'item:providers': 'providers', | ||
// EO to core | ||
'eo:instrument': 'instruments', | ||
'eo:platform': 'platform', | ||
'eo:constellation': 'constellation', | ||
// EO to proj | ||
'eo:epsg': 'proj:epsg', | ||
// EO to sat | ||
'eo:off_nadir': 'sat:off_nadir_angle', | ||
'eo:azimuth': 'sat:azimuth_angle', | ||
'eo:sun_azimuth': 'sat:sun_azimuth_angle', | ||
'eo:sun_elevation': 'sat:sun_elevation_angle', | ||
// Datetime Range to core | ||
'dtr:start_datetime': 'start_datetime', | ||
'dtr:end_datetime': 'end_datetime', | ||
// Point Cloud | ||
'pc:schema': 'pc:schemas', | ||
// SAR rename | ||
'sar:type': 'sar:product_type', | ||
'sar:polarization': 'sar:polarizations', | ||
// SAR to core | ||
'sar:instrument': 'instruments', | ||
'sar:platform': 'platform', | ||
'sar:constellation': 'constellation', | ||
// SAR to sat | ||
'sar:off_nadir': 'sat:off_nadir_angle', | ||
'sar:relative_orbit': 'sat:relative_orbit', | ||
// The following four fields don't translate directly, see code below | ||
'sar:pass_direction': 'sat:orbit_state', | ||
// sar:resolution => sar:resolution_range, sar:resolution_azimuth | ||
// sar:pixel_spacing => sar:pixel_spacing_range, sar:pixel_spacing_azimuth | ||
// sar:looks => sar:looks_range, sar:looks_azimuth, sar:looks_equivalent_number (opt) | ||
}; | ||
const moveToRoot = [ | ||
'cube:dimensions', | ||
'sci:publications', | ||
'sci:doi', | ||
'sci:citation' | ||
]; | ||
class MigrateCollections { | ||
// Always returns a copy of the input collection object | ||
convertCollectionToLatestSpec(originalCollection, version) { | ||
if (!version || typeof version !== 'string') { | ||
throw new Error("No version specified"); | ||
static convertCollectionToLatestSpec(originalCollection, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
if (Utils.compareVersion(version, "0.5.x") >= 0) { | ||
throw "Migrating collections from API version 0.4.0 is not supported yet"; | ||
// Make sure we don't alter the original object | ||
let collection = Utils.deepClone(originalCollection); | ||
// If collection has no id => seems to be an invalid collection => abort | ||
if (typeof collection.id !== 'string' || collection.id.length === 0) { | ||
return {}; | ||
} | ||
var collection = Object.assign({}, originalCollection); | ||
if (!Object.keys(collection).length) { | ||
return collection; | ||
// Update stac_version | ||
if (!Versions.validate(collection.stac_version) || Versions.compare(collection.stac_version, "0.9.0", "<")) { | ||
collection.stac_version = "0.9.0"; | ||
} | ||
// convert v0.3 processes to v0.4 format | ||
if (Utils.compareVersion(version, "0.3.x") === 0) { | ||
// name => id | ||
collection.id = collection.name; | ||
delete collection.name; | ||
// Add stac_version | ||
collection.stac_version = '0.6.1'; | ||
// Rename provider => providers | ||
if (Array.isArray(collection.provider)) { | ||
collection.providers = collection.provider; | ||
delete collection.provider; | ||
// Add missing extent upfront. Makes the following code simpler as it works on the object. | ||
if (!Utils.isObject(collection.extent)) { | ||
collection.extent = {}; | ||
} | ||
// convert v0.4 collections to latest version | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
// Restructure spatial extent | ||
if (Array.isArray(collection.extent.spatial)) { | ||
collection.extent.spatial = { | ||
bbox: [ | ||
collection.extent.spatial | ||
] | ||
}; | ||
} | ||
if (typeof collection.properties !== 'object') { | ||
collection.properties = {}; | ||
// Restructure temporal extent | ||
if (Array.isArray(collection.extent.temporal)) { | ||
collection.extent.temporal = { | ||
interval: [ | ||
collection.extent.temporal | ||
] | ||
}; | ||
} | ||
// Migrate eo:bands | ||
if (collection['eo:bands'] !== null && typeof collection['eo:bands'] === 'object' && !Array.isArray(collection['eo:bands'])) { | ||
var bands = []; | ||
for(let key in collection['eo:bands']) { | ||
var band = Object.assign({}, collection['eo:bands'][key]); | ||
band.name = key; | ||
if (typeof band.resolution !== 'undefined' && typeof band.gsd === 'undefined') { | ||
band.gsd = band.resolution; | ||
delete band.resolution; | ||
// move properties to other_properties | ||
if (Utils.isObject(collection.properties)) { | ||
if (!Utils.isObject(collection.other_properties)) { | ||
collection.other_properties = {}; | ||
} | ||
for(let key in collection.properties) { | ||
collection.other_properties[key] = { | ||
values: [ | ||
collection.properties[key] | ||
] | ||
}; | ||
} | ||
} | ||
delete collection.properties; | ||
// now we can work on all properties and migrate to summaries | ||
let props = Utils.isObject(collection.other_properties) ? collection.other_properties : {}; | ||
for(let key in props) { | ||
let val = props[key]; | ||
if (Utils.isObject(val) && (Array.isArray(val.extent) || Array.isArray(val.values))) { | ||
if (Array.isArray(val.extent)) { | ||
props[key] = { | ||
min: val.extent[0], | ||
max: val.extent[1] | ||
}; | ||
} | ||
if (typeof band.wavelength !== 'undefined' && typeof band.center_wavelength === 'undefined') { | ||
band.center_wavelength = band.wavelength; | ||
delete band.wavelength; | ||
else { // val.value is an array | ||
let is2dArray = val.values.filter(v => !Array.isArray(v)).length === 0; | ||
if (is2dArray) { | ||
if (val.values.length < 2) { | ||
props[key] = val.values[0]; | ||
} | ||
else { | ||
props[key] = val.values.reduce((a, b) => a.concat(b)); | ||
} | ||
} | ||
else { | ||
props[key] = val.values; | ||
} | ||
} | ||
bands.push(band); | ||
} | ||
collection['eo:bands'] = bands; | ||
else { | ||
// If not valid, move to top-level | ||
if (typeof collection[key] === 'undefined') { | ||
collection[key] = val; | ||
} | ||
delete props[key]; | ||
} | ||
} | ||
// Move all other properties into properties. | ||
for (let key in collection) { | ||
if (key.includes(':')) { | ||
collection.properties[key] = collection[key]; | ||
delete collection[key]; | ||
delete collection.other_properties; | ||
collection.summaries = Utils.isObject(collection.summaries) ? collection.summaries : {}; | ||
for(let key in props) { | ||
let val = props[key]; | ||
if (key === 'sar:pass_direction') { | ||
// Convert null to geostationary | ||
val = val.map(v => v === null ? 'geostationary' : v); | ||
} | ||
// Convert arrays into separate fields as needed for some SAR fields | ||
if ((key === 'sar:resolution' || key === 'sar:pixel_spacing' || key === 'sar:looks') && Array.isArray(val) && val.length >= 2) { | ||
collection.summaries[key + '_range'] = val.slice(0,1); | ||
collection.summaries[key + '_azimuth'] = val.slice(1,2); | ||
if (val.length > 2) { | ||
collection.summaries[key + '_equivalent_number'] = val.slice(2,3); | ||
} | ||
} | ||
// Do the renaming of fields | ||
else if (typeof fieldMap[key] === 'string') { | ||
collection.summaries[fieldMap[key]] = val; | ||
} | ||
// Move invalid summaries to the top level | ||
else if (moveToRoot.includes(key) && Array.isArray(val) && val.length === 1) { | ||
collection[key] = val[0]; | ||
} | ||
// Do the general conversion | ||
else { | ||
collection.summaries[key] = val; | ||
} | ||
} | ||
} | ||
// Add missing required fields | ||
if (typeof collection.description !== 'string') { | ||
collection.description = ""; | ||
} | ||
if (!Utils.isObject(collection.extent.spatial)) { | ||
collection.extent.spatial = {}; | ||
} | ||
if (!Utils.isObject(collection.extent.temporal)) { | ||
collection.extent.temporal = {}; | ||
} | ||
if (typeof collection.license !== 'string') { | ||
collection.license = "proprietary"; | ||
} | ||
if (!Utils.isObject(collection.summaries)) { | ||
collection.summaries = {}; | ||
} | ||
if (!Utils.isObject(collection['cube:dimensions'])) { | ||
collection['cube:dimensions'] = {}; | ||
} | ||
// Fix links | ||
if (!Array.isArray(collection.links)) { | ||
collection.links = []; | ||
} | ||
// Add missing rel type | ||
collection.links = collection.links.map(l => { | ||
l.rel = typeof l.rel === 'string' ? l.rel : "related"; | ||
return l; | ||
}); | ||
// Fix stac_extensions | ||
var extensions = Array.isArray(collection.stac_extensions) ? collection.stac_extensions : []; | ||
for(var key in collection) { | ||
let ext = null; | ||
let prefix = key.split(':', 1); | ||
if (key === 'deprecated' || key === 'version') { | ||
ext = 'version'; | ||
} | ||
else if (typeof extMap[prefix] === 'string') { | ||
ext = extMap[prefix]; | ||
} | ||
if (ext !== null && !extensions.includes(ext)) { | ||
extensions.push(ext); | ||
} | ||
} | ||
extensions.sort(); | ||
collection.stac_extensions = extensions; | ||
return collection; | ||
} | ||
}; | ||
} | ||
module.exports = MigrateCollections; |
const Utils = require('../utils.js'); | ||
const Versions = require('../versions.js'); | ||
var MigrateProcesses = { | ||
class MigrateProcesses { | ||
// Always returns a copy of the input process object | ||
convertProcessToLatestSpec(originalProcess, version) { | ||
if (!version || typeof version !== 'string') { | ||
throw new Error("No version specified"); | ||
static convertProcessToLatestSpec(originalProcess, version) { | ||
if (Versions.compare(version, "0.3.x", "<=")) { | ||
throw "Migrating from API version 0.3.0 and older is not supported."; | ||
} | ||
// Make sure we don't alter the original object | ||
var process = Object.assign({}, originalProcess); | ||
let process = Utils.deepClone(originalProcess); | ||
let isVersion03 = Utils.compareVersion(version, "0.3.x") === 0; | ||
// name => id | ||
if (isVersion03) { | ||
process.id = process.name; | ||
delete process.name; | ||
} | ||
// If process has no id => seems to be an invalid process, abort | ||
// If process has no id => seems to be an invalid process => abort | ||
if (typeof process.id !== 'string' || process.id.length === 0) { | ||
@@ -26,2 +20,34 @@ return {}; | ||
// Convert the parameters from object to array | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
// Determine the parameter order | ||
if (!Array.isArray(process.parameter_order) || process.parameter_order.length === 0) { | ||
process.parameter_order = []; | ||
for(let param in process.parameters) { | ||
process.parameter_order.push(param); | ||
} | ||
} | ||
// Upgrade parameters and convert from array to object | ||
let params = []; | ||
for(let name of process.parameter_order) { | ||
// Add name | ||
let obj = {name: name}; | ||
if (Utils.isObject(process.parameters[name])) { | ||
Object.assign(obj, process.parameters[name]); | ||
} | ||
// Migrate from required to optional | ||
if (!obj.required) { | ||
obj.optional = true; | ||
} | ||
delete obj.required; | ||
// Add to list of ordered params | ||
params.push(obj); | ||
} | ||
delete process.parameter_order; | ||
process.parameters = params; | ||
} | ||
// Set required field description if not a string | ||
@@ -32,52 +58,28 @@ if (typeof process.description !== 'string') { | ||
// Parameters | ||
if (Utils.isObject(process.parameters)) { | ||
for(var key in process.parameters) { | ||
process.parameters[key] = upgradeParamAndReturn(process.parameters[key], version); | ||
// Update parameters | ||
if (Array.isArray(process.parameters)) { | ||
for(let i in process.parameters) { | ||
let param = process.parameters[i]; | ||
if (!Utils.isObject(param)) { | ||
continue; | ||
} | ||
// Set required field description if not a string | ||
if (typeof param.description !== 'string') { | ||
param.description = ""; | ||
} | ||
// Upgrade parameter schema | ||
process.parameters[i] = upgradeSchema(param, version); | ||
} | ||
} | ||
else { | ||
process.parameters = {}; | ||
process.parameters = []; | ||
} | ||
// Return value | ||
process.returns = upgradeParamAndReturn(process.returns, version); | ||
if (isVersion03) { | ||
// exception object | ||
if (Utils.isObject(process.exceptions)) { | ||
for(let key in process.exceptions) { | ||
var e = process.exceptions[key]; | ||
if (typeof e.message === 'undefined') { | ||
process.exceptions[key] = Object.assign({}, e, { | ||
message: e.description | ||
}); | ||
} | ||
} | ||
} | ||
// examples object | ||
if (Utils.isObject(process.examples)) { | ||
var examples = []; | ||
for(let key in process.examples) { | ||
var old = process.examples[key]; | ||
var example = { | ||
title: old.summary || key, | ||
description: old.description | ||
}; | ||
if (old.process_graph) { | ||
example.process_graph = old.process_graph; | ||
} | ||
examples.push(example); | ||
} | ||
process.examples = examples; | ||
} | ||
// Fill parameter order | ||
if (typeof process.parameters === 'object' && !Array.isArray(process.parameter_order)) { | ||
var parameter_order = Object.keys(process.parameters); | ||
if (parameter_order.length > 1) { | ||
process.parameter_order = parameter_order; | ||
} | ||
} | ||
// Update return value | ||
if (!Utils.isObject(process.returns)) { | ||
process.returns = {}; | ||
} | ||
process.returns = upgradeSchema(process.returns, version, false); | ||
@@ -87,37 +89,20 @@ return process; | ||
}; | ||
} | ||
function upgradeParamAndReturn(obj, version) { | ||
// Not an object => return minimum required fields | ||
if (!Utils.isObject(obj)) { | ||
return { | ||
description: "", | ||
schema: {} | ||
}; | ||
function upgradeSchema(obj, version, isParam = true) { | ||
var schema = {}; | ||
if (obj.schema && typeof obj.schema === 'object') { // array or object? | ||
schema = obj.schema; | ||
} | ||
var param = Object.assign({}, obj); | ||
// v0.3 => v0.4: mime_type => media_type | ||
if (Utils.compareVersion(version, "0.3.x") === 0 && typeof param.mime_type !== 'undefined') { | ||
param.media_type = param.mime_type; | ||
delete param.mime_type; | ||
} | ||
// Set required fields if not valid yet | ||
if (typeof param.description !== 'string') { | ||
param.description = ""; | ||
} | ||
if (typeof param.schema !== 'object' || !param.schema) { | ||
param.schema = {}; | ||
} | ||
if (Utils.compareVersion(version, "0.4.x") <= 0) { | ||
if (Versions.compare(version, "0.4.x", "=")) { | ||
// Remove anyOf/oneOf wrapper | ||
for(var type in {anyOf: null, oneOf: null}) { | ||
if (Array.isArray(param.schema[type])) { | ||
if (typeof param.schema.default !== 'undefined') { | ||
param.default = param.schema.default; | ||
for(let type of ['anyOf', 'oneOf']) { | ||
if (Array.isArray(schema[type])) { | ||
// Parameters only: Move default value to parameter-level | ||
if (isParam && typeof schema.default !== 'undefined') { | ||
obj.default = schema.default; | ||
} | ||
param.schema = param.schema[type]; | ||
// Move array one level up, removing anyOf and oneOf | ||
schema = schema[type]; | ||
break; | ||
@@ -127,39 +112,71 @@ } | ||
// Remove default value from schema, add on parameter-level instead | ||
var moveMediaType = (Utils.compareVersion(version, "0.4.x") <= 0 && typeof param.media_type !== 'undefined'); | ||
var schemas = Array.isArray(param.schema) ? param.schema : [param.schema]; | ||
for(var i in schemas) { | ||
if (typeof schemas[i].default !== 'undefined') { | ||
param.default = schemas[i].default; | ||
delete schemas[i].default; | ||
let moveMediaType = (Versions.compare(version, "0.4.x") <= 0 && typeof obj.media_type !== 'undefined'); | ||
let schemas = Array.isArray(schema) ? schema : [schema]; | ||
for(let subSchema of schemas) { | ||
// Rename format to subtype recursively | ||
subSchema = renameFormat(subSchema); | ||
// Parameters only: Move default value to parameter-level | ||
if (isParam && typeof subSchema.default !== 'undefined') { | ||
obj.default = subSchema.default; | ||
delete subSchema.default; | ||
} | ||
// v0.3 => v0.4: mime_type => media_type | ||
// Replace media_type field with contentMediaType from JSON Schemas | ||
if (moveMediaType) { | ||
schemas[i].contentMediaType = param.media_type; | ||
subSchema.contentMediaType = obj.media_type; | ||
} | ||
renameFormat(schemas[i]); | ||
} | ||
// Remove the media type, has been moved to JSON Schema above. | ||
// Remove the media type | ||
if (moveMediaType) { | ||
delete param.media_type; | ||
delete obj.media_type; | ||
} | ||
} | ||
return param; | ||
obj.schema = schema; | ||
return obj; | ||
} | ||
function renameFormat(schema) { | ||
for(var i in schema) { | ||
if (i === 'format') { | ||
schema.subtype = schema.format; | ||
if (!['date-time', 'time', 'date', 'uri'].includes(schema.format)) { | ||
delete schema.format; | ||
} | ||
if (Utils.isObject(schema) && typeof schema.type !== 'undefined' && typeof schema.format === 'string') { | ||
switch(schema.format) { | ||
case 'url': | ||
schema.format = 'uri'; | ||
break; | ||
case 'proj-definition': | ||
schema.deprecated = true; | ||
break; | ||
case 'callback': | ||
schema.format = 'process-graph'; | ||
if (Utils.isObject(schema.parameters)) { | ||
let params = []; | ||
for(let name in schema.parameters) { | ||
let paramSchema = schema.parameters[name]; | ||
let param = { | ||
name: name, | ||
description: typeof paramSchema.description === 'string' ? paramSchema.description : "", | ||
schema: paramSchema | ||
}; | ||
params.push(param); | ||
} | ||
schema.parameters = params; | ||
} | ||
break; | ||
} | ||
else if (schema[i] && typeof schema[i] === 'object') { | ||
renameFormat(schema[i]); | ||
schema.subtype = schema.format; | ||
// Leave format for "well-known" formats defined in JSON Schema | ||
if (!['date-time', 'time', 'date', 'uri'].includes(schema.format)) { | ||
delete schema.format; | ||
} | ||
} | ||
for(let i in schema) { | ||
if (schema[i] && typeof schema[i] === 'object') { | ||
schema[i] = renameFormat(schema[i]); | ||
} | ||
} | ||
return schema; | ||
} | ||
module.exports = MigrateProcesses; |
@@ -1,40 +0,59 @@ | ||
const compareVersions = require('compare-versions'); | ||
class Utils { | ||
var Utils = { | ||
/** | ||
* Checks whether a variable is a real object or not. | ||
* | ||
* This is a more strict version of `typeof x === 'object'` as this example would also succeeds for arrays and `null`. | ||
* This function only returns `true` for real objects and not for arrays, `null` or any other data types. | ||
* | ||
* @param {*} obj - A variable to check. | ||
* @returns {boolean} - `true` is the given variable is an object, `false` otherwise. | ||
*/ | ||
static isObject(obj) { | ||
return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj)); | ||
} | ||
compareVersion(v1, v2) { | ||
try { | ||
return compareVersions(v1, v2); | ||
} catch (error) { | ||
return null; | ||
} | ||
}, | ||
/** | ||
* Checks whether a variable is numeric. | ||
* | ||
* Numeric is every string with numeric data or a number, excluding NaN and finite numbers. | ||
* | ||
* @param {*} n - A variable to check. | ||
* @returns {boolean} - `true` is the given variable is numeric, `false` otherwise. | ||
*/ | ||
static isNumeric(n) { | ||
return !isNaN(parseFloat(n)) && isFinite(n); | ||
} | ||
/** | ||
* Deep clone for JSON-compatible data. | ||
* | ||
* @param {*} x | ||
* @returns {*} | ||
*/ | ||
static deepClone(x) { | ||
return JSON.parse(JSON.stringify(x)); | ||
} | ||
isObject(obj) { | ||
return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj)); | ||
}, | ||
size(obj) { | ||
if (typeof obj === 'object' && obj !== null) { | ||
if (Array.isArray(obj)) { | ||
return obj.length; | ||
/** | ||
* Normalize a URL (mostly handling slashes). | ||
* | ||
* @static | ||
* @param {string} baseUrl - The URL to normalize | ||
* @param {string} path - An optional path to add to the URL | ||
* @returns {string} Normalized URL. | ||
*/ | ||
static normalizeUrl(baseUrl, path = null) { | ||
let url = baseUrl.replace(/\/$/, ""); // Remove trailing slash from base URL | ||
if (typeof path === 'string') { | ||
if (path.substr(0, 1) !== '/') { | ||
path = '/' + path; // Add leading slash to path | ||
} | ||
else { | ||
return Object.keys(obj).length; | ||
} | ||
url = url + path.replace(/\/$/, ""); // Remove trailing slash from path | ||
} | ||
return 0; | ||
}, | ||
replacePlaceholders(message, variables = {}) { | ||
if (typeof message === 'string' && this.isObject(variables)) { | ||
for(var placeholder in variables) { | ||
message = message.replace('{' + placeholder + '}', variables[placeholder]); | ||
} | ||
} | ||
return message; | ||
return url; | ||
} | ||
}; | ||
} | ||
module.exports = Utils; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
1
1
94689
11
1696
36
1