diff --git a/package-lock.json b/package-lock.json index 4885ddcb..f21536c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "version": "18.14.2", "license": "Apache-2.0", "dependencies": { - "@elastic/elasticsearch": "9.1.0", + "@elastic/elasticsearch": "9.1.1", "@hapi/basic": "7.0.2", "@hapi/boom": "10.0.1", "@hapi/good": "9.0.1", - "@hapi/hapi": "21.4.2", + "@hapi/hapi": "21.4.3", "@hapi/inert": "7.1.0", "@hapi/vision": "7.0.3", "@mojaloop/central-services-logger": "11.9.0", @@ -46,13 +46,13 @@ "json-rules-engine": "7.3.1", "jsonwebtoken": "9.0.2", "lodash": "4.17.21", - "mongoose": "8.17.1", + "mongoose": "8.17.2", "multer": "2.0.2", "mustache": "4.2.0", "mv": "2.1.1", "node-dir": "0.1.17", "node-strings": "1.0.2", - "openapi-backend": "5.13.0", + "openapi-backend": "5.14.0", "parse-strings-in-object": "1.6.0", "passport": "0.7.0", "passport-jwt": "4.0.1", @@ -711,9 +711,9 @@ } }, "node_modules/@elastic/elasticsearch": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-9.1.0.tgz", - "integrity": "sha512-lV5uZlzBuXSkn7KdYaArqN3PJEO98bAKaSIO4Fh2B565/bJcnLLP3zbGX/h2cn2CbdfmLnoOczgzQ0H09KDuWQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-9.1.1.tgz", + "integrity": "sha512-s/JZtHZjtbAYC2gdSzm4LLOSReR724e7cf7ZauIAZlGvAyMgZPZCJpq7xHazSy4rZZhule4ubMs4vepBgWvKQA==", "license": "Apache-2.0", "dependencies": { "@elastic/transport": "^9.0.1", @@ -1051,9 +1051,9 @@ "license": "BSD-3-Clause" }, "node_modules/@hapi/hapi": { - "version": "21.4.2", - "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.4.2.tgz", - "integrity": "sha512-wkjpjc80Hnl0cBfDAG+EtX876AGtpg9fpt6q9GXTnGyoW5C4+hBCem8MmBPuwlhlfJ9U9FYGm3Yf7nWp9+/NDQ==", + "version": "21.4.3", + "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.4.3.tgz", + "integrity": "sha512-Q7g0ZY4gxU69wabFKH75qR0AFOdiOECj6vGqTHBSO5Lrwe6TwD8r9LkYQIbvtG8N423VDpdVsiZP8MnBwmD6Hw==", "license": "BSD-3-Clause", "dependencies": { "@hapi/accept": "^6.0.3", @@ -1064,13 +1064,13 @@ "@hapi/catbox": "^12.1.1", "@hapi/catbox-memory": "^6.0.2", "@hapi/heavy": "^8.0.1", - "@hapi/hoek": "^11.0.6", + "@hapi/hoek": "^11.0.7", "@hapi/mimos": "^7.0.1", - "@hapi/podium": "^5.0.1", - "@hapi/shot": "^6.0.1", + "@hapi/podium": "^5.0.2", + "@hapi/shot": "^6.0.2", "@hapi/somever": "^4.1.1", "@hapi/statehood": "^8.2.0", - "@hapi/subtext": "^8.1.0", + "@hapi/subtext": "^8.1.1", "@hapi/teamwork": "^6.0.0", "@hapi/topo": "^6.0.2", "@hapi/validate": "^2.0.1" @@ -1235,9 +1235,9 @@ } }, "node_modules/@hapi/shot": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-6.0.1.tgz", - "integrity": "sha512-s5ynMKZXYoDd3dqPw5YTvOR/vjHvMTxc388+0qL0jZZP1+uwXuUD32o9DuuuLsmTlyXCWi02BJl1pBpwRuUrNA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-6.0.2.tgz", + "integrity": "sha512-WKK1ShfJTrL1oXC0skoIZQYzvLsyMDEF8lfcWuQBjpjCN29qivr9U36ld1z0nt6edvzv28etNMOqUF4klnHryw==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^11.0.2", @@ -1290,9 +1290,9 @@ } }, "node_modules/@hapi/subtext": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@hapi/subtext/-/subtext-8.1.0.tgz", - "integrity": "sha512-PyaN4oSMtqPjjVxLny1k0iYg4+fwGusIhaom9B2StinBclHs7v46mIW706Y+Wo21lcgulGyXbQrmT/w4dus6ww==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@hapi/subtext/-/subtext-8.1.1.tgz", + "integrity": "sha512-ex1Y2s/KuJktS8Ww0k6XJ5ysSKrzNym4i5pDVuCwlSgHHviHUsT1JNzE6FYhNU9TTHSNdyfue/t2m89bpkX9Jw==", "license": "BSD-3-Clause", "dependencies": { "@hapi/boom": "^10.0.1", @@ -2088,6 +2088,23 @@ } } }, + "node_modules/@mojaloop/central-services-shared/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", + "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, "node_modules/@mojaloop/central-services-shared/node_modules/@hapi/boom": { "version": "9.1.4", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", @@ -2183,6 +2200,15 @@ "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", "license": "BSD-3-Clause" }, + "node_modules/@mojaloop/central-services-shared/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@mojaloop/central-services-shared/node_modules/dotenv": { "version": "17.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", @@ -2195,6 +2221,30 @@ "url": "https://dotenvx.com" } }, + "node_modules/@mojaloop/central-services-shared/node_modules/openapi-backend": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.13.0.tgz", + "integrity": "sha512-dE2f0MCpL2ZKctVG4w+Nl+1C4GQput5dQ4QYy6XeblGvM2Z1b3JcP2FzL6DrLzzDYEKTLgAaQM3jD7yhftwKSg==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.1.0", + "ajv": "^8.6.2", + "bath-es5": "^3.0.3", + "cookie": "^1.0.1", + "dereference-json-schema": "^0.2.1", + "lodash": "^4.17.15", + "mock-json-schema": "^1.0.7", + "openapi-schema-validator": "^12.0.0", + "openapi-types": "^12.0.2", + "qs": "^6.9.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/anttiviljami" + } + }, "node_modules/@mojaloop/event-sdk": { "version": "14.6.1", "resolved": "https://registry.npmjs.org/@mojaloop/event-sdk/-/event-sdk-14.6.1.tgz", @@ -2279,6 +2329,32 @@ "@rollup/rollup-linux-x64-musl": "4.45.1" } }, + "node_modules/@mojaloop/ml-schema-transformer-lib/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", + "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@mojaloop/ml-schema-transformer-lib/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@mojaloop/ml-schema-transformer-lib/node_modules/ilp-packet": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ilp-packet/-/ilp-packet-2.2.0.tgz", @@ -2306,6 +2382,30 @@ "integrity": "sha512-JTRqe1iQuB0weu1Mppu0YUApL6CU0CxtmB8pJIhTyTm4X7rmps6p18GVRzwHRfvSP7YUGakzgA+xPqZseF1FOA==", "license": "Apache-2.0" }, + "node_modules/@mojaloop/ml-schema-transformer-lib/node_modules/openapi-backend": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.13.0.tgz", + "integrity": "sha512-dE2f0MCpL2ZKctVG4w+Nl+1C4GQput5dQ4QYy6XeblGvM2Z1b3JcP2FzL6DrLzzDYEKTLgAaQM3jD7yhftwKSg==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.1.0", + "ajv": "^8.6.2", + "bath-es5": "^3.0.3", + "cookie": "^1.0.1", + "dereference-json-schema": "^0.2.1", + "lodash": "^4.17.15", + "mock-json-schema": "^1.0.7", + "openapi-schema-validator": "^12.0.0", + "openapi-types": "^12.0.2", + "qs": "^6.9.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/anttiviljami" + } + }, "node_modules/@mojaloop/ml-testing-toolkit-shared-lib": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/@mojaloop/ml-testing-toolkit-shared-lib/-/ml-testing-toolkit-shared-lib-14.2.0.tgz", @@ -12122,9 +12222,10 @@ } }, "node_modules/mongoose": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.1.tgz", - "integrity": "sha512-aodS4cacux5caoxB5ErEwRmrafIUsVRJxHnvP7URnSUnTenr32j1qBVV+KjYxryyLSisQkxglAFF69TNLeZTLg==", + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.2.tgz", + "integrity": "sha512-zp0xzzphKZrr9azDayn0w08gp0jzeZO8EQKvCKNd6c1I/Y9PRmTKj7x+60IWF3+DKmqWq7WZ4ihDEvOFUtWkSA==", + "license": "MIT", "dependencies": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -13268,9 +13369,9 @@ } }, "node_modules/openapi-backend": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.13.0.tgz", - "integrity": "sha512-dE2f0MCpL2ZKctVG4w+Nl+1C4GQput5dQ4QYy6XeblGvM2Z1b3JcP2FzL6DrLzzDYEKTLgAaQM3jD7yhftwKSg==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-5.14.0.tgz", + "integrity": "sha512-oHCI2vWwx7RY+PTYdaYx5VhJFYsg2+iT+//V1PBQDSknEAzDw7JBtOL2kfHl+QT3pjOso0etVKb0zJ6ZPDshJQ==", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.1.0", diff --git a/package.json b/package.json index 8f61f34f..de573079 100644 --- a/package.json +++ b/package.json @@ -71,11 +71,11 @@ "genexec": "pkg -t node8-win ." }, "dependencies": { - "@elastic/elasticsearch": "9.1.0", + "@elastic/elasticsearch": "9.1.1", "@hapi/basic": "7.0.2", "@hapi/boom": "10.0.1", "@hapi/good": "9.0.1", - "@hapi/hapi": "21.4.2", + "@hapi/hapi": "21.4.3", "@hapi/inert": "7.1.0", "@hapi/vision": "7.0.3", "@mojaloop/central-services-logger": "11.9.0", @@ -108,13 +108,13 @@ "json-rules-engine": "7.3.1", "jsonwebtoken": "9.0.2", "lodash": "4.17.21", - "mongoose": "8.17.1", + "mongoose": "8.17.2", "multer": "2.0.2", "mustache": "4.2.0", "mv": "2.1.1", "node-dir": "0.1.17", "node-strings": "1.0.2", - "openapi-backend": "5.13.0", + "openapi-backend": "5.14.0", "parse-strings-in-object": "1.6.0", "passport": "0.7.0", "passport-jwt": "4.0.1", diff --git a/src/lib/api-management.js b/src/lib/api-management.js index 1f8ededb..f2172af0 100644 --- a/src/lib/api-management.js +++ b/src/lib/api-management.js @@ -29,6 +29,7 @@ const addFormats = require('ajv-formats') const OpenApiBackend = require('openapi-backend').default +const fs = require('node:fs') const Utils = require('./utils') const path = require('path') const Config = require('./config') @@ -37,9 +38,24 @@ const OpenApiMockHandler = require('./mocking/openApiMockHandler') const apiDefinitionsPath = 'spec_files/api_definitions/' +// check if the file contains URL and return it instead of the file name +const checkUrl = async fileName => { + const buffer = Buffer.alloc(10) + const fileHandle = await fs.promises.open(fileName, 'r') + try { + await fileHandle.read(buffer, 0, 10, 0) + } finally { + await fileHandle.close() + } + const prefix = buffer.toString('utf8').replace('\uFEFF', '') // Remove BOM + return (prefix.startsWith('"http')) + ? JSON.parse(fs.readFileSync(fileName).toString('utf8')) + : fileName +} + const validateDefinition = async (apiFilePath) => { const newApi = new OpenApiBackend({ - definition: path.join(apiFilePath), + definition: await checkUrl(path.join(apiFilePath)), customizeAjv: ajv => addFormats(ajv), strict: true, quick: true diff --git a/test/api_spec_url.json b/test/api_spec_url.json new file mode 100644 index 00000000..899ed19d --- /dev/null +++ b/test/api_spec_url.json @@ -0,0 +1 @@ +"http://localhost/api_spec_sync_empty.yaml" \ No newline at end of file diff --git a/test/unit/lib/api-management.test.js b/test/unit/lib/api-management.test.js index d1c0c438..b4c34ba7 100644 --- a/test/unit/lib/api-management.test.js +++ b/test/unit/lib/api-management.test.js @@ -63,6 +63,9 @@ describe('API Management', () => { it('should not throw an error', async () => { await expect(APIManagement.addDefinition(specFilePrefix + 'api_spec_sync.yaml', 'name', '1.0', 'false')).resolves.toBeUndefined() }) + it('should load definition from URL', async () => { + await expect(APIManagement.addDefinition(specFilePrefix + 'api_spec_url.json', 'name', '1.0', 'false')).resolves.toBeUndefined() + }) }) describe('deleteDefinition', () => { it('Happy path', async () => {