From 7d34a175a44f814319f16ebef9dec4865e200c8f Mon Sep 17 00:00:00 2001 From: Carl Brugger Date: Mon, 6 Nov 2023 16:53:10 -0600 Subject: [PATCH] feat: JSON Schema implementation (#294) * feat: JSON Schema implementation * temp commit * add new description methods * add exports and better descriptions * fix: required constraint * chore: update package.json * create tests * run prettier * Accept multiple JSON schemas * feat: update test --------- Co-authored-by: madaley1 --- .changeset/cyan-oranges-care.md | 5 + package-lock.json | 658 +++++++++++++++++- plugins/json-schema/package.json | 36 +- plugins/json-schema/src/index.ts | 28 + plugins/json-schema/src/setup.factory.ts | 199 ++++++ .../json-schema/tests/json-schema.e2e.spec.ts | 133 ++++ .../json-schema/tests/test-server/server.ts | 13 + 7 files changed, 1059 insertions(+), 13 deletions(-) create mode 100644 .changeset/cyan-oranges-care.md create mode 100644 plugins/json-schema/src/index.ts create mode 100644 plugins/json-schema/src/setup.factory.ts create mode 100644 plugins/json-schema/tests/json-schema.e2e.spec.ts create mode 100644 plugins/json-schema/tests/test-server/server.ts diff --git a/.changeset/cyan-oranges-care.md b/.changeset/cyan-oranges-care.md new file mode 100644 index 000000000..0fe4f9136 --- /dev/null +++ b/.changeset/cyan-oranges-care.md @@ -0,0 +1,5 @@ +--- +'@flatfile/plugin-json-schema': patch +--- + +Introducting the @flatfile/plugin-convert-json-schema plugin to configure a Flatfile Space based on a provided JSON Schema. diff --git a/package-lock.json b/package-lock.json index a8b90c3b0..0ea09c876 100644 --- a/package-lock.json +++ b/package-lock.json @@ -781,6 +781,14 @@ "npm": ">=6.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "engines": { + "node": ">=14" + } + }, "node_modules/@flatfile/api": { "version": "1.5.34", "resolved": "https://registry.npmjs.org/@flatfile/api/-/api-1.5.34.tgz", @@ -888,6 +896,10 @@ "resolved": "plugins/merge-connection", "link": true }, + "node_modules/@flatfile/plugin-convert-json-schema": { + "resolved": "plugins/json-schema", + "link": true + }, "node_modules/@flatfile/plugin-dedupe": { "resolved": "plugins/dedupe", "link": true @@ -916,10 +928,6 @@ "resolved": "plugins/json-extractor", "link": true }, - "node_modules/@flatfile/plugin-json-schema": { - "resolved": "plugins/json-schema", - "link": true - }, "node_modules/@flatfile/plugin-openapi": { "resolved": "plugins/openapi", "link": true @@ -998,6 +1006,61 @@ "resolved": "utils/testing", "link": true }, + "node_modules/@hyperjump/json-pointer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@hyperjump/json-pointer/-/json-pointer-1.0.1.tgz", + "integrity": "sha512-vV2pSc7JCwbKEMzh8kr/ICZdO+UZbA3aZ7N8t7leDi9cduWKa9yoP5LS04LnsbErlPbUNHvWBFlbTaR/o/uf7A==", + "dependencies": { + "just-curry-it": "^5.3.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/json-schema": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@hyperjump/json-schema/-/json-schema-1.6.4.tgz", + "integrity": "sha512-GaROZHhIBvOcigBZkQVMcuBJFe0RKhOxydLoTWkqtyhAoX17RqK1kKgfs7oTll8jO42ITnC6Ehj7CjjMDMBBLQ==", + "dependencies": { + "@hyperjump/json-pointer": "^1.0.0", + "@hyperjump/pact": "^1.2.0", + "@hyperjump/uri": "^1.2.0", + "content-type": "^1.0.4", + "fastest-stable-stringify": "^2.0.2", + "just-curry-it": "^5.3.0", + "undici": "^5.19.1", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/pact": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@hyperjump/pact/-/pact-1.2.0.tgz", + "integrity": "sha512-+NirBesJkhgZMRXzza8flnh0wwIuHZ9wSYjXSNAA1KQjHtn4Nho1wi3Y5PC7izBvoPKrPFt7J+qtEUkosav+zQ==", + "dependencies": { + "just-curry-it": "^5.3.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, + "node_modules/@hyperjump/uri": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@hyperjump/uri/-/uri-1.2.2.tgz", + "integrity": "sha512-Zn8AZb/j54KKUCckmcOzKCSCKpIpMVBc60zYaajD8Dq/1g4UN6TfAFi+uDa5o/6rf+I+5xDZjZpdzwfuhlC0xQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jdesrosiers" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "license": "ISC", @@ -3632,6 +3695,19 @@ "dev": true, "license": "MIT" }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/adm-zip": { "version": "0.5.10", "license": "MIT", @@ -3728,6 +3804,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, "node_modules/array-union": { "version": "2.1.0", "dev": true, @@ -4044,6 +4126,60 @@ "node": ">=4" } }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/boolbase": { "version": "1.0.0", "dev": true, @@ -4150,6 +4286,15 @@ "dev": true, "license": "MIT" }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "license": "MIT", @@ -4357,10 +4502,45 @@ "version": "0.0.1", "license": "MIT" }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "license": "MIT" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, "node_modules/cookiejar": { "version": "2.1.4", "license": "MIT" @@ -4727,6 +4907,25 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "dev": true, @@ -4874,6 +5073,12 @@ "node": ">=10" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "node_modules/effect": { "version": "2.0.0-next.31", "resolved": "https://registry.npmjs.org/effect/-/effect-2.0.0-next.31.tgz", @@ -4913,6 +5118,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enquirer": { "version": "2.4.1", "dev": true, @@ -5040,6 +5254,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "license": "MIT", @@ -5091,6 +5311,15 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/execa": { "version": "5.1.1", "dev": true, @@ -5134,6 +5363,78 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/extendable-error": { "version": "0.1.7", "dev": true, @@ -5175,6 +5476,11 @@ "version": "2.1.1", "license": "MIT" }, + "node_modules/fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, "node_modules/fastq": { "version": "1.15.0", "dev": true, @@ -5200,6 +5506,39 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/find-up": { "version": "4.1.0", "license": "MIT", @@ -5278,6 +5617,24 @@ "url": "https://ko-fi.com/tunnckoCore/commissions" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-extra": { "version": "7.0.1", "dev": true, @@ -5697,6 +6054,22 @@ "entities": "^3.0.1" } }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.0", "license": "MIT", @@ -5861,6 +6234,15 @@ "version": "1.1.8", "license": "MIT" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "dev": true, @@ -7732,6 +8114,11 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/just-curry-it": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/just-curry-it/-/just-curry-it-5.3.0.tgz", + "integrity": "sha512-silMIRiFjUWlfaDhkgSzpuAyQ6EX/o09Eu8ZBfmFwQMbax7+LQzeIU2CBrICT6Ne4l86ITCGvUCBpCubWYy0Yw==" + }, "node_modules/kind-of": { "version": "6.0.3", "dev": true, @@ -7940,6 +8327,15 @@ "optional": true, "peer": true }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/meow": { "version": "6.1.1", "dev": true, @@ -7975,6 +8371,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, "node_modules/merge-stream": { "version": "2.0.0", "license": "MIT" @@ -8135,6 +8537,15 @@ "version": "1.4.0", "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/neo-async": { "version": "2.6.2", "dev": true, @@ -8255,6 +8666,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "license": "ISC", @@ -8576,6 +8999,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "license": "MIT", @@ -8603,6 +9035,12 @@ "dev": true, "license": "MIT" }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "dev": true, @@ -8820,6 +9258,19 @@ "node": ">= 6" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-agent": { "version": "6.3.1", "license": "MIT", @@ -8921,6 +9372,30 @@ "node": ">=8" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react-error-overlay": { "version": "6.0.9", "dev": true, @@ -9214,11 +9689,89 @@ "version": "4.0.0", "license": "ISC" }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "dev": true, "license": "ISC" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "dev": true, @@ -9555,6 +10108,15 @@ "node": ">=8" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stream-transform": { "version": "2.1.3", "dev": true, @@ -9804,6 +10366,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/trim-newlines": { "version": "3.0.1", "dev": true, @@ -10008,6 +10579,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.0", "dev": true, @@ -10107,6 +10691,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.0.tgz", + "integrity": "sha512-l3ydWhlhOJzMVOYkymLykcRRXqbUaQriERtR70B9LzNkZ4bX52Fc8wbTDneMiwo8T+AemZXvXaTx+9o5ROxrXg==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/universalify": { "version": "0.1.2", "license": "MIT", @@ -10114,6 +10709,15 @@ "node": ">= 4.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "funding": [ @@ -10154,6 +10758,27 @@ "node": ">= 4" } }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.1.0", "dev": true, @@ -10181,6 +10806,15 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/walker": { "version": "1.0.8", "license": "Apache-2.0", @@ -10584,9 +11218,21 @@ } }, "plugins/json-schema": { - "name": "@flatfile/plugin-json-schema", + "name": "@flatfile/plugin-convert-json-schema", "version": "0.0.2", - "license": "ISC" + "license": "ISC", + "dependencies": { + "@flatfile/api": "^1.5.33", + "@flatfile/plugin-space-configure": "^0.1.5", + "@hyperjump/json-schema": "^1.6.4", + "axios": "^1.5.1" + }, + "devDependencies": { + "express": "^4.18.2" + }, + "engines": { + "node": ">= 16" + } }, "plugins/merge-connection": { "name": "@flatfile/plugin-connect-via-merge", diff --git a/plugins/json-schema/package.json b/plugins/json-schema/package.json index 4bcaa5650..ef1bb4844 100644 --- a/plugins/json-schema/package.json +++ b/plugins/json-schema/package.json @@ -1,16 +1,38 @@ { - "name": "@flatfile/plugin-json-schema", + "name": "@flatfile/plugin-convert-json-schema", "version": "0.0.2", - "description": "A plugin for converting json schema to Flatfile blueprint.", + "description": "A plugin for converting JSON Schema definitions to Flatfile Blueprint.", "registryMetadata": { - "status": "Coming soon", - "category": "schema" + "category": "schema-converters" }, - "author": "Alex Hollenbeck", + "engines": { + "node": ">= 16" + }, + "source": "src/index.ts", + "main": "dist/main.js", + "module": "dist/module.mjs", + "types": "dist/types.d.ts", + "scripts": { + "build": "parcel build", + "dev": "parcel watch", + "check": "tsc ./**/*.ts --noEmit --esModuleInterop", + "test": "jest ./**/*.spec.ts --config=../../jest.config.js --runInBand" + }, + "keywords": [], + "author": "Flatfile, Inc.", "repository": { "type": "git", "url": "https://github.com/FlatFilers/flatfile-plugins.git", - "directory": "plugins/json-schema" + "directory": "plugins/openapi-schema-converter" + }, + "license": "ISC", + "dependencies": { + "@flatfile/api": "^1.5.33", + "@flatfile/plugin-space-configure": "^0.1.5", + "@hyperjump/json-schema": "^1.6.4", + "axios": "^1.5.1" }, - "license": "ISC" + "devDependencies": { + "express": "^4.18.2" + } } diff --git a/plugins/json-schema/src/index.ts b/plugins/json-schema/src/index.ts new file mode 100644 index 000000000..69f0a2f5f --- /dev/null +++ b/plugins/json-schema/src/index.ts @@ -0,0 +1,28 @@ +import { Flatfile } from '@flatfile/api' +import { FlatfileEvent, FlatfileListener } from '@flatfile/listener' +import { configureSpace } from '@flatfile/plugin-space-configure' +import { + ModelToSheetConfig, + PartialWorkbookConfig, + generateSetup, +} from './setup.factory' + +export function configureSpaceWithJsonSchema( + models?: ModelToSheetConfig[], + options?: { + workbookConfig?: PartialWorkbookConfig + debug?: boolean + }, + callback?: ( + event: FlatfileEvent, + workbookIds: string[], + tick: (progress?: number, message?: string) => Promise + ) => any | Promise +) { + return async function (listener: FlatfileListener) { + listener.use(configureSpace(await generateSetup(models, options), callback)) + } +} + +export type { SetupFactory } from '@flatfile/plugin-space-configure' +export * from './setup.factory' diff --git a/plugins/json-schema/src/setup.factory.ts b/plugins/json-schema/src/setup.factory.ts new file mode 100644 index 000000000..40805373a --- /dev/null +++ b/plugins/json-schema/src/setup.factory.ts @@ -0,0 +1,199 @@ +import { Flatfile } from '@flatfile/api' +import { Setup, SetupFactory } from '@flatfile/plugin-space-configure' +import axios from 'axios' + +export interface ModelToSheetConfig extends PartialSheetConfig { + sourceUrl: string +} + +export type PartialWorkbookConfig = Omit< + Flatfile.CreateWorkbookConfig, + 'sheets' | 'name' +> & { + name?: string +} +export type PartialSheetConfig = Omit< + Flatfile.SheetConfig, + 'fields' | 'name' +> & { + name?: string +} + +export async function generateSetup( + models?: ModelToSheetConfig[], + options?: { + workbookConfig?: PartialWorkbookConfig + debug?: boolean + } +): Promise { + const sheets = await Promise.all( + models.map(async (model: ModelToSheetConfig) => { + const data = await fetchExternalReference(model.sourceUrl) + const fields = await generateFields(data) + return { + name: model?.name || data.title, + ...(data?.description && { description: data.description }), + fields, + ...model, + } + }) + ) + const setup: Setup = { + workbooks: [ + { + name: options?.workbookConfig?.name || 'JSON Schema Workbook', + sheets, + }, + ], + ...options?.workbookConfig, + } + if (options?.debug) { + console.dir(setup, { depth: null }) + } + return setup +} + +export async function generateFields(data: any): Promise { + if (!data.properties) return [] + + const url = new URL(data.$id) + const origin = url.origin + + const fields = await Promise.all( + Object.keys(data.properties).map((key) => + getPropertyType( + data, + data.properties[key], + key, + (data.required && data.required.includes(key)) || false, + origin + ) + ) + ) + return fields.flat().filter(Boolean) +} + +export async function getPropertyType( + schema: any, + property: any, + parentKey = '', + isRequired = false, + origin: string +): Promise { + if (property.$ref) { + return getPropertyType( + schema, + await resolveReference(schema, property.$ref, origin), + parentKey, + false, + origin + ) + } + + if (property.type === 'object' && property.properties) { + return ( + await Promise.all( + Object.keys(property.properties).map(async (key) => { + return getPropertyType( + property, + property.properties[key], + parentKey ? `${parentKey}_${key}` : key, + (property.required && property.required.includes(key)) || false, + origin + ) + }) + ) + ).flat() + } + + const fieldTypes: Record = { + string: { key: parentKey, type: 'string' }, + number: { key: parentKey, type: 'number' }, + integer: { key: parentKey, type: 'number' }, + boolean: { key: parentKey, type: 'boolean' }, + array: { + key: parentKey, + type: 'enum', + description: 'An enum of Selected Values', + config: property.enum + ? { + options: property.enum.map((value: any) => ({ + value, + label: String(value), + })), + } + : { + options: [], + }, + }, + enum: { + key: parentKey, + type: 'enum', + config: property?.enum + ? { + options: property.enum.map((value: any) => ({ + value, + label: String(value), + })), + } + : { + options: [], + }, + }, + } + + const fieldConfig: Flatfile.Property = { + label: parentKey, + ...(property?.description && { description: property.description }), + ...(isRequired && { constraints: [{ type: 'required' }] }), + ...fieldTypes[property.type], + } + + return fieldTypes[fieldConfig.type] ? [fieldConfig] : [] +} + +export async function resolveReference( + schema: any, + ref: string, + origin: string +): Promise { + const hashIndex = ref.indexOf('#') + + if (ref.startsWith('#/')) return resolveLocalReference(schema, ref) + + const urlPart = hashIndex >= 0 ? ref.substring(0, hashIndex) : ref + const fragmentPart = hashIndex >= 0 ? ref.substring(hashIndex) : '' + + const externalSchema = await fetchExternalReference(`${origin}${urlPart}`) + + return fragmentPart + ? resolveLocalReference(externalSchema, fragmentPart) + : externalSchema +} + +export function resolveLocalReference(schema: any, ref: string): any { + const resolved = ref + .split('/') + .slice(1) + .reduce( + (acc, part) => + acc && (acc[part] || acc.$defs?.[part] || acc.definitions?.[part]), + schema + ) + + if (!resolved) throw new Error(`Cannot resolve reference: ${ref}`) + return resolved +} + +export async function fetchExternalReference(url: string): Promise { + try { + const { status, data } = await axios.get(url, { + validateStatus: () => true, + }) + if (status !== 200) + throw new Error(`API returned status ${status}: ${data.statusText}`) + return data + } catch (error: any) { + throw new Error(`Error fetching external reference: ${error.message}`) + } +} diff --git a/plugins/json-schema/tests/json-schema.e2e.spec.ts b/plugins/json-schema/tests/json-schema.e2e.spec.ts new file mode 100644 index 000000000..8ca1d8ccc --- /dev/null +++ b/plugins/json-schema/tests/json-schema.e2e.spec.ts @@ -0,0 +1,133 @@ +import api from '@flatfile/api' +import { deleteSpace, setupListener, setupSpace } from '@flatfile/utils-testing' +import express from 'express' +import { configureSpaceWithJsonSchema } from '../src' +import { startServer, stopServer } from './test-server/server' + +const app = express() +const port = 8080 +const url = `http://localhost:${port}/` + +let server + +describe('configureSpaceWithJsonSchema() e2e', () => { + const pureDataSchema = { + $id: 'https://localhost:3000/json', + description: + 'A basic set of JSON Schema to test data type conversions simply', + type: 'object', + title: 'ExampleData', + properties: { + stringColumn: { + description: 'A column for strings!', + type: 'string', + }, + integerColumn: { + description: 'A column for integers!', + type: 'integer', + }, + arrayColumn: { + description: 'A column for string arrays!', + type: 'array', + items: { + type: 'string', + }, + }, + objectColumn: { + description: + 'A column for nested columns containing numbers and strings!', + type: 'object', + properties: { + nestedUniqueNumberColumn: { + description: 'A column for unique numbers!', + type: 'number', + uniqueItems: true, + }, + nestedStringColumn: { + type: 'string', + }, + }, + }, + }, + } + + const expectedWorkbookData = { + name: 'JSON Schema Workbook', + labels: [], + sheets: [ + { + name: 'ExampleData', + config: { + name: 'ExampleData', + description: + 'A basic set of JSON Schema to test data type conversions simply', + fields: [ + { + type: 'string', + key: 'stringColumn', + label: 'stringColumn', + description: 'A column for strings!', + }, + { + type: 'number', + key: 'integerColumn', + label: 'integerColumn', + description: 'A column for integers!', + }, + { + type: 'enum', + config: { options: [] }, + key: 'arrayColumn', + label: 'arrayColumn', + description: 'An enum of Selected Values', + }, + { + type: 'number', + key: 'objectColumn_nestedUniqueNumberColumn', + label: 'objectColumn_nestedUniqueNumberColumn', + description: 'A column for unique numbers!', + }, + { + type: 'string', + key: 'objectColumn_nestedStringColumn', + label: 'objectColumn_nestedStringColumn', + }, + ], + }, + }, + ], + } + + let spaceId: string + const listener = setupListener() + + beforeAll(async () => { + console.log(`Starting temporary server on port ${port}`) + server = startServer(app, port, pureDataSchema) + console.log('Setting up Space and Retrieving spaceId') + + await listener.use(configureSpaceWithJsonSchema([{ sourceUrl: url }])) + + const space = await setupSpace() + spaceId = space.id + }) + + afterAll(async () => { + stopServer(server) + await deleteSpace(spaceId) + }) + + it('should configure a space and correctly format and flatten the JSON Schema', async () => { + console.log('starting configuration') + await listener.waitFor('job:ready', 1, 'space:configure') + + const space = await api.spaces.get(spaceId) + const workspace = await api.workbooks.get(space.data.primaryWorkbookId) + const workspaceData = workspace.data + + expect(workspaceData.name).toBe(expectedWorkbookData.name) + expect(workspaceData.sheets[0].config).toMatchObject( + expectedWorkbookData.sheets[0].config + ) + }) +}) diff --git a/plugins/json-schema/tests/test-server/server.ts b/plugins/json-schema/tests/test-server/server.ts new file mode 100644 index 000000000..74fe85025 --- /dev/null +++ b/plugins/json-schema/tests/test-server/server.ts @@ -0,0 +1,13 @@ +export const startServer = (app, port, data) => { + app.get('/', (req, res) => { + res.send(data) + }) + + return app.listen(port, () => { + console.log(`Example app listening on port ${port}`) + }) +} + +export const stopServer = (app) => { + app.close() +}