Skip to content

Commit

Permalink
Merge pull request #84 from opencaesar/feat/74-json-schemas
Browse files Browse the repository at this point in the history
Feat/74-json-schemas
  • Loading branch information
aematei authored Jun 10, 2024
2 parents 97fd2e7 + ffb9757 commit a1a32a7
Show file tree
Hide file tree
Showing 15 changed files with 452 additions and 75 deletions.
2 changes: 1 addition & 1 deletion controller/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ export function activate(context: vscode.ExtensionContext) {
"**/src/vision/sparql/*.sparql"
);
let viewpointsFolderWatcher = vscode.workspace.createFileSystemWatcher(
"**/src/vision/viewpoints/*.json"
"**/src/vision/viewpoints/**/*.json"
);
let commandsFolderWatcher = vscode.workspace.createFileSystemWatcher(
"**/src/vision/commands/*.json"
Expand Down
24 changes: 24 additions & 0 deletions controller/src/interfaces/ICommandSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Defines the structure of the JSON object that is received from the JSON files in the commands directory.
*
* @field name - The name of the command
* @field id - The id of the command
* @field command - The command object
*
*/
export default interface ICommandSchema {
name: string;
id: string;
command: Command;
}

/**
* Defines the structure of the command
*
* @field type - CRUD command
*
*/
interface Command {
type: string;
}

31 changes: 31 additions & 0 deletions controller/src/interfaces/IPagesSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Defines the structure of the JSON object that is received from the pages.json file.
*
* @field title - The title of the page item
* @field type - The type of the page item. Home = Home Page (there can only be one home page), Group = collection of pages, Diagram = Diagram Page, Tree = Tree Page, and Table = Table Page
* @field path - The path to the configuration of the page item
* @field iconUrl - The url to the icon that gets displayed in the home page. This is typically the url to a SVG published to the web
* @field children - The children of the group items
*
*/
export default interface IPagesSchema {
title: string;
type: string;
path?: string;
iconUrl?: string;
children?: Children[];
}

/**
* Defines the structure of the child pages
*
* @field title - The title of the child page item
* @field type - The type of the child page item. Home = Home Page (there can only be one home page), Group = collection of pages, Diagram = Diagram Page, Tree = Tree Page, and Table = Table Page
* @field path - The path to the configuration of the child page item
*
*/
interface Children {
title: string;
type: string;
path: string;
}
18 changes: 18 additions & 0 deletions controller/src/interfaces/ISparqlConfigSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Defines the structure of the JSON object that is received from the sparqlConfig.json file.
*
* @remarks
* This interface relates to {@link https://jena.apache.org/documentation/fuseki2/fuseki-server-info.html | Fuseki endpoints}.
*
* @field queryEndpoint - The query endpoint
* @field updateAssertionEndpoint - The update endpoint for assertions
* @field updateInferenceEndpoint - The update endpoint for inferences
* @field pingEndpoint - The ping endpoint
*
*/
export default interface ISparqlConfigSchema {
queryEndpoint: string;
updateAssertionEndpoint: string;
updateInferenceEndpoint: string;
pingEndpoint: string;
}
38 changes: 38 additions & 0 deletions controller/src/schemas/commands/commandSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import ICommandSchema from "../../interfaces/ICommandSchema";
import { JSONSchemaType } from "ajv";

/**
* This JSON schema is used with AJV to validate JSON files stored in the commands directory that is stored within OML models.
*
* @remarks
* This constant will help validate JSON data using {@link https://ajv.js.org | AJV}.
*
* The data within the constant was generated with {@link https://www.jsonschema.net | JSON Schema}.
*
*/

export const commandSchema: JSONSchemaType<ICommandSchema[]> = {
$schema: "http://json-schema.org/draft-07/schema#",
type: "array",
items: {
type: "object",
properties: {
name: {
type: "string",
},
id: {
type: "string",
},
command: {
type: "object",
properties: {
type: {
type: "string",
},
},
required: ["type"],
},
},
required: ["name", "id", "command"],
},
};
40 changes: 40 additions & 0 deletions controller/src/schemas/config/sparqlConfigSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import ISparqlConfigSchema from '../../interfaces/ISparqlConfigSchema';
import { JSONSchemaType } from "ajv";

/**
* This JSON schema is used with AJV to validate sparqlConfig.json data that is stored within OML models.
*
* @remarks
* This constant will help validate JSON data using {@link https://ajv.js.org | AJV}.
*
* The data within the constant was generated with {@link https://www.jsonschema.net | JSON Schema}.
*
*/

export const sparqlConfigSchema: JSONSchemaType<ISparqlConfigSchema> = {
type: 'object',
properties: {
queryEndpoint: {
type: 'string',
title: 'Query Endpoint',
default: 'http://example.com/sparql',
examples: ['http://example.com/sparql']
},
updateAssertionEndpoint: {
type: 'string',
title: 'Update Assertion Endpoint',
default: 'http://example.com/update/assertion'
},
updateInferenceEndpoint: {
type: 'string',
title: 'Update Inference Endpoint',
default: 'http://example.com/update/inference'
},
pingEndpoint: {
type: 'string',
title: 'Ping Endpoint',
default: 'http://example.com/ping'
}
},
required: ['queryEndpoint', 'updateAssertionEndpoint', 'updateInferenceEndpoint', 'pingEndpoint']
};
34 changes: 34 additions & 0 deletions controller/src/schemas/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Ajv, { JSONSchemaType } from "ajv";
import ISparqlConfigSchema from "../interfaces/ISparqlConfigSchema";
import ICommandSchema from "../interfaces/ICommandSchema";
import IPagesSchema from "../interfaces/IPagesSchema";

/**
* This schema validator uses AJV to validate JSON data based on a schema interface.
*
* @remarks
* This function will help validate JSON data using {@link https://ajv.js.org | AJV}.
*
* To learn more about Typescript interfaces refer to the official {@link https://www.typescriptlang.org/docs/handbook/2/objects.html | docs}.
*
* @param schema: The JSON schema that will be used to validate the data
* @param data: The JSON data that will be validated against a schema
*
*/
export const validateSchema = (
schema: JSONSchemaType<ISparqlConfigSchema | ICommandSchema[] | IPagesSchema[]>,
data: Object
) => {
const ajv = new Ajv();

// Validates the provided schema
const validate = ajv.compile(schema);

// Log errors
if (!validate(data)) {
console.error(validate.errors);
}

// Return true if schema is valid and false if invalid
return validate(data);
};
60 changes: 60 additions & 0 deletions controller/src/schemas/viewpoints/pagesSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import IPagesSchema from "../../interfaces/IPagesSchema";
import { JSONSchemaType } from "ajv";

/**
* This JSON schema is used with AJV to validate pages.json data that is stored within OML models.
*
* @remarks
* This constant will help validate JSON data using {@link https://ajv.js.org | AJV}.
*
* This schema has a recursive schema which you can read more {@link https://ajv.js.org/guide/combining-schemas.html | here}.
*
* This schema has nullable properties which you can read more {@link https://ajv.js.org/guide/typescript.html#utility-types-for-schemas | here}.
*
* The data within the constant was generated with {@link https://www.jsonschema.net | JSON Schema}.
*
*/

export const pagesSchema: JSONSchemaType<IPagesSchema[]> = {
$schema: "http://json-schema.org/draft-07/schema#",
type: "array",
items: {
type: "object",
properties: {
title: {
type: "string",
},
type: {
type: "string",
},
path: {
type: "string",
nullable: true,
},
iconUrl: {
type: "string",
nullable: true,
},
children: {
type: "array",
nullable: true,
items: {
type: "object",
properties: {
title: {
type: "string",
},
type: {
type: "string",
},
path: {
type: "string",
},
},
required: ["title", "type", "path"],
},
},
},
required: ["title", "type"],
},
};
17 changes: 10 additions & 7 deletions controller/src/utilities/loaders/loadCommandFiles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { workspace, Uri, commands, window, FileType } from "vscode";
import { TablePanel } from "../../panels/TablePanel";
import { PropertyPanelProvider } from "../../panels/PropertyPanelProvider";
import { validateSchema } from "../../schemas/validator";
import { commandSchema } from "../../schemas/commands/commandSchema";
// TODO: handle multiple workspaces (currently assumes model is in the 1st)

/**
Expand Down Expand Up @@ -35,7 +37,14 @@ export const loadCommandFiles = async (
const fileUri = Uri.joinPath(commandFolderUri, file);
const buffer = await workspace.fs.readFile(fileUri);
const content = JSON.parse(buffer.toString());

// Validate if the content matches the JSON Command Schema
const validate = validateSchema(commandSchema, content);
if (files.length > 0 && validate) {
commands.executeCommand("setContext", "vision:hasCommand", true);
window.showInformationMessage(`${file} loaded successfully.`);
} else {
window.showErrorMessage(`Invalid or missing ${file}.`);
}
try {
TablePanel.updateCommands();
PropertyPanelProvider.updateCommands();
Expand All @@ -46,12 +55,6 @@ export const loadCommandFiles = async (
}
}
}
if (files.length > 0) {
commands.executeCommand("setContext", "vision:hasCommand", true);
window.showInformationMessage("Command files loaded successfully.");
} else {
window.showWarningMessage("Command files not found.");
}
} catch (err) {
if (
err instanceof Error &&
Expand Down
Loading

0 comments on commit a1a32a7

Please sign in to comment.