Skip to content

Commit e7ac5f8

Browse files
authored
Merge pull request #157 from redhat-developer/add-delete-modify-api
Implemented API for modifying and deleting parts of schemas in memory
2 parents 5fae492 + 93aab43 commit e7ac5f8

6 files changed

Lines changed: 298 additions & 12 deletions

File tree

src/languageservice/services/yamlSchemaService.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,27 @@ const localize = nls.loadMessageBundle();
1616

1717
export declare type CustomSchemaProvider = (uri: string) => Thenable<string>;
1818

19+
export enum MODIFICATION_ACTIONS {
20+
'delete',
21+
'add'
22+
}
23+
24+
export interface SchemaAdditions {
25+
schema: string,
26+
action: MODIFICATION_ACTIONS.add,
27+
path: string,
28+
key: string,
29+
// tslint:disable-next-line: no-any
30+
content: any
31+
}
32+
33+
export interface SchemaDeletions {
34+
schema: string,
35+
action: MODIFICATION_ACTIONS.delete,
36+
path: string,
37+
key: string
38+
}
39+
1940
export class FilePatternAssociation {
2041

2142
private schemas: string[];
@@ -45,6 +66,9 @@ export class FilePatternAssociation {
4566
}
4667

4768
export class YAMLSchemaService extends JSONSchemaService {
69+
// To allow to use schemasById from super.
70+
// tslint:disable-next-line: no-any
71+
[x: string]: any;
4872

4973
private customSchemaProvider: CustomSchemaProvider | undefined;
5074
private filePatternAssociations: FilePatternAssociation[];
@@ -236,6 +260,89 @@ export class YAMLSchemaService extends JSONSchemaService {
236260
}
237261
}
238262

263+
/**
264+
* Save a schema with schema ID and schema content.
265+
* Overrides previous schemas set for that schema ID.
266+
*/
267+
public async saveSchema(schemaId: string, schemaContent: JSONSchema): Promise<void> {
268+
const id = this.normalizeId(schemaId);
269+
this.getOrAddSchemaHandle(id, schemaContent);
270+
return Promise.resolve(undefined);
271+
}
272+
273+
/**
274+
* Delete a schema with schema ID.
275+
*/
276+
public async deleteSchema(schemaId: string): Promise<void> {
277+
const id = this.normalizeId(schemaId);
278+
if ( this.schemasById[id] ) {
279+
delete this.schemasById[id];
280+
}
281+
return Promise.resolve(undefined);
282+
}
283+
284+
/**
285+
* Add content to a specified schema at a specified path
286+
*/
287+
public async addContent(additions: SchemaAdditions) {
288+
const schema = await this.getResolvedSchema(additions.schema);
289+
if (schema) {
290+
const resolvedSchemaLocation = this.resolveJSONSchemaToSection(schema.schema, additions.path);
291+
292+
if (typeof resolvedSchemaLocation === 'object') {
293+
resolvedSchemaLocation[additions.key] = additions.content;
294+
}
295+
await this.saveSchema(additions.schema, schema.schema);
296+
}
297+
}
298+
299+
/**
300+
* Delete content in a specified schema at a specified path
301+
*/
302+
public async deleteContent(deletions: SchemaDeletions) {
303+
const schema = await this.getResolvedSchema(deletions.schema);
304+
if (schema) {
305+
const resolvedSchemaLocation = this.resolveJSONSchemaToSection(schema.schema, deletions.path);
306+
307+
if (typeof resolvedSchemaLocation === 'object') {
308+
delete resolvedSchemaLocation[deletions.key];
309+
}
310+
await this.saveSchema(deletions.schema, schema.schema);
311+
}
312+
}
313+
314+
/**
315+
* Take a JSON Schema and the path that you would like to get to
316+
* @returns the JSON Schema resolved at that specific path
317+
*/
318+
private resolveJSONSchemaToSection(schema: JSONSchema, paths: string): JSONSchema {
319+
const splitPathway = paths.split('/');
320+
let resolvedSchemaLocation = schema;
321+
for (const path of splitPathway) {
322+
if (path === '') {
323+
continue;
324+
}
325+
this.resolveNext(resolvedSchemaLocation, path);
326+
resolvedSchemaLocation = resolvedSchemaLocation[path];
327+
}
328+
return resolvedSchemaLocation;
329+
}
330+
331+
/**
332+
* Resolve the next Object if they have compatible types
333+
* @param object a location in the JSON Schema
334+
* @param token the next token that you want to search for
335+
*/
336+
// tslint:disable-next-line: no-any
337+
private resolveNext(object: any, token: any) {
338+
// tslint:disable-next-line: no-any
339+
if (Array.isArray(object) && isNaN(token)) {
340+
throw new Error('Expected a number after the array object');
341+
} else if (typeof object === 'object' && typeof token !== 'string') {
342+
throw new Error('Expected a string after the object');
343+
}
344+
}
345+
239346
/**
240347
* Everything below here is needed because we're importing from vscode-json-languageservice umd and we need
241348
* to provide a wrapper around the javascript methods we are calling since they have no type
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const KUBERNETES_SCHEMA_URL = 'https://raw.githubusercontent.com/instrumenta/kubernetes-json-schema/master/v1.14.0-standalone-strict/all.json';
2+
export const JSON_SCHEMASTORE_URL = 'http://schemastore.org/api/json/catalog.json';

src/languageservice/yamlLanguageService.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
* Licensed under the MIT License. See License.txt in the project root for license information.
55
*--------------------------------------------------------------------------------------------*/
66

7-
import { YAMLSchemaService, CustomSchemaProvider } from './services/yamlSchemaService';
7+
import { YAMLSchemaService, CustomSchemaProvider, SchemaAdditions, SchemaDeletions } from './services/yamlSchemaService';
88
import { TextDocument, Position, CompletionList, Diagnostic, Hover, SymbolInformation, DocumentSymbol, CompletionItem, TextEdit } from 'vscode-languageserver-types';
99
import { JSONSchema } from './jsonSchema';
1010
import { YAMLDocumentSymbols } from './services/documentSymbols';
1111
import { YAMLCompletion } from './services/yamlCompletion';
1212
import { YAMLHover } from './services/yamlHover';
1313
import { YAMLValidation } from './services/yamlValidation';
1414
import { YAMLFormatter } from './services/yamlFormatter';
15-
import { LanguageService as JSONLanguageService, getLanguageService as getJSONLanguageService, JSONWorkerContribution } from 'vscode-json-languageservice';
15+
import { getLanguageService as getJSONLanguageService, JSONWorkerContribution } from 'vscode-json-languageservice';
1616

1717
export interface LanguageSettings {
1818
validate?: boolean; //Setting for whether we want to validate the schema
@@ -118,6 +118,10 @@ export interface LanguageService {
118118
doResolve(completionItem): Thenable<CompletionItem>;
119119
resetSchema(uri: string): boolean;
120120
doFormat(document: TextDocument, options: CustomFormatterOptions): TextEdit[];
121+
addSchema(schemaID: string, schema: JSONSchema): void;
122+
deleteSchema(schemaID: string): void;
123+
modifySchemaContent(schemaAdditions: SchemaAdditions): void;
124+
deleteSchemaContent(schemaDeletions: SchemaDeletions): void;
121125
}
122126

123127
export function getLanguageService(schemaRequestService: SchemaRequestService,
@@ -157,6 +161,10 @@ export function getLanguageService(schemaRequestService: SchemaRequestService,
157161
findDocumentSymbols: yamlDocumentSymbols.findDocumentSymbols.bind(yamlDocumentSymbols),
158162
findDocumentSymbols2: yamlDocumentSymbols.findHierarchicalDocumentSymbols.bind(yamlDocumentSymbols),
159163
resetSchema: (uri: string) => schemaService.onResourceChange(uri),
160-
doFormat: formatter.format.bind(formatter)
164+
doFormat: formatter.format.bind(formatter),
165+
addSchema: (schemaID: string, schema: JSONSchema) => schemaService.saveSchema(schemaID, schema),
166+
deleteSchema: (schemaID: string) => schemaService.deleteSchema(schemaID),
167+
modifySchemaContent: (schemaAdditions: SchemaAdditions) => schemaService.addContent(schemaAdditions),
168+
deleteSchemaContent: (schemaDeletions: SchemaDeletions) => schemaService.deleteContent(schemaDeletions)
161169
};
162170
}

src/requestTypes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { NotificationType, RequestType } from 'vscode-languageserver';
2+
import { SchemaAdditions, SchemaDeletions } from './languageservice/services/yamlSchemaService';
23

34
export namespace SchemaAssociationNotification {
45
export const type: NotificationType<{ }, { }> = new NotificationType('json/schemaAssociations');
@@ -23,3 +24,7 @@ export namespace CustomSchemaRequest {
2324
export namespace ColorSymbolRequest {
2425
export const type: RequestType<{ }, { }, { }, { }> = new RequestType('json/colorSymbols');
2526
}
27+
28+
export namespace SchemaModificationNotification {
29+
export const type: RequestType<SchemaAdditions | SchemaDeletions, void, { }, { }> = new RequestType('json/schema/modify');
30+
}

src/server.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,16 @@ import * as URL from 'url';
1616
import { removeDuplicatesObj } from './languageservice/utils/arrUtils';
1717
import { getLanguageService as getCustomLanguageService, LanguageSettings, CustomFormatterOptions, WorkspaceContextService } from './languageservice/yamlLanguageService';
1818
import * as nls from 'vscode-nls';
19-
import { CustomSchemaProvider, FilePatternAssociation } from './languageservice/services/yamlSchemaService';
19+
import { CustomSchemaProvider, FilePatternAssociation, SchemaDeletions, SchemaAdditions, MODIFICATION_ACTIONS } from './languageservice/services/yamlSchemaService';
2020
import { JSONSchema } from './languageservice/jsonSchema';
21-
import { SchemaAssociationNotification, DynamicCustomSchemaRequestRegistration, CustomSchemaRequest } from './requestTypes';
21+
import { SchemaAssociationNotification, DynamicCustomSchemaRequestRegistration, CustomSchemaRequest, SchemaModificationNotification } from './requestTypes';
2222
import { schemaRequestHandler } from './languageservice/services/schemaRequestHandler';
2323
import { isRelativePath, relativeToAbsolutePath } from './languageservice/utils/paths';
2424
import { URI } from 'vscode-uri';
25-
25+
import { KUBERNETES_SCHEMA_URL, JSON_SCHEMASTORE_URL } from './languageservice/utils/schemaUrls';
2626
// tslint:disable-next-line: no-any
2727
nls.config(process.env['VSCODE_NLS_CONFIG'] as any);
2828

29-
/****************
30-
* Constants
31-
****************/
32-
const KUBERNETES_SCHEMA_URL = 'https://raw.githubusercontent.com/instrumenta/kubernetes-json-schema/master/v1.17.0-standalone-strict/all.json';
33-
const JSON_SCHEMASTORE_URL = 'http://schemastore.org/api/json/catalog.json';
34-
3529
/**************************
3630
* Generic helper functions
3731
**************************/
@@ -237,6 +231,7 @@ function updateConfiguration() {
237231
* @param languageSettings current server settings
238232
*/
239233
function configureSchemas(uri: string, fileMatch: string[], schema: any, languageSettings: LanguageSettings) {
234+
240235
uri = checkSchemaURI(uri);
241236

242237
if (schema === null) {
@@ -569,5 +564,14 @@ connection.onDocumentFormatting(formatParams => {
569564
return customLanguageService.doFormat(document, customFormatterSettings);
570565
});
571566

567+
connection.onRequest(SchemaModificationNotification.type, (modifications: SchemaAdditions | SchemaDeletions) => {
568+
if (modifications.action === MODIFICATION_ACTIONS.add) {
569+
customLanguageService.modifySchemaContent(modifications);
570+
} else if (modifications.action === MODIFICATION_ACTIONS.delete) {
571+
customLanguageService.deleteSchemaContent(modifications);
572+
}
573+
return Promise.resolve();
574+
});
575+
572576
// Start listening for any messages from the client
573577
connection.listen();

0 commit comments

Comments
 (0)