mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-03-10 00:32:25 +00:00
feat: improve monorepo dev experience
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
"test:unit": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 mocha --exit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "8.4.0",
|
||||
"@elastic/elasticsearch": "8.10.0",
|
||||
"@openstapps/core": "workspace:*",
|
||||
"@openstapps/core-tools": "workspace:*",
|
||||
"@openstapps/logger": "workspace:*",
|
||||
@@ -56,6 +56,8 @@
|
||||
"@types/nodemailer": "6.4.7",
|
||||
"@types/promise-queue": "2.2.0",
|
||||
"@types/uuid": "8.3.4",
|
||||
"ajv": "8.12.0",
|
||||
"ajv-formats": "2.1.1",
|
||||
"body-parser": "1.20.2",
|
||||
"cors": "2.8.5",
|
||||
"cosmiconfig": "8.1.3",
|
||||
@@ -102,16 +104,6 @@
|
||||
"tsup": "6.7.0",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"tsup": {
|
||||
"entry": [
|
||||
"src/cli.ts"
|
||||
],
|
||||
"sourcemap": true,
|
||||
"clean": true,
|
||||
"target": "es2022",
|
||||
"format": "esm",
|
||||
"outDir": "lib"
|
||||
},
|
||||
"prettier": "@openstapps/prettier-config",
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {
|
||||
SCConfigFile,
|
||||
SCNotFoundErrorResponse,
|
||||
SCRequestBodyTooLargeErrorResponse,
|
||||
SCSyntaxErrorResponse,
|
||||
@@ -23,8 +24,7 @@ import {Logger} from '@openstapps/logger';
|
||||
import cors from 'cors';
|
||||
import {Express} from 'express';
|
||||
import morgan from 'morgan';
|
||||
import path from 'path';
|
||||
import {DEFAULT_TIMEOUT, isTestEnvironment, mailer, plugins, validator} from './common.js';
|
||||
import {DEFAULT_TIMEOUT, isTestEnvironment, mailer, plugins} from './common.js';
|
||||
import {getPrometheusMiddleware} from './middleware/prometheus.js';
|
||||
import {MailQueue} from './notification/mail-queue.js';
|
||||
import {bulkAddRouter} from './routes/bulk-add-route.js';
|
||||
@@ -39,7 +39,7 @@ import {virtualPluginRoute} from './routes/virtual-plugin-route.js';
|
||||
import {BulkStorage} from './storage/bulk-storage.js';
|
||||
import {DatabaseConstructor} from './storage/database.js';
|
||||
import {backendConfig} from './config.js';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {createValidator} from './validator.js';
|
||||
|
||||
/**
|
||||
* Configure the backend
|
||||
@@ -143,19 +143,10 @@ export async function configureApp(app: Express, databases: {[name: string]: Dat
|
||||
request.on('data', chunkGatherer).on('end', endCallback);
|
||||
});
|
||||
|
||||
// validate config file
|
||||
const directory = path.dirname(fileURLToPath(import.meta.url));
|
||||
await validator.addSchemas(
|
||||
path.join(directory, '..', 'node_modules', '@openstapps', 'core', 'lib', 'schema'),
|
||||
);
|
||||
|
||||
// validate the config file
|
||||
const configValidation = validator.validate(backendConfig, 'SCConfigFile');
|
||||
|
||||
// validation failed
|
||||
if (configValidation.errors.length > 0) {
|
||||
const configFileValid = createValidator<SCConfigFile>('SCConfigFile');
|
||||
if (!configFileValid(backendConfig)) {
|
||||
throw new Error(
|
||||
`Validation of config file failed. Errors were: ${JSON.stringify(configValidation.errors)}`,
|
||||
`Validation of config file failed. Errors were: ${JSON.stringify(configFileValid.errors)}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCPluginMetaData} from '@openstapps/core';
|
||||
import {Validator} from '@openstapps/core-tools';
|
||||
import {BackendTransport} from './notification/backend-transport.js';
|
||||
|
||||
/**
|
||||
@@ -22,11 +21,6 @@ import {BackendTransport} from './notification/backend-transport.js';
|
||||
*/
|
||||
export const mailer = BackendTransport.getTransportInstance();
|
||||
|
||||
/**
|
||||
* A validator instance to check if something is a valid JSON object (e.g. a request or a thing)
|
||||
*/
|
||||
export const validator = new Validator();
|
||||
|
||||
/**
|
||||
* Provides information if the backend is executed in the "test" (non-production) environment
|
||||
*/
|
||||
|
||||
@@ -19,12 +19,12 @@ import {
|
||||
SCRoute,
|
||||
SCValidationErrorResponse,
|
||||
} from '@openstapps/core';
|
||||
import {ValidationError} from '@openstapps/core-tools/src/types/validator.js';
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import {Application, Router} from 'express';
|
||||
import PromiseRouter from 'express-promise-router';
|
||||
import {isTestEnvironment, validator} from '../common.js';
|
||||
import {isTestEnvironment} from '../common.js';
|
||||
import {isHttpMethod} from './http-types.js';
|
||||
import {createValidator} from '../validator.js';
|
||||
|
||||
/**
|
||||
* Creates a router from a route class and a handler function which implements the logic
|
||||
@@ -44,6 +44,8 @@ export function createRoute<REQUESTTYPE, RETURNTYPE>(
|
||||
): Router {
|
||||
// create router
|
||||
const router = PromiseRouter({mergeParams: true});
|
||||
const requestValidator = createValidator<REQUESTTYPE>(routeClass.requestBodyName);
|
||||
const responseValidator = createValidator<RETURNTYPE>(routeClass.responseBodyName);
|
||||
|
||||
// create route
|
||||
// the given type has no index signature so we have to cast to get the IRouteHandler when a HTTP method is given
|
||||
@@ -56,11 +58,8 @@ export function createRoute<REQUESTTYPE, RETURNTYPE>(
|
||||
// create a route handler for the given HTTP method
|
||||
route[verb](async (request, response) => {
|
||||
try {
|
||||
// validate request
|
||||
const requestValidation = validator.validate(request.body, routeClass.requestBodyName);
|
||||
|
||||
if (requestValidation.errors.length > 0) {
|
||||
const error = new SCValidationErrorResponse(requestValidation.errors, isTestEnvironment);
|
||||
if (!requestValidator(request.body)) {
|
||||
const error = new SCValidationErrorResponse(requestValidator.errors as any, isTestEnvironment);
|
||||
response.status(error.statusCode);
|
||||
response.json(error);
|
||||
await Logger.error(error);
|
||||
@@ -68,17 +67,13 @@ export function createRoute<REQUESTTYPE, RETURNTYPE>(
|
||||
return;
|
||||
}
|
||||
|
||||
// hand over request to handler with path parameters
|
||||
const handlerResponse = await handler(request.body, request.app, request.params);
|
||||
|
||||
// validate response generated by handler
|
||||
const responseErrors: ValidationError[] = validator.validate(
|
||||
handlerResponse,
|
||||
routeClass.responseBodyName,
|
||||
).errors;
|
||||
|
||||
if (responseErrors.length > 0) {
|
||||
const validationError = new SCValidationErrorResponse(responseErrors, isTestEnvironment);
|
||||
if (!responseValidator(handlerResponse)) {
|
||||
const validationError = new SCValidationErrorResponse(
|
||||
responseValidator.errors as any,
|
||||
isTestEnvironment,
|
||||
);
|
||||
// The validation error is not caused by faulty user input, but through an error that originates somewhere in
|
||||
// the backend, therefore we use this "stacked" error.
|
||||
const internalServerError = new SCInternalServerErrorResponse(validationError, isTestEnvironment);
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import {SCInternalServerErrorResponse, SCPluginMetaData, SCValidationErrorResponse} from '@openstapps/core';
|
||||
import {Request} from 'express';
|
||||
import got from 'got';
|
||||
import {isTestEnvironment, validator} from '../common.js';
|
||||
import {isTestEnvironment} from '../common.js';
|
||||
import {backendConfig} from '../config.js';
|
||||
import {validator} from '../validator.js';
|
||||
|
||||
/**
|
||||
* Generic route function used to proxy actual requests to plugins
|
||||
@@ -28,10 +29,9 @@ import {backendConfig} from '../config.js';
|
||||
*/
|
||||
export async function virtualPluginRoute(request: Request, plugin: SCPluginMetaData): Promise<object> {
|
||||
try {
|
||||
const requestValidation = validator.validate(request.body, plugin.requestSchema);
|
||||
if (requestValidation.errors.length > 0) {
|
||||
if (!validator.validate(request.body, plugin.requestSchema)) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new SCValidationErrorResponse(requestValidation.errors, isTestEnvironment);
|
||||
throw new SCValidationErrorResponse(validator.errors as any, isTestEnvironment);
|
||||
}
|
||||
// send the request to the plugin (forward the body) and save the response
|
||||
const response = await got.post(plugin.route.replaceAll(/^\//gi, ''), {
|
||||
@@ -43,10 +43,9 @@ export async function virtualPluginRoute(request: Request, plugin: SCPluginMetaD
|
||||
responseType: 'json',
|
||||
});
|
||||
const responseBody = response.body;
|
||||
const responseValidation = validator.validate(responseBody, plugin.responseSchema);
|
||||
if (responseValidation.errors.length > 0) {
|
||||
if (!validator.validate(responseBody, plugin.responseSchema)) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw new SCValidationErrorResponse(responseValidation.errors, isTestEnvironment);
|
||||
throw new SCValidationErrorResponse(validator.errors as any, isTestEnvironment);
|
||||
}
|
||||
return responseBody as object;
|
||||
} catch (error) {
|
||||
|
||||
25
backend/backend/src/validator.ts
Normal file
25
backend/backend/src/validator.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import Ajv from 'ajv';
|
||||
import addFormats from 'ajv-formats';
|
||||
import schema from '@openstapps/core?json-schema';
|
||||
|
||||
export const validator = new Ajv.default({
|
||||
schemas: [schema],
|
||||
verbose: true,
|
||||
allowUnionTypes: true,
|
||||
});
|
||||
addFormats.default(validator, {
|
||||
formats: ['date-time', 'time', 'uuid', 'duration'],
|
||||
mode: 'fast',
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a validator function
|
||||
* @example
|
||||
* import schema from '@openstapps/core#schema:SCThings'
|
||||
* createValidator<SCThings>(schema)
|
||||
*/
|
||||
export function createValidator<T>(schemaName: string): Ajv.ValidateFunction<T> {
|
||||
return validator.compile({
|
||||
$ref: `#/definitions/${schemaName}`,
|
||||
});
|
||||
}
|
||||
10
backend/backend/tsup.config.ts
Normal file
10
backend/backend/tsup.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {defineConfig} from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/cli.ts'],
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
target: 'es2022',
|
||||
format: 'esm',
|
||||
outDir: 'lib',
|
||||
});
|
||||
@@ -40,5 +40,10 @@
|
||||
"turbo-ignore": "1.10.6",
|
||||
"typedoc": "0.24.8",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"@elastic/elasticsearch@8.10.0": "patches/@elastic__elasticsearch@8.10.0.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"@types/cli-progress": "3.11.0",
|
||||
"@types/express": "4.17.17",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/json-schema": "7.0.11",
|
||||
"@types/json-schema": "7.0.14",
|
||||
"@types/junit-report-builder": "3.0.0",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/node": "18.15.3",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"@openstapps/logger": "workspace:*",
|
||||
"@types/body-parser": "1.19.2",
|
||||
"@types/express": "4.17.17",
|
||||
"@types/json-schema": "7.0.11",
|
||||
"@types/json-schema": "7.0.14",
|
||||
"@types/morgan": "1.9.4",
|
||||
"body-parser": "1.20.2",
|
||||
"express": "4.18.2",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"@openstapps/easy-ast": "workspace:*",
|
||||
"@openstapps/logger": "workspace:*",
|
||||
"ajv": "8.12.0",
|
||||
"ajv-formats": "2.1.1",
|
||||
"better-ajv-errors": "1.2.0",
|
||||
"commander": "10.0.0",
|
||||
"deepmerge": "4.3.1",
|
||||
@@ -60,11 +61,10 @@
|
||||
"humanize-string": "3.0.0",
|
||||
"json-schema": "0.4.0",
|
||||
"mustache": "4.2.0",
|
||||
"openapi-types": "12.1.0",
|
||||
"openapi-types": "12.1.3",
|
||||
"plantuml-encoder": "1.4.0",
|
||||
"re2": "1.18.2",
|
||||
"toposort": "2.0.2",
|
||||
"ts-json-schema-generator": "1.2.0"
|
||||
"ts-json-schema-generator": "1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openstapps/eslint-config": "workspace:*",
|
||||
@@ -73,7 +73,7 @@
|
||||
"@types/chai": "4.3.5",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/glob": "8.0.1",
|
||||
"@types/json-schema": "7.0.11",
|
||||
"@types/json-schema": "7.0.14",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/mustache": "4.2.2",
|
||||
"@types/node": "18.15.3",
|
||||
|
||||
@@ -17,15 +17,17 @@ import {Command} from 'commander';
|
||||
import {existsSync, readFileSync, writeFileSync} from 'fs';
|
||||
import path from 'path';
|
||||
import {lightweightDefinitionsFromPath, lightweightProjectFromPath} from '@openstapps/easy-ast';
|
||||
import {openapi3Template} from './resources/openapi-303-template.js';
|
||||
import {gatherRouteInformation, generateOpenAPIForRoute} from './routes.js';
|
||||
import {Converter, getValidatableTypesInPath} from './schema.js';
|
||||
import {openapi3Template} from '../../openapi-generator/src/openapi-303-template.js';
|
||||
import {
|
||||
gatherRouteInformation,
|
||||
generateOpenAPIForRoute,
|
||||
} from '../../openapi-generator/src/generator/routes.js';
|
||||
import {Converter, getValidatableTypesInPath, mergeSchemas} from './schema.js';
|
||||
import {createDiagram, createDiagramFromString} from './uml/create-diagram.js';
|
||||
import {UMLConfig} from './uml/uml-config.js';
|
||||
import {capitalize} from './util/string.js';
|
||||
import {validateFiles, writeReport} from './validate.js';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {mkdir, readFile} from 'fs/promises';
|
||||
import {readFile} from 'fs/promises';
|
||||
|
||||
// handle unhandled promise rejections
|
||||
process.on('unhandledRejection', async (reason: unknown) => {
|
||||
@@ -52,56 +54,10 @@ commander.command('prototype <srcBundle> <out>').action(async (sourcePath, out)
|
||||
|
||||
commander
|
||||
.command('openapi <srcPath> <outDirPath>')
|
||||
.action(async (relativeSourceBundlePath, relativeOutDirectoryPath) => {
|
||||
// get absolute paths
|
||||
const sourcePath = path.resolve(relativeSourceBundlePath);
|
||||
const outDirectoryPath = path.resolve(relativeOutDirectoryPath);
|
||||
const outDirectorySchemasPath = path.join(outDirectoryPath, 'schema');
|
||||
.action(async (relativeSourceBundlePath, relativeOutDirectoryPath) => {});
|
||||
|
||||
// get information about routes
|
||||
const routes = await gatherRouteInformation(sourcePath);
|
||||
routes.sort((a, b) => a.route.urlPath.localeCompare(b.route.urlPath));
|
||||
|
||||
// change url path parameters to openapi notation
|
||||
for (const routeWithMetaInformation of routes) {
|
||||
routeWithMetaInformation.route.urlPath = routeWithMetaInformation.route.urlPath.replaceAll(
|
||||
/:\w+/g,
|
||||
(match: string) => `{${match.replace(':', '')}}`,
|
||||
);
|
||||
}
|
||||
|
||||
// keep openapi tags for routes that actually share url fragments
|
||||
let tagsToKeep = routes.map(routeWithMetaInformation =>
|
||||
capitalize(routeWithMetaInformation.route.urlPath.split('/')[1]),
|
||||
);
|
||||
tagsToKeep = tagsToKeep.filter(
|
||||
(element, i, array) => array.indexOf(element) === i && array.lastIndexOf(element) !== i,
|
||||
);
|
||||
|
||||
// initialize json output
|
||||
const output = openapi3Template;
|
||||
|
||||
// generate documentation for all routes
|
||||
for (const routeWithMetaInformation of routes) {
|
||||
routeWithMetaInformation.tags = [capitalize(routeWithMetaInformation.route.urlPath.split('/')[1])];
|
||||
|
||||
output.paths[routeWithMetaInformation.route.urlPath] = generateOpenAPIForRoute(
|
||||
routeWithMetaInformation,
|
||||
path.relative(relativeOutDirectoryPath, outDirectorySchemasPath),
|
||||
tagsToKeep,
|
||||
);
|
||||
}
|
||||
|
||||
// write openapi object to file (prettified)
|
||||
writeFileSync(path.join(outDirectoryPath, 'openapi.json'), JSON.stringify(output, undefined, 2));
|
||||
|
||||
Logger.ok(`OpenAPI representation resources written to ${outDirectoryPath} .`);
|
||||
});
|
||||
|
||||
commander.command('schema <srcPath> <schemaPath>').action(async (relativeSourcePath, relativeSchemaPath) => {
|
||||
// get absolute paths
|
||||
commander.command('schema <srcPath> <schemaPath>').action(async (relativeSourcePath, schemaPath) => {
|
||||
const absoluteSourcePath = path.resolve(relativeSourcePath);
|
||||
const schemaPath = path.resolve(relativeSchemaPath);
|
||||
|
||||
// initialize new core converter
|
||||
const coreConverter = new Converter(absoluteSourcePath);
|
||||
@@ -111,10 +67,6 @@ commander.command('schema <srcPath> <schemaPath>').action(async (relativeSourceP
|
||||
|
||||
Logger.info(`Found ${validatableTypes.length} type(s) to generate schemas for.`);
|
||||
|
||||
await mkdir(schemaPath, {
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
Logger.info(`Trying to find a package.json for ${absoluteSourcePath}.`);
|
||||
|
||||
let packagePath = absoluteSourcePath;
|
||||
@@ -134,53 +86,12 @@ commander.command('schema <srcPath> <schemaPath>').action(async (relativeSourceP
|
||||
Logger.log(`Using ${coreVersion} as version for schemas.`);
|
||||
|
||||
// generate and write JSONSchema files for validatable types
|
||||
for (const type of validatableTypes) {
|
||||
const schema = coreConverter.getSchema(type, coreVersion);
|
||||
|
||||
const stringifiedSchema = JSON.stringify(schema, undefined, 2);
|
||||
|
||||
const file = path.join(schemaPath, `${type}.json`);
|
||||
|
||||
// write schema to file
|
||||
writeFileSync(file, stringifiedSchema);
|
||||
|
||||
Logger.info(`Generated schema for ${type} and saved to ${file}.`);
|
||||
}
|
||||
const schema = mergeSchemas(validatableTypes.map(type => coreConverter.getSchema(type, coreVersion)));
|
||||
writeFileSync(schemaPath, JSON.stringify(schema, undefined, 2));
|
||||
|
||||
Logger.ok(`Generated schemas for ${validatableTypes.length} type(s).`);
|
||||
});
|
||||
|
||||
commander
|
||||
.command('validate <schemaPath> <testPath> [reportPath]')
|
||||
.action(async (relativeSchemaPath, relativeTestPath, relativeReportPath) => {
|
||||
// get absolute paths
|
||||
const schemaPath = path.resolve(relativeSchemaPath);
|
||||
const testPath = path.resolve(relativeTestPath);
|
||||
|
||||
const errorsPerFile = await validateFiles(schemaPath, testPath);
|
||||
|
||||
let unexpected = false;
|
||||
for (const file in errorsPerFile) {
|
||||
if (!errorsPerFile.hasOwnProperty(file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unexpected = unexpected || errorsPerFile[file].some(error => !error.expected);
|
||||
}
|
||||
|
||||
if (relativeReportPath !== undefined) {
|
||||
const reportPath = path.resolve(relativeReportPath);
|
||||
await writeReport(reportPath, errorsPerFile);
|
||||
}
|
||||
|
||||
if (unexpected) {
|
||||
await Logger.error('Unexpected errors occurred during validation');
|
||||
process.exit(1);
|
||||
} else {
|
||||
Logger.ok('Successfully finished validation.');
|
||||
}
|
||||
});
|
||||
|
||||
commander
|
||||
.command('plantuml <srcPath> <plantumlserver>')
|
||||
.option('--definitions <definitions>', 'Shows these specific definitions (class, interface or enum)', it =>
|
||||
|
||||
26
packages/core-tools/src/better-ajv-errors.d.ts
vendored
26
packages/core-tools/src/better-ajv-errors.d.ts
vendored
@@ -1,26 +0,0 @@
|
||||
declare module 'better-ajv-errors' {
|
||||
import type {ErrorObject} from 'ajv';
|
||||
|
||||
export interface IOutputError {
|
||||
start: {line: number; column: number; offset: number};
|
||||
// Optional for required
|
||||
end?: {line: number; column: number; offset: number};
|
||||
error: string;
|
||||
suggestion?: string;
|
||||
}
|
||||
|
||||
export interface IInputOptions {
|
||||
format?: 'cli' | 'js';
|
||||
indent?: number | null;
|
||||
|
||||
/** Raw JSON used when highlighting error location */
|
||||
json?: string | null;
|
||||
}
|
||||
|
||||
export default function <S, T, Options extends IInputOptions>(
|
||||
schema: S,
|
||||
data: T,
|
||||
errors: Array<ErrorObject>,
|
||||
options?: Options,
|
||||
): Options extends {format: 'js'} ? Array<IOutputError> : string;
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
export * from './validate.js';
|
||||
export * from './types/validator.js';
|
||||
|
||||
export * from './uml/uml-config.js';
|
||||
export * from './uml/create-diagram.js';
|
||||
|
||||
export * from './routes.js';
|
||||
export * from './types/routes.js';
|
||||
export * from '../../openapi-generator/src/generator/routes.js';
|
||||
export * from '../../openapi-generator/src/generator/types/routes.js';
|
||||
|
||||
export * from './schema.js';
|
||||
export * from './types/schema.js';
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {OpenAPIV3} from 'openapi-types';
|
||||
import {
|
||||
isLightweightClass,
|
||||
lightweightProjectFromPath,
|
||||
LightweightProjectWithIndex,
|
||||
} from '@openstapps/easy-ast';
|
||||
import {RouteInstanceWithMeta, RouteWithMetaInformation} from './types/routes.js';
|
||||
import {rejectNil} from './util/collections.js';
|
||||
import {capitalize} from './util/string.js';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Gather relevant information of routes
|
||||
*
|
||||
* This gathers the information for all routes that implement the abstract class SCAbstractRoute.
|
||||
* Furthermore it instantiates every route and adds it to the information.
|
||||
*/
|
||||
export async function gatherRouteInformation(path: string): Promise<RouteWithMetaInformation[]> {
|
||||
const project = new LightweightProjectWithIndex(lightweightProjectFromPath(path));
|
||||
|
||||
// find all classes that implement the SCAbstractRoute
|
||||
return rejectNil(
|
||||
await Promise.all(
|
||||
Object.values(project.definitions)
|
||||
.filter(isLightweightClass)
|
||||
.map(async node => {
|
||||
if (!node.extendedDefinitions?.some(it => it.referenceName === 'SCAbstractRoute')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const instantiatedRoute = (await project.instantiateDefinitionByName(
|
||||
node.name,
|
||||
)) as RouteInstanceWithMeta;
|
||||
// instantiate all errors
|
||||
instantiatedRoute.errors = await Promise.all(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
instantiatedRoute.errorNames.map(async (error: any) =>
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
Object.assign((await project.instantiateDefinitionByName(error.name)) as object, {
|
||||
name: error.name,
|
||||
}),
|
||||
),
|
||||
);
|
||||
instantiatedRoute.responseBodyDescription =
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||
project.definitions[instantiatedRoute.responseBodyName]?.comment?.shortSummary!;
|
||||
instantiatedRoute.requestBodyDescription =
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||
project.definitions[instantiatedRoute.requestBodyName]?.comment?.shortSummary!;
|
||||
|
||||
return {
|
||||
description: {
|
||||
shortText: node.comment?.shortSummary,
|
||||
text: node.comment?.description,
|
||||
},
|
||||
name: node.name!,
|
||||
route: instantiatedRoute,
|
||||
};
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate documentation snippet for one route
|
||||
* @param routeWithInfo A route instance with its meta information
|
||||
* @param outDirectorySchemasPath Path to directory that will contain relevant schemas for the route
|
||||
* @param tagsToKeep Tags / keywords that can be used for grouping routes
|
||||
*/
|
||||
export function generateOpenAPIForRoute(
|
||||
routeWithInfo: RouteWithMetaInformation,
|
||||
outDirectorySchemasPath: string,
|
||||
tagsToKeep: string[],
|
||||
): OpenAPIV3.PathItemObject {
|
||||
const route = routeWithInfo.route;
|
||||
const openapiPath: OpenAPIV3.PathItemObject = {};
|
||||
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods] = {
|
||||
summary: capitalize(routeWithInfo.description.shortText?.replace(/(Route to |Route for )/gim, '')),
|
||||
description: routeWithInfo.description.text,
|
||||
requestBody: {
|
||||
description: route.responseBodyDescription ?? undefined,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: path.join(outDirectorySchemasPath, `${route.requestBodyName}.json`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'X-StApps-Version',
|
||||
in: 'header',
|
||||
schema: {
|
||||
type: 'string',
|
||||
example: '2.0.0',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
responses: {},
|
||||
tags: routeWithInfo.tags?.filter(value => tagsToKeep.includes(value)),
|
||||
};
|
||||
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods]!.responses![route.statusCodeSuccess] = {
|
||||
description: route.responseBodyDescription,
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: path.join(outDirectorySchemasPath, `${route.responseBodyName}.json`),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const error of route.errors) {
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods]!.responses![error.statusCode] = {
|
||||
description:
|
||||
error.message ?? capitalize(error.name.replaceAll(/([A-Z][a-z])/g, ' $1').replace('SC ', '')),
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: path.join(outDirectorySchemasPath, `${error.name}.json`),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof route.obligatoryParameters === 'object') {
|
||||
for (const [parameter, schemaDefinition] of Object.entries(route.obligatoryParameters)) {
|
||||
const openapiParameter: OpenAPIV3.ParameterObject = {
|
||||
in: 'path',
|
||||
name: parameter,
|
||||
required: true,
|
||||
schema: {
|
||||
// TODO make this less of a hack and search copied schemas for the first occurring definition
|
||||
$ref: `schema/SCSearchResponse.json#/definitions/${schemaDefinition}`,
|
||||
},
|
||||
};
|
||||
openapiPath[route.method.toLowerCase() as OpenAPIV3.HttpMethods]?.parameters?.push(openapiParameter);
|
||||
}
|
||||
}
|
||||
|
||||
return openapiPath;
|
||||
}
|
||||
@@ -12,17 +12,18 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import Ajv from 'ajv';
|
||||
import {JSONSchema7 as JSONSchema} from 'json-schema';
|
||||
import {Config, DEFAULT_CONFIG, Definition, SchemaGenerator} from 'ts-json-schema-generator';
|
||||
import {createFormatter} from 'ts-json-schema-generator';
|
||||
import {createParser} from 'ts-json-schema-generator';
|
||||
import {createProgram} from 'ts-json-schema-generator';
|
||||
import Ajv, {JSONSchemaType} from 'ajv';
|
||||
import {
|
||||
Config,
|
||||
DEFAULT_CONFIG,
|
||||
SchemaGenerator,
|
||||
createParser,
|
||||
createFormatter,
|
||||
createProgram,
|
||||
} from 'ts-json-schema-generator';
|
||||
import {getTsconfigPath} from './common.js';
|
||||
import {definitionsOf, lightweightProjectFromPath} from '@openstapps/easy-ast';
|
||||
import {isSchemaWithDefinitions} from './util/guards.js';
|
||||
import path from 'path';
|
||||
import re2 from 're2';
|
||||
|
||||
/**
|
||||
* StAppsCore converter
|
||||
@@ -30,74 +31,35 @@ import re2 from 're2';
|
||||
* Converts TypeScript source files to JSON schema files
|
||||
*/
|
||||
export class Converter {
|
||||
/**
|
||||
* Generator instance
|
||||
*/
|
||||
private readonly generator: SchemaGenerator;
|
||||
|
||||
/**
|
||||
* Schema validator instance
|
||||
*/
|
||||
private readonly schemaValidator: Ajv.default;
|
||||
|
||||
/**
|
||||
* Create a new converter
|
||||
* @param projectPath Path to the project
|
||||
* @param sourcePath Path to optionally point to a different directory of / or single source file
|
||||
*/
|
||||
constructor(projectPath: string, sourcePath?: string) {
|
||||
// set config for schema generator
|
||||
const config: Config = {
|
||||
...DEFAULT_CONFIG,
|
||||
path: sourcePath,
|
||||
sortProps: true,
|
||||
topRef: false,
|
||||
tsconfig: path.join(getTsconfigPath(projectPath), 'tsconfig.json'),
|
||||
type: 'SC',
|
||||
};
|
||||
|
||||
// create TypeScript program from config
|
||||
const program = createProgram(config);
|
||||
|
||||
// create generator
|
||||
this.generator = new SchemaGenerator(program, createParser(program, config), createFormatter(config));
|
||||
|
||||
// create Ajv instance
|
||||
this.schemaValidator = new Ajv.default({code: {regExp: re2 as never}});
|
||||
this.schemaValidator = new Ajv.default();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get schema for specific StAppsCore type
|
||||
* @param type Type to get the schema for
|
||||
* @param version Version to set for the schema
|
||||
* @param _version Version to set for the schema
|
||||
* @returns Generated schema
|
||||
*/
|
||||
getSchema(type: string, version: string): JSONSchema {
|
||||
// generate schema for this file/type
|
||||
const schema: JSONSchema = this.generator.createSchema(type);
|
||||
|
||||
// set id of schema
|
||||
schema.$id = `https://core.stapps.tu-berlin.de/v${version}/lib/schema/${type}.json`;
|
||||
|
||||
if (isSchemaWithDefinitions(schema)) {
|
||||
const selfReference = {
|
||||
...schema,
|
||||
};
|
||||
|
||||
delete selfReference.$schema;
|
||||
delete selfReference.definitions;
|
||||
delete selfReference.$id;
|
||||
|
||||
// add self reference to definitions
|
||||
schema.definitions![`SC${type}`] = {
|
||||
...(selfReference as unknown as Definition),
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.schemaValidator.validateSchema(schema)) {
|
||||
throw new Error(`Generated schema for ${type} is invalid!`);
|
||||
}
|
||||
|
||||
getSchema(type: string, _version: string): JSONSchemaType<unknown> {
|
||||
const schema = this.generator.createSchema(type) as JSONSchemaType<unknown>;
|
||||
this.schemaValidator.validateSchema(schema, true);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
@@ -110,3 +72,14 @@ export function getValidatableTypesInPath(path: string): string[] {
|
||||
.filter(type => !!type.comment?.tags?.find(it => it.name === 'validatable'))
|
||||
.map(type => type.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge multiple schemas
|
||||
*/
|
||||
export function mergeSchemas(schemas: JSONSchemaType<unknown>[]): JSONSchemaType<unknown> {
|
||||
const completeSchema = {definitions: {}} as JSONSchemaType<unknown>;
|
||||
for (const schema of schemas) {
|
||||
Object.assign(completeSchema.definitions!, schema.definitions);
|
||||
}
|
||||
return completeSchema;
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type SCRoute = any;
|
||||
|
||||
export interface RouteInstanceWithMeta extends SCRoute {
|
||||
/**
|
||||
* Possible errors on a route
|
||||
*/
|
||||
errors: SCErrorResponse[];
|
||||
|
||||
/**
|
||||
* Description of the request body
|
||||
*/
|
||||
requestBodyDescription: string;
|
||||
|
||||
/**
|
||||
* Description of the response body
|
||||
*/
|
||||
responseBodyDescription: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A route instance with its relevant meta information
|
||||
*/
|
||||
export interface RouteWithMetaInformation {
|
||||
/**
|
||||
* Description of the route
|
||||
*/
|
||||
description: {
|
||||
/**
|
||||
* Short text of the description - title
|
||||
*/
|
||||
shortText?: string;
|
||||
/**
|
||||
* Text of the description
|
||||
*/
|
||||
text?: string;
|
||||
};
|
||||
/**
|
||||
* Name of the route
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Instance of the route
|
||||
*/
|
||||
route: RouteInstanceWithMeta;
|
||||
|
||||
/**
|
||||
* Possible tags/keywords the route can be associated with
|
||||
*/
|
||||
tags?: [string];
|
||||
}
|
||||
|
||||
/**
|
||||
* A node with its relevant meta information
|
||||
*/
|
||||
export interface NodeWithMetaInformation {
|
||||
/**
|
||||
* Module the node belongs to
|
||||
*/
|
||||
module: string;
|
||||
|
||||
/**
|
||||
* Type of the node
|
||||
*/
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic error that can be returned by the backend if somethings fails during the processing of a request
|
||||
*/
|
||||
export interface SCErrorResponse extends Error {
|
||||
/**
|
||||
* Additional data that describes the error
|
||||
*/
|
||||
additionalData?: unknown;
|
||||
|
||||
/**
|
||||
* HTTP status code to return this error with
|
||||
*/
|
||||
statusCode: number;
|
||||
}
|
||||
@@ -12,9 +12,3 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Creates sentence cased string
|
||||
*/
|
||||
export function capitalize(string?: string): string {
|
||||
return `${string?.charAt(0).toUpperCase()}${string?.slice(1).toLowerCase()}`;
|
||||
}
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Logger} from '@openstapps/logger';
|
||||
import Ajv from 'ajv';
|
||||
import betterAjvErrors, {IOutputError} from 'better-ajv-errors';
|
||||
import type {PathLike} from 'fs';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import mustache from 'mustache';
|
||||
import {Schema} from 'ts-json-schema-generator';
|
||||
import {ExpectedValidationErrors, ValidationError, ValidationResult} from './types/validator.js';
|
||||
import {isThingWithType} from './util/guards.js';
|
||||
import path from 'path';
|
||||
import re2 from 're2';
|
||||
import {glob} from 'glob';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
/**
|
||||
* StAppsCore validator
|
||||
*/
|
||||
export class Validator {
|
||||
/**
|
||||
* JSON Schema Validator
|
||||
*/
|
||||
private readonly ajv = new Ajv.default({
|
||||
verbose: true,
|
||||
allowUnionTypes: true,
|
||||
code: {regExp: re2 as never},
|
||||
});
|
||||
|
||||
/**
|
||||
* Map of schema names to schemas
|
||||
*/
|
||||
private readonly schemas: {[type: string]: Schema} = {};
|
||||
|
||||
/**
|
||||
* A wrapper function for Ajv that transforms the error into the compatible old error
|
||||
* @param schema the schema that will be validated against
|
||||
* @param instance the instance that will be validated
|
||||
*/
|
||||
private ajvValidateWrapper(schema: JSONSchema7, instance: unknown): ValidationResult {
|
||||
return fromAjvResult(this.ajv.validate(schema, instance), schema, instance, this.ajv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Feed the schema files to the validator
|
||||
* @param schemaDirectory Path to directory that contains schema files
|
||||
*/
|
||||
public async addSchemas(schemaDirectory: string): Promise<string[]> {
|
||||
const searchGlob = path.posix.join(schemaDirectory.replaceAll(path.sep, path.posix.sep), '*.json');
|
||||
const schemaFiles = await glob(searchGlob);
|
||||
|
||||
if (schemaFiles.length === 0) {
|
||||
throw new Error(`No schema files in ${schemaDirectory.toString()}!`);
|
||||
}
|
||||
|
||||
Logger.log(`Adding schemas from ${schemaDirectory} to validator.`);
|
||||
|
||||
await Promise.all(
|
||||
schemaFiles.map(async (file: string) => {
|
||||
// read schema file
|
||||
const buffer = await readFile(file);
|
||||
|
||||
// add schema to map
|
||||
this.schemas[path.basename(file, '.json')] = JSON.parse(buffer.toString());
|
||||
|
||||
Logger.info(`Added ${file} to validator.`);
|
||||
}),
|
||||
);
|
||||
|
||||
return schemaFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates anything against a given schema name or infers schema name from object
|
||||
* @param instance Instance to validate
|
||||
* @param schema Name of schema to validate instance against or the schema itself
|
||||
*/
|
||||
public validate(instance: unknown, schema?: string | Schema): ValidationResult {
|
||||
if (schema === undefined) {
|
||||
if (isThingWithType(instance)) {
|
||||
// schema name can be inferred from type string
|
||||
const schemaSuffix = (instance as {type: string}).type
|
||||
.split(' ')
|
||||
.map((part: string) => {
|
||||
return part.slice(0, 1).toUpperCase() + part.slice(1);
|
||||
})
|
||||
.join('');
|
||||
const schemaName = `SC${schemaSuffix}`;
|
||||
|
||||
return this.validate(instance, schemaName);
|
||||
}
|
||||
throw new Error('Instance.type does not exist.');
|
||||
}
|
||||
if (typeof schema === 'string') {
|
||||
// if you want to access a schema that is contained in the validator object
|
||||
if (typeof this.schemas[schema] !== 'object') {
|
||||
throw new TypeError(`No schema available for ${schema}.`);
|
||||
}
|
||||
|
||||
// schema will be cached
|
||||
return this.ajvValidateWrapper(this.schemas[schema], instance);
|
||||
}
|
||||
|
||||
return this.ajvValidateWrapper(schema, instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ValidationResult from ajv
|
||||
*
|
||||
* Implemented for compatibility purposes
|
||||
* @param result the result, now a ValidationResult
|
||||
* @param schema the schema that has been validated against
|
||||
* @param instance the data that has been validated
|
||||
* @param ajvInstance the ajv instance with which the validation was done
|
||||
*/
|
||||
function fromAjvResult(
|
||||
result: boolean | PromiseLike<unknown>,
|
||||
schema: JSONSchema7,
|
||||
instance: unknown,
|
||||
ajvInstance: Ajv.default,
|
||||
): ValidationResult {
|
||||
const betterErrorObject: IOutputError[] | undefined = betterAjvErrors(
|
||||
schema,
|
||||
instance,
|
||||
ajvInstance.errors ?? [],
|
||||
// eslint-disable-next-line unicorn/no-null
|
||||
{format: 'js', indent: null},
|
||||
);
|
||||
|
||||
return {
|
||||
errors:
|
||||
ajvInstance.errors?.map((ajvError, index) => {
|
||||
const error: ValidationError = {
|
||||
dataPath: ajvError.instancePath,
|
||||
instance: instance,
|
||||
message: betterErrorObject?.[index]?.error ?? ajvError.message,
|
||||
name: ajvError.keyword,
|
||||
schemaPath: ajvError.schemaPath,
|
||||
suggestion: betterErrorObject?.[index]?.suggestion,
|
||||
};
|
||||
// (validationError as ValidationError).humanReadableError = betterErrorCLI?.[index] as unknown as string;
|
||||
|
||||
return error;
|
||||
}) ?? [],
|
||||
valid: typeof result === 'boolean' ? result : false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate all test files in the given resources directory against schema files in the given (schema) directory
|
||||
* @param schemaDirectory The directory where the JSON schema files are
|
||||
* @param resourcesDirectory The directory where the test files are
|
||||
*/
|
||||
export async function validateFiles(
|
||||
schemaDirectory: string,
|
||||
resourcesDirectory: string,
|
||||
): Promise<ExpectedValidationErrors> {
|
||||
// instantiate new validator
|
||||
const v = new Validator();
|
||||
await v.addSchemas(schemaDirectory);
|
||||
|
||||
// get a list of files to test
|
||||
const testFiles = await glob(
|
||||
path.posix.join(resourcesDirectory.replaceAll(path.sep, path.posix.sep), '*.json'),
|
||||
{absolute: true},
|
||||
);
|
||||
|
||||
if (testFiles.length === 0) {
|
||||
throw new Error(`No test files in ${resourcesDirectory}!`);
|
||||
}
|
||||
|
||||
Logger.log(`Found ${testFiles.length} file(s) to test.`);
|
||||
|
||||
// map of errors per file
|
||||
const errors: ExpectedValidationErrors = {};
|
||||
|
||||
await Promise.all(
|
||||
testFiles.map(async (testFile: string) => {
|
||||
const testFileName = path.basename(testFile);
|
||||
|
||||
const buffer = await readFile(testFile);
|
||||
|
||||
// read test description from file
|
||||
const testDescription = JSON.parse(buffer.toString());
|
||||
|
||||
// validate instance from test description
|
||||
const result = v.validate(testDescription.instance, testDescription.schema);
|
||||
|
||||
// list of expected errors for this test description
|
||||
const expectedErrors: string[] = [...testDescription.errorNames];
|
||||
|
||||
// number of unexpected errors
|
||||
let unexpectedErrors = 0;
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
errors[testFileName] = [];
|
||||
|
||||
// iterate over errors
|
||||
for (const error of result.errors) {
|
||||
const errorIndex = expectedErrors.indexOf(error.name);
|
||||
let expected = false;
|
||||
|
||||
if (errorIndex >= 0) {
|
||||
expectedErrors.splice(errorIndex, 1);
|
||||
expected = true;
|
||||
} else {
|
||||
unexpectedErrors++;
|
||||
await Logger.error(`Unexpected error ${error.name} in ${testFile}`);
|
||||
}
|
||||
|
||||
// add error to list of errors
|
||||
errors[testFileName].push({
|
||||
...error,
|
||||
expected,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedErrors.length > 0) {
|
||||
for (const error of expectedErrors) {
|
||||
await Logger.error(`Extraneous expected error '${error}' in ${testFile}.`);
|
||||
|
||||
errors[testFileName].push({
|
||||
dataPath: 'undefined',
|
||||
expected: false,
|
||||
instance: undefined,
|
||||
// instance: testDescription.instance,
|
||||
message: 'undefined',
|
||||
name: `expected ${error}`,
|
||||
schemaPath: 'undefined',
|
||||
suggestion: 'undefined',
|
||||
});
|
||||
}
|
||||
} else if (unexpectedErrors === 0) {
|
||||
Logger.info(`Successfully validated ${testFile}.`);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a report for errors that occurred in validation
|
||||
* @param reportPath Path to write report to
|
||||
* @param errors Errors that occurred in validation
|
||||
*/
|
||||
export async function writeReport(reportPath: PathLike, errors: ExpectedValidationErrors): Promise<void> {
|
||||
let buffer = await readFile(
|
||||
path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'resources', 'file.html.mustache'),
|
||||
);
|
||||
const fileTemplate = buffer.toString();
|
||||
|
||||
buffer = await readFile(
|
||||
path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'resources', 'error.html.mustache'),
|
||||
);
|
||||
const errorTemplate = buffer.toString();
|
||||
|
||||
let output = '';
|
||||
|
||||
for (const fileName in errors) {
|
||||
if (!errors.hasOwnProperty(fileName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fileOutput = '';
|
||||
|
||||
for (const [index, error] of errors[fileName].entries()) {
|
||||
fileOutput += mustache.render(errorTemplate, {
|
||||
idx: index + 1,
|
||||
instance: JSON.stringify(error.instance, undefined, 2),
|
||||
message: error.message,
|
||||
name: error.name,
|
||||
schemaPath: error.schemaPath,
|
||||
status: error.expected ? 'alert-success' : 'alert-danger',
|
||||
suggestion: error.suggestion,
|
||||
});
|
||||
}
|
||||
|
||||
output += mustache.render(fileTemplate, {
|
||||
errors: fileOutput,
|
||||
testFile: fileName,
|
||||
});
|
||||
}
|
||||
|
||||
buffer = await readFile(
|
||||
path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'resources', 'report.html.mustache'),
|
||||
);
|
||||
const reportTemplate = buffer.toString();
|
||||
|
||||
await writeFile(
|
||||
reportPath,
|
||||
mustache.render(reportTemplate, {
|
||||
report: output,
|
||||
timestamp: new Date().toISOString(),
|
||||
}),
|
||||
);
|
||||
|
||||
Logger.ok(`Wrote report to ${reportPath}.`);
|
||||
}
|
||||
199
packages/core-validator/README.md
Normal file
199
packages/core-validator/README.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# @openstapps/core-tools
|
||||
|
||||
[](https://gitlab.com/openstapps/core-tools/commits/master)
|
||||
[](https://npmjs.com/package/@openstapps/core-tools)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
[](https://openstapps.gitlab.io/core-tools)
|
||||
|
||||
Tools to convert and validate StAppsCore
|
||||
|
||||
## What are the tools for?
|
||||
|
||||
The StAppsCore Converter is a tool for converting SC-types (TypeScript) into JSON schema files.
|
||||
|
||||
JSON schema files are needed for run-time validation of SC-type objects, as this is a tedious task to do using SC-types defined in TypeScript (not possible without additional coding). That said, StAppsCore Converter practically prepares SC-types to be used for object validation (determining whether a JavaScript/JSON object is a valid object of the corresponding SC-type) using StAppsCore Validator.
|
||||
|
||||
The StAppsCore Validator is a tool for run-time validation of objects (determining whether a JavaScript/JSON object is a valid object of the corresponding SC-type. It consumes JSON schema files from StAppsCore as the definitions of SC-types against which are validated concrete (actual) objects (as an example SCDish object in the example below).
|
||||
|
||||
## Installation
|
||||
|
||||
Installation of the npm package (using `npm install`) makes the tool available as an executable with the name `openstapps-core-tools`.
|
||||
|
||||
## How to use the converter?
|
||||
|
||||
Add `@validatable` to the Typedoc comment of the types that you want to convert to JSONSchema.
|
||||
|
||||
The command `openstapps-core-tools` can then be called using these arguments:
|
||||
|
||||
```shell
|
||||
openstapps-core-tools schema <srcPath> <schemaPath>
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
- `<srcPath>` is path to the project (where used `*.ts` files are, e.g. `src/core`,
|
||||
- `<schemaPath>` is directory to save output files to, e.g. `lib/schema`.
|
||||
|
||||
Complete command with the example arguments is then:
|
||||
|
||||
```shell
|
||||
openstapps-core-tools schema src/core lib/schema
|
||||
```
|
||||
|
||||
Inside of a script in `package.json` or if the npm package is installed globally, the tool `stapps-convert` can be called without its local path (`node_modules/.bin`):
|
||||
|
||||
```shell
|
||||
openstapps-core-tools schema src/core lib/schema
|
||||
```
|
||||
|
||||
## How to use the validator?
|
||||
|
||||
### Using the validator programatically
|
||||
|
||||
```typescript
|
||||
import {Validator} from '@openstapps/core-tools/lib/validate';
|
||||
import {SCDish, SCThingType} from '@openstapps/core';
|
||||
import {ValidatorResult} from 'jsonschema';
|
||||
import {join} from 'path';
|
||||
|
||||
const objectToValidate: SCDish = {
|
||||
type: SCThingType.Dish,
|
||||
// more properties
|
||||
};
|
||||
|
||||
// instantiate a new validator
|
||||
const validator = new Validator();
|
||||
|
||||
// make the validator read the schema files
|
||||
validator.addSchemas(join('node_modules', '@openstapps', 'core', 'lib', 'schema')).then(() => {
|
||||
// validate an object
|
||||
const result: ValidatorResult = validator.validate(objectToValidate, 'SCDish');
|
||||
});
|
||||
```
|
||||
|
||||
#### Using validateFiles function
|
||||
|
||||
The JSON files passed to the validateFiles method have an added layer.
|
||||
That layer encapsulates the actual JSON data of the object to be verified and adds a property to enable true negative testing.
|
||||
|
||||
Your basic JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"property1": "value1",
|
||||
"property2": "value2",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
JSON for validateFiles:
|
||||
|
||||
```json
|
||||
{
|
||||
"errorNames": [],
|
||||
"instance": {
|
||||
"property1": "value1",
|
||||
"property2": "value2",
|
||||
...
|
||||
},
|
||||
"schema": "NameOfSchema"
|
||||
}
|
||||
```
|
||||
|
||||
Where `errorNames` holds the string values of the name property of the expected ValidationErrors from JSON Schema. Empty array means no errors are expected.
|
||||
|
||||
`schema` holds the name of the schema to validate the instance against.
|
||||
|
||||
### How to use validator as a CLI tool (executable)?
|
||||
|
||||
The command `openstapps-core-tools` can then be called using these arguments:
|
||||
|
||||
```shell
|
||||
openstapps-core-tools validate <schemaPath> <testPath> [reportPath]
|
||||
```
|
||||
|
||||
where:
|
||||
|
||||
- `<schemaPath>` is a directory where JSON schema files are, e.g. `lib/schema`,
|
||||
- `<testPath>` is a directory where test files are, e.g. `src/test/resources`,
|
||||
- `[reportPath]` is a file where the HTML report of the validation will be saved to, e.g. `report.html` (optional argument - if it's not provided no report will be written).
|
||||
|
||||
Command with the example arguments is then for example:
|
||||
|
||||
```shell
|
||||
openstapps-core-tools validate lib/schema src/test/resources
|
||||
```
|
||||
|
||||
Inside of a script in `package.json` or if the npm package is installed globally, the tool `openstapps-validate` can be called without its local path (`node_modules/.bin`):
|
||||
|
||||
```shell
|
||||
openstapps-core-tools validate lib/schema src/test/resources report.html
|
||||
```
|
||||
|
||||
## Generate openapi JSON file for routes
|
||||
|
||||
To generate a openapi JSON file that represents the routes according to openapi version 3.0.3 use the following command.
|
||||
|
||||
```shell
|
||||
openstapps-core-tools openapi PATH/TO/CORE/lib PATH/TO/PUT/FILES/TO
|
||||
```
|
||||
|
||||
## How to use the UML generator
|
||||
|
||||
The UML Generator generates PlantUML from the project reflection of the source files. By default it will include externals, which will take considerably longer to execute, you can disable this behaviour via an option. It can help you to visually explore the data model or document a specific part.
|
||||
|
||||
You can either use the public PlantUML-server or start your own local instance. To run, restart or stop the container use the scripts provided in the `package.json`.
|
||||
|
||||
### Generating from source-files
|
||||
|
||||
```shell
|
||||
openstapps-core-tools plantuml PATH/TO/SOURCEFILES http://PLANTUMLSERVER
|
||||
```
|
||||
|
||||
Executing this command will generate a `.svg` file in your current working directory.
|
||||
|
||||
Multiple options can be set to enhance the diagram. By default all additional information other than the definitions are disabled. You can use:
|
||||
|
||||
- `--showProperties` to show all mandatory attributes of the classes and interfaces.
|
||||
- `--showOptionalProperties` to show all mandatory attributes of the classes and interfaces. `--showProperties` must be set!
|
||||
- `--showInheritedProperties` to show all inherited attributes of the classes and interfaces. `--showProperties` must be set!
|
||||
- `--showEnumValues` to show all enumeration and type (enumeration-like) values
|
||||
- `--showInheritance` to show the hierarchy of the classes and interfaces. Inherited attributes will only be shown in their parent.
|
||||
- `--showAssociations` to show all references of classes and interfaces between one another
|
||||
- `--excludeExternals` to exclude external definitions
|
||||
- `--definitions <definitons>` to show only specific definitions to reduce the output of the diagram. `<definitions>` is a comma seperated list of definitions.
|
||||
- `--outputFileName <fileName>` for a custom file name, the file extension will be added automatically (.svg). Otherwise a generic file with a timestamp will be generated into the execution directory. If a file with the same name already exists it will be overwritten!
|
||||
|
||||
The best way to explore models is to enable `--showInheritance` and `--showAssociations`. Start with just one definition in your `--definition <definitions>`-list, generate the diagram, look at it, add a new definition that you have seen to your command and generate anew.
|
||||
|
||||
#### Examples
|
||||
|
||||
Show the class hierarchy of the whole project:
|
||||
|
||||
```shell
|
||||
openstapps-core-tools plantuml PATH/TO/SRCDIR http://PLANTUMLSERVER --showInheritance
|
||||
```
|
||||
|
||||
Show the dish-module:
|
||||
|
||||
```shell
|
||||
openstapps-core-tools plantuml ../core http://localhost:8080 --showProperties --showOptionalProperties --showInheritance --showAssociations --showEnumValues --definitions SCDish,SCThingThatCanBeOfferedOffer
|
||||
```
|
||||
|
||||
### Generating from existing file
|
||||
|
||||
The plantuml code is persisted inside the generated file at the very bottom. You can tweak the model by using the function to generate UML from a PlantUML-file(simple text file). Extract the code (starting from `@startuml` to `@enduml`), edit it manually and execute this function.
|
||||
|
||||
```shell
|
||||
openstapps-core-tools plantuml-file /PATH/TO/Project.plantuml http://PLANTUMLSERVER OptionalCustomFileName
|
||||
```
|
||||
|
||||
Example-File-Content of Project.plantuml
|
||||
|
||||
```
|
||||
@startuml
|
||||
interface MyClass{
|
||||
myProperty: string
|
||||
}
|
||||
@enduml
|
||||
```
|
||||
14
packages/core-validator/compiler/append-schema-map.js
Normal file
14
packages/core-validator/compiler/append-schema-map.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// @ts-check
|
||||
|
||||
import {writeFile, readFile} from 'fs/promises';
|
||||
|
||||
const schemaNames = Object.keys(
|
||||
JSON.parse(await readFile('schema/core.schema.json', 'utf8')).definitions,
|
||||
).filter(it => /^[a-z][0-9a-z<>]*$/i.test(it));
|
||||
const source =
|
||||
"import type * as core from '@openstapps/core';\n\n" +
|
||||
'export interface SchemaMap {\n' +
|
||||
schemaNames.map(name => ` '${name}': core.${name.replaceAll('<', '<core.')};`).join('\n') +
|
||||
'\n}\n';
|
||||
|
||||
await writeFile('schema/core.schema.d.ts', source, 'utf8');
|
||||
80
packages/core-validator/package.json
Normal file
80
packages/core-validator/package.json
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "@openstapps/core-validator",
|
||||
"description": "Validator for @openstapps/core",
|
||||
"version": "3.0.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": "git@gitlab.com:openstapps/openstapps.git",
|
||||
"author": "Thea Schöbl <dev@theaninova.de>",
|
||||
"keywords": [
|
||||
"StApps",
|
||||
"StAppsCore",
|
||||
"converter",
|
||||
"core",
|
||||
"validator"
|
||||
],
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"schema",
|
||||
"Dockerfile",
|
||||
"README.md",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup-node --dts",
|
||||
"docs": "typedoc --json ./docs/docs.json --options ../../typedoc.base.json src/index.ts",
|
||||
"format": "prettier . -c --ignore-path ../../.gitignore",
|
||||
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --ext .ts src/",
|
||||
"lint:fix": "eslint --fix --ext .ts src/",
|
||||
"test": "c8 mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@openstapps/core": "workspace:*",
|
||||
"ajv": "8.12.0",
|
||||
"ajv-formats": "2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openstapps/core-tools": "workspace:*",
|
||||
"@openstapps/eslint-config": "workspace:*",
|
||||
"@openstapps/prettier-config": "workspace:*",
|
||||
"@openstapps/tsconfig": "workspace:*",
|
||||
"@types/chai": "4.3.5",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/glob": "8.0.1",
|
||||
"@types/json-schema": "7.0.14",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/mustache": "4.2.2",
|
||||
"@types/node": "18.15.3",
|
||||
"c8": "7.14.0",
|
||||
"chai": "4.3.7",
|
||||
"mocha": "10.2.0",
|
||||
"mocha-junit-reporter": "2.2.0",
|
||||
"nock": "13.3.1",
|
||||
"ts-node": "10.9.1",
|
||||
"tsup": "6.7.0",
|
||||
"typedoc": "0.24.8",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"tsup": {
|
||||
"entry": [
|
||||
"src/app.ts",
|
||||
"src/index.ts"
|
||||
],
|
||||
"sourcemap": true,
|
||||
"clean": true,
|
||||
"format": "esm",
|
||||
"outDir": "lib"
|
||||
},
|
||||
"prettier": "@openstapps/prettier-config",
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"@openstapps"
|
||||
]
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"resources"
|
||||
]
|
||||
}
|
||||
2
packages/core-validator/schema/.gitignore
vendored
Normal file
2
packages/core-validator/schema/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
core.schema.json
|
||||
core.schema.d.ts
|
||||
48
packages/core-validator/src/index.ts
Normal file
48
packages/core-validator/src/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import Ajv, {AnySchema} from 'ajv';
|
||||
import addFormats from 'ajv-formats';
|
||||
import schema from '../schema/core.schema.json';
|
||||
import {SchemaMap} from '../schema/core.schema.js';
|
||||
|
||||
export type RemoveNeverProperties<T> = {
|
||||
[K in Exclude<
|
||||
keyof T,
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
[P in keyof T]: T[P] extends Function ? P : never;
|
||||
}[keyof T]
|
||||
>]: T[K];
|
||||
};
|
||||
|
||||
export type IncludeProperty<T extends object, E> = RemoveNeverProperties<{
|
||||
[K in keyof T]: T[K] extends E ? T[K] : never;
|
||||
}>;
|
||||
|
||||
type NameOf<I extends SchemaMap[keyof SchemaMap]> = keyof IncludeProperty<SchemaMap, I>;
|
||||
|
||||
/**
|
||||
* StAppsCore validator
|
||||
*/
|
||||
export class Validator {
|
||||
private readonly ajv: Ajv.default;
|
||||
|
||||
constructor(additionalSchemas: AnySchema[] = []) {
|
||||
this.ajv = new Ajv.default({
|
||||
schemas: [schema, ...additionalSchemas],
|
||||
verbose: true,
|
||||
allowUnionTypes: true,
|
||||
});
|
||||
addFormats.default(this.ajv, {
|
||||
formats: ['date-time', 'time', 'uuid', 'duration'],
|
||||
mode: 'fast',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates anything against a given schema name or infers schema name from object
|
||||
* @param instance Instance to validate
|
||||
* @param schema Name of schema to validate instance against or the schema itself
|
||||
*/
|
||||
public validate<T>(instance: unknown, schema: NameOf<T>): instance is T {
|
||||
return this.ajv.validate(schema as string, instance);
|
||||
}
|
||||
}
|
||||
7
packages/core-validator/tsconfig.json
Normal file
7
packages/core-validator/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "@openstapps/tsconfig",
|
||||
"compilerOptions": {
|
||||
"noUnusedLocals": false,
|
||||
"stripInternal": true
|
||||
}
|
||||
}
|
||||
@@ -32,15 +32,12 @@
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup-node --dts && pnpm run mappings && pnpm run schema && pnpm run openapi && cp api-doc.html lib/api-doc.html",
|
||||
"build": "tsup-node --dts && cp api-doc.html lib/api-doc.html",
|
||||
"docs": "typedoc --json ./docs/docs.json --options ../../typedoc.base.json src/index.ts",
|
||||
"format": "prettier . -c --ignore-path ../../.gitignore",
|
||||
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --ext .ts src/",
|
||||
"lint:fix": "eslint --fix --ext .ts src/",
|
||||
"mappings": "openstapps-es-mapping-generator mapping ../core/src -i minlength,pattern,see,tjs-format -m lib/mappings/mappings.json -a lib/mappings/aggregations.json",
|
||||
"openapi": "openstapps-core-tools openapi lib lib && node -e \"assert(JSON.parse(require('fs').readFileSync('lib/openapi.json', 'utf8')).paths['/search'] !== undefined)\"",
|
||||
"schema": "node --max-old-space-size=8192 --stack-size=10240 ./node_modules/@openstapps/core-tools/lib/app.js schema src lib/schema",
|
||||
"test": "c8 mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -56,12 +53,14 @@
|
||||
"@openstapps/easy-ast": "workspace:*",
|
||||
"@openstapps/es-mapping-generator": "workspace:*",
|
||||
"@openstapps/eslint-config": "workspace:*",
|
||||
"@openstapps/json-schema-generator": "workspace:*",
|
||||
"@openstapps/logger": "workspace:*",
|
||||
"@openstapps/openapi-generator": "workspace:*",
|
||||
"@openstapps/prettier-config": "workspace:*",
|
||||
"@openstapps/tsconfig": "workspace:*",
|
||||
"@types/chai": "4.3.5",
|
||||
"@types/json-patch": "0.0.30",
|
||||
"@types/json-schema": "7.0.11",
|
||||
"@types/json-schema": "7.0.14",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/node": "18.15.3",
|
||||
"c8": "7.14.0",
|
||||
@@ -97,21 +96,11 @@
|
||||
{
|
||||
"definedTags": [
|
||||
"internal",
|
||||
"aggregatable",
|
||||
"float",
|
||||
"indexable",
|
||||
"integer",
|
||||
"keyword",
|
||||
"sortable",
|
||||
"text",
|
||||
"date",
|
||||
"validatable",
|
||||
"filterable",
|
||||
"inheritTags",
|
||||
"elasticsearch",
|
||||
"minLength",
|
||||
"pattern",
|
||||
"typeparam",
|
||||
"TJS-format"
|
||||
"integer",
|
||||
"format"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
import {Polygon} from 'geojson';
|
||||
import {SCTranslations} from '../general/i18n.js';
|
||||
import {SCMap} from '../general/map.js';
|
||||
import {SCLanguageSetting, SCSetting, SCUserGroupSetting} from '../things/setting.js';
|
||||
import {SCAuthorizationProviderType} from './authorization.js';
|
||||
import {SCFeatureConfiguration} from './feature.js';
|
||||
@@ -89,7 +88,7 @@ export interface SCAppConfiguration {
|
||||
*
|
||||
* Mapping route -> page config
|
||||
*/
|
||||
aboutPages: SCMap<SCAboutPage>;
|
||||
aboutPages: Record<string, SCAboutPage>;
|
||||
|
||||
/**
|
||||
* Polygon that encapsulates the main campus
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMap, SCRestrictedMap} from '../general/map.js';
|
||||
import {SCUuid} from '../general/uuid.js';
|
||||
import {SCSearchSortType} from '../protocol/search/sort.js';
|
||||
import {SCThingType} from '../things/abstract/thing.js';
|
||||
@@ -105,7 +104,7 @@ export type SCSearchContext = 'default' | 'dining' | 'place';
|
||||
/**
|
||||
* A boosting configuration for one context
|
||||
*/
|
||||
export type SCBackendConfigurationSearchBoostingContext = SCRestrictedMap<
|
||||
export type SCBackendConfigurationSearchBoostingContext = Record<
|
||||
SCSearchContext,
|
||||
SCBackendConfigurationSearchBoostingType[]
|
||||
>;
|
||||
@@ -128,10 +127,11 @@ export interface SCBackendConfigurationSearchBoostingType {
|
||||
* Value of the field that should be boosted by the given number
|
||||
* For example `"SS 2019": 2`
|
||||
*/
|
||||
fields?: SCMap<SCMap<number>>;
|
||||
fields?: Record<string, Record<string, number>>;
|
||||
|
||||
/**
|
||||
* Type of things the factor should be applied to
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ export interface SCBackendInternalConfiguration {
|
||||
/**
|
||||
* Configuration of the database
|
||||
*/
|
||||
export interface SCBackendConfigurationDatabaseConfiguration extends SCMap<unknown> {
|
||||
export interface SCBackendConfigurationDatabaseConfiguration extends Record<string, unknown> {
|
||||
/**
|
||||
* Name of the database used by the backend
|
||||
*/
|
||||
|
||||
@@ -12,20 +12,18 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {SCMap} from '../general/map.js';
|
||||
import {SCAuthorizationProviderType} from './authorization.js';
|
||||
|
||||
export interface SCFeatureConfiguration {
|
||||
/**
|
||||
* Map of extern services mapped by their name (statically)
|
||||
*/
|
||||
extern?: SCMap<SCFeatureConfigurationExtern>;
|
||||
extern?: Record<string, SCFeatureConfigurationExtern>;
|
||||
|
||||
/**
|
||||
* Map of plugins registered with the backend mapped by their name.
|
||||
*/
|
||||
plugins?: SCMap<SCFeatureConfigurationPlugin>;
|
||||
plugins?: Record<string, SCFeatureConfigurationPlugin>;
|
||||
}
|
||||
|
||||
export interface SCFeatureConfigurationPlugin {
|
||||
|
||||
@@ -96,7 +96,8 @@ export interface SCMonitoringMinimumLengthCondition {
|
||||
length: number;
|
||||
|
||||
/**
|
||||
* Type of the condition
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: 'MinimumLength';
|
||||
}
|
||||
@@ -111,7 +112,8 @@ export interface SCMonitoringMaximumLengthCondition {
|
||||
length: number;
|
||||
|
||||
/**
|
||||
* Type of the condition
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: 'MaximumLength';
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
export interface SCLanguage {
|
||||
/**
|
||||
* The two letter ISO 639-1 Code of the Language
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
code: SCLanguageCode;
|
||||
|
||||
/**
|
||||
* The Fulltext name of the Language
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
name: SCLanguageName;
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2022 Open StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Capsulation for a map with a string as key with values of type `T`
|
||||
*
|
||||
* !!! BEWARE !!!
|
||||
* Can't be refactored to a `Map<K, V>`, because it can't be serialized via JSON.stringify(map)
|
||||
* @typeparam T Can be any type.
|
||||
*/
|
||||
export interface SCMap<T> {
|
||||
/**
|
||||
* One value for each key
|
||||
*/
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricted map with keys, limited to values of `U`, and corresponding values of type `T`
|
||||
*
|
||||
* !!! BEWARE !!!
|
||||
* Can't be refactored to a `Map<K, V>`, because it can't be serialized via JSON.stringify(map)
|
||||
* Also note, that this is a type not an interface
|
||||
* @typeparam U Must be a type the `in` operator can be applied to and contains only strings or numbers
|
||||
* @typeparam T Can be any type
|
||||
*/
|
||||
export type SCRestrictedMap<U extends string | number, T> = {
|
||||
/**
|
||||
* One value for each key
|
||||
*/
|
||||
[key in U]: T;
|
||||
};
|
||||
@@ -14,21 +14,18 @@
|
||||
*/
|
||||
/**
|
||||
* An ISO8601 date
|
||||
* @pattern ^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])(T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])([\.,][0-9]{0,7})?(Z|[+-](?:2[0-3]|[01][0-9])(:?[0-5][0-9])?)?)?$
|
||||
* @see https://gist.github.com/philipashlock/8830168
|
||||
* @date
|
||||
* @format date-time
|
||||
*/
|
||||
export type SCISO8601Date = string;
|
||||
|
||||
/**
|
||||
* An ISO8601 duration
|
||||
* @pattern ^(R\d*\/)?P(?:\d+(?:\.\d+)?Y)?(?:\d+(?:\.\d+)?M)?(?:\d+(?:\.\d+)?W)?(?:\d+(?:\.\d+)?D)?(?:T(?:\d+(?:\.\d+)?H)?(?:\d+(?:\.\d+)?M)?(?:\d+(?:\.\d+)?S)?)?$
|
||||
* @see https://gist.github.com/philipashlock/8830168
|
||||
* @format duration
|
||||
*/
|
||||
export type SCISO8601Duration = string;
|
||||
|
||||
/**
|
||||
* An ISO8601 time
|
||||
* @pattern ^(2[0-3]|[01][0-9]):?([0-5][0-9]):?([0-5][0-9])$
|
||||
* @format time
|
||||
*/
|
||||
export type SCISO8601Time = string;
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
*/
|
||||
/**
|
||||
* Universally unique identifier of the thing
|
||||
* @filterable
|
||||
* @pattern ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
* @see http://stackoverflow.com/questions/7905929/how-to-test-valid-uuid-guid
|
||||
* @elasticsearch filterable
|
||||
* @format uuid
|
||||
*/
|
||||
export type SCUuid = string;
|
||||
|
||||
@@ -11,7 +11,6 @@ export * from './config/monitoring.js';
|
||||
export * from './config/user.js';
|
||||
|
||||
export * from './general/i18n.js';
|
||||
export * from './general/map.js';
|
||||
export * from './general/namespaces.js';
|
||||
export * from './general/time.js';
|
||||
export * from './general/uuid.js';
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMap} from '../general/map.js';
|
||||
import {SCErrorResponse} from './error.js';
|
||||
import {SCIndexRequest, SCIndexResponse, SCIndexRoute} from './routes/index.js';
|
||||
import {
|
||||
@@ -61,7 +60,7 @@ export interface SCRoute {
|
||||
/**
|
||||
* Map of obligatory parameters and their type that have to be set via the requested path
|
||||
*/
|
||||
obligatoryParameters?: SCMap<string>;
|
||||
obligatoryParameters?: Record<string, string>;
|
||||
|
||||
/**
|
||||
* Name of the type of the request body
|
||||
@@ -101,7 +100,7 @@ export abstract class SCAbstractRoute implements SCRoute {
|
||||
/**
|
||||
* @see SCRoute.obligatoryParameters
|
||||
*/
|
||||
obligatoryParameters?: SCMap<string>;
|
||||
obligatoryParameters?: Record<string, string>;
|
||||
|
||||
/**
|
||||
* @see SCRoute.requestBodyName
|
||||
@@ -127,7 +126,7 @@ export abstract class SCAbstractRoute implements SCRoute {
|
||||
* Get "compiled" URL path
|
||||
* @param parameters Parameters to compile URL path with
|
||||
*/
|
||||
public getUrlPath(parameters: SCMap<string> = {}): string {
|
||||
public getUrlPath(parameters: Record<string, string> = {}): string {
|
||||
let obligatoryParameters: string[] = [];
|
||||
|
||||
if (typeof this.obligatoryParameters === 'object') {
|
||||
|
||||
@@ -53,8 +53,8 @@ export interface SCBulkParameters {
|
||||
source: string;
|
||||
|
||||
/**
|
||||
* Type of things that are indexed in this bulk.
|
||||
*
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {StatusCodes} from 'http-status-codes';
|
||||
import {SCMap} from '../../general/map.js';
|
||||
import {SCInternalServerErrorResponse} from '../errors/internal-server-error.js';
|
||||
import {SCMethodNotAllowedErrorResponse} from '../errors/method-not-allowed.js';
|
||||
import {SCRequestBodyTooLargeErrorResponse} from '../errors/request-body-too-large.js';
|
||||
@@ -33,7 +32,7 @@ import {SCSearchResult} from '../search/result.js';
|
||||
* **CAUTION: This is limited to an amount of queries. Currently this limit is 5.**
|
||||
* @validatable
|
||||
*/
|
||||
export type SCMultiSearchRequest = SCMap<SCSearchQuery>;
|
||||
export type SCMultiSearchRequest = Record<string, SCSearchQuery>;
|
||||
|
||||
/**
|
||||
* A multi search response
|
||||
@@ -41,7 +40,7 @@ export type SCMultiSearchRequest = SCMap<SCSearchQuery>;
|
||||
* This is a map of [[SCSearchResponse]]s indexed by name
|
||||
* @validatable
|
||||
*/
|
||||
export type SCMultiSearchResponse = SCMap<SCSearchResult>;
|
||||
export type SCMultiSearchResponse = Record<string, SCSearchResult>;
|
||||
|
||||
/**
|
||||
* Route for submission of multiple search requests at once
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMap} from '../../general/map.js';
|
||||
/**
|
||||
* All available filter types
|
||||
*/
|
||||
import {SCSearchAvailabilityFilter} from './filters/availability.js';
|
||||
import {SCSearchBooleanFilter} from './filters/boolean.js';
|
||||
import {SCSearchDistanceFilter} from './filters/distance.js';
|
||||
@@ -53,7 +49,7 @@ export interface SCSearchAbstractFilter<T extends SCSearchAbstractFilterArgument
|
||||
/**
|
||||
* Arguments for the filter instruction
|
||||
*/
|
||||
export type SCSearchAbstractFilterArguments = SCMap<unknown>;
|
||||
export type SCSearchAbstractFilterArguments = Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Available filter instructions
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMap} from '../../general/map.js';
|
||||
import {SCThingsField} from '../../meta.js';
|
||||
import {SCDistanceSort} from './sorts/distance.js';
|
||||
import {SCDucetSort} from './sorts/ducet.js';
|
||||
@@ -42,7 +41,7 @@ export interface SCSearchAbstractSort<T extends SCSearchAbstractSortArguments> {
|
||||
/**
|
||||
* Map of arguments for the sort instruction
|
||||
*/
|
||||
export interface SCSearchAbstractSortArguments extends SCMap<unknown> {
|
||||
export interface SCSearchAbstractSortArguments extends Record<string, unknown> {
|
||||
/**
|
||||
* Field to sort by
|
||||
*/
|
||||
|
||||
@@ -21,8 +21,7 @@ import {SCThing, SCThingMeta, SCThingWithoutReferences} from './thing.js';
|
||||
export interface SCAcademicDegreeWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* The achievable academic degree
|
||||
* @filterable
|
||||
* @sortable ducet
|
||||
* @elasticsearch filterable sortable:ducet
|
||||
*/
|
||||
academicDegree: string;
|
||||
|
||||
|
||||
@@ -22,33 +22,31 @@ import {SCThing, SCThingMeta, SCThingWithoutReferences} from './thing.js';
|
||||
export interface SCAcademicTermWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Short name of the academic term, using the given pattern
|
||||
* @aggregatable
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch aggregatable filterable
|
||||
*/
|
||||
acronym: string;
|
||||
|
||||
/**
|
||||
* End date of the academic term
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
endDate: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* End date of lectures in the academic term
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
eventsEndDate?: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* Start date of lectures in the academic term
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
eventsStartDate?: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* Start date of the academic term
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
startDate: SCISO8601Date;
|
||||
}
|
||||
|
||||
@@ -35,33 +35,28 @@ export interface SCCreativeWorkWithoutReferences extends SCThingWithoutReference
|
||||
|
||||
/**
|
||||
* Edition of a creative work (e.g. the book edition or edition of an article)
|
||||
* @keyword
|
||||
*/
|
||||
edition?: string;
|
||||
|
||||
/**
|
||||
* Date (in text form) the creative work was published for the first time
|
||||
* @keyword
|
||||
*/
|
||||
firstPublished?: string;
|
||||
|
||||
/**
|
||||
* Languages this creative work is written/recorded/... in
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
inLanguage?: SCLanguageCode;
|
||||
|
||||
/**
|
||||
* Keywords of the creative work
|
||||
* @aggregatable
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch aggregatable filterable
|
||||
*/
|
||||
keywords?: string[];
|
||||
|
||||
/**
|
||||
* Date (in text form) the creative work was most recently
|
||||
* @keyword
|
||||
*/
|
||||
lastPublished?: string;
|
||||
|
||||
@@ -112,7 +107,6 @@ export interface SCCreativeWork extends SCCreativeWorkWithoutReferences, SCThing
|
||||
export interface SCCreativeWorkTranslatableProperties extends SCThingTranslatableProperties {
|
||||
/**
|
||||
* Translation of the keywords of the creative work
|
||||
* @keyword
|
||||
*/
|
||||
keywords?: string[];
|
||||
}
|
||||
|
||||
@@ -39,37 +39,37 @@ export interface SCGeoInformation {
|
||||
export interface SCPostalAddress {
|
||||
/**
|
||||
* Country of the address
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
addressCountry: string;
|
||||
|
||||
/**
|
||||
* City of the address
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
addressLocality: string;
|
||||
|
||||
/**
|
||||
* State of the address
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
addressRegion?: string;
|
||||
|
||||
/**
|
||||
* Zip code of the address
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
postalCode: string;
|
||||
|
||||
/**
|
||||
* Optional post box number
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
postOfficeBoxNumber?: string;
|
||||
|
||||
/**
|
||||
* Street of the address - with house number!
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
streetAddress: string;
|
||||
}
|
||||
@@ -94,7 +94,6 @@ export interface SCPlaceWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Opening hours of the place
|
||||
* @see http://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
|
||||
* @keyword
|
||||
*/
|
||||
openingHours?: string;
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ import {SCISO8601Date} from '../../general/time.js';
|
||||
|
||||
/**
|
||||
* Date Range
|
||||
*
|
||||
* CAUTION: Changing the name requires changes in the core-tools premaps
|
||||
*/
|
||||
export type SCISO8601DateRange = SCRange<SCISO8601Date>;
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ export interface SCSaveableThing extends SCSaveableThingWithoutReferences, SCThi
|
||||
*/
|
||||
data: SCIndexableThings;
|
||||
/**
|
||||
* Type of the origin
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
origin: SCThingUserOrigin;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export type SCThingThatAcceptsPaymentsAcceptedPayments = 'cash' | 'credit' | 'ca
|
||||
export interface SCThingThatAcceptsPaymentsWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Accepted payments of the place
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
paymentsAccepted?: SCThingThatAcceptsPaymentsAcceptedPayments[];
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import {SCThing, SCThingMeta, SCThingTranslatableProperties, SCThingWithoutRefer
|
||||
export interface SCPriceGroup {
|
||||
/**
|
||||
* Default price of the thing
|
||||
* @sortable price
|
||||
* @float
|
||||
* @elasticsearch sortable:price
|
||||
*/
|
||||
default: number;
|
||||
}
|
||||
@@ -37,22 +36,19 @@ export interface SCPriceGroup {
|
||||
export interface SCAcademicPriceGroup extends SCPriceGroup {
|
||||
/**
|
||||
* Price for employees
|
||||
* @sortable price
|
||||
* @float
|
||||
* @elasticsearch sortable:price
|
||||
*/
|
||||
employee?: number;
|
||||
|
||||
/**
|
||||
* Price for guests
|
||||
* @sortable price
|
||||
* @float
|
||||
* @elasticsearch sortable:price
|
||||
*/
|
||||
guest?: number;
|
||||
|
||||
/**
|
||||
* Price for students
|
||||
* @sortable price
|
||||
* @float
|
||||
* @elasticsearch sortable:price
|
||||
*/
|
||||
student?: number;
|
||||
}
|
||||
@@ -115,7 +111,6 @@ export interface SCThingThatCanBeOfferedOffer<T extends SCPriceGroup> extends SC
|
||||
export interface SCThingThatCanBeOfferedTranslatableProperties extends SCThingTranslatableProperties {
|
||||
/**
|
||||
* Availability of an offer
|
||||
* @keyword
|
||||
*/
|
||||
'offers[].availability'?: string;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMetaTranslations, SCTranslations} from '../../general/i18n.js';
|
||||
import {SCMap} from '../../general/map.js';
|
||||
import {SCThing, SCThingMeta, SCThingTranslatableProperties, SCThingWithoutReferences} from './thing.js';
|
||||
|
||||
/**
|
||||
@@ -26,9 +25,7 @@ export interface SCThingWithCategoriesWithoutReferences<T, U extends SCThingWith
|
||||
extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Categories of a thing with categories
|
||||
* @sortable ducet
|
||||
* @aggregatable
|
||||
* @filterable
|
||||
* @elasticsearch aggregatable sortable:ducet filterable
|
||||
*/
|
||||
categories: T[];
|
||||
|
||||
@@ -37,7 +34,7 @@ export interface SCThingWithCategoriesWithoutReferences<T, U extends SCThingWith
|
||||
*
|
||||
* A map from categories to their specific values.
|
||||
*/
|
||||
categorySpecificValues?: SCMap<U>;
|
||||
categorySpecificValues?: Record<string, U>;
|
||||
|
||||
/**
|
||||
* Translated fields of a thing with categories
|
||||
@@ -62,46 +59,42 @@ export interface SCThingWithCategories<T, U extends SCThingWithCategoriesSpecifi
|
||||
*/
|
||||
export interface SCThingWithCategoriesTranslatableProperties extends SCThingTranslatableProperties {
|
||||
/**
|
||||
* translations of the categories of a thing with categories
|
||||
* @sortable ducet
|
||||
* translations of the categories for a thing with categories
|
||||
* @elasticsearch filterable sortable:ducet
|
||||
*/
|
||||
categories?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Category specific values of a thing with categories
|
||||
* Category-specific values of a thing with categories
|
||||
*
|
||||
* This interface contains properties that can be specific to a certain category.
|
||||
*/
|
||||
export interface SCThingWithCategoriesSpecificValues {
|
||||
/**
|
||||
* Category specific alternate names of a thing
|
||||
* @keyword
|
||||
* Category-specific alternate names of a thing
|
||||
*/
|
||||
alternateNames?: string[];
|
||||
|
||||
/**
|
||||
* Category specific description of a thing
|
||||
* @text
|
||||
* Category-specific description of a thing
|
||||
* @elasticsearch text
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* URL of a category specific image of a thing
|
||||
* @keyword
|
||||
* URL of a category-specific image of a thing
|
||||
*/
|
||||
image?: string;
|
||||
|
||||
/**
|
||||
* Category specific name of a thing
|
||||
* @sortable ducet
|
||||
* @text
|
||||
* @elasticsearch text sortable:ducet
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Category specific URL of a thing
|
||||
* @keyword
|
||||
* Category-specific URL of a thing
|
||||
*/
|
||||
url?: string;
|
||||
}
|
||||
@@ -130,7 +123,7 @@ export class SCThingWithCategoriesWithoutReferencesMeta<T, U extends SCThingWith
|
||||
};
|
||||
|
||||
/**
|
||||
* Translations of values of fields
|
||||
* Translations of field values
|
||||
*/
|
||||
fieldValueTranslations = {
|
||||
de: {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMetaTranslations, SCTranslations} from '../../general/i18n.js';
|
||||
import {SCMap} from '../../general/map.js';
|
||||
import {SCISO8601Date} from '../../general/time.js';
|
||||
import {SCUuid} from '../../general/uuid.js';
|
||||
import {SCOrganizationWithoutReferences} from '../organization.js';
|
||||
@@ -62,37 +61,33 @@ export enum SCThingType {
|
||||
export interface SCThingWithoutReferences {
|
||||
/**
|
||||
* Alternate names of the thing
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
alternateNames?: string[];
|
||||
|
||||
/**
|
||||
* Description of the thing
|
||||
* @minLength 1
|
||||
* @text
|
||||
* @elasticsearch text
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* The identifier property represents any kind of additional identifier for any kind of SCThing
|
||||
*
|
||||
* E.g. GTIN codes, UUIDs, Database IDs etc.
|
||||
* E.g., GTIN codes, UUIDs, Database IDs, etc.
|
||||
*/
|
||||
identifiers?: SCMap<string>;
|
||||
identifiers?: Record<string, string>;
|
||||
|
||||
/**
|
||||
* URL of an image of the thing
|
||||
* @keyword
|
||||
* URL to an image of the thing
|
||||
*/
|
||||
image?: string;
|
||||
|
||||
/**
|
||||
* Name of the thing
|
||||
* @filterable
|
||||
* @minLength 1
|
||||
* @sortable ducet
|
||||
* @text
|
||||
* @elasticsearch text filterable sortable:ducet
|
||||
*/
|
||||
name: string;
|
||||
|
||||
@@ -111,10 +106,8 @@ export interface SCThingWithoutReferences {
|
||||
translations?: SCTranslations<SCThingTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the thing
|
||||
* @sortable ducet
|
||||
* @filterable
|
||||
* @aggregatable global
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType;
|
||||
|
||||
@@ -159,7 +152,8 @@ export interface SCThingOrigin {
|
||||
modified?: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* Type of the origin
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingOriginType;
|
||||
}
|
||||
@@ -175,7 +169,7 @@ export interface SCThingRemoteOrigin extends SCThingOrigin {
|
||||
|
||||
/**
|
||||
* Name of the origin
|
||||
* @text
|
||||
* @elasticsearch text
|
||||
*/
|
||||
name: string;
|
||||
|
||||
@@ -192,7 +186,8 @@ export interface SCThingRemoteOrigin extends SCThingOrigin {
|
||||
responsibleEntity?: SCPersonWithoutReferences | SCOrganizationWithoutReferences;
|
||||
|
||||
/**
|
||||
* Type of the origin
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingOriginType.Remote;
|
||||
|
||||
@@ -217,7 +212,8 @@ export interface SCThingUserOrigin extends SCThingOrigin {
|
||||
deleted?: boolean;
|
||||
|
||||
/**
|
||||
* Type of the origin
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingOriginType.User;
|
||||
|
||||
@@ -233,13 +229,12 @@ export interface SCThingUserOrigin extends SCThingOrigin {
|
||||
export interface SCThingTranslatableProperties {
|
||||
/**
|
||||
* Translation of the description of the thing
|
||||
* @text
|
||||
* @elasticsearch text
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Translation of the name of the thing
|
||||
* @sortable ducet
|
||||
* @text
|
||||
* @elasticsearch text sortable:ducet
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
@@ -254,7 +249,7 @@ export interface SCThingTranslatableProperties {
|
||||
export interface SCThingTranslatablePropertyOrigin {
|
||||
/**
|
||||
* Translation of the name of the origin
|
||||
* @text
|
||||
* @elasticsearch text
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -31,16 +31,13 @@ export interface SCAcademicEventWithoutReferences
|
||||
SCThingWithCategoriesWithoutReferences<SCAcademicEventCategories, SCThingWithCategoriesSpecificValues> {
|
||||
/**
|
||||
* Majors of the academic event that this event belongs to
|
||||
* @aggregatable
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable aggregatable
|
||||
*/
|
||||
majors?: string[];
|
||||
|
||||
/**
|
||||
* Original unmapped category from the source of the academic event
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
originalCategory?: string;
|
||||
|
||||
@@ -50,7 +47,8 @@ export interface SCAcademicEventWithoutReferences
|
||||
translations?: SCTranslations<SCAcademicEventTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an academic event
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.AcademicEvent;
|
||||
}
|
||||
@@ -58,7 +56,7 @@ export interface SCAcademicEventWithoutReferences
|
||||
/**
|
||||
* An academic event
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCAcademicEvent
|
||||
extends SCEvent,
|
||||
@@ -70,7 +68,8 @@ export interface SCAcademicEvent
|
||||
translations?: SCTranslations<SCAcademicEventTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an academic event
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.AcademicEvent;
|
||||
}
|
||||
@@ -101,13 +100,11 @@ export type SCAcademicEventCategories =
|
||||
export interface SCAcademicEventTranslatableProperties extends SCThingWithCategoriesTranslatableProperties {
|
||||
/**
|
||||
* Translations of the majors of the academic event that this event belongs to
|
||||
* @keyword
|
||||
*/
|
||||
majors?: string[];
|
||||
|
||||
/**
|
||||
* Translation of the original unmapped category from the source of the academic event
|
||||
* @keyword
|
||||
*/
|
||||
originalCategory?: string;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export interface SCArticleWithoutReferences
|
||||
SCThingWithCategoriesWithoutReferences<SCArticleCategories, SCThingWithCategoriesSpecificValues> {
|
||||
/**
|
||||
* Article itself as markdown
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
articleBody?: string;
|
||||
|
||||
@@ -64,7 +64,8 @@ export interface SCArticleWithoutReferences
|
||||
translations?: SCTranslations<SCArticleTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an article
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Article;
|
||||
}
|
||||
@@ -72,7 +73,7 @@ export interface SCArticleWithoutReferences
|
||||
/**
|
||||
* An article
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCArticle
|
||||
extends SCCreativeWork,
|
||||
@@ -93,7 +94,8 @@ export interface SCArticle
|
||||
translations?: SCTranslations<SCArticleTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an article
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Article;
|
||||
}
|
||||
@@ -107,7 +109,7 @@ export interface SCArticleTranslatableProperties
|
||||
SCCreativeWorkTranslatableProperties {
|
||||
/**
|
||||
* Translation of the article itself as markdown
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
articleBody?: string[];
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ export interface SCAssessmentWithoutReferences
|
||||
|
||||
/**
|
||||
* ECTS (credit-points)
|
||||
* @float
|
||||
*/
|
||||
ects?: number;
|
||||
|
||||
@@ -72,7 +71,8 @@ export interface SCAssessmentWithoutReferences
|
||||
translations?: SCTranslations<SCAssessmentTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an assessment
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Assessment;
|
||||
}
|
||||
@@ -101,7 +101,8 @@ export interface SCAssessment
|
||||
translations?: SCTranslations<SCAssessmentTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an assessment
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Assessment;
|
||||
}
|
||||
|
||||
@@ -73,8 +73,7 @@ export interface SCBookWithoutReferences
|
||||
|
||||
/**
|
||||
* ISBNs of a book
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
ISBNs?: string[];
|
||||
|
||||
@@ -90,7 +89,8 @@ export interface SCBookWithoutReferences
|
||||
translations?: SCTranslations<SCBookTranslatableFields>;
|
||||
|
||||
/**
|
||||
* Type of a book
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Book;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export interface SCBookWithoutReferences
|
||||
/**
|
||||
* A book
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCBook
|
||||
extends SCCreativeWork,
|
||||
@@ -110,7 +110,8 @@ export interface SCBook
|
||||
translations?: SCTranslations<SCBookTranslatableFields>;
|
||||
|
||||
/**
|
||||
* Type of a book
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Book;
|
||||
}
|
||||
|
||||
@@ -43,8 +43,7 @@ export interface SCBuildingWithoutReferences
|
||||
SCPlaceWithoutReferences {
|
||||
/**
|
||||
* List of floor names of the place
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
floors?: string[];
|
||||
|
||||
@@ -54,7 +53,8 @@ export interface SCBuildingWithoutReferences
|
||||
translations?: SCTranslations<SCBuildingTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the building
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Building;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export interface SCBuildingWithoutReferences
|
||||
/**
|
||||
* A building
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCBuilding
|
||||
extends SCBuildingWithoutReferences,
|
||||
@@ -74,7 +74,8 @@ export interface SCBuilding
|
||||
translations?: SCTranslations<SCBuildingTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the building
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Building;
|
||||
}
|
||||
|
||||
@@ -32,13 +32,14 @@ export interface SCCatalogWithoutReferences
|
||||
* Level of the catalog (0 for 'root catalog', 1 for its subcatalog, 2 for its subcatalog etc.)
|
||||
*
|
||||
* Needed for keeping order in catalog inheritance array.
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
* @integer
|
||||
*/
|
||||
level: number;
|
||||
|
||||
/**
|
||||
* Type of a catalog
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Catalog;
|
||||
}
|
||||
@@ -46,7 +47,7 @@ export interface SCCatalogWithoutReferences
|
||||
/**
|
||||
* A catalog
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCCatalog
|
||||
extends SCCatalogWithoutReferences,
|
||||
@@ -73,7 +74,8 @@ export interface SCCatalog
|
||||
translations?: SCTranslations<SCThingWithCategoriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a catalog
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Catalog;
|
||||
}
|
||||
|
||||
@@ -39,13 +39,14 @@ export interface SCCertificationWithoutReferences
|
||||
translations?: SCTranslations<SCCertificationTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of certification
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Certification;
|
||||
}
|
||||
|
||||
/**
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
* @validatable
|
||||
*/
|
||||
export interface SCCertification
|
||||
@@ -63,7 +64,8 @@ export interface SCCertification
|
||||
translations?: SCTranslations<SCCertificationTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of certification
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Certification;
|
||||
}
|
||||
|
||||
@@ -23,37 +23,33 @@ import {SCRoomWithoutReferences} from './room.js';
|
||||
export interface SCContactPointWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* E-mail at the work location
|
||||
* @keyword
|
||||
*/
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
* Fax number at the work location
|
||||
* @keyword
|
||||
*/
|
||||
faxNumber?: string;
|
||||
|
||||
/**
|
||||
* Office hours for contacting someone at the work location
|
||||
* @see http://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
|
||||
* @keyword
|
||||
*/
|
||||
officeHours?: string;
|
||||
|
||||
/**
|
||||
* Contact number at the work location
|
||||
* @keyword
|
||||
*/
|
||||
telephone?: string;
|
||||
|
||||
/**
|
||||
* Type of a contact point
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.ContactPoint;
|
||||
|
||||
/**
|
||||
* URL at the work location
|
||||
* @keyword
|
||||
*/
|
||||
url?: string;
|
||||
}
|
||||
@@ -62,7 +58,7 @@ export interface SCContactPointWithoutReferences extends SCThingWithoutReference
|
||||
* A contact point
|
||||
* @see http://schema.org/ContactPoint
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCContactPoint extends SCContactPointWithoutReferences, SCThing {
|
||||
/**
|
||||
@@ -71,7 +67,8 @@ export interface SCContactPoint extends SCContactPointWithoutReferences, SCThing
|
||||
areaServed?: SCRoomWithoutReferences;
|
||||
|
||||
/**
|
||||
* Type of a contact point
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.ContactPoint;
|
||||
}
|
||||
|
||||
@@ -43,13 +43,13 @@ export interface SCCourseOfStudyWithoutReferences
|
||||
|
||||
/**
|
||||
* The modes the course of study is offered in
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
mode?: SCCourseOfStudyMode;
|
||||
|
||||
/**
|
||||
* The time modes the course of study is offered in
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
timeMode?: SCCourseOfStudyTimeMode;
|
||||
|
||||
@@ -59,7 +59,8 @@ export interface SCCourseOfStudyWithoutReferences
|
||||
translations?: SCTranslations<SCCourseOfStudyTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the course of study
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.CourseOfStudy;
|
||||
}
|
||||
@@ -67,7 +68,7 @@ export interface SCCourseOfStudyWithoutReferences
|
||||
/**
|
||||
* A course of study
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCCourseOfStudy
|
||||
extends SCCourseOfStudyWithoutReferences,
|
||||
@@ -95,7 +96,8 @@ export interface SCCourseOfStudy
|
||||
translations?: SCTranslations<SCCourseOfStudyTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the course of study
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.CourseOfStudy;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import {SCSportCourseWithoutReferences} from './sport-course.js';
|
||||
export interface SCSportCoursePriceGroup extends SCAcademicPriceGroup {
|
||||
/**
|
||||
* Price for alumnis
|
||||
* @float
|
||||
*/
|
||||
alumni?: number;
|
||||
}
|
||||
@@ -44,7 +43,7 @@ export interface SCSportCoursePriceGroup extends SCAcademicPriceGroup {
|
||||
export interface SCDateSeriesWithoutReferences extends SCThingThatCanBeOfferedWithoutReferences {
|
||||
/**
|
||||
* Dates of the date series that are initially planned to be held
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
dates: SCISO8601Date[];
|
||||
|
||||
@@ -60,7 +59,7 @@ export interface SCDateSeriesWithoutReferences extends SCThingThatCanBeOfferedWi
|
||||
|
||||
/**
|
||||
* Frequency of the date series
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
repeatFrequency?: SCISO8601Duration;
|
||||
|
||||
@@ -70,7 +69,8 @@ export interface SCDateSeriesWithoutReferences extends SCThingThatCanBeOfferedWi
|
||||
translations?: SCTranslations<SCDateSeriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a date series
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.DateSeries;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export interface SCDateSeriesWithoutReferences extends SCThingThatCanBeOfferedWi
|
||||
/**
|
||||
* A date series
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCDateSeries
|
||||
extends SCDateSeriesWithoutReferences,
|
||||
@@ -100,7 +100,8 @@ export interface SCDateSeries
|
||||
translations?: SCTranslations<SCDateSeriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a date series
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.DateSeries;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,8 @@ export interface SCDiffWithoutReferences extends SCThingWithoutReferences {
|
||||
dateCreated: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* Type of a diff
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Diff;
|
||||
}
|
||||
@@ -54,7 +55,8 @@ export interface SCDiff extends SCDiffWithoutReferences, SCThing {
|
||||
object: SCIndexableThings;
|
||||
|
||||
/**
|
||||
* Type of a diff
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Diff;
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ export interface SCDishWithoutReferences
|
||||
SCThingWithCategoriesWithoutReferences<SCDishCategories, SCThingWithCategoriesSpecificValues> {
|
||||
/**
|
||||
* Additives of the dish
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
additives?: string[];
|
||||
|
||||
@@ -64,7 +63,8 @@ export interface SCDishWithoutReferences
|
||||
translations?: SCTranslations<SCDishTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a dish
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Dish;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ export interface SCDishWithoutReferences
|
||||
/**
|
||||
* A dish
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCDish
|
||||
extends SCDishWithoutReferences,
|
||||
@@ -94,7 +94,8 @@ export interface SCDish
|
||||
translations?: SCTranslations<SCDishTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a dish
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Dish;
|
||||
}
|
||||
@@ -104,8 +105,7 @@ export interface SCDishTranslatableProperties
|
||||
SCThingThatCanBeOfferedTranslatableProperties {
|
||||
/**
|
||||
* Additives of the dish
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
additives?: string[];
|
||||
/**
|
||||
@@ -120,14 +120,12 @@ export interface SCDishTranslatableProperties
|
||||
export interface SCDishCharacteristic {
|
||||
/**
|
||||
* URL of an image of the characteristic
|
||||
* @keyword
|
||||
*/
|
||||
image?: string;
|
||||
|
||||
/**
|
||||
* Name of the characteristic
|
||||
* @filterable
|
||||
* @text
|
||||
* @elasticsearch text filterable
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
@@ -144,43 +142,36 @@ export type SCDishCategories = 'appetizer' | 'salad' | 'main dish' | 'dessert' |
|
||||
export interface SCNutritionInformation {
|
||||
/**
|
||||
* Number of calories contained (in kcal)
|
||||
* @float
|
||||
*/
|
||||
calories?: number;
|
||||
|
||||
/**
|
||||
* Content of carbohydrates (in grams)
|
||||
* @float
|
||||
*/
|
||||
carbohydrateContent?: number;
|
||||
|
||||
/**
|
||||
* Content of fat (in grams)
|
||||
* @float
|
||||
*/
|
||||
fatContent?: number;
|
||||
|
||||
/**
|
||||
* Content of proteins (in grams)
|
||||
* @float
|
||||
*/
|
||||
proteinContent?: number;
|
||||
|
||||
/**
|
||||
* Content of salt (in grams)
|
||||
* @float
|
||||
*/
|
||||
saltContent?: number;
|
||||
|
||||
/**
|
||||
* Content of saturated fat (in grams)
|
||||
* @float
|
||||
*/
|
||||
saturatedFatContent?: number;
|
||||
|
||||
/**
|
||||
* Content of sugar (in grams)
|
||||
* @float
|
||||
*/
|
||||
sugarContent?: number;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ import {SCThingMeta, SCThingType} from './abstract/thing.js';
|
||||
*/
|
||||
export interface SCFavoriteWithoutReferences extends SCSaveableThingWithoutReferences {
|
||||
/**
|
||||
* Type of a favorite
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Favorite;
|
||||
}
|
||||
@@ -31,7 +32,8 @@ export interface SCFavoriteWithoutReferences extends SCSaveableThingWithoutRefer
|
||||
*/
|
||||
export interface SCFavorite extends SCSaveableThing {
|
||||
/**
|
||||
* Type of a favorite
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Favorite;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import {SCRoomWithoutReferences} from './room.js';
|
||||
export interface SCFloorWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Floor name in the place it is in e.g. "first floor", "ground floor". This doesn't reference the building name.
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
floorName: string;
|
||||
|
||||
@@ -45,7 +45,8 @@ export interface SCFloorWithoutReferences extends SCThingWithoutReferences {
|
||||
translations?: SCTranslations<SCFloorTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a floor
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Floor;
|
||||
}
|
||||
@@ -53,7 +54,7 @@ export interface SCFloorWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* A floor
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCFloor extends SCFloorWithoutReferences, SCThingInPlace {
|
||||
/**
|
||||
@@ -62,7 +63,8 @@ export interface SCFloor extends SCFloorWithoutReferences, SCThingInPlace {
|
||||
translations?: SCTranslations<SCFloorTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a floor
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Floor;
|
||||
}
|
||||
@@ -95,7 +97,7 @@ export interface SCFloorFeatureWithPlace<T extends GeometryObject>
|
||||
export interface SCFloorTranslatableProperties extends SCThingTranslatableProperties {
|
||||
/**
|
||||
* Translation of the floor name
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
floorName?: string;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface SCIdCardWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* A message
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCIdCard extends SCIdCardWithoutReferences, SCThing {
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,8 @@ export interface SCJobPostingWithoutReferences
|
||||
extends SCThingWithCategoriesWithoutReferences<SCJobCategories, SCThingWithCategoriesSpecificValues>,
|
||||
SCInPlace {
|
||||
/**
|
||||
* Type of a job posting
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.JobPosting;
|
||||
}
|
||||
@@ -36,14 +37,14 @@ export interface SCJobPostingWithoutReferences
|
||||
/**
|
||||
* A JobPosting
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCJobPosting
|
||||
extends SCThingWithCategories<SCJobCategories, SCThingWithCategoriesSpecificValues>,
|
||||
SCJobPostingWithoutReferences {
|
||||
/**
|
||||
* A description of the employer
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
employerOverview?: SCOrganizationWithoutReferences;
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export interface SCMessageWithoutReferences
|
||||
|
||||
/**
|
||||
* Roles for which the message is intended
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
audiences: SCUserGroup[];
|
||||
|
||||
@@ -59,13 +59,13 @@ export interface SCMessageWithoutReferences
|
||||
|
||||
/**
|
||||
* When the message was created
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
dateCreated?: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* Message itself
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
messageBody: string;
|
||||
|
||||
@@ -80,7 +80,8 @@ export interface SCMessageWithoutReferences
|
||||
translations?: SCTranslations<SCMessageTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a message
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Message;
|
||||
}
|
||||
@@ -88,7 +89,7 @@ export interface SCMessageWithoutReferences
|
||||
/**
|
||||
* A message
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCMessage extends SCCreativeWork, SCMessageWithoutReferences {
|
||||
/**
|
||||
@@ -97,7 +98,8 @@ export interface SCMessage extends SCCreativeWork, SCMessageWithoutReferences {
|
||||
translations?: SCTranslations<SCMessageTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a message
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Message;
|
||||
}
|
||||
@@ -110,7 +112,7 @@ export interface SCMessageTranslatableProperties
|
||||
SCThingThatCanBeOfferedTranslatableProperties {
|
||||
/**
|
||||
* Message itself
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
messageBody?: string;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import {SCContactPointWithoutReferences} from './contact-point.js';
|
||||
*/
|
||||
export interface SCOrganizationWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Type of an organization
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Organization;
|
||||
}
|
||||
@@ -30,7 +31,7 @@ export interface SCOrganizationWithoutReferences extends SCThingWithoutReference
|
||||
/**
|
||||
* An organization
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCOrganization extends SCOrganizationWithoutReferences, SCThingInPlace {
|
||||
/**
|
||||
@@ -39,7 +40,8 @@ export interface SCOrganization extends SCOrganizationWithoutReferences, SCThing
|
||||
contactPoints?: SCContactPointWithoutReferences[];
|
||||
|
||||
/**
|
||||
* Type of an organization
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Organization;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ export interface SCPeriodicalWithoutReferences
|
||||
categories: SCPeriodicalCategories[];
|
||||
/**
|
||||
* A list of ISSNs of a periodical
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
ISSNs?: string[];
|
||||
|
||||
@@ -63,7 +62,8 @@ export interface SCPeriodicalWithoutReferences
|
||||
translations?: SCTranslations<SCPeriodicalTranslatableFields>;
|
||||
|
||||
/**
|
||||
* Type of a periodical
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Periodical;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export interface SCPeriodicalWithoutReferences
|
||||
/**
|
||||
* A publication published at regular intervals (e.g. a magazine or newspaper)
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCPeriodical
|
||||
extends SCCreativeWork,
|
||||
@@ -83,7 +83,8 @@ export interface SCPeriodical
|
||||
translations?: SCTranslations<SCPeriodicalTranslatableFields>;
|
||||
|
||||
/**
|
||||
* Type of a periodical
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Periodical;
|
||||
}
|
||||
|
||||
@@ -27,77 +27,67 @@ import {SCRoomWithoutReferences} from './room.js';
|
||||
export interface SCPersonWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Additional first names of the person.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
additionalName?: string;
|
||||
|
||||
/**
|
||||
* The birth date of the person.
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
birthDate?: SCISO8601Date;
|
||||
|
||||
/**
|
||||
* The private email address of the person.
|
||||
* @TJS-format email
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
* The family name of the person.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
familyName?: string;
|
||||
|
||||
/**
|
||||
* The private fax number of the person.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
faxNumber?: string;
|
||||
|
||||
/**
|
||||
* The gender of the person.
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
gender?: SCPersonGender;
|
||||
|
||||
/**
|
||||
* The first name of the person.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
givenName?: string;
|
||||
|
||||
/**
|
||||
* Honorific prefix of the person.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
honorificPrefix?: string;
|
||||
|
||||
/**
|
||||
* Honorific suffix of the person.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
honorificSuffix?: string;
|
||||
|
||||
/**
|
||||
* Titles of jobs that the person has.
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
jobTitles?: string[];
|
||||
|
||||
/**
|
||||
* The complete name of the person combining all the parts of the name into one.
|
||||
* @filterable
|
||||
* @text
|
||||
* @elasticsearch text filterable
|
||||
*/
|
||||
name: string;
|
||||
|
||||
@@ -108,12 +98,12 @@ export interface SCPersonWithoutReferences extends SCThingWithoutReferences {
|
||||
|
||||
/**
|
||||
* The private telephone number of the person.
|
||||
* @keyword
|
||||
*/
|
||||
telephone?: string;
|
||||
|
||||
/**
|
||||
* Type of a person
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Person;
|
||||
}
|
||||
@@ -121,7 +111,7 @@ export interface SCPersonWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* A person
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCPerson extends SCPersonWithoutReferences, SCThing {
|
||||
/**
|
||||
@@ -137,7 +127,8 @@ export interface SCPerson extends SCPersonWithoutReferences, SCThing {
|
||||
>;
|
||||
|
||||
/**
|
||||
* Type of a person
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Person;
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ export interface SCPointOfInterestWithoutReferences
|
||||
translations?: SCTranslations<SCThingWithCategoriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a point of interest
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.PointOfInterest;
|
||||
}
|
||||
@@ -47,7 +48,7 @@ export interface SCPointOfInterestWithoutReferences
|
||||
/**
|
||||
* A point of interest
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCPointOfInterest
|
||||
extends SCPointOfInterestWithoutReferences,
|
||||
@@ -60,7 +61,8 @@ export interface SCPointOfInterest
|
||||
translations?: SCTranslations<SCThingWithCategoriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of a point of interest
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.PointOfInterest;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ export interface SCPublicationEventWithoutReferences extends SCEventWithoutRefer
|
||||
translations?: SCTranslations<SCPublicationEventTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an publication event
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.PublicationEvent;
|
||||
}
|
||||
@@ -44,7 +45,7 @@ export interface SCPublicationEventWithoutReferences extends SCEventWithoutRefer
|
||||
/**
|
||||
* An publication event
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCPublicationEvent extends SCEvent, SCPublicationEventWithoutReferences {
|
||||
/**
|
||||
@@ -53,7 +54,8 @@ export interface SCPublicationEvent extends SCEvent, SCPublicationEventWithoutRe
|
||||
translations?: SCTranslations<SCPublicationEventTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of an publication event
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.PublicationEvent;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCMetaTranslations, SCTranslations} from '../general/i18n.js';
|
||||
import {SCMap} from '../general/map.js';
|
||||
import {SCPlace, SCPlaceWithoutReferences, SCPlaceWithoutReferencesMeta} from './abstract/place.js';
|
||||
|
||||
import {SCThingMeta, SCThingType} from './abstract/thing.js';
|
||||
@@ -58,15 +57,14 @@ export interface SCRoomWithoutReferences
|
||||
SCThingWithCategoriesWithoutReferences<SCRoomCategories, SCRoomSpecificValues> {
|
||||
/**
|
||||
* The name of the floor in which the room is in.
|
||||
* @filterable
|
||||
* @text
|
||||
* @elasticsearch text filterable
|
||||
*/
|
||||
floorName?: string;
|
||||
|
||||
/**
|
||||
* The inventory of the place/room as a list of items and their quantity.
|
||||
*/
|
||||
inventory?: SCMap<number>;
|
||||
inventory?: Record<string, number>;
|
||||
|
||||
/**
|
||||
* Translations of specific values of the object
|
||||
@@ -76,7 +74,8 @@ export interface SCRoomWithoutReferences
|
||||
translations?: SCTranslations<SCThingWithCategoriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the room
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Room;
|
||||
}
|
||||
@@ -84,7 +83,7 @@ export interface SCRoomWithoutReferences
|
||||
/**
|
||||
* A room
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCRoom
|
||||
extends SCRoomWithoutReferences,
|
||||
@@ -100,7 +99,8 @@ export interface SCRoom
|
||||
translations?: SCTranslations<SCThingWithCategoriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the room
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Room;
|
||||
}
|
||||
@@ -111,14 +111,12 @@ export interface SCRoom
|
||||
export interface SCRoomSpecificValues extends SCThingWithCategoriesSpecificValues {
|
||||
/**
|
||||
* Category specific opening hours of the room
|
||||
* @keyword
|
||||
*/
|
||||
openingHours?: string;
|
||||
|
||||
/**
|
||||
* Category specific service hours of the room (e.g. cooked food serving hours)
|
||||
* @see http://wiki.openstreetmap.org/wiki/Key:opening_hours/specification
|
||||
* @keyword
|
||||
*/
|
||||
serviceHours?: string;
|
||||
}
|
||||
|
||||
@@ -26,14 +26,13 @@ import {SCThingMeta, SCThingType} from './abstract/thing.js';
|
||||
export interface SCSemesterWithoutReferences extends SCAcademicTermWithoutReferences {
|
||||
/**
|
||||
* The short name of the semester, using the given pattern.
|
||||
* @filterable
|
||||
* @pattern ^(WS|SS|WiSe|SoSe) [0-9]{4}(/[0-9]{2})?$
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
acronym: string;
|
||||
|
||||
/**
|
||||
* Type of the semester
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Semester;
|
||||
}
|
||||
@@ -41,11 +40,12 @@ export interface SCSemesterWithoutReferences extends SCAcademicTermWithoutRefere
|
||||
/**
|
||||
* A semester
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCSemester extends SCSemesterWithoutReferences, SCAcademicTerm {
|
||||
/**
|
||||
* Type of the semester
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Semester;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,6 @@ export type SCSettingValues = SCSettingValue[];
|
||||
export interface SCSettingValueTranslatableProperties extends SCThingWithCategoriesTranslatableProperties {
|
||||
/**
|
||||
* The translations of the possible values of a setting
|
||||
* @keyword
|
||||
*/
|
||||
values?: string[];
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import {SCThingMeta, SCThingType} from './abstract/thing.js';
|
||||
*/
|
||||
export interface SCSportCourseWithoutReferences extends SCEventWithoutReferences {
|
||||
/**
|
||||
* Type of a sport course
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.SportCourse;
|
||||
}
|
||||
@@ -29,11 +30,12 @@ export interface SCSportCourseWithoutReferences extends SCEventWithoutReferences
|
||||
/**
|
||||
* A sport course
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCSportCourse extends SCEvent, SCSportCourseWithoutReferences {
|
||||
/**
|
||||
* Type of a sport course
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.SportCourse;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {SCLanguage, SCMetaTranslations, SCTranslations} from '../general/i18n.js';
|
||||
import {SCMap} from '../general/map.js';
|
||||
import {SCThingMeta, SCThingType} from './abstract/thing.js';
|
||||
import {
|
||||
SCAcademicPriceGroup,
|
||||
@@ -32,7 +31,6 @@ import {SCPersonWithoutReferences} from './person.js';
|
||||
export interface SCStudyModuleWithoutReferences extends SCThingThatCanBeOfferedWithoutReferences {
|
||||
/**
|
||||
* ECTS points (European Credit Transfer System)
|
||||
* @float
|
||||
*/
|
||||
ects: number;
|
||||
|
||||
@@ -43,15 +41,14 @@ export interface SCStudyModuleWithoutReferences extends SCThingThatCanBeOfferedW
|
||||
|
||||
/**
|
||||
* Majors that this study module is meant for
|
||||
* @filterable
|
||||
* @keyword
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
majors: string[];
|
||||
|
||||
/**
|
||||
* Represents the modules necessity for each given major (of the major property)
|
||||
*/
|
||||
necessity: SCMap<SCStudyModuleNecessity>;
|
||||
necessity: Record<string, SCStudyModuleNecessity>;
|
||||
|
||||
/**
|
||||
* Translated fields of a study module
|
||||
@@ -59,7 +56,9 @@ export interface SCStudyModuleWithoutReferences extends SCThingThatCanBeOfferedW
|
||||
translations?: SCTranslations<SCStudyModuleTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the study module
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.StudyModule;
|
||||
}
|
||||
@@ -67,7 +66,7 @@ export interface SCStudyModuleWithoutReferences extends SCThingThatCanBeOfferedW
|
||||
/**
|
||||
* A study module
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCStudyModule
|
||||
extends SCStudyModuleWithoutReferences,
|
||||
@@ -104,7 +103,8 @@ export interface SCStudyModule
|
||||
translations?: SCTranslations<SCStudyModuleTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the study module
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.StudyModule;
|
||||
}
|
||||
@@ -112,14 +112,13 @@ export interface SCStudyModule
|
||||
export interface SCStudyModuleTranslatableProperties extends SCThingThatCanBeOfferedTranslatableProperties {
|
||||
/**
|
||||
* Translations of the majors that this study module is meant for
|
||||
* @keyword
|
||||
*/
|
||||
majors?: string[];
|
||||
|
||||
/**
|
||||
* Translations of the modules necessity for each given major (of the major property)
|
||||
*/
|
||||
necessity: SCMap<SCStudyModuleNecessity>;
|
||||
necessity: Record<string, SCStudyModuleNecessity>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,6 @@ export interface SCTicketWithoutReferences extends SCThingWithoutReferences {
|
||||
|
||||
/**
|
||||
* Waiting number of the ticket
|
||||
* @keyword
|
||||
*/
|
||||
currentTicketNumber: string;
|
||||
|
||||
@@ -38,7 +37,8 @@ export interface SCTicketWithoutReferences extends SCThingWithoutReferences {
|
||||
serviceType: string;
|
||||
|
||||
/**
|
||||
* Type of a ticket
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Ticket;
|
||||
}
|
||||
@@ -46,11 +46,12 @@ export interface SCTicketWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* A ticket
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCTicket extends SCTicketWithoutReferences, SCThingInPlace {
|
||||
/**
|
||||
* Type of a ticket
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Ticket;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface SCToDoWithoutReferences
|
||||
|
||||
/**
|
||||
* A date when the "to do" is due
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
dueDate?: SCISO8601Date;
|
||||
|
||||
@@ -45,7 +45,8 @@ export interface SCToDoWithoutReferences
|
||||
priority: SCToDoPriority;
|
||||
|
||||
/**
|
||||
* Type of the "to do"
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.ToDo;
|
||||
}
|
||||
@@ -53,7 +54,7 @@ export interface SCToDoWithoutReferences
|
||||
/**
|
||||
* A "to do"
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCToDo
|
||||
extends SCToDoWithoutReferences,
|
||||
@@ -65,7 +66,8 @@ export interface SCToDo
|
||||
translations?: SCTranslations<SCThingWithCategoriesTranslatableProperties>;
|
||||
|
||||
/**
|
||||
* Type of the "to do"
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.ToDo;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {SCThing, SCThingMeta, SCThingType, SCThingWithoutReferences} from './abs
|
||||
export interface SCTourWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* Init script for the tour
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
init?: string;
|
||||
|
||||
@@ -31,8 +31,8 @@ export interface SCTourWithoutReferences extends SCThingWithoutReferences {
|
||||
steps: SCTourStep[];
|
||||
|
||||
/**
|
||||
* Type of a tour
|
||||
* @keyword
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Tour;
|
||||
}
|
||||
@@ -40,11 +40,12 @@ export interface SCTourWithoutReferences extends SCThingWithoutReferences {
|
||||
/**
|
||||
* A tour
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCTour extends SCTourWithoutReferences, SCThing {
|
||||
/**
|
||||
* Type of a tour
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Tour;
|
||||
}
|
||||
@@ -95,12 +96,12 @@ export type SCTourStep = SCTourStepMenu | SCTourStepLocation | SCTourStepTooltip
|
||||
export interface SCTourStepLocation {
|
||||
/**
|
||||
* Location to go to
|
||||
* @keyword
|
||||
*/
|
||||
location: string;
|
||||
|
||||
/**
|
||||
* Type of the step
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: 'location';
|
||||
}
|
||||
@@ -116,7 +117,6 @@ export interface SCTourStepTooltip {
|
||||
|
||||
/**
|
||||
* Element that the tooltip shall be pointing at or a list of elements to try in the specified order
|
||||
* @keyword
|
||||
*/
|
||||
element: string | string[];
|
||||
|
||||
@@ -132,7 +132,7 @@ export interface SCTourStepTooltip {
|
||||
|
||||
/**
|
||||
* Text that the tooltip shall contain
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
text: string;
|
||||
|
||||
@@ -143,7 +143,8 @@ export interface SCTourStepTooltip {
|
||||
tries?: number;
|
||||
|
||||
/**
|
||||
* Type of the step
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: 'tooltip';
|
||||
}
|
||||
@@ -174,7 +175,6 @@ export interface SCTourStepMenu {
|
||||
export interface SCTourResolvedElement {
|
||||
/**
|
||||
* Element name
|
||||
* @keyword
|
||||
*/
|
||||
element: string;
|
||||
}
|
||||
@@ -185,7 +185,6 @@ export interface SCTourResolvedElement {
|
||||
export interface SCTourResolvedEvent {
|
||||
/**
|
||||
* Event name
|
||||
* @keyword
|
||||
*/
|
||||
event: string;
|
||||
}
|
||||
@@ -206,7 +205,6 @@ export interface SCTourResolvedLocation {
|
||||
export interface SCTourResolvedLocationTypeIs {
|
||||
/**
|
||||
* Specific location name
|
||||
* @keyword
|
||||
*/
|
||||
is: string;
|
||||
}
|
||||
@@ -217,7 +215,6 @@ export interface SCTourResolvedLocationTypeIs {
|
||||
export interface SCTourResolvedLocationTypeMatch {
|
||||
/**
|
||||
* Regex location name
|
||||
* @keyword
|
||||
*/
|
||||
match: string;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ export interface SCVideoWithoutReferences
|
||||
|
||||
/**
|
||||
* URLs to a thumbnails for the Video
|
||||
* @keyword
|
||||
*/
|
||||
thumbnails?: string[];
|
||||
|
||||
@@ -59,7 +58,7 @@ export interface SCVideoWithoutReferences
|
||||
|
||||
/**
|
||||
* A Transcript of the Video
|
||||
* @text
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
transcript?: string;
|
||||
|
||||
@@ -69,7 +68,8 @@ export interface SCVideoWithoutReferences
|
||||
translations?: SCTranslations<SCVideoTranslatableFields>;
|
||||
|
||||
/**
|
||||
* Type of an Video
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Video;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export interface SCVideoSource {
|
||||
|
||||
/**
|
||||
* MIME-Type of the source File
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
mimeType: SCVideoMimeType;
|
||||
|
||||
@@ -95,7 +95,6 @@ export interface SCVideoSource {
|
||||
|
||||
/**
|
||||
* URL to the Video File
|
||||
* @keyword
|
||||
*/
|
||||
url: string;
|
||||
|
||||
@@ -114,13 +113,12 @@ export interface SCVideoTrack {
|
||||
|
||||
/**
|
||||
* Content Type of the Track File
|
||||
* @filterable
|
||||
* @elasticsearch filterable
|
||||
*/
|
||||
type: SCVideoTrackTypes;
|
||||
|
||||
/**
|
||||
* URL to the Track File
|
||||
* @keyword
|
||||
*/
|
||||
url: string;
|
||||
}
|
||||
@@ -128,7 +126,7 @@ export interface SCVideoTrack {
|
||||
/**
|
||||
* A video
|
||||
* @validatable
|
||||
* @indexable
|
||||
* @elasticsearch indexable
|
||||
*/
|
||||
export interface SCVideo
|
||||
extends SCCreativeWork,
|
||||
@@ -145,7 +143,8 @@ export interface SCVideo
|
||||
translations?: SCTranslations<SCVideoTranslatableFields>;
|
||||
|
||||
/**
|
||||
* Type of a video
|
||||
* Type discriminator
|
||||
* @elasticsearch type
|
||||
*/
|
||||
type: SCThingType.Video;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import {validateFiles, writeReport} from '@openstapps/core-tools';
|
||||
import {expect} from 'chai';
|
||||
import {mkdir} from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
describe('Schema', function () {
|
||||
this.timeout(15_000);
|
||||
this.slow(10_000);
|
||||
|
||||
it('should validate against test files', async function () {
|
||||
// TODO
|
||||
/*it('should validate against test files', async function () {
|
||||
const errorsPerFile = await validateFiles(
|
||||
path.resolve('lib', 'schema'),
|
||||
path.resolve('test', 'resources'),
|
||||
@@ -21,5 +17,5 @@ describe('Schema', function () {
|
||||
expect(error.expected).to.be.true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});*/
|
||||
});
|
||||
|
||||
16
packages/core/tsup.config.ts
Normal file
16
packages/core/tsup.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import {defineConfig} from 'tsup';
|
||||
import {jsonSchemaPlugin} from '@openstapps/json-schema-generator';
|
||||
import {openapiPlugin} from '@openstapps/openapi-generator';
|
||||
import {elasticsearchMappingGenerator} from '@openstapps/es-mapping-generator';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
format: 'esm',
|
||||
outDir: 'lib',
|
||||
plugins: [
|
||||
jsonSchemaPlugin('index.schema.json', elasticsearchMappingGenerator('elasticsearch.json')),
|
||||
openapiPlugin('openapi.json', 'index.schema.json'),
|
||||
],
|
||||
});
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
require('./lib/app.js');
|
||||
@@ -2,14 +2,11 @@
|
||||
"name": "@openstapps/es-mapping-generator",
|
||||
"description": "Tool to convert TypeScript Interfaces to Elasticsearch Mappings",
|
||||
"version": "3.0.0",
|
||||
"type": "commonjs",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"author": "Thea Schöbl <dev@theaninova.de>",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"bin": {
|
||||
"openstapps-es-mapping-generator": "app.js"
|
||||
},
|
||||
"files": [
|
||||
"app.js",
|
||||
"lib",
|
||||
@@ -18,7 +15,8 @@
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf lib && tsc",
|
||||
"build": "tsup-node --dts",
|
||||
"dev": "tsup --watch --onSuccess \"node lib/index.js\"",
|
||||
"format": "prettier . -c --ignore-path ../../.gitignore",
|
||||
"format:fix": "prettier --write . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --ext .ts src/ test/",
|
||||
@@ -26,28 +24,33 @@
|
||||
"test": "c8 mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "8.4.0",
|
||||
"commander": "10.0.0",
|
||||
"deepmerge": "4.3.1",
|
||||
"flatted": "3.2.7",
|
||||
"typedoc": "0.18.0",
|
||||
"typescript": "3.8.3"
|
||||
"@elastic/elasticsearch": "8.10.0",
|
||||
"@openstapps/json-schema-generator": "workspace:*",
|
||||
"@openstapps/tsup-plugin": "workspace:*",
|
||||
"@types/json-schema": "7.0.14",
|
||||
"ajv": "8.12.0",
|
||||
"better-ajv-errors": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openstapps/eslint-config": "workspace:*",
|
||||
"@openstapps/prettier-config": "workspace:*",
|
||||
"@testdeck/mocha": "0.3.3",
|
||||
"@openstapps/tsconfig": "workspace:*",
|
||||
"@types/chai": "4.3.5",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/glob": "8.0.1",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/node": "14.18.38",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/mustache": "4.2.2",
|
||||
"@types/node": "18.15.3",
|
||||
"c8": "7.14.0",
|
||||
"chai": "4.3.7",
|
||||
"esbuild": "0.17.19",
|
||||
"mocha": "10.2.0",
|
||||
"mocha-junit-reporter": "2.2.0",
|
||||
"nock": "13.3.1",
|
||||
"rimraf": "5.0.0",
|
||||
"ts-node": "10.9.1"
|
||||
"ts-node": "10.9.1",
|
||||
"tsup": "6.7.0",
|
||||
"typedoc": "0.24.8",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"prettier": "@openstapps/prettier-config",
|
||||
"eslintConfig": {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Command} from 'commander';
|
||||
import {copyFileSync, mkdirSync, readFileSync, writeFileSync} from 'fs';
|
||||
import path from 'path';
|
||||
import {generateTemplate} from './mapping';
|
||||
import {getProjectReflection} from './project-reflection';
|
||||
|
||||
// handle unhandled promise rejections
|
||||
process.on('unhandledRejection', async (reason: unknown) => {
|
||||
if (reason instanceof Error) {
|
||||
await console.error(reason.message);
|
||||
console.info(reason.stack);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
const commander = new Command('openstapps-core-tools');
|
||||
|
||||
// eslint-disable-next-line unicorn/prefer-module
|
||||
commander.version(JSON.parse(readFileSync(path.resolve(__dirname, '..', 'package.json')).toString()).version);
|
||||
|
||||
commander
|
||||
.command('mapping <relativeSrcPath>')
|
||||
.option('-m, --mappingPath <relativeMappingPath>', 'Mapping Path')
|
||||
.option('-i, --ignoredTags <ignoredTags>', 'Ignored Tags (comma-separated)')
|
||||
.option('-a, --aggPath <relativeAggregationPath>', 'Aggregations Path')
|
||||
.option('-e, --errorPath <relativeErrorPath>', 'Error Path')
|
||||
.action(async (relativeSourcePath, options) => {
|
||||
// get absolute paths
|
||||
const sourcePath = path.resolve(relativeSourcePath);
|
||||
|
||||
let ignoredTagsList: string[] = [];
|
||||
if (typeof options.ignoredTags === 'string') {
|
||||
ignoredTagsList = options.ignoredTags.split(',');
|
||||
}
|
||||
|
||||
// get project reflection
|
||||
const projectReflection = getProjectReflection(sourcePath);
|
||||
|
||||
const result = generateTemplate(projectReflection, ignoredTagsList, true);
|
||||
if (result.errors.length > 0) {
|
||||
await console.error('Mapping generated with errors!');
|
||||
} else {
|
||||
console.log('Mapping generated without errors!');
|
||||
}
|
||||
|
||||
// write documentation to file
|
||||
if (options.aggPath !== undefined) {
|
||||
const aggPath = path.resolve(options.aggPath);
|
||||
mkdirSync(path.dirname(aggPath), {recursive: true});
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
writeFileSync(aggPath, JSON.stringify(result.aggregations, null, 2));
|
||||
copyFileSync(
|
||||
// eslint-disable-next-line unicorn/prefer-module
|
||||
require.resolve('../schema/aggregations.d.ts'),
|
||||
path.join(path.dirname(aggPath), 'aggregations.json.d.ts'),
|
||||
);
|
||||
console.log(`Elasticsearch aggregations written to ${aggPath}.`);
|
||||
}
|
||||
if (options.mappingPath !== undefined) {
|
||||
const mappingPath = path.resolve(options.mappingPath);
|
||||
mkdirSync(path.dirname(mappingPath), {recursive: true});
|
||||
writeFileSync(mappingPath, JSON.stringify(result.mappings, null, 2));
|
||||
copyFileSync(
|
||||
// eslint-disable-next-line unicorn/prefer-module
|
||||
require.resolve('../schema/mappings.d.ts'),
|
||||
path.join(path.dirname(mappingPath), 'mappings.json.d.ts'),
|
||||
);
|
||||
console.log(`Elasticsearch mappings written to ${mappingPath}.`);
|
||||
}
|
||||
if (options.errorPath !== undefined) {
|
||||
const errorPath = path.resolve(options.errorPath);
|
||||
mkdirSync(path.dirname(errorPath), {recursive: true});
|
||||
// tslint:disable-next-line:no-magic-numbers
|
||||
writeFileSync(errorPath, JSON.stringify(result.errors, null, 2));
|
||||
console.log(`Mapping errors written to ${errorPath}.`);
|
||||
} else if (result.errors.length > 0) {
|
||||
for (const error of result.errors) {
|
||||
await console.error(error);
|
||||
}
|
||||
|
||||
throw new Error('Mapping generation failed');
|
||||
}
|
||||
});
|
||||
|
||||
commander.parse(process.argv);
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {MappingProperty} from '@elastic/elasticsearch/lib/api/types';
|
||||
import type {ElasticsearchFieldmap, SimpleType} from '../../schema/mappings.js';
|
||||
|
||||
const ducetSort = {
|
||||
type: 'icu_collation_keyword',
|
||||
language: 'de',
|
||||
country: 'DE',
|
||||
variant: '@collation=phonebook',
|
||||
};
|
||||
|
||||
const keyword: MappingProperty['type'] = 'keyword';
|
||||
|
||||
export const fieldmap: ElasticsearchFieldmap = {
|
||||
aggregatable: {
|
||||
default: {
|
||||
raw: {
|
||||
ignore_above: 10_000,
|
||||
type: keyword,
|
||||
},
|
||||
},
|
||||
ignore: ['global'],
|
||||
},
|
||||
sortable: {
|
||||
default: {
|
||||
sort: ducetSort,
|
||||
},
|
||||
ducet: {
|
||||
sort: ducetSort,
|
||||
},
|
||||
ignore: ['price'],
|
||||
},
|
||||
};
|
||||
|
||||
export const filterableTagName = 'filterable';
|
||||
|
||||
export const filterableMap: Record<string, SimpleType> = {
|
||||
date: 'keyword',
|
||||
keyword: 'keyword',
|
||||
text: 'keyword',
|
||||
integer: 'integer',
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {MappingProperty} from '@elastic/elasticsearch/lib/api/types';
|
||||
|
||||
export const premaps: Record<string, MappingProperty> = {
|
||||
'CoordinateReferenceSystem': {
|
||||
dynamic: true,
|
||||
properties: {
|
||||
type: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
'LineString': {
|
||||
type: 'geo_shape',
|
||||
},
|
||||
'Point': {
|
||||
properties: {
|
||||
type: {
|
||||
type: 'keyword',
|
||||
},
|
||||
coordinates: {
|
||||
type: 'geo_point',
|
||||
},
|
||||
},
|
||||
dynamic: 'strict',
|
||||
},
|
||||
'Polygon': {
|
||||
type: 'geo_shape',
|
||||
},
|
||||
'SCISO8601DateRange': {
|
||||
type: 'date_range',
|
||||
},
|
||||
'jsonpatch.OpPatch': {
|
||||
dynamic: 'strict',
|
||||
properties: {
|
||||
from: {
|
||||
type: 'keyword',
|
||||
},
|
||||
op: {
|
||||
type: 'keyword',
|
||||
},
|
||||
path: {
|
||||
type: 'keyword',
|
||||
},
|
||||
value: {
|
||||
// this is actually an 'any' type; however, ES does not really support that.
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2021 StApps
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {MappingFloatNumberProperty} from '@elastic/elasticsearch/lib/api/types';
|
||||
import type {ElasticsearchTypemap} from '../../schema/mappings';
|
||||
|
||||
export const PARSE_ERROR = 'PARSE_ERROR' as MappingFloatNumberProperty['type'];
|
||||
export const MISSING_PREMAP = 'MISSING_PREMAP' as MappingFloatNumberProperty['type'];
|
||||
export const TYPE_CONFLICT = 'TYPE_CONFLICT' as MappingFloatNumberProperty['type'];
|
||||
|
||||
export const typemap: ElasticsearchTypemap = {
|
||||
boolean: {
|
||||
default: 'boolean',
|
||||
},
|
||||
false: {
|
||||
default: 'boolean',
|
||||
},
|
||||
number: {
|
||||
default: 'integer',
|
||||
float: 'float',
|
||||
integer: 'integer',
|
||||
date: 'date',
|
||||
},
|
||||
string: {
|
||||
default: 'text',
|
||||
keyword: 'keyword',
|
||||
text: 'text',
|
||||
date: 'date',
|
||||
},
|
||||
stringLiteral: {
|
||||
default: 'keyword',
|
||||
},
|
||||
true: {
|
||||
default: 'boolean',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* If the string is a tag type
|
||||
*/
|
||||
export function isTagType(string_: string): boolean {
|
||||
for (const key in typemap) {
|
||||
if (typemap.hasOwnProperty(key) && typemap[key][string_] !== undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export const dynamicTypes = ['any', 'unknown'];
|
||||
60
packages/es-mapping-generator/src/dsl/schema.ts
Normal file
60
packages/es-mapping-generator/src/dsl/schema.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import {MappingProperty, SearchRequest} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import Ajv from 'ajv';
|
||||
import {readFile} from 'fs/promises';
|
||||
import {fileURLToPath} from 'url';
|
||||
import path from 'path';
|
||||
import {Context} from '../generator/context.js';
|
||||
|
||||
/**
|
||||
* @validatable
|
||||
*/
|
||||
export interface ElasticsearchOptionsDSL {
|
||||
/**
|
||||
* Mark an interface as indexable
|
||||
*/
|
||||
indexable?: true;
|
||||
/**
|
||||
* Inherit customization options from another item
|
||||
*/
|
||||
extends?: string[];
|
||||
/**
|
||||
* Completely override the property
|
||||
*/
|
||||
override?: MappingProperty;
|
||||
/**
|
||||
* Merge property values
|
||||
*/
|
||||
merge?: MappingProperty;
|
||||
/**
|
||||
* Modify the search request
|
||||
*
|
||||
* Supports `{name}`, `{type}` and `{prop}` templates substitutions anywhere
|
||||
*/
|
||||
search?: Partial<SearchRequest>;
|
||||
}
|
||||
|
||||
const schema = JSON.parse(
|
||||
await readFile(path.join(path.dirname(fileURLToPath(import.meta.url)), 'index.schema.json'), 'utf8'),
|
||||
);
|
||||
const ajv = new Ajv.default({schemas: [schema], allowUnionTypes: true});
|
||||
|
||||
/**
|
||||
* Validate that the options are valid
|
||||
*/
|
||||
export function validateElasticsearchOptionsDsl(
|
||||
context: Context,
|
||||
value: unknown,
|
||||
): value is ElasticsearchOptionsDSL {
|
||||
return ajv.validate('#/definitions/ElasticsearchOptionsDSL', value)
|
||||
? true
|
||||
: context.bail(JSON.stringify(ajv.errors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the mapping result is correct
|
||||
*/
|
||||
export function validateMappingResult(context: Context, value: unknown): value is MappingProperty {
|
||||
return ajv.validate('#/definitions/MappingProperty', value)
|
||||
? true
|
||||
: context.bail(JSON.stringify(ajv.errors));
|
||||
}
|
||||
47
packages/es-mapping-generator/src/generator/base.ts
Normal file
47
packages/es-mapping-generator/src/generator/base.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import {MappingProperty} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import {transformObject} from './transformers/object.js';
|
||||
import {transformString} from './transformers/string.js';
|
||||
import {Context} from './context.js';
|
||||
import {transformDefinition} from './definition.js';
|
||||
|
||||
/**
|
||||
* Transform JSONSchema without applying custom tag logic
|
||||
*/
|
||||
export function transformBase(context: Context, definition: JSONSchema7): MappingProperty {
|
||||
if (definition.anyOf) {
|
||||
return context.resolveUnion(definition.anyOf as JSONSchema7[]);
|
||||
}
|
||||
|
||||
switch (definition.type) {
|
||||
case 'array': {
|
||||
if (Array.isArray(definition.items)) {
|
||||
return context.resolveUnion(definition.items as JSONSchema7[]);
|
||||
} else if (typeof definition.items === 'object') {
|
||||
return transformDefinition(context, definition.items);
|
||||
} else {
|
||||
return context.bail(`Not implemented array type ${typeof definition.items}`);
|
||||
}
|
||||
}
|
||||
case 'object': {
|
||||
return transformObject(context, definition);
|
||||
}
|
||||
case 'string': {
|
||||
return transformString(definition);
|
||||
}
|
||||
case 'number': {
|
||||
return {type: 'float'};
|
||||
}
|
||||
case 'integer': {
|
||||
return {type: 'integer'};
|
||||
}
|
||||
case 'boolean': {
|
||||
return {type: 'boolean'};
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
dynamic: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
115
packages/es-mapping-generator/src/generator/context.ts
Normal file
115
packages/es-mapping-generator/src/generator/context.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import {transformDefinition} from './definition.js';
|
||||
import crypto from 'crypto';
|
||||
import {
|
||||
MappingProperty,
|
||||
MappingDynamicProperty,
|
||||
SearchRequest,
|
||||
} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import deepmerge from 'deepmerge';
|
||||
import {MappingGenerator, sanitizeTypeName} from './mapping-generator.js';
|
||||
import {renderTemplate} from '../template.js';
|
||||
|
||||
/**
|
||||
* Get the name from a $ref such as `#/definitions/SCThing`
|
||||
*/
|
||||
function getNameFromRef(ref: string): string {
|
||||
return decodeURI(ref).replace(/^#\/definitions\//, '');
|
||||
}
|
||||
|
||||
export class Context {
|
||||
constructor(
|
||||
readonly generator: MappingGenerator,
|
||||
readonly thingType: string,
|
||||
readonly path: string[],
|
||||
readonly propertyPath: string[],
|
||||
readonly dependencies: Map<string, Set<string>>,
|
||||
) {}
|
||||
|
||||
resolveReference(reference: string): MappingProperty {
|
||||
return this.deriveContext(reference)[1];
|
||||
}
|
||||
|
||||
resolveUnion(types: JSONSchema7[]): MappingDynamicProperty {
|
||||
for (const type of types) {
|
||||
const [name] = this.deriveContext(type.$ref ?? type);
|
||||
this.addDependency(name, this.propertyPath.join('.'));
|
||||
}
|
||||
|
||||
return {
|
||||
type: '{dynamic_property}',
|
||||
};
|
||||
}
|
||||
|
||||
private deriveContext(reference: string | JSONSchema7): [dependencyName: string, mapping: MappingProperty] {
|
||||
let referenceName = typeof reference === 'string' ? getNameFromRef(reference) : undefined;
|
||||
let definition = typeof reference === 'string' ? undefined : reference;
|
||||
|
||||
if (!definition && !this.generator.cache.has(referenceName!)) {
|
||||
definition = this.generator.project.definitions![referenceName!] as JSONSchema7;
|
||||
if (typeof definition !== 'object') this.bail(`Invalid path ${referenceName!}`);
|
||||
}
|
||||
|
||||
if (!referenceName || !this.generator.cache.has(referenceName)) {
|
||||
const derivedContext = new Context(
|
||||
this.generator,
|
||||
this.thingType,
|
||||
referenceName ? [referenceName] : [],
|
||||
this.propertyPath,
|
||||
new Map(),
|
||||
);
|
||||
const result = transformDefinition(derivedContext, definition);
|
||||
referenceName ??= crypto.createHash('md5').update(JSON.stringify(result)).digest('hex');
|
||||
|
||||
this.generator.cache.set(referenceName, {mapping: result, dependencies: derivedContext.dependencies});
|
||||
}
|
||||
|
||||
const {mapping, dependencies} = this.generator.cache.get(referenceName)!;
|
||||
for (const [name, paths] of dependencies) {
|
||||
for (const path of paths) {
|
||||
this.addDependency(name, [...this.propertyPath.slice(0, -1), path].join('.'));
|
||||
}
|
||||
}
|
||||
return [referenceName, mapping];
|
||||
}
|
||||
|
||||
registerSearchMod(modification: Partial<SearchRequest>) {
|
||||
this.generator.searchMods.mods = deepmerge<Partial<SearchRequest>>(
|
||||
this.generator.searchMods.mods,
|
||||
renderTemplate(modification, [
|
||||
['{name}', this.path[0]],
|
||||
['{prop}', this.propertyPath.join('.')],
|
||||
['{type}', this.thingType],
|
||||
['{_type}', sanitizeTypeName(this.thingType)],
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private addDependency(name: string, path: string) {
|
||||
if (!this.dependencies.has(name)) {
|
||||
this.dependencies.set(name, new Set([path]));
|
||||
} else {
|
||||
this.dependencies.get(name)!.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step down into a property
|
||||
*/
|
||||
step(property: string): Context {
|
||||
return new Context(
|
||||
this.generator,
|
||||
this.thingType,
|
||||
[...this.path, property],
|
||||
[...this.propertyPath, property],
|
||||
this.dependencies,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bail and throw
|
||||
*/
|
||||
bail(reason: string): never {
|
||||
throw new Error(`${this.path.join('.')} ${reason}`);
|
||||
}
|
||||
}
|
||||
32
packages/es-mapping-generator/src/generator/definition.ts
Normal file
32
packages/es-mapping-generator/src/generator/definition.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import {MappingProperty} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import {transformBase} from './base.js';
|
||||
import {Context} from './context.js';
|
||||
import deepmerge from 'deepmerge';
|
||||
import {resolveDsl} from './dsl.js';
|
||||
import {getTags, INDEXABLE_TAG_NAME} from './tags.js';
|
||||
|
||||
/**
|
||||
* Transform JSONSchema
|
||||
*/
|
||||
export function transformDefinition(context: Context, definition: JSONSchema7): MappingProperty {
|
||||
if (definition.$ref) return context.resolveReference(definition.$ref);
|
||||
const tags = getTags(definition);
|
||||
tags.delete(INDEXABLE_TAG_NAME);
|
||||
let base = transformBase(context, definition);
|
||||
|
||||
if (tags.size > 0) {
|
||||
const options = resolveDsl(context, {extends: [...tags]});
|
||||
if (options.override) {
|
||||
base = options.override;
|
||||
}
|
||||
if (options.merge) {
|
||||
base = deepmerge<MappingProperty>(base, options.merge);
|
||||
}
|
||||
if (options.search) {
|
||||
context.registerSearchMod(options.search);
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
48
packages/es-mapping-generator/src/generator/dsl.ts
Normal file
48
packages/es-mapping-generator/src/generator/dsl.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {Context} from './context.js';
|
||||
import {ElasticsearchOptionsDSL} from '../dsl/schema.js';
|
||||
import deepmerge from 'deepmerge';
|
||||
|
||||
type ResolvedOptions = Omit<ElasticsearchOptionsDSL, 'extends'>;
|
||||
|
||||
/**
|
||||
* Resolve DSL inheritance
|
||||
*/
|
||||
export function resolveDsl(
|
||||
context: Context,
|
||||
{extends: parents, ...result}: ElasticsearchOptionsDSL,
|
||||
): ResolvedOptions {
|
||||
for (const reference of parents ?? []) {
|
||||
result = deepmerge<ResolvedOptions>(
|
||||
result,
|
||||
reference.startsWith('@')
|
||||
? resolveReferencePath(context, reference)
|
||||
: resolvePresetReference(context, reference),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve preset references
|
||||
*/
|
||||
function resolvePresetReference(context: Context, reference: string): ResolvedOptions {
|
||||
if (!context.generator.presets.has(reference)) return context.bail(`Missing preset ${reference}`);
|
||||
return resolveDsl(context, context.generator.presets.get(reference)!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve @references
|
||||
*/
|
||||
function resolveReferencePath(context: Context, reference: string): ResolvedOptions {
|
||||
const [type, ...path] = reference.replace(/^@/, '').split('.');
|
||||
let declaration = context.resolveReference(type);
|
||||
while (path.length > 0) {
|
||||
const property = path.shift()!;
|
||||
if (!('properties' in declaration && declaration.properties && property in declaration.properties))
|
||||
context.bail(`Invalid reference ${reference}`);
|
||||
|
||||
declaration = declaration.properties[property];
|
||||
}
|
||||
return {override: declaration};
|
||||
}
|
||||
171
packages/es-mapping-generator/src/generator/index.ts
Normal file
171
packages/es-mapping-generator/src/generator/index.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import type {JSONSchema7} from 'json-schema';
|
||||
import {ElasticsearchOptionsDSL} from '../dsl/schema.js';
|
||||
import {IndicesPutTemplateRequest, MappingProperty} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import {MappingGenerator} from './mapping-generator.js';
|
||||
import {getTags, INDEXABLE_TAG_NAME} from './tags.js';
|
||||
|
||||
export interface GeneratorOptions {
|
||||
/**
|
||||
* Presets you can extend
|
||||
*/
|
||||
presets: Record<string, ElasticsearchOptionsDSL>;
|
||||
/**
|
||||
* Override specific types
|
||||
*/
|
||||
overrides: Record<string, MappingProperty>;
|
||||
/**
|
||||
* Template for the generated index request
|
||||
*
|
||||
* Supports `{type}` and `{sanitized_type}` (same as `{type}`, but no spaces) template substitutions
|
||||
*/
|
||||
template: Partial<IndicesPutTemplateRequest>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully transform a project
|
||||
*/
|
||||
export function transformProject(project: JSONSchema7) {
|
||||
const context = new MappingGenerator(project, OPTIONS);
|
||||
|
||||
const results = [];
|
||||
for (const name in project.definitions) {
|
||||
const definition = project.definitions[name];
|
||||
if (typeof definition !== 'object' || !getTags(definition).has(INDEXABLE_TAG_NAME)) continue;
|
||||
|
||||
results.push(context.buildTemplate(name));
|
||||
}
|
||||
return {
|
||||
mappings: results,
|
||||
search: context.searchMods.mods,
|
||||
};
|
||||
}
|
||||
|
||||
const OPTIONS: GeneratorOptions = {
|
||||
template: {
|
||||
name: 'template_{_type}',
|
||||
index_patterns: 'stapps_{_type}*',
|
||||
settings: {
|
||||
'mapping.total_fields.limit': 10_000,
|
||||
'max_result_window': 30_000,
|
||||
'number_of_replicas': 0,
|
||||
'number_of_shards': 1,
|
||||
},
|
||||
mappings: {
|
||||
date_detection: false,
|
||||
_source: {
|
||||
excludes: ['creation_date'],
|
||||
},
|
||||
properties: {
|
||||
creation_date: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
overrides: {
|
||||
'CoordinateReferenceSystem': {
|
||||
dynamic: true,
|
||||
properties: {
|
||||
type: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
'LineString': {
|
||||
type: 'geo_shape',
|
||||
},
|
||||
'Point': {
|
||||
properties: {
|
||||
type: {
|
||||
type: 'keyword',
|
||||
},
|
||||
coordinates: {
|
||||
type: 'geo_point',
|
||||
},
|
||||
},
|
||||
dynamic: 'strict',
|
||||
},
|
||||
'Polygon': {
|
||||
type: 'geo_shape',
|
||||
},
|
||||
'SCISO8601DateRange': {
|
||||
type: 'date_range',
|
||||
},
|
||||
'jsonpatch.OpPatch': {
|
||||
dynamic: 'strict',
|
||||
properties: {
|
||||
from: {
|
||||
type: 'keyword',
|
||||
},
|
||||
op: {
|
||||
type: 'keyword',
|
||||
},
|
||||
path: {
|
||||
type: 'keyword',
|
||||
},
|
||||
value: {
|
||||
// this is actually an 'any' type; however, ES does not really support that.
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
presets: {
|
||||
'type': {extends: ['sortable:ducet', 'filterable', 'aggregatable:global']},
|
||||
'text': {
|
||||
merge: {type: 'text'},
|
||||
},
|
||||
'filterable': {
|
||||
merge: {
|
||||
fields: {
|
||||
raw: {type: 'keyword'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'sortable:ducet': {
|
||||
merge: {
|
||||
fields: {
|
||||
sort: {
|
||||
type: 'icu_collation_keyword',
|
||||
language: 'de',
|
||||
country: 'DE',
|
||||
variant: '@collation=phonebook',
|
||||
} as never,
|
||||
},
|
||||
},
|
||||
},
|
||||
'sortable': {
|
||||
extends: ['sortable:ducet'],
|
||||
},
|
||||
'sortable:price': {},
|
||||
'aggregatable': {
|
||||
search: {
|
||||
aggs: {
|
||||
'{type}': {
|
||||
aggs: {
|
||||
'{prop}': {
|
||||
terms: {
|
||||
field: '{prop}.raw',
|
||||
size: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
filter: {term: {type: '{type}'}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'aggregatable:global': {
|
||||
search: {
|
||||
aggs: {
|
||||
'@all.{prop}': {
|
||||
terms: {
|
||||
field: '{prop}.raw',
|
||||
size: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
import {GeneratorOptions} from './index.js';
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import {ElasticsearchOptionsDSL} from '../dsl/schema.js';
|
||||
import {
|
||||
IndicesPutTemplateRequest,
|
||||
MappingProperty,
|
||||
MappingTypeMapping,
|
||||
SearchRequest,
|
||||
} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import {Context} from './context.js';
|
||||
import deepmerge from 'deepmerge';
|
||||
import {renderTemplate} from '../template.js';
|
||||
|
||||
/**
|
||||
* Sanitize a type name
|
||||
*/
|
||||
export function sanitizeTypeName(typeName: string): string {
|
||||
return typeName.replaceAll(' ', '_');
|
||||
}
|
||||
|
||||
export class MappingGenerator {
|
||||
readonly presets: Map<string, ElasticsearchOptionsDSL>;
|
||||
|
||||
readonly cache: Map<string, {mapping: MappingProperty; dependencies: Map<string, Set<string>>}>;
|
||||
|
||||
readonly searchMods: {mods: Partial<SearchRequest>} = {mods: {}};
|
||||
|
||||
readonly template: Partial<IndicesPutTemplateRequest>;
|
||||
|
||||
constructor(readonly project: JSONSchema7, options: GeneratorOptions) {
|
||||
this.template = options.template ?? {};
|
||||
this.presets = new Map(Object.entries(options.presets));
|
||||
this.cache = new Map(
|
||||
Object.entries(options.overrides).map(([name, mapping]) => [
|
||||
name,
|
||||
{
|
||||
mapping,
|
||||
dependencies: new Map(),
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
buildTemplate(name: string): IndicesPutTemplateRequest {
|
||||
const thingType = ((this.project.definitions![name] as JSONSchema7).properties!.type as JSONSchema7)
|
||||
.const;
|
||||
if (typeof thingType !== 'string') {
|
||||
throw new TypeError(`${name} needs a valid thing type`);
|
||||
}
|
||||
const mappingContext = new Context(this, thingType, [name], [], new Map());
|
||||
const mappings = mappingContext.resolveReference(name);
|
||||
|
||||
const request: IndicesPutTemplateRequest = deepmerge(
|
||||
{mappings: mappings as MappingTypeMapping},
|
||||
renderTemplate(this.template, [
|
||||
['{name}', name],
|
||||
['{type}', thingType],
|
||||
['{_type}', sanitizeTypeName(thingType)],
|
||||
]),
|
||||
);
|
||||
|
||||
if (mappingContext.dependencies.size > 0) {
|
||||
request.mappings!.dynamic_templates = [];
|
||||
for (const [name, paths] of mappingContext.dependencies) {
|
||||
request.mappings!.dynamic_templates.push({
|
||||
[name]: {
|
||||
path_match: paths.size > 1 ? [...paths] : paths.values().next().value,
|
||||
mapping: mappingContext.resolveReference(name),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
13
packages/es-mapping-generator/src/generator/tags.ts
Normal file
13
packages/es-mapping-generator/src/generator/tags.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
|
||||
export const INDEXABLE_TAG_NAME = 'indexable';
|
||||
|
||||
/**
|
||||
* Get elasticsearch tags
|
||||
*/
|
||||
export function getTags(definition: JSONSchema7): Set<string> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return new Set(
|
||||
(definition as {elasticsearch: string}).elasticsearch?.split(/\s/).map(it => it.trim()) ?? [],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import {MappingProperty} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
import {transformDefinition} from '../definition.js';
|
||||
import {Context} from '../context.js';
|
||||
|
||||
/**
|
||||
* Transform a JSON Schema with `object` type
|
||||
*/
|
||||
export function transformObject(context: Context, definition: JSONSchema7): MappingProperty {
|
||||
const value = {
|
||||
dynamic: definition.additionalProperties === true ? undefined : 'strict',
|
||||
properties: {} as Record<string, MappingProperty>,
|
||||
} satisfies MappingProperty;
|
||||
|
||||
for (const key in definition.properties!) {
|
||||
value.properties[key] = transformDefinition(context.step(key), definition.properties[key] as JSONSchema7);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {JSONSchema7} from 'json-schema';
|
||||
import {MappingProperty} from '@elastic/elasticsearch/lib/api/types.js';
|
||||
|
||||
const stringFormats = new Map<string, MappingProperty>([
|
||||
['date', {type: 'date', format: 'date'}],
|
||||
['time', {type: 'date', format: 'time'}],
|
||||
['date-time', {type: 'date', format: 'date_optional_time'}],
|
||||
['ipv4', {type: 'ip'}],
|
||||
['ipv6', {type: 'ip'}],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Transform a JSON Schema with `string` type
|
||||
*/
|
||||
export function transformString(definition: JSONSchema7): MappingProperty {
|
||||
return definition.format && stringFormats.has(definition.format)
|
||||
? stringFormats.get(definition.format)!
|
||||
: {type: 'keyword'};
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
export * from './mapping';
|
||||
export * from './project-reflection';
|
||||
import {transformProject} from './generator/index.js';
|
||||
import {SchemaConsumer} from '@openstapps/json-schema-generator';
|
||||
|
||||
export * from './config/premap';
|
||||
export * from './config/fieldmap';
|
||||
export * from './config/settings';
|
||||
export * from './config/typemap';
|
||||
/**
|
||||
* JSON Schema Generator plugin for Elasticsearch Mappings
|
||||
*/
|
||||
export function elasticsearchMappingGenerator(fileName: string): [string, SchemaConsumer] {
|
||||
return [
|
||||
'Elasticsearch-Mappings',
|
||||
function (schema) {
|
||||
return {
|
||||
[fileName]: JSON.stringify(transformProject(schema)),
|
||||
};
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user