diff --git a/packages/plugin-manifest-validator/README.md b/packages/plugin-manifest-validator/README.md index 2fad1cb6dd..9acd81e80c 100644 --- a/packages/plugin-manifest-validator/README.md +++ b/packages/plugin-manifest-validator/README.md @@ -27,11 +27,11 @@ console.log(result.valid); // true or false console.log(result.errors); // array of ajv error objects ``` -[ajv error objects](https://github.com/epoberezkin/ajv#validation-errors) is like: +[ajv error objects](https://ajv.js.org/docs/api.html#validation-errors) is like: ```js { - dataPath: '.version', + dataPath: '/version', keyword: 'type', message: 'should be integer', params: { diff --git a/packages/plugin-manifest-validator/manifest-schema.d.ts b/packages/plugin-manifest-validator/manifest-schema.d.ts index 00c42f8815..e2cd4eafce 100644 --- a/packages/plugin-manifest-validator/manifest-schema.d.ts +++ b/packages/plugin-manifest-validator/manifest-schema.d.ts @@ -5,7 +5,414 @@ * and run json-schema-to-typescript to regenerate this file. */ -export type Resources = string[]; +export type Resources = + | [] + | [string] + | [string, string] + | [string, string, string] + | [string, string, string, string] + | [string, string, string, string, string] + | [string, string, string, string, string, string] + | [string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string, string, string, string, string, string] + | [string, string, string, string, string, string, string, string, string, string, string, string, string, string] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ] + | [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string + ]; export interface KintonePluginManifestJson { manifest_version: number; diff --git a/packages/plugin-manifest-validator/manifest-schema.json b/packages/plugin-manifest-validator/manifest-schema.json index 9c48ac4e25..cf9a801119 100644 --- a/packages/plugin-manifest-validator/manifest-schema.json +++ b/packages/plugin-manifest-validator/manifest-schema.json @@ -163,9 +163,9 @@ }, { "format": "relative-path", "maxFileSize": "20MB" - }], - "maxItems": 30 - } + }] + }, + "maxItems": 30 } } } diff --git a/packages/plugin-manifest-validator/package.json b/packages/plugin-manifest-validator/package.json index 0a3bf0d094..9020f92392 100644 --- a/packages/plugin-manifest-validator/package.json +++ b/packages/plugin-manifest-validator/package.json @@ -24,7 +24,7 @@ "clean": "rimraf dist" }, "dependencies": { - "ajv": "^6.12.6", + "ajv": "^7.0.3", "bytes": "^3.1.0" }, "devDependencies": { diff --git a/packages/plugin-manifest-validator/src/__tests__/index.ts b/packages/plugin-manifest-validator/src/__tests__/index.ts index 27e0ea2306..2cb9c5e3d0 100644 --- a/packages/plugin-manifest-validator/src/__tests__/index.ts +++ b/packages/plugin-manifest-validator/src/__tests__/index.ts @@ -22,9 +22,9 @@ describe("validator", () => { valid: false, errors: [ { - dataPath: ".version", + dataPath: "", keyword: "required", - message: "is a required property", + message: "should have required property 'version'", params: { missingProperty: "version", }, @@ -39,7 +39,7 @@ describe("validator", () => { valid: false, errors: [ { - dataPath: ".version", + dataPath: "/version", keyword: "type", message: "should be integer", params: { @@ -56,12 +56,11 @@ describe("validator", () => { valid: false, errors: [ { - dataPath: ".version", + dataPath: "/version", keyword: "minimum", message: "should be >= 1", params: { comparison: ">=", - exclusive: false, limit: 1, }, schemaPath: "#/properties/version/minimum", @@ -75,7 +74,7 @@ describe("validator", () => { valid: false, errors: [ { - dataPath: ".type", + dataPath: "/type", keyword: "enum", message: "should be equal to one of the allowed values", params: { @@ -92,9 +91,9 @@ describe("validator", () => { valid: false, errors: [ { - dataPath: ".description.en", + dataPath: "/description", keyword: "required", - message: "is a required property", + message: "should have required property 'en'", params: { missingProperty: "en", }, @@ -164,7 +163,7 @@ describe("validator", () => { assert(actual.valid === false); assert(actual.errors?.length === 1); assert.deepStrictEqual(actual.errors[0], { - dataPath: ".icon", + dataPath: "/icon", keyword: "maxFileSize", message: "file size should be <= 20MB", params: { @@ -190,7 +189,7 @@ describe("validator", () => { assert(actual.valid === false); assert(actual.errors?.length === 3); assert.deepStrictEqual(actual.errors[1], { - dataPath: ".desktop.js[0]", + dataPath: "/desktop/js/0", keyword: "maxFileSize", message: "file size should be <= 20MB", params: { @@ -216,7 +215,7 @@ describe("validator", () => { assert(actual.valid === false); assert(actual.errors?.length === 3); assert.deepStrictEqual(actual.errors[1], { - dataPath: ".desktop.css[0]", + dataPath: "/desktop/css/0", keyword: "maxFileSize", message: "file size should be <= 20MB", params: { @@ -239,6 +238,31 @@ describe("validator", () => { assert(actual.errors === null); }); }); + describe("maxItems", () => { + it("exceed the max item counts", () => { + const urls = [...new Array(100)].map( + (_, i) => `https://example.com/${i}.js` + ); + const actual = validator( + json({ + desktop: { + js: urls, + }, + }) + ); + assert.strictEqual(actual.valid, false); + assert.strictEqual(actual.errors?.length, 1); + assert.deepStrictEqual(actual.errors[0], { + dataPath: "/desktop/js", + keyword: "maxItems", + message: "should NOT have more than 30 items", + params: { + limit: 30, + }, + schemaPath: "#/definitions/resources/maxItems", + }); + }); + }); }); /** diff --git a/packages/plugin-manifest-validator/src/index.ts b/packages/plugin-manifest-validator/src/index.ts index fb4124b09d..0fc2be26a5 100644 --- a/packages/plugin-manifest-validator/src/index.ts +++ b/packages/plugin-manifest-validator/src/index.ts @@ -1,15 +1,22 @@ "use strict"; -import Ajv from "ajv"; +import Ajv, { ErrorObject } from "ajv"; import bytes from "bytes"; import jsonSchema from "../manifest-schema.json"; import validateUrl from "./validate-https-url"; type ValidateResult = { valid: boolean | PromiseLike; - errors: null | Ajv.ErrorObject[]; + errors: null | ErrorObject[]; }; +// https://ajv.js.org/docs/keywords.html#define-keyword-with-validation-function +// FIXME: use the type definition that Ajv provides if https://github.com/ajv-validator/ajv/pull/1460 has been merged +interface SchemaValidateFunction { + (schema: string, data: string): boolean; + errors?: Array>; +} + /** * @param {Object} json * @param {Object=} options @@ -27,10 +34,9 @@ export = function ( if (typeof options.maxFileSize === "function") { maxFileSize = options.maxFileSize; } + const ajv = new Ajv({ allErrors: true, - unknownFormats: true, - errorDataPath: "property", formats: { "http-url": (str: string) => validateUrl(str, true), "https-url": (str: string) => validateUrl(str), @@ -38,14 +44,7 @@ export = function ( }, }); - ajv.removeKeyword("propertyNames"); - ajv.removeKeyword("contains"); - ajv.removeKeyword("const"); - ajv.removeKeyword("if"); - ajv.removeKeyword("then"); - ajv.removeKeyword("else"); - - const validateMaxFileSize: Ajv.SchemaValidateFunction = ( + const validateMaxFileSize: SchemaValidateFunction = ( schema: string, data: string ) => { @@ -55,7 +54,6 @@ export = function ( const valid = maxFileSize(maxBytes, data); if (!valid) { validateMaxFileSize.errors = [ - // @ts-expect-error TODO: Ajv.ErrorObject has need fixing. { keyword: "maxFileSize", params: { @@ -68,7 +66,8 @@ export = function ( return valid; }; - ajv.addKeyword("maxFileSize", { + ajv.addKeyword({ + keyword: "maxFileSize", validate: validateMaxFileSize, }); @@ -82,8 +81,8 @@ export = function ( * @return {null|Array} shallow copy of the input or null */ function transformErrors( - errors: undefined | null | Ajv.ErrorObject[] -): null | Ajv.ErrorObject[] { + errors: undefined | null | ErrorObject[] +): null | ErrorObject[] { if (!errors) { return null; } diff --git a/packages/plugin-packer/test/packer.spec.ts b/packages/plugin-packer/test/packer.spec.ts index 1325b819c0..38871735d1 100644 --- a/packages/plugin-packer/test/packer.spec.ts +++ b/packages/plugin-packer/test/packer.spec.ts @@ -84,9 +84,7 @@ describe("packer", () => { it("throws an error if the contents.zip is invalid", (done) => { const contentsZip = fs.readFileSync(invalidMaxFileSizeContentsZipPath); packer(contentsZip).catch((error) => { - expect( - error.message.indexOf('".icon" file size should be <= 20MB') - ).not.toBe(-1); + expect(error.message).toBe('"/icon" file size should be <= 20MB'); done(); }); }); diff --git a/yarn.lock b/yarn.lock index 2aa4404f25..5ce525941c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3043,7 +3043,7 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: +ajv@^7.0.2, ajv@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.0.3.tgz#13ae747eff125cafb230ac504b2406cf371eece2" integrity sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==