fix: read type tags correctly after the first time

This commit is contained in:
Wieland Schöbl
2019-08-20 17:48:37 +02:00
parent 36bf17e323
commit 77e49146c0
4 changed files with 88 additions and 62 deletions

View File

@@ -93,11 +93,16 @@ commander
// get project reflection // get project reflection
const projectReflection = getProjectReflection(srcPath); const projectReflection = getProjectReflection(srcPath);
const mapping = generateTemplate(projectReflection, ignoredTagsList); const result = generateTemplate(projectReflection, ignoredTagsList, true);
if (result.errors.length !== 0) {
await Logger.error('Mapping generated with errors!');
} else {
Logger.ok('Mapping generated without errors!');
}
// write documentation to file // write documentation to file
// tslint:disable-next-line:no-magic-numbers // tslint:disable-next-line:no-magic-numbers
writeFileSync(mappingPath, JSON.stringify(mapping.template, null, 2)); writeFileSync(mappingPath, JSON.stringify(result.template, null, 2));
Logger.ok(`Elasticsearch mapping written to ${mappingPath}.`); Logger.ok(`Elasticsearch mapping written to ${mappingPath}.`);
}); });

View File

@@ -108,8 +108,11 @@ function composeErrorMessage(path: string, typeName: string, object: string, mes
* @param type the ReferenceType of a DeclarationReflection * @param type the ReferenceType of a DeclarationReflection
* @param out the previous reflection, it then overrides all parameters or keeps old ones * @param out the previous reflection, it then overrides all parameters or keeps old ones
* @param path the current path to the object we are in * @param path the current path to the object we are in
* @param tags any tags attached to the type
*/ */
function getReflectionGeneric(type: ReferenceType, out: ReflectionGeneric[], path: string): ReflectionGeneric[] { function getReflectionGeneric(type: ReferenceType,
out: ReflectionGeneric[],
path: string, tags: CommentTag[]): ReflectionGeneric[] {
if (typeof type.typeArguments !== 'undefined' if (typeof type.typeArguments !== 'undefined'
&& type.reflection instanceof DeclarationReflection && type.reflection instanceof DeclarationReflection
&& typeof type.reflection.typeParameters !== 'undefined' && typeof type.reflection.typeParameters !== 'undefined'
@@ -118,14 +121,14 @@ function getReflectionGeneric(type: ReferenceType, out: ReflectionGeneric[], pat
let replaced = false; let replaced = false;
for (const old of out) { for (const old of out) {
if (old.name === type.reflection.typeParameters[i].name) { if (old.name === type.reflection.typeParameters[i].name) {
old.value = handleType(type.typeArguments[i], out, path); old.value = handleType(type.typeArguments[i], out, path, tags);
replaced = true; replaced = true;
} }
} }
if (!replaced) { if (!replaced) {
out.push({ out.push({
name: type.reflection.typeParameters[i].name, name: type.reflection.typeParameters[i].name,
value: handleType(type.typeArguments[i], out, path), value: handleType(type.typeArguments[i], out, path, tags),
}); });
} }
} }
@@ -143,7 +146,7 @@ function getReflectionGeneric(type: ReferenceType, out: ReflectionGeneric[], pat
* @param tags any tags attached to the type * @param tags any tags attached to the type
*/ */
function handleRefWithoutReflection(ref: ReferenceType, generics: ReflectionGeneric[], function handleRefWithoutReflection(ref: ReferenceType, generics: ReflectionGeneric[],
path: string, tags?: CommentTag[]): ElasticsearchValue { path: string, tags: CommentTag[]): ElasticsearchValue {
for (const premap in premaps) { for (const premap in premaps) {
if (premap === ref.name) { if (premap === ref.name) {
return readFieldTags(premaps[premap], path, tags); return readFieldTags(premaps[premap], path, tags);
@@ -157,7 +160,7 @@ function handleRefWithoutReflection(ref: ReferenceType, generics: ReflectionGene
return {type: ElasticsearchDataType.parse_error}; return {type: ElasticsearchDataType.parse_error};
} }
return readFieldTags(handleType(ref.typeArguments[0], getReflectionGeneric(ref, generics, path), path), return readFieldTags(handleType(ref.typeArguments[0], getReflectionGeneric(ref, generics, path, tags), path, tags),
path, tags); path, tags);
} }
if (ref.name === '__type') { // empty object if (ref.name === '__type') { // empty object
@@ -179,12 +182,15 @@ function handleRefWithoutReflection(ref: ReferenceType, generics: ReflectionGene
* @param generics the generics from levels above, so we can use them without having access to the parent * @param generics the generics from levels above, so we can use them without having access to the parent
* @param path the current path to the object we are in * @param path the current path to the object we are in
*/ */
function handleDeclarationReflection(decl: DeclarationReflection, generics: ReflectionGeneric[], path: string): function handleDeclarationReflection(decl: DeclarationReflection,
generics: ReflectionGeneric[],
path: string):
ElasticsearchValue { ElasticsearchValue {
// check if we have an object referencing a generic // check if we have an object referencing a generic
for (const gRefl of generics) { for (const gRefl of generics) {
if (gRefl.name === decl.name) { // if the object name is the same as the generic name if (gRefl.name === decl.name) { // if the object name is the same as the generic name
return readFieldTags(gRefl.value, path, typeof decl.comment !== 'undefined' ? decl.comment.tags : undefined); return readFieldTags(gRefl.value, path,
typeof decl.comment !== 'undefined' ? typeof decl.comment.tags !== 'undefined' ? decl.comment.tags : [] : []);
// use the value defined by the generic // use the value defined by the generic
} }
} }
@@ -219,17 +225,18 @@ function handleDeclarationReflection(decl: DeclarationReflection, generics: Refl
} }
} else if (decl.type instanceof Type) { // if the object is a type, so we are dealing with a PROPERTY } else if (decl.type instanceof Type) { // if the object is a type, so we are dealing with a PROPERTY
return handleType(decl.type, generics, path, return handleType(decl.type, generics, path,
typeof decl.comment !== 'undefined' ? decl.comment.tags : undefined); typeof decl.comment !== 'undefined' ? typeof decl.comment.tags !== 'undefined' ? decl.comment.tags : [] : []);
} else if (decl.kindString === 'Enumeration member') { } else if (decl.kindString === 'Enumeration member') {
return readTypeTags(typeof decl.defaultValue, path, return readTypeTags(typeof decl.defaultValue, path,
typeof decl.comment !== 'undefined' ? decl.comment.tags : undefined); typeof decl.comment !== 'undefined' ? typeof decl.comment.tags !== 'undefined' ? decl.comment.tags : [] : []);
} }
if (empty) { if (empty) {
composeErrorMessage(path, 'object', decl.name, 'Empty object'); composeErrorMessage(path, 'object', decl.name, 'Empty object');
} }
return readFieldTags(out, path, typeof decl.comment !== 'undefined' ? decl.comment.tags : undefined); return readFieldTags(out, path,
typeof decl.comment !== 'undefined' ? typeof decl.comment.tags !== 'undefined' ? decl.comment.tags : [] : []);
} }
/** /**
@@ -241,15 +248,19 @@ function handleDeclarationReflection(decl: DeclarationReflection, generics: Refl
* @param type the type object * @param type the type object
* @param generics the generics from levels above, so we can use them without having access to the parent * @param generics the generics from levels above, so we can use them without having access to the parent
* @param path the current path to the object we are in * @param path the current path to the object we are in
* @param tags any tags attached to the type
*/ */
function handleUnionType(type: UnionType, generics: ReflectionGeneric[], path: string): ElasticsearchValue { function handleUnionType(type: UnionType,
generics: ReflectionGeneric[],
path: string,
tags: CommentTag[]): ElasticsearchValue {
const list: ElasticsearchValue[] = []; const list: ElasticsearchValue[] = [];
for (const subType of type.types) { for (const subType of type.types) {
if (subType instanceof IntrinsicType && subType.name === 'undefined') { if (subType instanceof IntrinsicType && subType.name === 'undefined') {
continue; continue;
} }
list.push(handleType(subType, generics, path)); list.push(handleType(subType, generics, path, tags));
} }
if (list.length > 0) { if (list.length > 0) {
@@ -275,7 +286,7 @@ function handleUnionType(type: UnionType, generics: ReflectionGeneric[], path: s
* @param path the current path to the object we are in * @param path the current path to the object we are in
* @param tags any tags attached to the type * @param tags any tags attached to the type
*/ */
function handleType(type: Type, generics: ReflectionGeneric[], path: string, tags?: CommentTag[]): ElasticsearchValue { function handleType(type: Type, generics: ReflectionGeneric[], path: string, tags: CommentTag[]): ElasticsearchValue {
// logger.log((type as any).name); // logger.log((type as any).name);
if (type instanceof ArrayType) { // array is irrelevant in Elasticsearch, so just go with the element type if (type instanceof ArrayType) { // array is irrelevant in Elasticsearch, so just go with the element type
return handleType(type.elementType, generics, path, tags); return handleType(type.elementType, generics, path, tags);
@@ -287,13 +298,13 @@ function handleType(type: Type, generics: ReflectionGeneric[], path: string, tag
return readTypeTags(type.name, path, tags); return readTypeTags(type.name, path, tags);
} }
if (type instanceof UnionType) { // the union type... if (type instanceof UnionType) { // the union type...
return handleUnionType(type, generics, path); return handleUnionType(type, generics, path, tags);
} }
if (type instanceof ReferenceType) { if (type instanceof ReferenceType) {
if (typeof type.reflection !== 'undefined') { if (typeof type.reflection !== 'undefined') {
// there is really no way to make this typesafe, every element in DeclarationReflection is optional. // there is really no way to make this typesafe, every element in DeclarationReflection is optional.
return handleDeclarationReflection(type.reflection as DeclarationReflection, return handleDeclarationReflection(type.reflection as DeclarationReflection,
getReflectionGeneric(type, generics, path), path); getReflectionGeneric(type, generics, path, tags), path);
} }
return handleRefWithoutReflection(type, generics, path, tags); return handleRefWithoutReflection(type, generics, path, tags);
@@ -325,40 +336,46 @@ function handleType(type: Type, generics: ReflectionGeneric[], path: string, tag
* @param prev the previous ElasticsearchValue, for example and object * @param prev the previous ElasticsearchValue, for example and object
* @param path the current path to the object we are in * @param path the current path to the object we are in
* @param tags tags attached to the value * @param tags tags attached to the value
* @param dataType the ElasticsearchDataType, for checking if a tag is a type tag
*/ */
function readFieldTags(prev: ElasticsearchValue, path: string, tags?: CommentTag[]): ElasticsearchValue { function readFieldTags(prev: ElasticsearchValue,
if (typeof tags !== 'undefined') { path: string,
for (const tag of tags) { tags: CommentTag[],
if (!ignoredTagsList.includes(tag.tagName)) { dataType?: string): ElasticsearchValue {
if (typeof fieldmap[tag.tagName] !== 'undefined') { for (const tag of tags) {
if (typeof prev.fields === 'undefined') { if (!ignoredTagsList.includes(tag.tagName)) {
prev.fields = {}; if (typeof fieldmap[tag.tagName] !== 'undefined') {
} if (typeof prev.fields === 'undefined') {
if (tag.text.trim() === '') { // create in case it doesn't exist
prev.fields = {...prev.fields, ...fieldmap[tag.tagName].default}; prev.fields = {};
} else if (typeof fieldmap[tag.tagName][tag.text.trim()] !== 'undefined') { }
// merge the fields if (tag.text.trim() === '') {
prev.fields = {...prev.fields, ...fieldmap[tag.tagName][tag.text.trim()]}; // merge fields
} else if (!fieldmap[tag.tagName].ignore.includes(tag.text.trim())) { prev.fields = {...prev.fields, ...fieldmap[tag.tagName].default};
composeErrorMessage(path, 'tag', tag.tagName, `Not implemented tag param "${tag.text.trim()}"`); } else if (typeof fieldmap[tag.tagName][tag.text.trim()] !== 'undefined') {
} // merge fields
} else if (tag.tagName === filterableTagName) { prev.fields = {...prev.fields, ...fieldmap[tag.tagName][tag.text.trim()]};
if (typeof prev.fields === 'undefined') { } else if (!fieldmap[tag.tagName].ignore.includes(tag.text.trim())) {
prev.fields = {}; // when there is an unidentified tag
} composeErrorMessage(path, 'tag', tag.tagName, `Not implemented tag param "${tag.text.trim()}"`);
if ('type' in prev) { }
const type = filterableMap[prev.type]; } else if (tag.tagName === filterableTagName) {
if (typeof type !== 'undefined') { if (typeof prev.fields === 'undefined') {
prev.fields = {...prev.fields, ...{raw: {type: type}}}; prev.fields = {};
} else { }
composeErrorMessage(path, 'tag', tag.tagName, `Not implemented for ${prev.type}`); if ('type' in prev) {
} const type = filterableMap[prev.type];
if (typeof type !== 'undefined') {
// merge fields
prev.fields = {...prev.fields, ...{raw: {type: type}}};
} else { } else {
composeErrorMessage(path, 'tag', tag.tagName, 'Not applicable for object types'); composeErrorMessage(path, 'tag', tag.tagName, `Not implemented for ${prev.type}`);
} }
} else { } else {
composeErrorMessage(path, 'tag', tag.tagName, `Not implemented tag`); composeErrorMessage(path, 'tag', tag.tagName, 'Not applicable for object types');
} }
} else if (typeof dataType === 'undefined' || typeof typemap[dataType][tag.tagName] === 'undefined') {
composeErrorMessage(path, 'tag', tag.tagName, `Not implemented tag`);
} }
} }
} }
@@ -373,23 +390,20 @@ function readFieldTags(prev: ElasticsearchValue, path: string, tags?: CommentTag
* @param path the current path to the object we are in * @param path the current path to the object we are in
* @param tags tags attached to the value * @param tags tags attached to the value
*/ */
function readTypeTags(type: string, path: string, tags?: CommentTag[]): ElasticsearchValue { function readTypeTags(type: string, path: string, tags: CommentTag[]): ElasticsearchValue {
let out: ElasticsearchValue = {type: ElasticsearchDataType.parse_error}; let out: ElasticsearchValue = {type: ElasticsearchDataType.parse_error};
if (typeof typemap[type] !== 'undefined') { // first look if the value has a definition in the typemap if (typeof typemap[type] !== 'undefined') { // first look if the value has a definition in the typemap
if (typeof tags !== 'undefined') { // look if there are any tags for (let i = tags.length - 1; i >= 0; i--) {
for (let i = tags.length - 1; i >= 0; i--) { if (!ignoredTagsList.includes(tags[i].tagName) && typeof typemap[type][tags[i].tagName] !== 'undefined') {
if (!ignoredTagsList.includes(tags[i].tagName) && typeof typemap[type][tags[i].tagName] !== 'undefined') { // if we have a tag that indicates a type
// if we have a tag that indicates a type if (out.type !== ElasticsearchDataType.parse_error) {
if (out.type !== ElasticsearchDataType.parse_error) { composeErrorMessage(path, 'type', type, `Type conflict; "${typemap[type][tags[i].tagName]}" would` +
composeErrorMessage(path, 'type', type, `Type conflict; "${typemap[type][tags[i].tagName]}" would` + ` override "${out.type}"`);
` override "${out.type}"`); out.type = ElasticsearchDataType.type_conflict;
out.type = ElasticsearchDataType.type_conflict; continue;
continue;
}
out.type = typemap[type][tags[i].tagName];
tags.splice(i, 1); // we need this so readFieldTags can process correctly
} }
out.type = typemap[type][tags[i].tagName];
} }
} }
@@ -397,7 +411,7 @@ function readTypeTags(type: string, path: string, tags?: CommentTag[]): Elastics
out.type = typemap[type].default; out.type = typemap[type].default;
} }
out = readFieldTags(out, path, tags); out = readFieldTags(out, path, tags, type);
return out; return out;
} }

View File

@@ -53,5 +53,6 @@ export const filterableTagName = 'filterable';
export const filterableMap: ElasticsearchFilterableMap = { export const filterableMap: ElasticsearchFilterableMap = {
date: ElasticsearchDataType.keyword, date: ElasticsearchDataType.keyword,
keyword: ElasticsearchDataType.keyword,
text: ElasticsearchDataType.keyword, text: ElasticsearchDataType.keyword,
}; };

View File

@@ -44,7 +44,10 @@ export const premaps: ElasticsearchPremap = {
dynamic: 'strict', dynamic: 'strict',
properties: { properties: {
bbox: {type: ElasticsearchDataType.float}, bbox: {type: ElasticsearchDataType.float},
coordinates: {type: ElasticsearchDataType.geo_point}, // TODO: filterable coordinates: {
fields: {raw: {type: ElasticsearchDataType.keyword}},
type: ElasticsearchDataType.geo_point,
},
crs: { crs: {
dynamic: 'strict', dynamic: 'strict',
properties: { properties: {
@@ -62,7 +65,10 @@ export const premaps: ElasticsearchPremap = {
dynamic: 'strict', dynamic: 'strict',
properties: { properties: {
bbox: {type: ElasticsearchDataType.float}, bbox: {type: ElasticsearchDataType.float},
coordinates: {type: ElasticsearchDataType.geo_point}, // TODO: filterable coordinates: {
fields: {raw: {type: ElasticsearchDataType.keyword}},
type: ElasticsearchDataType.geo_point,
},
crs: { crs: {
dynamic: 'strict', dynamic: 'strict',
properties: { properties: {