Skip to content

Commit

Permalink
feat: pptional primitive types limits
Browse files Browse the repository at this point in the history
  • Loading branch information
GreenRover committed Nov 29, 2024
1 parent 981b514 commit b5511f0
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 241 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,35 @@ message Point {

### Per message annotation


| annotation | description |
|------------|:---------------------------------------------------------------------------------------------------------|
| @RootNode | If there are multiple types without an parent you can give a hint to the root node with this annotation. |

### Head annotation

| annotation | description |
|------------|:----------------------------------------------------------|
| @Option | In head of your file you can place options for the parser |


### Head annotation "Option"

The `@Option` have to follow by space separated options key and another space separated value

```
// @Option primitiveTypesWithLimits false
message Point {
}
```

Possible options are:


| option | description | def |
|--------------------------|:-----------------------------------------------------------------------------------------------------------|:-----|
| primitiveTypesWithLimits | If you dont like to get default Min and Max limits for primitive types, you can set this option to `false` | true |



65 changes: 64 additions & 1 deletion src/primitive-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class PrimitiveTypes {
private static readonly MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
private static readonly MIN_SAFE_INTEGER = -this.MAX_SAFE_INTEGER;

public static readonly PRIMITIVE_TYPES: AsyncApiTypeMap = {
public static readonly PRIMITIVE_TYPES_WITH_LIMITS: AsyncApiTypeMap = {
bytes: {
type: 'string',
'x-primitive': 'bytes',
Expand Down Expand Up @@ -85,4 +85,67 @@ export class PrimitiveTypes {
'x-primitive': 'double',
},
};

public static readonly PRIMITIVE_TYPES_MINIMAL: AsyncApiTypeMap = {
bytes: {
type: 'string',
'x-primitive': 'bytes',
},
string: {
type: 'string',
'x-primitive': 'string',
},
bool: {
type: 'boolean',
'x-primitive': 'bool',
},
int32: {
type: 'integer',
'x-primitive': 'int32',
},
sint32: {
type: 'integer',
'x-primitive': 'sint32',
},
uint32: {
type: 'integer',
'x-primitive': 'uint32',
},
int64: {
type: 'integer',
'x-primitive': 'int64',
},
sint64: {
type: 'integer',
'x-primitive': 'sint64',
},
uint64: {
type: 'integer',
'x-primitive': 'uint64',
},
fixed32: {
type: 'number',
'x-primitive': 'fixed32',
},
fixed64: {
type: 'number',
'x-primitive': 'fixed64',
},
sfixed32: {
type: 'number',
'x-primitive': 'sfixed32',
},
sfixed64: {
type: 'number',
'x-primitive': 'sfixed64',
},
float: {
type: 'number',
'x-primitive': 'float',
},
double: {
type: 'number',
'x-primitive': 'double',
},
};
}
47 changes: 40 additions & 7 deletions src/protoj2jsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {AsyncAPISchemaDefinition} from '@asyncapi/parser/esm/spec-types/v3';

const ROOT_FILENAME = 'root';
const COMMENT_ROOT_NODE = '@RootNode';
const COMMENT_OPTION = '@Option';
const COMMENT_EXAMPLE = '@Example';
const COMMENT_DEFAULT = '@Default';

Expand All @@ -18,11 +19,39 @@ class Proto2JsonSchema {
keepCase: true,
alternateCommentMode: true
};
private mapperOptions: { [key: string]: string | boolean } = {
primitiveTypesWithLimits: true
};

constructor(rawSchema: string) {
this.parseOptionsAnnotation(rawSchema);

this.process(ROOT_FILENAME, rawSchema);
}

private parseOptionsAnnotation(rawSchema: string) {
const regex = /\s*(\/\/|\*)\s*@Option\s+(?<key>\w{1,50})\s+(?<value>[^\r\n]{1,200})/g;
let m: RegExpExecArray | null;
while ((m = regex.exec(rawSchema)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}

if (m.groups === undefined) {
break;
}

if (m.groups.value === 'true') {
this.mapperOptions[m.groups.key] = true;
} else if (m.groups.value === 'false') {
this.mapperOptions[m.groups.key] = false;
} else {
this.mapperOptions[m.groups.key] = m.groups.value;
}
}
}

private process(filename: string, source: string | ProtoAsJson) {
if (!isString(source)) {
const srcObject = source as ProtoAsJson;
Expand Down Expand Up @@ -182,7 +211,7 @@ class Proto2JsonSchema {
*/
// eslint-disable-next-line sonarjs/cognitive-complexity
private compileMessage(item: protobuf.Type, stack: string[]): AsyncAPISchemaDefinition {
const properties: {[key: string]: AsyncAPISchemaDefinition} = {};
const properties: { [key: string]: AsyncAPISchemaDefinition } = {};

const obj: v3.AsyncAPISchemaDefinition = {
title: item.name,
Expand Down Expand Up @@ -228,7 +257,7 @@ class Proto2JsonSchema {
}

if (field.comment) {
const minItemsPattern = /@maxItems\\s(\\d+?)/i;
const minItemsPattern = /@minItems\\s(\\d+?)/i;
const maxItemsPattern = /@maxItems\\s(\\d+?)/i;
let m: RegExpExecArray | null;
if ((m = minItemsPattern.exec(field.comment)) !== null) {
Expand Down Expand Up @@ -294,8 +323,10 @@ class Proto2JsonSchema {
private compileField(field: protobuf.Field, parentItem: protobuf.Type, stack: string[]): v3.AsyncAPISchemaDefinition {
let obj: v3.AsyncAPISchemaDefinition = {};

if (PrimitiveTypes.PRIMITIVE_TYPES[field.type.toLowerCase()]) {
obj = Object.assign(obj, PrimitiveTypes.PRIMITIVE_TYPES[field.type.toLowerCase()]);
if (PrimitiveTypes.PRIMITIVE_TYPES_WITH_LIMITS[field.type.toLowerCase()]) {
obj = (this.mapperOptions.primitiveTypesWithLimits) ?
Object.assign(obj, PrimitiveTypes.PRIMITIVE_TYPES_WITH_LIMITS[field.type.toLowerCase()]) :
Object.assign(obj, PrimitiveTypes.PRIMITIVE_TYPES_MINIMAL[field.type.toLowerCase()]);
obj['x-primitive'] = field.type;
} else {
const item = parentItem.lookupTypeOrEnum(field.type);
Expand Down Expand Up @@ -339,6 +370,7 @@ class Proto2JsonSchema {
comment = comment
.replace(new RegExp(`\\s{0,15}${COMMENT_EXAMPLE}\\s{0,15}(.+)`, 'ig'), '')
.replace(new RegExp(`\\s{0,15}${COMMENT_DEFAULT}\\s{0,15}(.+)`, 'ig'), '')
.replace(new RegExp(`\\s{0,15}${COMMENT_OPTION}\\s{0,15}(.+)`, 'ig'), '')
.replace(new RegExp(`\\s{0,15}${COMMENT_ROOT_NODE}`, 'ig'), '')
.replace(new RegExp('\\s{0,15}@(Min|Max|Pattern|Minimum|Maximum|ExclusiveMinimum|ExclusiveMaximum|MultipleOf|MaxLength|MinLength|MaxItems|MinItems)\\s{0,15}[\\d.]{1,20}', 'ig'), '')
.trim();
Expand All @@ -350,12 +382,12 @@ class Proto2JsonSchema {
return comment;
}

private extractExamples(comment: string | null): (string|ProtoAsJson)[] | null {
private extractExamples(comment: string | null): (string | ProtoAsJson)[] | null {
if (!comment) {
return null;
}

const examples: (string|ProtoAsJson)[] = [];
const examples: (string | ProtoAsJson)[] = [];

let m: RegExpExecArray | null;
const examplePattern = new RegExp(`\\s*${COMMENT_EXAMPLE}\\s(.+)$`, 'i');
Expand Down Expand Up @@ -413,6 +445,7 @@ class Proto2JsonSchema {
}
}
}

/* eslint-enable security/detect-unsafe-regex */

private addDefaultFromCommentAnnotations(obj: AsyncAPISchemaDefinition, comment: string | null) {
Expand Down Expand Up @@ -442,7 +475,7 @@ function tryParseToObject(value: string): string | ProtoAsJson {
try {
const json = JSON.parse(value);
if (json) {
return json;
return json;
}
} catch (_) {
// Ignored error, seams not to be a valid json. Maybe just an example starting with an "{" but is not a json.
Expand Down
Loading

0 comments on commit b5511f0

Please sign in to comment.