From f8b08f38b187f87748c35532a1ba9536d277c707 Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 12:46:31 +0100 Subject: [PATCH 1/9] fix: stricter JSON Schemas - Type.Strict() - Type.Object({ additionalProperties: false }) - Type.String({ format: 'email' | 'uri' }) --- server/src/schemas/global-data-schemas.ts | 73 +++--- server/src/schemas/skill-schemas.ts | 247 +++++++++++++-------- server/src/schemas/voice-config-schemas.ts | 61 +++-- 3 files changed, 240 insertions(+), 141 deletions(-) diff --git a/server/src/schemas/global-data-schemas.ts b/server/src/schemas/global-data-schemas.ts index fdbd0b34..0cf35d98 100644 --- a/server/src/schemas/global-data-schemas.ts +++ b/server/src/schemas/global-data-schemas.ts @@ -1,34 +1,57 @@ import type { Static } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -export const globalEntitySchemaObject = Type.Object({ - options: Type.Record( - Type.String(), - Type.Object({ - synonyms: Type.Array(Type.String()), - data: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))) - }) +export const globalEntitySchemaObject = Type.Strict( + Type.Object( + { + options: Type.Record( + Type.String(), + Type.Object( + { + synonyms: Type.Array(Type.String()), + data: Type.Optional( + Type.Record(Type.String(), Type.Array(Type.String())) + ) + }, + { additionalProperties: false } + ) + ) + }, + { additionalProperties: false } ) -}) -export const globalResolverSchemaObject = Type.Object({ - name: Type.String(), - intents: Type.Record( - Type.String(), - Type.Object({ - utterance_samples: Type.Array(Type.String()), - value: Type.Unknown() - }) +) +export const globalResolverSchemaObject = Type.Strict( + Type.Object( + { + name: Type.String(), + intents: Type.Record( + Type.String(), + Type.Object( + { + utterance_samples: Type.Array(Type.String()), + value: Type.Unknown() + }, + { additionalProperties: false } + ) + ) + }, + { additionalProperties: false } ) -}) -export const globalAnswersSchemaObject = Type.Object({ - answers: Type.Record( - Type.String(), - Type.Union([ - Type.Record(Type.String(), Type.String()), - Type.Array(Type.String()) - ]) +) +export const globalAnswersSchemaObject = Type.Strict( + Type.Object( + { + answers: Type.Record( + Type.String(), + Type.Union([ + Type.Record(Type.String(), Type.String()), + Type.Array(Type.String()) + ]) + ) + }, + { additionalProperties: false } ) -}) +) export type GlobalEntity = Static export type GlobalResolver = Static diff --git a/server/src/schemas/skill-schemas.ts b/server/src/schemas/skill-schemas.ts index 7b787c9f..a9573ca9 100644 --- a/server/src/schemas/skill-schemas.ts +++ b/server/src/schemas/skill-schemas.ts @@ -1,6 +1,8 @@ import type { Static } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' +import { globalResolverSchemaObject } from '@/schemas/global-data-schemas' + const skillBridges = [Type.Literal('python'), Type.Null()] const skillActionTypes = [Type.Literal('logic'), Type.Literal('dialog')] const skillDataTypes = [ @@ -10,109 +12,168 @@ const skillDataTypes = [ ] const skillCustomEntityTypes = [ Type.Array( - Type.Object({ - type: Type.Literal('trim'), - name: Type.String({ minLength: 1 }), - conditions: Type.Array( - Type.Object({ - type: Type.Union([ - Type.Literal('between'), - Type.Literal('after'), - Type.Literal('after_first'), - Type.Literal('after_last'), - Type.Literal('before'), - Type.Literal('before_first'), - Type.Literal('before_last') - ]), - from: Type.Optional( - Type.Union([ - Type.Array(Type.String({ minLength: 1 })), - Type.String({ minLength: 1 }) - ]) - ), - to: Type.Optional( - Type.Union([ - Type.Array(Type.String({ minLength: 1 })), - Type.String({ minLength: 1 }) - ]) + Type.Object( + { + type: Type.Literal('trim'), + name: Type.String({ minLength: 1 }), + conditions: Type.Array( + Type.Object( + { + type: Type.Union([ + Type.Literal('between'), + Type.Literal('after'), + Type.Literal('after_first'), + Type.Literal('after_last'), + Type.Literal('before'), + Type.Literal('before_first'), + Type.Literal('before_last') + ]), + from: Type.Optional( + Type.Union([ + Type.Array(Type.String({ minLength: 1 })), + Type.String({ minLength: 1 }) + ]) + ), + to: Type.Optional( + Type.Union([ + Type.Array(Type.String({ minLength: 1 })), + Type.String({ minLength: 1 }) + ]) + ) + }, + { additionalProperties: false } ) - }) - ) - }) + ) + }, + { additionalProperties: false } + ) ), Type.Array( - Type.Object({ - type: Type.Literal('regex'), - name: Type.String({ minLength: 1 }), - regex: Type.String({ minLength: 1 }) - }) + Type.Object( + { + type: Type.Literal('regex'), + name: Type.String({ minLength: 1 }), + regex: Type.String({ minLength: 1 }) + }, + { additionalProperties: false } + ) ), Type.Array( - Type.Object({ - type: Type.Literal('enum'), - name: Type.String(), - options: Type.Record( - Type.String({ minLength: 1 }), - Type.Object({ - synonyms: Type.Array(Type.String({ minLength: 1 })) - }) - ) - }) + Type.Object( + { + type: Type.Literal('enum'), + name: Type.String(), + options: Type.Record( + Type.String({ minLength: 1 }), + Type.Object({ + synonyms: Type.Array(Type.String({ minLength: 1 })) + }) + ) + }, + { additionalProperties: false } + ) ) ] -export const domainSchemaObject = Type.Object({ - name: Type.String({ minLength: 1 }) -}) -export const skillSchemaObject = Type.Object({ - name: Type.String({ minLength: 1 }), - bridge: Type.Union(skillBridges), - version: Type.String({ minLength: 1 }), - description: Type.String({ minLength: 1 }), - author: Type.Object({ - name: Type.String({ minLength: 1 }), - email: Type.Optional(Type.String({ minLength: 1, maxLength: 254 })), - url: Type.Optional(Type.String({ minLength: 1, maxLength: 255 })) - }) -}) -export const skillConfigSchemaObject = Type.Object({ - variables: Type.Optional(Type.Record(Type.String(), Type.String())), - actions: Type.Record( - Type.String(), - Type.Object({ - type: Type.Union(skillActionTypes), - loop: Type.Optional( - Type.Object({ - expected_item: Type.Object({ - type: Type.Union(skillDataTypes), - name: Type.String() - }) - }) - ), - utterance_samples: Type.Optional(Type.Array(Type.String())), - answers: Type.Optional(Type.Array(Type.String())), - unknown_answers: Type.Optional(Type.Array(Type.String())), - suggestions: Type.Optional(Type.Array(Type.String())), - slots: Type.Optional( - Type.Array( - Type.Object({ - name: Type.String(), - item: Type.Object({ - type: Type.Union(skillDataTypes), - name: Type.String() - }), - questions: Type.Array(Type.String()), - suggestions: Type.Optional(Type.Array(Type.String())) - }) +export const domainSchemaObject = Type.Strict( + Type.Object( + { + name: Type.String({ minLength: 1 }) + }, + { additionalProperties: false } + ) +) +export const skillSchemaObject = Type.Strict( + Type.Object( + { + name: Type.String({ minLength: 1 }), + bridge: Type.Union(skillBridges), + version: Type.String({ minLength: 1 }), + description: Type.String({ minLength: 1 }), + author: Type.Object( + { + name: Type.String({ minLength: 1 }), + email: Type.Optional( + Type.String({ minLength: 1, maxLength: 254, format: 'email' }) + ), + url: Type.Optional( + Type.String({ minLength: 1, maxLength: 255, format: 'uri' }) + ) + }, + { additionalProperties: false } + ) + }, + { additionalProperties: false } + ) +) +export const skillConfigSchemaObject = Type.Strict( + Type.Object( + { + variables: Type.Optional(Type.Record(Type.String(), Type.String())), + actions: Type.Record( + Type.String(), + Type.Object( + { + type: Type.Union(skillActionTypes), + loop: Type.Optional( + Type.Object( + { + expected_item: Type.Object({ + type: Type.Union(skillDataTypes), + name: Type.String() + }) + }, + { additionalProperties: false } + ) + ), + utterance_samples: Type.Optional(Type.Array(Type.String())), + answers: Type.Optional(Type.Array(Type.String())), + unknown_answers: Type.Optional(Type.Array(Type.String())), + suggestions: Type.Optional(Type.Array(Type.String())), + slots: Type.Optional( + Type.Array( + Type.Object( + { + name: Type.String(), + item: Type.Object( + { + type: Type.Union(skillDataTypes), + name: Type.String() + }, + { additionalProperties: false } + ), + questions: Type.Array(Type.String()), + suggestions: Type.Optional(Type.Array(Type.String())) + }, + { additionalProperties: false } + ) + ) + ), + entities: Type.Optional(Type.Union(skillCustomEntityTypes)), + next_action: Type.Optional(Type.String()) + }, + { additionalProperties: false } ) ), - entities: Type.Optional(Type.Union(skillCustomEntityTypes)), - next_action: Type.Optional(Type.String()) - }) - ), - answers: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))), - entities: Type.Optional(Type.Record(Type.String(), Type.String())) -}) + answers: Type.Optional( + Type.Record(Type.String(), Type.Array(Type.String())) + ), + entities: Type.Optional(Type.Record(Type.String(), Type.String())), + resolvers: Type.Optional( + Type.Record( + Type.String(), + Type.Object( + { + intents: globalResolverSchemaObject.properties.intents + }, + { additionalProperties: false } + ) + ) + ) + }, + { additionalProperties: false } + ) +) export type Domain = Static export type Skill = Static diff --git a/server/src/schemas/voice-config-schemas.ts b/server/src/schemas/voice-config-schemas.ts index f7a57e60..0caf9154 100644 --- a/server/src/schemas/voice-config-schemas.ts +++ b/server/src/schemas/voice-config-schemas.ts @@ -1,29 +1,44 @@ import type { Static } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -export const amazonVoiceConfiguration = Type.Object({ - credentials: Type.Object({ - accessKeyId: Type.String(), - secretAccessKey: Type.String() - }), - region: Type.String() -}) -export const googleCloudVoiceConfiguration = Type.Object({ - type: Type.Literal('service_account'), - project_id: Type.String(), - private_key_id: Type.String(), - private_key: Type.String(), - client_email: Type.String(), - client_id: Type.String(), - auth_uri: Type.String(), - token_uri: Type.String(), - auth_provider_x509_cert_url: Type.String(), - client_x509_cert_url: Type.String() -}) -export const watsonVoiceConfiguration = Type.Object({ - apikey: Type.String(), - url: Type.String() -}) +export const amazonVoiceConfiguration = Type.Strict( + Type.Object( + { + credentials: Type.Object({ + accessKeyId: Type.String(), + secretAccessKey: Type.String() + }), + region: Type.String() + }, + { additionalProperties: false } + ) +) +export const googleCloudVoiceConfiguration = Type.Strict( + Type.Object( + { + type: Type.Literal('service_account'), + project_id: Type.String(), + private_key_id: Type.String(), + private_key: Type.String(), + client_email: Type.String({ format: 'email' }), + client_id: Type.String(), + auth_uri: Type.String({ format: 'uri' }), + token_uri: Type.String({ format: 'uri' }), + auth_provider_x509_cert_url: Type.String({ format: 'uri' }), + client_x509_cert_url: Type.String({ format: 'uri' }) + }, + { additionalProperties: false } + ) +) +export const watsonVoiceConfiguration = Type.Strict( + Type.Object( + { + apikey: Type.String(), + url: Type.String({ format: 'uri' }) + }, + { additionalProperties: false } + ) +) export type AmazonVoiceConfiguration = Static export type GoogleCloudVoiceConfiguration = Static< From d973ab43184b464137ffe189a8530c8be09aa2cd Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 12:48:43 +0100 Subject: [PATCH 2/9] chore: delete schemas.ts files outside server --- core/config/voice/schemas.ts | 32 ------------- core/data/schemas.ts | 45 ------------------ skills/schemas.ts | 90 ------------------------------------ 3 files changed, 167 deletions(-) delete mode 100644 core/config/voice/schemas.ts delete mode 100644 core/data/schemas.ts delete mode 100644 skills/schemas.ts diff --git a/core/config/voice/schemas.ts b/core/config/voice/schemas.ts deleted file mode 100644 index 27dddadb..00000000 --- a/core/config/voice/schemas.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' - -const amazonVoiceConfiguration = Type.Object({ - credentials: Type.Object({ - accessKeyId: Type.String(), - secretAccessKey: Type.String() - }), - region: Type.String() -}) -const googleCloudVoiceConfiguration = Type.Object({ - type: Type.Literal('service_account'), - project_id: Type.String(), - private_key_id: Type.String(), - private_key: Type.String(), - client_email: Type.String({ format: 'email' }), - client_id: Type.String(), - auth_uri: Type.String({ format: 'uri' }), - token_uri: Type.String({ format: 'uri' }), - auth_provider_x509_cert_url: Type.String({ format: 'uri' }), - client_x509_cert_url: Type.String({ format: 'uri' }) -}) -const watsonVoiceConfiguration = Type.Object({ - apikey: Type.String(), - url: Type.String({ format: 'uri' }) -}) - -export type AmazonVoiceConfiguration = Static -export type GoogleCloudVoiceConfiguration = Static< - typeof googleCloudVoiceConfiguration -> -export type WatsonVoiceConfiguration = Static diff --git a/core/data/schemas.ts b/core/data/schemas.ts deleted file mode 100644 index 74a7e288..00000000 --- a/core/data/schemas.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' - -const globalEntity = { - options: Type.Record( - Type.String(), - Type.Object({ - synonyms: Type.Array(Type.String()), - data: Type.Record(Type.String(), Type.Array(Type.String())) - }) - ) -} -const globalResolver = { - name: Type.String(), - intents: Type.Record( - Type.String(), - Type.Object({ - utterance_samples: Type.Array(Type.String()), - value: Type.Unknown() - }) - ) -} -const answers = { - answers: Type.Record( - Type.String(), - Type.Union([ - Type.Record(Type.String(), Type.String()), - Type.Array(Type.String()) - ]) - ) -} - -const globalEntitySchemaObject = Type.Strict( - Type.Object(globalEntity, { additionalProperties: false }) -) -const globalResolverSchemaObject = Type.Strict( - Type.Object(globalResolver, { additionalProperties: false }) -) -const answersSchemaObject = Type.Strict( - Type.Object(answers, { additionalProperties: false }) -) - -export type GlobalEntity = Static -export type GlobalResolver = Static -export type Answers = Static diff --git a/skills/schemas.ts b/skills/schemas.ts deleted file mode 100644 index d85f45c5..00000000 --- a/skills/schemas.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' - -const domainSchema = { - name: Type.String({ minLength: 1 }) -} -const skillBridges = [Type.Literal('python')] -const skillSchema = { - name: Type.String({ minLength: 1 }), - bridge: Type.Union(skillBridges), - version: Type.String({ minLength: 1 }), - description: Type.String({ minLength: 1 }), - author: Type.Object({ - name: Type.String({ minLength: 1 }), - email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }), - url: Type.String({ minLength: 1, maxLength: 255, format: 'uri' }) - }) -} -const skillActionTypes = [Type.Literal('logic'), Type.Literal('dialog')] -const skillDataTypes = [ - Type.Literal('skill_resolver'), - Type.Literal('global_resolver'), - Type.Literal('entity') -] -const skillConfigSchema = { - variables: Type.Optional(Type.Record(Type.String(), Type.String())), - actions: Type.Record( - Type.String(), - Type.Object({ - type: Type.Union(skillActionTypes), - loop: Type.Optional( - Type.Object({ - expected_item: Type.Object({ - type: Type.Union(skillDataTypes), - name: Type.String() - }) - }) - ), - utterance_samples: Type.Optional(Type.Array(Type.String())), - answers: Type.Optional(Type.Array(Type.String())), - unknown_answers: Type.Optional(Type.Array(Type.String())), - suggestions: Type.Optional(Type.Array(Type.String())), - slots: Type.Optional( - Type.Array( - Type.Object({ - name: Type.String(), - item: Type.Object({ - type: Type.Union(skillDataTypes), - name: Type.String() - }), - questions: Type.Array(Type.String()), - suggestions: Type.Optional(Type.Array(Type.String())) - }) - ) - ), - entities: Type.Optional( - Type.Array( - Type.Object({ - type: Type.Literal('enum'), - name: Type.String(), - options: Type.Record( - Type.String(), - Type.Object({ - synonyms: Type.Array(Type.String()) - }) - ) - }) - ) - ), - next_action: Type.Optional(Type.String()) - }) - ), - answers: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))), - entities: Type.Optional(Type.Record(Type.String(), Type.String())) -} - -const domainSchemaObject = Type.Strict( - Type.Object(domainSchema, { additionalProperties: false }) -) -const skillSchemaObject = Type.Strict( - Type.Object(skillSchema, { additionalProperties: false }) -) -const skillConfigSchemaObject = Type.Strict( - Type.Object(skillConfigSchema, { additionalProperties: false }) -) - -export type Domain = Static -export type Skill = Static -export type SkillConfig = Static -export type SkillBridge = Static From fb75b40fb65b86ab0a100549b79e5e4331d70b6a Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 13:00:15 +0100 Subject: [PATCH 3/9] feat: validate JSON Schemas with AJV --- package-lock.json | 264 +++++++++++++++++++++------------------- package.json | 5 +- server/src/ajv.ts | 25 ++++ server/src/pre-check.ts | 120 +++++++++--------- 4 files changed, 225 insertions(+), 189 deletions(-) create mode 100644 server/src/ajv.ts diff --git a/package-lock.json b/package-lock.json index 312c398f..6a61a5eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,10 @@ "@nlpjs/core-loader": "^4.22.7", "@nlpjs/lang-all": "^4.22.12", "@nlpjs/nlp": "^4.22.17", - "@sinclair/typebox": "0.24.49", + "@segment/ajv-human-errors": "2.1.2", + "@sinclair/typebox": "0.25.8", + "ajv": "8.11.0", + "ajv-formats": "2.1.1", "archiver": "^5.3.1", "async": "^3.2.0", "axios": "1.1.2", @@ -1644,6 +1647,28 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/@fast-csv/format": { "version": "4.3.5", "dev": true, @@ -1700,24 +1725,6 @@ "fast-uri": "^2.0.0" } }, - "node_modules/@fastify/ajv-compiler/node_modules/ajv": { - "version": "8.11.0", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/@fastify/deepmerge": { "version": "1.1.0", "license": "MIT" @@ -3311,10 +3318,18 @@ "version": "1.1.0", "license": "BSD-3-Clause" }, + "node_modules/@segment/ajv-human-errors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@segment/ajv-human-errors/-/ajv-human-errors-2.1.2.tgz", + "integrity": "sha512-d1uQndRFLRO01+xW1y5m+joxDgHf5SLJ70YCY2ArLoo2FJ50o6AoX2mEbuGvnKz/IdwnvDugm9Ti3mZQkW1OoA==", + "peerDependencies": { + "ajv": "^8.0.0" + } + }, "node_modules/@sinclair/typebox": { - "version": "0.24.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.49.tgz", - "integrity": "sha512-qWgJVeCThMWTJSAZHyHDlHBkJY0LARFoq/96RH4oIFAwJptMwB3Isq62c4zRVRIAF2r4RMOc2WOhtOLj5C4InA==" + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.8.tgz", + "integrity": "sha512-/7GXgMOCfnxpIov52cPetqQ7bxRBaTBJAgp04Se2UQB1J0vUfEOIMpn63cLc3S5JXDUflCWxELKDV8eiPpmUTQ==" }, "node_modules/@sinonjs/commons": { "version": "1.8.5", @@ -4107,38 +4122,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { "version": "8.11.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -4150,9 +4136,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } }, "node_modules/ansi-colors": { "version": "4.1.3", @@ -6607,6 +6605,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", @@ -6634,6 +6648,12 @@ "node": ">=10" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "node_modules/espree": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", @@ -6952,24 +6972,6 @@ "rfdc": "^1.2.0" } }, - "node_modules/fast-json-stringify/node_modules/ajv": { - "version": "8.11.0", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/fast-levenshtein": { "version": "2.0.6", "dev": true, @@ -10080,9 +10082,9 @@ "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -15544,6 +15546,26 @@ "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } } }, "@fast-csv/format": { @@ -15598,20 +15620,6 @@ "ajv": "^8.11.0", "ajv-formats": "^2.1.1", "fast-uri": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0" - } } }, "@fastify/deepmerge": { @@ -16839,10 +16847,16 @@ "@protobufjs/utf8": { "version": "1.1.0" }, + "@segment/ajv-human-errors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@segment/ajv-human-errors/-/ajv-human-errors-2.1.2.tgz", + "integrity": "sha512-d1uQndRFLRO01+xW1y5m+joxDgHf5SLJ70YCY2ArLoo2FJ50o6AoX2mEbuGvnKz/IdwnvDugm9Ti3mZQkW1OoA==", + "requires": {} + }, "@sinclair/typebox": { - "version": "0.24.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.49.tgz", - "integrity": "sha512-qWgJVeCThMWTJSAZHyHDlHBkJY0LARFoq/96RH4oIFAwJptMwB3Isq62c4zRVRIAF2r4RMOc2WOhtOLj5C4InA==" + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.8.tgz", + "integrity": "sha512-/7GXgMOCfnxpIov52cPetqQ7bxRBaTBJAgp04Se2UQB1J0vUfEOIMpn63cLc3S5JXDUflCWxELKDV8eiPpmUTQ==" }, "@sinonjs/commons": { "version": "1.8.5", @@ -17338,33 +17352,22 @@ } }, "ajv": { - "version": "6.12.6", - "dev": true, + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "ajv-formats": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "requires": { "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0" - } } }, "ansi-colors": { @@ -18715,6 +18718,18 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", @@ -18731,6 +18746,12 @@ "dev": true } } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true } } }, @@ -19139,20 +19160,6 @@ "fast-deep-equal": "^3.1.3", "fast-uri": "^2.1.0", "rfdc": "^1.2.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0" - } } }, "fast-levenshtein": { @@ -21282,8 +21289,9 @@ "dev": true }, "json-schema-traverse": { - "version": "0.4.1", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", diff --git a/package.json b/package.json index 471d9ca5..4f9cd59f 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,10 @@ "@nlpjs/core-loader": "^4.22.7", "@nlpjs/lang-all": "^4.22.12", "@nlpjs/nlp": "^4.22.17", - "@sinclair/typebox": "0.24.49", + "@segment/ajv-human-errors": "2.1.2", + "@sinclair/typebox": "0.25.8", + "ajv": "8.11.0", + "ajv-formats": "2.1.1", "archiver": "^5.3.1", "async": "^3.2.0", "axios": "1.1.2", diff --git a/server/src/ajv.ts b/server/src/ajv.ts new file mode 100644 index 00000000..ce46ae23 --- /dev/null +++ b/server/src/ajv.ts @@ -0,0 +1,25 @@ +import addFormats from 'ajv-formats' +import Ajv from 'ajv' + +export const ajv = addFormats( + new Ajv({ + allErrors: true, + verbose: true + }), + [ + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' + ] +) diff --git a/server/src/pre-check.ts b/server/src/pre-check.ts index 0f9ea60d..3c7b0e08 100644 --- a/server/src/pre-check.ts +++ b/server/src/pre-check.ts @@ -1,8 +1,9 @@ import fs from 'node:fs' import path from 'node:path' -import { TypeCompiler } from '@sinclair/typebox/compiler' +import { AggregateAjvError } from '@segment/ajv-human-errors' +import { ajv } from '@/ajv' import { amazonVoiceConfiguration, googleCloudVoiceConfiguration, @@ -31,6 +32,29 @@ import { SkillDomainHelper } from '@/helpers/skill-domain-helper' import { VOICE_CONFIG_PATH, GLOBAL_DATA_PATH } from '@/constants' import { getGlobalEntitiesPath, getGlobalResolversPath } from '@/utilities' +interface ObjectUnknown { + [key: string]: unknown +} + +const validateSchema = ( + schema: ObjectUnknown, + contentToValidate: ObjectUnknown, + customErrorMesage: string +): void => { + const validate = ajv.compile(schema) + const isValid = validate(contentToValidate) + if (!isValid) { + const errors = new AggregateAjvError(validate.errors ?? []) + const messages: string[] = [] + for (const error of errors) { + messages.push(error.message) + } + LogHelper.error(customErrorMesage) + LogHelper.error(messages.join('\n')) + process.exit(1) + } +} + /** * Pre-checking * Ensure JSON files are correctly formatted @@ -64,14 +88,11 @@ const GLOBAL_DATA_SCHEMAS = { await fs.promises.readFile(path.join(VOICE_CONFIG_PATH, file), 'utf8') ) const [configName] = file.split('.') as [keyof typeof VOICE_CONFIG_SCHEMAS] - const result = TypeCompiler.Compile(VOICE_CONFIG_SCHEMAS[configName]) - const errors = [...result.Errors(config)] - - if (errors.length > 0) { - LogHelper.error(`The voice configuration schema "${file}" is not valid:`) - LogHelper.error(JSON.stringify(errors, null, 2)) - process.exit(1) - } + validateSchema( + VOICE_CONFIG_SCHEMAS[configName], + config, + `The voice configuration schema "${file}" is not valid:` + ) } LogHelper.success('Voice configuration schemas checked') @@ -94,14 +115,11 @@ const GLOBAL_DATA_SCHEMAS = { const globalEntity: GlobalEntity = JSON.parse( await fs.promises.readFile(path.join(globalEntitiesPath, file), 'utf8') ) - const result = TypeCompiler.Compile(globalEntitySchemaObject) - const errors = [...result.Errors(globalEntity)] - - if (errors.length > 0) { - LogHelper.error(`The global entity schema "${file}" is not valid:`) - LogHelper.error(JSON.stringify(errors, null, 2)) - process.exit(1) - } + validateSchema( + globalEntitySchemaObject, + globalEntity, + `The global entity schema "${file}" is not valid:` + ) } /** @@ -116,14 +134,11 @@ const GLOBAL_DATA_SCHEMAS = { const globalResolver: GlobalResolver = JSON.parse( await fs.promises.readFile(path.join(globalResolversPath, file), 'utf8') ) - const result = TypeCompiler.Compile(globalResolverSchemaObject) - const errors = [...result.Errors(globalResolver)] - - if (errors.length > 0) { - LogHelper.error(`The global resolver schema "${file}" is not valid:`) - LogHelper.error(JSON.stringify(errors, null, 2)) - process.exit(1) - } + validateSchema( + globalResolverSchemaObject, + globalResolver, + `The global resolver schema "${file}" is not valid:` + ) } /** @@ -135,15 +150,11 @@ const GLOBAL_DATA_SCHEMAS = { 'utf8' ) ) - const result = TypeCompiler.Compile(GLOBAL_DATA_SCHEMAS.answers) - - const errors = [...result.Errors(answers)] - - if (errors.length > 0) { - LogHelper.error(`The global answers schema "answers.json" is not valid:`) - LogHelper.error(JSON.stringify(errors, null, 2)) - process.exit(1) - } + validateSchema( + GLOBAL_DATA_SCHEMAS.answers, + answers, + `The global answers schema "answers.json" is not valid:` + ) } LogHelper.success('Global data schemas checked') @@ -162,14 +173,11 @@ const GLOBAL_DATA_SCHEMAS = { const domainObject: Domain = JSON.parse( await fs.promises.readFile(pathToDomain, 'utf8') ) - const domainResult = TypeCompiler.Compile(domainSchemaObject) - const domainErrors = [...domainResult.Errors(domainObject)] - - if (domainErrors.length > 0) { - LogHelper.error(`The domain schema "${pathToDomain}" is not valid:`) - LogHelper.error(JSON.stringify(domainErrors, null, 2)) - process.exit(1) - } + validateSchema( + domainSchemaObject, + domainObject, + `The domain schema "${pathToDomain}" is not valid:` + ) const skillKeys = Object.keys(currentDomain.skills) @@ -184,14 +192,11 @@ const GLOBAL_DATA_SCHEMAS = { const skillObject: Skill = JSON.parse( await fs.promises.readFile(pathToSkill, 'utf8') ) - const skillResult = TypeCompiler.Compile(skillSchemaObject) - const skillErrors = [...skillResult.Errors(skillObject)] - - if (skillErrors.length > 0) { - LogHelper.error(`The skill schema "${pathToSkill}" is not valid:`) - LogHelper.error(JSON.stringify(skillErrors, null, 2)) - process.exit(1) - } + validateSchema( + skillSchemaObject, + skillObject, + `The skill schema "${pathToSkill}" is not valid:` + ) /** * Skills config checking @@ -206,16 +211,11 @@ const GLOBAL_DATA_SCHEMAS = { const skillConfig: SkillConfig = JSON.parse( await fs.promises.readFile(skillConfigPath, 'utf8') ) - const result = TypeCompiler.Compile(skillConfigSchemaObject) - const errors = [...result.Errors(skillConfig)] - - if (errors.length > 0) { - LogHelper.error( - `The skill config schema "${skillConfigPath}" is not valid:` - ) - LogHelper.error(JSON.stringify(errors, null, 2)) - process.exit(1) - } + validateSchema( + skillConfigSchemaObject, + skillConfig, + `The skill config schema "${skillConfigPath}" is not valid:` + ) } } } From 4ce9676319293d4d26c8ab4376ea9ab09535c7ff Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 13:02:51 +0100 Subject: [PATCH 4/9] fix: include voice configuration sample schemas --- core/config/voice/google-cloud.sample.json | 12 ++++++------ core/config/voice/watson-stt.sample.json | 2 +- core/config/voice/watson-tts.sample.json | 2 +- server/src/pre-check.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/config/voice/google-cloud.sample.json b/core/config/voice/google-cloud.sample.json index d411fdfc..cf1425a5 100644 --- a/core/config/voice/google-cloud.sample.json +++ b/core/config/voice/google-cloud.sample.json @@ -1,12 +1,12 @@ { - "type": "", + "type": "service_account", "project_id": "", "private_key_id": "", "private_key": "", - "client_email": "", + "client_email": "example@email.com", "client_id": "", - "auth_uri": "", - "token_uri": "", - "auth_provider_x509_cert_url": "", - "client_x509_cert_url": "" + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/" } diff --git a/core/config/voice/watson-stt.sample.json b/core/config/voice/watson-stt.sample.json index 12bf6c52..26e43d3c 100644 --- a/core/config/voice/watson-stt.sample.json +++ b/core/config/voice/watson-stt.sample.json @@ -1,4 +1,4 @@ { "apikey": "", - "url": "" + "url": "https://api.eu.apiconnect.ibmcloud.com/ibmcloud/ibmcloud-dev/sb/api/RESTs" } diff --git a/core/config/voice/watson-tts.sample.json b/core/config/voice/watson-tts.sample.json index 12bf6c52..26e43d3c 100644 --- a/core/config/voice/watson-tts.sample.json +++ b/core/config/voice/watson-tts.sample.json @@ -1,4 +1,4 @@ { "apikey": "", - "url": "" + "url": "https://api.eu.apiconnect.ibmcloud.com/ibmcloud/ibmcloud-dev/sb/api/RESTs" } diff --git a/server/src/pre-check.ts b/server/src/pre-check.ts index 3c7b0e08..10857312 100644 --- a/server/src/pre-check.ts +++ b/server/src/pre-check.ts @@ -81,7 +81,7 @@ const GLOBAL_DATA_SCHEMAS = { const voiceConfigFiles = ( await fs.promises.readdir(VOICE_CONFIG_PATH) - ).filter((file) => file.endsWith('.json') && !file.includes('.sample.')) + ).filter((file) => file.endsWith('.json')) for (const file of voiceConfigFiles) { const config: VoiceConfiguration = JSON.parse( From 194a9b1bbbcd4895d7a1e15682e2f8f05508c510 Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 13:08:32 +0100 Subject: [PATCH 5/9] fix: add http_api property to skill config schema http_api is used by `skills/news/github_trends/config/en.json`. --- server/src/schemas/skill-schemas.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/src/schemas/skill-schemas.ts b/server/src/schemas/skill-schemas.ts index a9573ca9..863e881d 100644 --- a/server/src/schemas/skill-schemas.ts +++ b/server/src/schemas/skill-schemas.ts @@ -126,6 +126,22 @@ export const skillConfigSchemaObject = Type.Strict( { additionalProperties: false } ) ), + http_api: Type.Optional( + Type.Object( + { + entities: Type.Array( + Type.Object( + { + entity: Type.String(), + resolution: Type.Array(Type.String()) + }, + { additionalProperties: false } + ) + ) + }, + { additionalProperties: false } + ) + ), utterance_samples: Type.Optional(Type.Array(Type.String())), answers: Type.Optional(Type.Array(Type.String())), unknown_answers: Type.Optional(Type.Array(Type.String())), From 28c15885fd922b7c27fdad2228e383e4ee8dc670 Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 13:36:54 +0100 Subject: [PATCH 6/9] fix: improve error display in pre-check --- server/src/pre-check.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/server/src/pre-check.ts b/server/src/pre-check.ts index 10857312..76e10724 100644 --- a/server/src/pre-check.ts +++ b/server/src/pre-check.ts @@ -44,13 +44,11 @@ const validateSchema = ( const validate = ajv.compile(schema) const isValid = validate(contentToValidate) if (!isValid) { - const errors = new AggregateAjvError(validate.errors ?? []) - const messages: string[] = [] - for (const error of errors) { - messages.push(error.message) - } LogHelper.error(customErrorMesage) - LogHelper.error(messages.join('\n')) + const errors = new AggregateAjvError(validate.errors ?? []) + for (const error of errors) { + LogHelper.error(error.message) + } process.exit(1) } } @@ -84,14 +82,15 @@ const GLOBAL_DATA_SCHEMAS = { ).filter((file) => file.endsWith('.json')) for (const file of voiceConfigFiles) { + const voiceConfigPath = path.join(VOICE_CONFIG_PATH, file) const config: VoiceConfiguration = JSON.parse( - await fs.promises.readFile(path.join(VOICE_CONFIG_PATH, file), 'utf8') + await fs.promises.readFile(voiceConfigPath, 'utf8') ) const [configName] = file.split('.') as [keyof typeof VOICE_CONFIG_SCHEMAS] validateSchema( VOICE_CONFIG_SCHEMAS[configName], config, - `The voice configuration schema "${file}" is not valid:` + `The voice configuration schema "${voiceConfigPath}" is not valid:` ) } LogHelper.success('Voice configuration schemas checked') @@ -112,13 +111,14 @@ const GLOBAL_DATA_SCHEMAS = { ).filter((file) => file.endsWith('.json')) for (const file of globalEntityFiles) { + const globalEntityPath = path.join(globalEntitiesPath, file) const globalEntity: GlobalEntity = JSON.parse( - await fs.promises.readFile(path.join(globalEntitiesPath, file), 'utf8') + await fs.promises.readFile(globalEntityPath, 'utf8') ) validateSchema( globalEntitySchemaObject, globalEntity, - `The global entity schema "${file}" is not valid:` + `The global entity schema "${globalEntityPath}" is not valid:` ) } @@ -131,29 +131,31 @@ const GLOBAL_DATA_SCHEMAS = { ).filter((file) => file.endsWith('.json')) for (const file of globalResolverFiles) { + const globalResolverPath = path.join(globalResolversPath, file) const globalResolver: GlobalResolver = JSON.parse( - await fs.promises.readFile(path.join(globalResolversPath, file), 'utf8') + await fs.promises.readFile(globalResolverPath, 'utf8') ) validateSchema( globalResolverSchemaObject, globalResolver, - `The global resolver schema "${file}" is not valid:` + `The global resolver schema "${globalResolverPath}" is not valid:` ) } /** * Global answers checking */ + const globalAnswersPath = path.join(GLOBAL_DATA_PATH, lang, 'answers.json') const answers: GlobalAnswers = JSON.parse( await fs.promises.readFile( - path.join(GLOBAL_DATA_PATH, lang, 'answers.json'), + globalAnswersPath, 'utf8' ) ) validateSchema( GLOBAL_DATA_SCHEMAS.answers, answers, - `The global answers schema "answers.json" is not valid:` + `The global answers schema "${globalAnswersPath}" is not valid:` ) } LogHelper.success('Global data schemas checked') From 2ada7f2efab310247405da2b2d16d860eb76e28e Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 13 Nov 2022 13:41:51 +0100 Subject: [PATCH 7/9] docs: make clear that commands should be run on Ubuntu in CONTRIBUTING to set up the Python Environment --- .github/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cf571620..eb4d9145 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -162,7 +162,8 @@ The TCP server is used to communicate between the core and third-party nodes, su To contribute to these parts, you need to set up a Python environment running with a specific Python version and a specific Pipenv version. It is recommended to use Pyenv to manage your Python versions. -If you are on Linux, you can run the following to install Pyenv, otherwise, please refer to the [Pyenv documentation to install it](https://github.com/pyenv/pyenv#installation): + +If you are on GNU/Linux Ubuntu, you can run the following to install Pyenv, otherwise, please refer to the [Pyenv documentation to install it](https://github.com/pyenv/pyenv#installation): ```bash # Update registry From ec1003095187ed35ba573d80f24a260548efc95f Mon Sep 17 00:00:00 2001 From: louistiti Date: Mon, 14 Nov 2022 23:49:44 +0800 Subject: [PATCH 8/9] refactor: modify voice config samples default values --- core/config/voice/google-cloud.sample.json | 4 ++-- core/config/voice/watson-stt.sample.json | 2 +- core/config/voice/watson-tts.sample.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/config/voice/google-cloud.sample.json b/core/config/voice/google-cloud.sample.json index cf1425a5..54245f2e 100644 --- a/core/config/voice/google-cloud.sample.json +++ b/core/config/voice/google-cloud.sample.json @@ -3,10 +3,10 @@ "project_id": "", "private_key_id": "", "private_key": "", - "client_email": "example@email.com", + "client_email": "example@iam.gserviceaccount.com", "client_id": "", "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", + "token_uri": "https://accounts.google.com/o/oauth2/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/" } diff --git a/core/config/voice/watson-stt.sample.json b/core/config/voice/watson-stt.sample.json index 26e43d3c..562bf08d 100644 --- a/core/config/voice/watson-stt.sample.json +++ b/core/config/voice/watson-stt.sample.json @@ -1,4 +1,4 @@ { "apikey": "", - "url": "https://api.eu.apiconnect.ibmcloud.com/ibmcloud/ibmcloud-dev/sb/api/RESTs" + "url": "https://stream.watsonplatform.net/speech-to-text/api" } diff --git a/core/config/voice/watson-tts.sample.json b/core/config/voice/watson-tts.sample.json index 26e43d3c..69d736c7 100644 --- a/core/config/voice/watson-tts.sample.json +++ b/core/config/voice/watson-tts.sample.json @@ -1,4 +1,4 @@ { "apikey": "", - "url": "https://api.eu.apiconnect.ibmcloud.com/ibmcloud/ibmcloud-dev/sb/api/RESTs" + "url": "https://stream.watsonplatform.net/text-to-speech/api" } From c7aa84f9c41964b12c21038a7b95dad6d7eb9651 Mon Sep 17 00:00:00 2001 From: louistiti Date: Mon, 14 Nov 2022 23:50:58 +0800 Subject: [PATCH 9/9] docs: remove sponsor --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index f1a63cc2..808e35b3 100644 --- a/README.md +++ b/README.md @@ -185,13 +185,6 @@ You'll find a write-up on this [blog post](https://blog.getleon.ai/the-story-beh
100 USD / month - - - - phareal -
- 30 USD / month -