/********************************************************************************
 * Copyright (C) 2018 TypeFox and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/ import { injectable , inject , named } from 'inversify' ; import { ContributionProvider } from '../common/contribution-provider' ; import { FrontendApplicationContribution } from './frontend-application' ; import { MaybePromise } from '../common' ; import { Endpoint } from './endpoint' ; import { timeout , Deferred } from '../common/promise-util' ; export interface JsonSchemaConfiguration { fileMatch : string | string [ ] ; url : string ; } export interface JsonSchemaRegisterContext { registerSchema ( config ) : void ; } export const JsonSchemaContribution = Symbol ( 'JsonSchemaContribution' ) ; export interface JsonSchemaContribution { registerSchemas ( store ) : MaybePromise < void > ; } @ injectable ( ) export class JsonSchemaStore implements FrontendApplicationContribution { @ inject ( ContributionProvider ) @ named ( JsonSchemaContribution ) protected readonly contributions : ContributionProvider < JsonSchemaContribution > ; protected readonly _schemas = new Deferred < JsonSchemaConfiguration [ ] > ( ) ; get schemas ( ) : Promise < JsonSchemaConfiguration [ ] > { return this . _schemas . promise ; } onStart ( ) { const pendingRegistrations = [ ] ; const schemas = [ ] ; const freeze = ( ) => { Object . freeze ( schemas ) ; this . _schemas . resolve ( schemas ) ; } ; const registerTimeout = this . getRegisterTimeout ( ) ; const frozenErrorCode = 'JsonSchemaRegisterContext.frozen' ; const context = { registerSchema : schema => { if ( Object . isFrozen ( schemas ) ) { throw new Error ( frozenErrorCode ) ; } schemas . push ( schema ) ; } } ; for ( const contribution of this . contributions . getContributions ( ) ) { const result = contribution . registerSchemas ( context ) ; if ( result ) { pendingRegistrations . push ( result . then ( ( ) => { } , e => { if ( e instanceof Error && e . message === frozenErrorCode ) { console . error ( `${ contribution . constructor . name }.registerSchemas is taking more than ${ registerTimeout . toFixed ( 1 ) } ms, new schemas are ignored.` ) ; } else { console . error ( e ) ; } } ) ) ; } } if ( pendingRegistrations . length ) { let pending = Promise . all ( pendingRegistrations ) . then ( ( ) => { } ) ; if ( registerTimeout ) { pending = Promise . race ( [ pending , timeout ( registerTimeout ) ] ) ; } pending . then ( freeze ) ; } else { freeze ( ) ; } } protected getRegisterTimeout ( ) { return 500 ; } } @ injectable ( ) export class DefaultJsonSchemaContribution implements JsonSchemaContribution { async registerSchemas ( context ) { const url = `${ new Endpoint ( ) . httpScheme }//schemastore.azurewebsites.net/api/json/catalog.json` ; const response = await fetch ( url ) ; const schemas = ( await response . json ( ) ) . schemas ! ; for ( const s of schemas ) { if ( s . fileMatch ) { context . registerSchema ( { fileMatch : s . fileMatch , url : s . url } ) ; } } } } export namespace DefaultJsonSchemaContribution { export interface SchemaData { name : string ; description : string ; fileMatch ? : string [ ] ; url : string ; schema : any ; } }