feat: add support for @inheritTags

This commit is contained in:
Wieland Schöbl
2020-02-21 15:31:26 +01:00
committed by Wieland Schöbl
parent 97f6c42407
commit 485430b7f2
8 changed files with 266 additions and 38 deletions

View File

@@ -50,11 +50,13 @@ let aggregations: AggregationSchema = {};
const indexableTag = 'indexable';
const aggregatableTag = 'aggregatable';
const aggregatableTagParameterGlobal = 'global';
const inheritTagsName = 'inherittags';
// clamp printed object to 1000 chars to keep error messages readable
const maxErrorObjectChars = 1000;
let ignoredTagsList = ['indexable', 'validatable'];
let ignoredTagsList = ['indexable', 'validatable', inheritTagsName];
let inheritTagsMap: { [path: string]: CommentTag[]; } = {};
/**
* Gets all interfaces that have an @indexable tag
@@ -107,8 +109,7 @@ function composeErrorMessage(path: string, topTypeName: string, typeName: string
errors.push(error);
if (showErrors) {
// tslint:disable-next-line:no-floating-promises
Logger.error(error)
.then();
void Logger.error(error);
}
}
@@ -139,7 +140,8 @@ function trimString(value: string, maxLength: number): string {
function getReflectionGeneric(type: ReferenceType,
out: Map<string, ElasticsearchValue>,
topTypeName: string,
path: string, tags: CommentTag[]): Map<string, ElasticsearchValue> {
path: string,
tags: CommentTag[]): Map<string, ElasticsearchValue> {
if (typeof type.typeArguments !== 'undefined'
&& type.reflection instanceof DeclarationReflection
&& typeof type.reflection.typeParameters !== 'undefined') {
@@ -177,8 +179,8 @@ function getReflectionGeneric(type: ReferenceType,
*/
function handleExternalType(ref: ReferenceType, generics: Map<string, ElasticsearchValue>,
path: string, topTypeName: string, tags: CommentTag[]): ElasticsearchValue {
for (const premap in premaps) {
if (premap === ref.name && premaps.hasOwnProperty(premap)) {
for (const premap of Object.keys(premaps)) {
if (premap === ref.name) {
return readFieldTags(premaps[premap], path, topTypeName, tags);
}
}
@@ -250,7 +252,8 @@ function handleDeclarationReflection(decl: DeclarationReflection,
mapping: handleType(
decl.indexSignature.type,
new Map(generics), path, topTypeName,
getCommentTags(decl.indexSignature)),
getCommentTags(decl.indexSignature, path, topTypeName),
),
match: '*',
match_mapping_type: '*',
path_match: `${path}*`,
@@ -260,7 +263,7 @@ function handleDeclarationReflection(decl: DeclarationReflection,
}
if (decl.kindString === 'Enumeration') {
return readTypeTags('string', path, topTypeName, getCommentTags(decl, inheritedTags));
return readTypeTags('string', path, topTypeName, getCommentTags(decl, path, topTypeName, inheritedTags));
}
// check all the children, so in this case we are dealing with an OBJECT
@@ -272,29 +275,35 @@ function handleDeclarationReflection(decl: DeclarationReflection,
}
} else if (decl.type instanceof Type) { // if the object is a type, so we are dealing with a PROPERTY
// get inherited tags
return handleType(decl.type, new Map(generics), path, topTypeName, getCommentTags(decl));
return handleType(decl.type, new Map(generics), path, topTypeName, getCommentTags(decl, path, topTypeName));
} else if (decl.kindString === 'Enumeration member') {
return readTypeTags(typeof decl.defaultValue, path, topTypeName, getCommentTags(decl, inheritedTags));
return readTypeTags(typeof decl.defaultValue, path, topTypeName,
getCommentTags(decl, path, topTypeName, inheritedTags));
}
if (empty) {
composeErrorMessage(path, topTypeName, 'object', decl.name, 'Empty object');
}
return readFieldTags(out, path, topTypeName, getCommentTags(decl));
return readFieldTags(out, path, topTypeName, getCommentTags(decl, path, topTypeName));
}
/**
* Reads all comment tags, including inherited ones
*
* @param decl the DeclarationReflection to read the tags from
* @param path the path on which the comments lie
* @param topTypeName the name of the SCThingType
* @param inheritedTags any tags that might have been inherited by a parent
* @param breakId the id of the previous reflection to prevent infinite recursion in some cases
*/
function getCommentTags(decl: DeclarationReflection | SignatureReflection,
inheritedTags: CommentTag[] = [],
// tslint:disable-next-line:no-unnecessary-initializer
breakId: number | undefined = undefined,
function getCommentTags(
decl: DeclarationReflection | SignatureReflection,
path: string,
topTypeName: string,
inheritedTags: CommentTag[] = [],
// tslint:disable-next-line:no-unnecessary-initializer
breakId: number | undefined = undefined,
): CommentTag[] {
if (decl.id === breakId) {
return [];
@@ -303,15 +312,52 @@ function getCommentTags(decl: DeclarationReflection | SignatureReflection,
let out: CommentTag[] = decl.comment instanceof Comment ?
typeof decl.comment.tags !== 'undefined' ? decl.comment.tags : inheritedTags : inheritedTags;
if (decl.overwrites instanceof ReferenceType && decl.overwrites.reflection instanceof DeclarationReflection) {
out = arrayPriorityJoin(getCommentTags(decl.overwrites.reflection, inheritedTags, decl.id), out);
out = arrayPriorityJoin(
getCommentTags(decl.overwrites.reflection, path, topTypeName, inheritedTags, decl.id), out);
}
if (decl.inheritedFrom instanceof ReferenceType && decl.inheritedFrom.reflection instanceof DeclarationReflection) {
out = arrayPriorityJoin(getCommentTags(decl.inheritedFrom.reflection, inheritedTags, decl.id), out);
out = arrayPriorityJoin(
getCommentTags(decl.inheritedFrom.reflection, path, topTypeName, inheritedTags, decl.id), out);
}
saveCommentTags(out, path, topTypeName);
const inheritTag = out.find(((value) => value.tagName === inheritTagsName));
if (typeof inheritTag !== 'undefined') {
out = arrayPriorityJoin(out, retrieveCommentTags(inheritTag.text.trim(), path, topTypeName));
}
return out;
}
/**
* Saves all comment tags to the map
*
* @param tags all tags to be saved (@see and @[inheritTags] will be stripped)
* @param path the path of field
* @param topTypeName the name of the SCThingType
*/
function saveCommentTags(tags: CommentTag[], path: string, topTypeName: string) {
inheritTagsMap[`${topTypeName}::${path.substr(0, path.length - 1)}`] =
tags.filter(((value) => value.tagName !== 'see' && value.tagName !== inheritTagsName));
}
/**
* Retrieves any saved tags
*
* @param path the path to the original field
* @param currentPath the current path to the object we are in
* @param topTypeName the name of the SCThingType
*/
function retrieveCommentTags(path: string, currentPath: string, topTypeName: string): CommentTag[] {
if (!(path in inheritTagsMap)) {
composeErrorMessage(currentPath, topTypeName, path, 'Comment', 'Referenced path to tags does not exist!');
return [];
}
return inheritTagsMap[path];
}
/**
* Joins two arrays of CommentTags, but overrides all original CommentTags with the same tagName
*
@@ -322,9 +368,7 @@ function arrayPriorityJoin(originals: CommentTag[], overrider: CommentTag[]): Co
const out: CommentTag[] = overrider;
originals.forEach((original) => {
const result = overrider.find((element) => {
return original.tagName === element.tagName;
});
const result = overrider.find((element) => original.tagName === element.tagName);
// no support for multiple tags with the same name
if (!(result instanceof CommentTag)) {
@@ -579,6 +623,31 @@ function readTypeTags(type: string, path: string, topTypeName: string, tags: Com
return out;
}
/**
* Reset the state
*
* This is kind of a suboptimal solution and should be changed in the future.
* https://gitlab.com/openstapps/core-tools/-/issues/49
*
* @param resetInheritTags whether inherited tags should be reset as well
*/
function reset(resetInheritTags = true) {
errors = [];
dynamicTemplates = [];
aggregations = {
'@all': {
aggs: {},
filter: {
match_all: {},
},
},
};
if (resetInheritTags) {
inheritTagsMap = {};
}
}
/**
* Takes a project reflection and generates an ElasticsearchTemplate from it
*
@@ -597,24 +666,63 @@ export function generateTemplate(projectReflection: ProjectReflection,
interfaceFilter: string[] = []):
// tslint:disable-next-line:completed-docs
{ aggregations: AggregationSchema; errors: string[]; mappings: ElasticsearchTemplateCollection; } {
errors = [];
aggregations = {
'@all': {
aggs: {},
filter: {
match_all: {},
},
},
};
reset();
showErrors = showErrorOutput;
ignoredTagsList = ['indexable', 'validatable'];
ignoredTagsList = ['indexable', 'validatable', inheritTagsName];
ignoredTagsList.push.apply(ignoredTagsList, ignoredTags);
const indexableInterfaces = getAllIndexableInterfaces(projectReflection);
const out: ElasticsearchTemplateCollection = {};
for (const _interface of indexableInterfaces) {
// TODO: lots of duplicate code, this all needs to be changed https://gitlab.com/openstapps/core-tools/-/issues/49
if (!Array.isArray(_interface.children) || _interface.children.length === 0) {
throw new Error('Interface needs at least some properties to be indexable');
}
const typeObject = _interface.children.find((declarationReflection) => {
return declarationReflection.name === 'type';
});
if (typeof typeObject === 'undefined' || typeof typeObject.type === 'undefined') {
throw new Error('Interface needs a type to be indexable');
}
let typeName = 'INVALID_TYPE';
if (typeObject.type instanceof ReferenceType) {
if (typeObject.type.reflection instanceof DeclarationReflection
&& typeof typeObject.type.reflection.defaultValue === 'string') {
typeName = typeObject.type.reflection.defaultValue.replace('"', '')
.replace('"', '');
} else {
// tslint:disable-next-line:no-floating-promises
void Logger.error('Your input files seem to be incorrect, or there is a major bug in the mapping generator.');
}
} else if (typeObject.type instanceof StringLiteralType) {
Logger.warn(`The interface ${_interface.name} uses a string literal as type, please use SCThingType.`);
typeName = typeObject.type.value;
} else {
// tslint:disable-next-line:no-floating-promises
void Logger.error(`The interface ${_interface.name} is required to use an SCThingType as a type, please do so.`);
}
// init aggregation schema for type
aggregations[typeName] = {
aggs: {},
filter: {
type: {
value: typeName,
},
},
};
handleDeclarationReflection(_interface, new Map(), '', typeName);
}
// second traversal
reset(false);
for (const _interface of indexableInterfaces) {
if (!Array.isArray(_interface.children) || _interface.children.length === 0) {
throw new Error('Interface needs at least some properties to be indexable');
@@ -636,16 +744,14 @@ export function generateTemplate(projectReflection: ProjectReflection,
.replace('"', '');
} else {
// tslint:disable-next-line:no-floating-promises
Logger.error('Your input files seem to be incorrect, or there is a major bug in the mapping generator.')
.then();
void Logger.error('Your input files seem to be incorrect, or there is a major bug in the mapping generator.');
}
} else if (typeObject.type instanceof StringLiteralType) {
Logger.warn(`The interface ${_interface.name} uses a string literal as type, please use SCThingType.`);
typeName = typeObject.type.value;
} else {
// tslint:disable-next-line:no-floating-promises
Logger.error(`The interface ${_interface.name} is required to use an SCThingType as a type, please do so.`)
.then();
void Logger.error(`The interface ${_interface.name} is required to use an SCThingType as a type, please do so.`);
}
// filter out