Skip to content

Commit

Permalink
Merge pull request #20 from alexa/apl-suggester-2023.2
Browse files Browse the repository at this point in the history
June 2023 Release of APL Suggester 2023.2
  • Loading branch information
Ao99 authored Jul 6, 2023
2 parents e73b4ec + dd0d7e6 commit f6a8fb3
Show file tree
Hide file tree
Showing 88 changed files with 393 additions and 711 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ npm install apl-suggester --save

Since June 7 2022, we have changed our versioning strategy. Existing users please make sure to pull the latest and update your `package.json`:
```
npm install apl-suggester@2022 --save
npm install apl-suggester@2023 --save
```

Then use it in your app:
Expand Down
12 changes: 5 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apl-suggester",
"version": "2023.1.0",
"version": "2023.2.0",
"description": "This Package is developed for providing suggestions and validations on APL templates.",
"engines": {
"node": ">=8.0.0"
Expand All @@ -19,9 +19,7 @@
"compile": "tsc",
"unittest-watch": "mocha --watch src/**/__tests__/*.spec.{ts,tsx} --watch-extensions ts,tsx --config ./.mocharc.json",
"unittest": "mocha --config ./.mocharc.json 'src/**/__tests__/*.spec.{ts,tsx}'",
"lintsrc": "tslint --type-check --project tsconfig.json -c tslint.json 'src/**/*.ts'",
"linttst": "tslint --type-check --project tsconfig.json -c tslint.json 'tst/**/*.ts'",
"lint": "npm run lintsrc && npm run linttst",
"lint": "tslint --project tsconfig.json -c tslint.json 'src/**/*.ts'",
"test": "nyc npm run unittest",
"test-watch": "nyc npm run unittest-watch",
"cleanup": "rm -rf dist",
Expand Down Expand Up @@ -58,7 +56,7 @@
"devDependencies": {
"@types/mocha": "^2.2.48",
"awesome-typescript-loader": "5.x",
"axios-mock-adapter": "^1.16.0",
"axios-mock-adapter": "1.16.0",
"chai": "^3.5.0",
"istanbul": "^0.4.x",
"jsdom": "^16.5.0",
Expand All @@ -74,11 +72,11 @@
"@types/core-js": "2.5.x",
"@types/node": "12.0.x",
"@types/sinon": "2.2.x",
"ajv": "6.12.3",
"ajv": "^7.2.4",
"axios": "0.27.2",
"immutable": "4.1.0",
"jsdom-global": "3.0.x",
"ts-node": "3.3.x",
"typescript": "3.x"
"typescript": "^4.9.4"
}
}
31 changes: 16 additions & 15 deletions src/CommandSchemaValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import { IJsonSchema } from './assets/IJsonSchema';
import { IValidationInfo } from './validation';
import * as ajv from 'ajv';
import ajv from './util/Ajv';
import { StaticAplTemplateValidator } from './StaticAplTemplateValidator';
import { ValidationErrorFilter } from './util/ValidationErrorFilter';
import { NotificationLevel } from './IValidationInfo';
Expand Down Expand Up @@ -110,22 +110,23 @@ export class CommandSchemaValidator {
* @memberof CommandSchemaValidator
*/
public validateCommand(jsonObject : object, commandType : string) : IValidationInfo[] {
const commandJsonSchema : IJsonSchema = CommandSchemaValidator.COMMAND_TYPE_TO_JSON_SCHEMA
.get(commandType);
let validateFunction = ajv.getSchema(commandType);
const results = [];
if (!commandJsonSchema) {
results.push({
errorMessage : 'Failed to find JSON schema for command: ' + commandType,
path : '/',
level : NotificationLevel.WARN
} as IValidationInfo);
return results;

if (!validateFunction) {
const commandJsonSchema : IJsonSchema = CommandSchemaValidator.COMMAND_TYPE_TO_JSON_SCHEMA
.get(commandType);
if (!commandJsonSchema) {
results.push({
errorMessage : 'Failed to find JSON schema for command: ' + commandType,
path : '/',
level : NotificationLevel.WARN
} as IValidationInfo);
return results;
}
validateFunction = ajv.addSchema(commandJsonSchema, commandType).getSchema(commandType);
}
const validateFunction = ajv({
jsonPointers : true,
allErrors : true,
verbose : true
}).compile(commandJsonSchema);

const succeed = validateFunction(jsonObject);
if (succeed) {
return [];
Expand Down
39 changes: 23 additions & 16 deletions src/ComponentSchemaController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import { IJsonSchema } from './assets/IJsonSchema';
import { IValidationInfo } from './validation';
import * as ajv from 'ajv';
import ajv from './util/Ajv';
import { StaticAplTemplateValidator } from './StaticAplTemplateValidator';
import { ValidationErrorFilter } from './util/ValidationErrorFilter';
import { getRootComponentName } from './util/Utils';
Expand Down Expand Up @@ -149,23 +149,30 @@ export class ComponentSchemaController {
aplTemplate : object,
jsonObject : object,
componentType : string,
parentComponentType? : string) : Promise<IValidationInfo[]> {
const componentJsonSchema : IJsonSchema =
await this.getComponentSchema(aplTemplate, componentType, parentComponentType);
parentComponentType? : string
) : Promise<IValidationInfo[]> {
const schemaId = (parentComponentType || '') + componentType;
let validateFunction = ajv.getSchema(schemaId);
const results = [];
if (!componentJsonSchema) {
results.push({
errorMessage : 'Failed to find JSON schema for component: ' + componentType,
path : '/',
level : NotificationLevel.WARN
} as IValidationInfo);
return results;

// not a compiled and cached schema
if (!validateFunction) {
const componentJsonSchema : IJsonSchema =
await this.getComponentSchema(aplTemplate, componentType, parentComponentType);

if (!componentJsonSchema) {
results.push({
errorMessage : 'Failed to find JSON schema for component: ' + componentType,
path : '/',
level : NotificationLevel.WARN
} as IValidationInfo);
return results;
}
// Please refer to Ajv documentation on the method chaining
// https://ajv.js.org/guide/managing-schemas.html#using-ajv-instance-cache
validateFunction = ajv.addSchema(componentJsonSchema, schemaId).getSchema(schemaId);
}
const validateFunction = ajv({
jsonPointers : true,
allErrors : true,
verbose : true
}).compile(componentJsonSchema);

const succeed = validateFunction(jsonObject);
if (succeed) {
return [];
Expand Down
32 changes: 17 additions & 15 deletions src/GraphicSchemaValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import { IJsonSchema } from './assets/IJsonSchema';
import { IValidationInfo } from './validation';
import * as ajv from 'ajv';
import ajv from './util/Ajv';
import { StaticAplTemplateValidator } from './StaticAplTemplateValidator';
import { ValidationErrorFilter } from './util/ValidationErrorFilter';
import { NotificationLevel } from './IValidationInfo';
Expand Down Expand Up @@ -74,22 +74,24 @@ export class GraphicSchemaValidator {
* @memberof GraphicSchemaValidator
*/
public validateGraphic(jsonObject : object, graphicType : string) : IValidationInfo[] {
const graphicJsonSchema : IJsonSchema = GraphicSchemaValidator.GRAPHIC_TYPE_TO_JSON_SCHEMA
.get(graphicType);
const schemaId = 'avg_' + graphicType;
const results = [];
if (!graphicJsonSchema) {
results.push({
errorMessage : 'Failed to find JSON schema for graphic: ' + graphicType,
path : '/',
level : NotificationLevel.WARN
} as IValidationInfo);
return results;
let validateFunction = ajv.getSchema(schemaId);

if (!validateFunction) {
const graphicJsonSchema : IJsonSchema = GraphicSchemaValidator.GRAPHIC_TYPE_TO_JSON_SCHEMA
.get(graphicType);
if (!graphicJsonSchema) {
results.push({
errorMessage : 'Failed to find JSON schema for graphic: ' + graphicType,
path : '/',
level : NotificationLevel.WARN
} as IValidationInfo);
return results;
}
validateFunction = ajv.addSchema(graphicJsonSchema, schemaId).getSchema(schemaId);
}
const validateFunction = ajv({
jsonPointers : true,
allErrors : true,
verbose : true
}).compile(graphicJsonSchema);

const succeed = validateFunction(jsonObject);
if (succeed) {
return [];
Expand Down
49 changes: 25 additions & 24 deletions src/StaticAplTemplateValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

'use strict';

import * as ajv from 'ajv';
import ajv from './util/Ajv';
import { JSON_SCHEMA } from './assets/JsonSchema';
import * as AVGJSONSchema from './assets/graphics/AVG';
import { IValidationInfo } from './validation/index.js';
Expand Down Expand Up @@ -134,13 +134,10 @@ export class StaticAplTemplateValidator {
* @memberof StaticAplTemplateValidator
*/
constructor() {
this.validateFunction = ajv({
jsonPointers : true,
allErrors : true,
verbose : true
})
.addSchema(AVGJSONSchema.JSON_SCHEMA)
.compile(JSON_SCHEMA);
if (!ajv.getSchema(AVGJSONSchema.JSON_SCHEMA.$id)) {
ajv.addSchema(AVGJSONSchema.JSON_SCHEMA);
}
this.validateFunction = ajv.getSchema(JSON_SCHEMA.$id) || ajv.compile(JSON_SCHEMA);
this.aplComponentExtractor = AplComponentsExtractor.getInstance();
this.componentSchemaController = ComponentSchemaController.getInstance();
this.componentStructureValidator = ComponentStructureValidator.getInstance();
Expand Down Expand Up @@ -193,28 +190,32 @@ export class StaticAplTemplateValidator {
const customComponentTypes = await this.customComponentsExtractor
.getCustomComponentTypesAndValidate(aplTemplate);
const availableComponentTypes = primitiveComponentTypes.concat(customComponentTypes);
const structureErrors = components

const nonCustomComponents = components
.filter((c) => !this.componentSchemaController.isCustomComponent(c.jsonPath)
&& this.isComponentTypeValid(c, availableComponentTypes))
.map((c) => {
&& this.isComponentTypeValid(c, availableComponentTypes));
let structureErrors : IValidationInfo[] = [];
let componentErrors : IValidationInfo[];
nonCustomComponents.forEach((c) => {
const validationSeed = new ValidationSeed(
new Seed(c.jsonObject, c.jsonPath), new Seed(c.parentComponent));
return this.componentStructureValidator.validate(validationSeed);
componentErrors = this.componentStructureValidator.validate(validationSeed);
structureErrors = structureErrors.concat(componentErrors);
});

// run schema validation against all supported APL Component types.
const schemaErrors = await Promise.all(components
.filter((c) => primitiveComponentTypes.includes(c.componentType))
.map(async (c) : Promise<IValidationInfo[]> => {
const errs : IValidationInfo[] =
await this.componentSchemaController.validateComponent(
aplTemplate, c.jsonObject, c.componentType, c.parentComponentType);
return errs.map((e) => this.getComponentErrorWithFullPath(e, c.jsonPath));
}));

return structureErrors
.concat(schemaErrors)
.reduce((result, nextItem) => result.concat(nextItem), []);
const primitiveComponents = components
.filter((item) => primitiveComponentTypes.includes(item.componentType));
let schemaErrors = [];
for (let c of primitiveComponents) {
// validation must be sequential for Ajv singleton to cache properly!
componentErrors = await this.componentSchemaController.validateComponent(
aplTemplate, c.jsonObject, c.componentType, c.parentComponentType
);
componentErrors = componentErrors.map((e) => this.getComponentErrorWithFullPath(e, c.jsonPath));
schemaErrors = schemaErrors.concat(componentErrors);
}
return structureErrors.concat(schemaErrors);
}

/**
Expand Down
14 changes: 8 additions & 6 deletions src/__tests__/CommandSchemaValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,14 @@ describe('CommandSchemaValidator.', () => {
it('should received correct amount of validation errors.', async () => {
const data = fs.readFileSync(`src/__tests__/commands/ErrorCommand.json`, 'utf8');
const result = commandSchemaValidator.validateCommand(JSON.parse(data), 'SetState');
expect(result.length).to.be.equal(2);
expect(result[0].path).to.be.equal('/state');
expect(result[0].level).to.be.equal(NotificationLevel.WARN);
expect(result[1].path).to.be.equal('/');
expect(result[1].level).to.be.equal(NotificationLevel.WARN);
expect(result[1].errorMessage.indexOf('value') > 0).to.be.equal(true);
expect(result.length).to.equal(2);

expect(result[0].path).to.equal('/');
expect(result[0].level).to.equal(NotificationLevel.WARN);
expect(result[0].errorMessage.indexOf('\'value\'') > 0).to.equal(true);

expect(result[1].path).to.equal('/state');
expect(result[1].level).to.equal(NotificationLevel.WARN);
});

async function verifyCommand(fileName : string, type : string) {
Expand Down
16 changes: 8 additions & 8 deletions src/__tests__/GraphicSchemaValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,22 @@ describe('GraphicSchemaValidator.', () => {
expect(result.length).to.be.equal(10);
expect(result[0].path).to.be.equal('/');
expect(result[0].level).to.be.equal(NotificationLevel.WARN);
expect(result[0].errorMessage.indexOf('pathData') > 0).to.be.equal(true);
expect(result[1].path).to.be.equal('/');
expect(result[1].level).to.be.equal(NotificationLevel.WARN);
expect(result[2].path).to.be.equal('/bind/0');
expect(result[2].path).to.be.equal('/');
expect(result[2].level).to.be.equal(NotificationLevel.WARN);
expect(result[2].errorMessage.indexOf('value') > 0).to.be.equal(true);
expect(result[3].path).to.be.equal('/filters/0/type');
expect(result[3].path).to.be.equal('/bind/0');
expect(result[3].level).to.be.equal(NotificationLevel.WARN);
expect(result[4].path).to.be.equal('/filters/0/horizontalOffset');
expect(result[3].errorMessage.indexOf('value') > 0).to.be.equal(true);
expect(result[4].path).to.be.equal('/filters/0/type');
expect(result[4].level).to.be.equal(NotificationLevel.WARN);
expect(result[5].path).to.be.equal('/when');
expect(result[5].path).to.be.equal('/filters/0/horizontalOffset');
expect(result[5].level).to.be.equal(NotificationLevel.WARN);
expect(result[6].path).to.be.equal('/fill');
expect(result[6].path).to.be.equal('/when');
expect(result[6].level).to.be.equal(NotificationLevel.WARN);
expect(result[7].path).to.be.equal('/');
expect(result[7].path).to.be.equal('/fill');
expect(result[7].level).to.be.equal(NotificationLevel.WARN);
expect(result[7].errorMessage.indexOf('pathData') > 0).to.be.equal(true);
expect(result[8].path).to.be.equal('/pathLength');
expect(result[8].level).to.be.equal(NotificationLevel.WARN);
expect(result[9].path).to.be.equal('/stroke');
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/StaticAplTemplateValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ describe('Integration Test to verify the JSON schema.', () => {
const result = await verifyTemplate('errorCommandTemplate.json');
expect(result.length).to.equal(3);
expect(result[0].path).to.equal('/onConfigChange/1/preservedSequencers');
expect(result[1].path).to.equal('/onMount/0/state');
expect(result[2].path).to.equal('/onMount/0');
expect(result[1].path).to.equal('/onMount/0');
expect(result[2].path).to.equal('/onMount/0/state');
});

it('should compile with video template when souce is array of string.', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "APL",
"version": "2023.1",
"version": "2023.2",
"theme": "light",
"description": "This is a sample APL document",
"extensions": [
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/templates/allowedRootHandlerTemplate.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "APL",
"version": "2023.1",
"version": "2023.2",
"license": "Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0\nLicensed under the Amazon Software License http://aws.amazon.com/asl/",
"settings": {},
"theme": "dark",
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/templates/errorCommandTemplate.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "APL",
"version": "2022.2",
"license": "Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0\nLicensed under the Amazon Software License http://aws.amazon.com/asl/",
"version": "2023.2",
"license": "Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0\nLicensed under the Amazon Software License http://aws.amazon.com/asl/",
"settings": {},
"theme": "dark",
"import": [],
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/templates/errorResourceAplTemplate.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "APL",
"version": "2022.2",
"version": "2023.2",
"resources": [
{
"colors": {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/templates/errorTemplate.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"import": [
{
"name": "alexa-layouts",
"version": "1.0.0"
"version": "1.6.0"
}
],
"resources": [
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/templates/gridSequenceAplTemplate.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"import": [
{
"name": "alexa-layouts",
"version": "1.0.0"
"version": "1.6.0"
}
],
"mainTemplate": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "APL",
"version": "2022.1",
"version": "2023.2",
"mainTemplate": {
"parameters": [
"payload"
Expand Down
Loading

0 comments on commit f6a8fb3

Please sign in to comment.