From 749a06ca38440abbd6acc762ce90e7c17494d100 Mon Sep 17 00:00:00 2001 From: Bret Comnes Date: Tue, 13 Dec 2022 13:16:38 -0700 Subject: [PATCH 1/3] Add support for V1 mapping Add a v1 definition and transformation step Update index.js Co-authored-by: Pelle Wessman Signed-off-by: Bret Comnes <166301+bcomnes@users.noreply.github.com> Remove beta field Update index.js Co-authored-by: Pelle Wessman Signed-off-by: Bret Comnes <166301+bcomnes@users.noreply.github.com> Update index.js Co-authored-by: Pelle Wessman Signed-off-by: Bret Comnes <166301+bcomnes@users.noreply.github.com> Update test/parse.spec.js Co-authored-by: Pelle Wessman Signed-off-by: Bret Comnes <166301+bcomnes@users.noreply.github.com> Feedback Options --- index.js | 56 +++++++++++++++++++++++++++++++++++++++------- lib/v1.js | 38 +++++++++++++++++++++++++++++++ package.json | 3 ++- test/parse.spec.js | 21 +++++++++++++++-- test/read.spec.js | 1 - test/sample-v1.yml | 5 +++++ test/sample.yml | 2 +- 7 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 lib/v1.js create mode 100644 test/sample-v1.yml diff --git a/index.js b/index.js index cf8551e..c9c4ab3 100644 --- a/index.js +++ b/index.js @@ -6,9 +6,10 @@ const { default: Ajv } = require('ajv') const { ErrorWithCause } = require('pony-cause') const { parse: yamlParse } = require('yaml') +const { socketTmlSchemaV1 } = require('./lib/v1') + /** * @typedef SocketYmlGitHub - * @property {boolean} [beta] beta opt in field * @property {boolean} [enabled] enable/disable the Socket.dev GitHub app entirely * @property {boolean} [projectReportsEnabled] enable/disable Github app project report checks * @property {boolean} [pullRequestAlertsEnabled] enable/disable GitHub app pull request alert checks @@ -31,22 +32,21 @@ const socketYmlSchema = { projectIgnorePaths: { type: 'array', items: { type: 'string' }, - nullable: true, + nullable: true }, issueRules: { type: 'object', nullable: true, required: [], - additionalProperties: { type: 'boolean' }, + additionalProperties: { type: 'boolean' } }, githubApp: { type: 'object', nullable: true, properties: { - beta: { type: 'boolean', nullable: true }, - enabled: { type: 'boolean', nullable: true }, - projectReportsEnabled: { type: 'boolean', nullable: true }, - pullRequestAlertsEnabled: { type: 'boolean', nullable: true }, + enabled: { type: 'boolean', nullable: true, default: true }, + projectReportsEnabled: { type: 'boolean', nullable: true, default: true }, + pullRequestAlertsEnabled: { type: 'boolean', nullable: true, default: true }, }, required: [], additionalProperties: false, @@ -56,14 +56,24 @@ const socketYmlSchema = { additionalProperties: false, } -const ajv = new Ajv({ +const ajvOptions = /** @type {const} */ ({ allErrors: true, coerceTypes: 'array', logger: false, + useDefaults: true +}) +const ajv = new Ajv({ + ...ajvOptions, removeAdditional: 'failing', }) const validate = ajv.compile(socketYmlSchema) +// We want to be strict and fail rather than removeAdditional when we parse a possible v1 config – only fallback to it when it actually matches well +const ajvV1 = new Ajv({ + ...ajvOptions +}) + +const validateV1 = ajvV1.compile(socketTmlSchemaV1) /** * @param {string} filePath @@ -101,6 +111,13 @@ async function parseSocketConfig (fileContent) { throw new ErrorWithCause('Error when parsing socket.yml config', { cause: err }) } + if (parsedContent && typeof parsedContent === 'object' && !('version' in parsedContent)) { + const parsedV1 = await parseV1SocketConfig(parsedContent) + if (parsedV1) { + return parsedV1 + } + } + if (!validate(parsedContent)) { throw new SocketValidationError( 'Invalid config definition', @@ -146,6 +163,29 @@ class SocketValidationError extends Error { } } +/** + * @param {object} parsedV1Content + * @returns {Promise} + */ +async function parseV1SocketConfig (parsedV1Content) { + if (!validateV1(parsedV1Content)) { + return + } + + /** @type {SocketYml} */ + const v2 = { + version: 2, + projectIgnorePaths: parsedV1Content?.ignore ?? [], + issueRules: parsedV1Content?.issues ?? {}, + githubApp: { + enabled: parsedV1Content?.enabled, + pullRequestAlertsEnabled: parsedV1Content?.pullRequestAlertsEnabled, + projectReportsEnabled: parsedV1Content?.projectReportsEnabled + } + } + return v2 +} + module.exports = { parseSocketConfig, readSocketConfig, diff --git a/lib/v1.js b/lib/v1.js new file mode 100644 index 0000000..4712d52 --- /dev/null +++ b/lib/v1.js @@ -0,0 +1,38 @@ +/** + * @typedef SocketYmlV1 + * @property {string[]} [ignore] + * @property {{ [issueName: string]: boolean }} [issues] + * @property {boolean} [beta] unused v1 option + * @property {boolean} [enabled] enable/disable the Socket.dev GitHub app entirely + * @property {boolean} [projectReportsEnabled] enable/disable Github app project report checks + * @property {boolean} [pullRequestAlertsEnabled] enable/disable GitHub app pull request alert checks + */ + +/** @type {import('ajv').JSONSchemaType} */ +const socketTmlSchemaV1 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + ignore: { + type: 'array', + items: { type: 'string' }, + nullable: true, + }, + issues: { + type: 'object', + nullable: true, + required: [], + additionalProperties: { type: 'boolean' }, + }, + beta: { type: 'boolean', nullable: true, default: true }, + enabled: { type: 'boolean', nullable: true, default: true }, + projectReportsEnabled: { type: 'boolean', nullable: true, default: true }, + pullRequestAlertsEnabled: { type: 'boolean', nullable: true, default: true }, + }, + minProperties: 1, + additionalProperties: false, +} + +module.exports = { + socketTmlSchemaV1 +} diff --git a/package.json b/package.json index 64d1c71..ce4665d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "index.js", "index.d.ts", "index.d.ts.map", - "schema.json" + "schema.json", + "lib/*" ], "scripts": { "build:0": "run-s clean", diff --git a/test/parse.spec.js b/test/parse.spec.js index d316589..339773d 100644 --- a/test/parse.spec.js +++ b/test/parse.spec.js @@ -14,13 +14,12 @@ const { chai.use(chaiAsPromised) chai.should() -describe('readSocketConfig()', () => { +describe('parseSocketConfig()', () => { it('should read and parse socket.yml', async () => { const fileContent = await readFile(path.resolve(__dirname, 'sample.yml'), 'utf8') await parseSocketConfig(fileContent).should.eventually.become({ 'githubApp': { - 'beta': false, 'enabled': true, 'projectReportsEnabled': true, 'pullRequestAlertsEnabled': true, @@ -36,6 +35,24 @@ describe('readSocketConfig()', () => { }) }) + it('should read and parse socket.yml v1', async () => { + const fileContent = await readFile(path.resolve(__dirname, 'sample-v1.yml'), 'utf8') + + await parseSocketConfig(fileContent).should.eventually.become({ + 'githubApp': { + 'enabled': true, + 'projectReportsEnabled': false, + 'pullRequestAlertsEnabled': true, + }, + 'issueRules': {}, + 'projectIgnorePaths': [ + 'foo', + 'bar', + ], + 'version': 2, + }) + }) + it('should throw on invalid document structure', async () => { await parseSocketConfig(` projectIgnorePaths: true diff --git a/test/read.spec.js b/test/read.spec.js index 15eb29e..c53e078 100644 --- a/test/read.spec.js +++ b/test/read.spec.js @@ -16,7 +16,6 @@ describe('readSocketConfig()', () => { it('should read and parse socket.yml', async () => { await readSocketConfig(path.resolve(__dirname, 'sample.yml')).should.eventually.become({ 'githubApp': { - 'beta': false, 'enabled': true, 'projectReportsEnabled': true, 'pullRequestAlertsEnabled': true, diff --git a/test/sample-v1.yml b/test/sample-v1.yml new file mode 100644 index 0000000..6076956 --- /dev/null +++ b/test/sample-v1.yml @@ -0,0 +1,5 @@ +ignore: + - foo + - bar +projectReportsEnabled: false +beta: true diff --git a/test/sample.yml b/test/sample.yml index 91db9d5..f185fd2 100644 --- a/test/sample.yml +++ b/test/sample.yml @@ -2,7 +2,7 @@ version: 2 projectIgnorePaths: - workspaces/test* - - '!workspaces/test-framework' + - "!workspaces/test-framework" issueRules: unresolvedRequire: false From f9d745f065e7757bc5f20131aa558fb523098231 Mon Sep 17 00:00:00 2001 From: Pelle Wessman Date: Tue, 13 Dec 2022 23:20:43 +0100 Subject: [PATCH 2/3] Minor nits --- index.js | 2 ++ lib/v1.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/index.js b/index.js index c9c4ab3..816ad47 100644 --- a/index.js +++ b/index.js @@ -62,12 +62,14 @@ const ajvOptions = /** @type {const} */ ({ logger: false, useDefaults: true }) + const ajv = new Ajv({ ...ajvOptions, removeAdditional: 'failing', }) const validate = ajv.compile(socketYmlSchema) + // We want to be strict and fail rather than removeAdditional when we parse a possible v1 config – only fallback to it when it actually matches well const ajvV1 = new Ajv({ ...ajvOptions diff --git a/lib/v1.js b/lib/v1.js index 4712d52..d29083b 100644 --- a/lib/v1.js +++ b/lib/v1.js @@ -1,3 +1,5 @@ +'use strict' + /** * @typedef SocketYmlV1 * @property {string[]} [ignore] From 7f2b741d7dc70d35560aad48c8b5ed9facb084d1 Mon Sep 17 00:00:00 2001 From: Pelle Wessman Date: Tue, 13 Dec 2022 23:21:15 +0100 Subject: [PATCH 3/3] Fix spelling error --- index.js | 4 ++-- lib/v1.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 816ad47..370db03 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const { default: Ajv } = require('ajv') const { ErrorWithCause } = require('pony-cause') const { parse: yamlParse } = require('yaml') -const { socketTmlSchemaV1 } = require('./lib/v1') +const { socketYmlSchemaV1 } = require('./lib/v1') /** * @typedef SocketYmlGitHub @@ -75,7 +75,7 @@ const ajvV1 = new Ajv({ ...ajvOptions }) -const validateV1 = ajvV1.compile(socketTmlSchemaV1) +const validateV1 = ajvV1.compile(socketYmlSchemaV1) /** * @param {string} filePath diff --git a/lib/v1.js b/lib/v1.js index d29083b..f4d6fde 100644 --- a/lib/v1.js +++ b/lib/v1.js @@ -11,7 +11,7 @@ */ /** @type {import('ajv').JSONSchemaType} */ -const socketTmlSchemaV1 = { +const socketYmlSchemaV1 = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -36,5 +36,5 @@ const socketTmlSchemaV1 = { } module.exports = { - socketTmlSchemaV1 + socketYmlSchemaV1 }