Add enum message parsing
This commit is contained in:
@@ -6,6 +6,7 @@ export enum MessageTypeEnum {
|
|||||||
Map = 'map',
|
Map = 'map',
|
||||||
Object = 'object',
|
Object = 'object',
|
||||||
Raw = 'raw',
|
Raw = 'raw',
|
||||||
|
Enum = 'enum',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StringMessage extends MessageConfiguration {
|
export interface StringMessage extends MessageConfiguration {
|
||||||
@@ -34,6 +35,10 @@ export interface ObjectMessage extends MessageConfiguration {
|
|||||||
|
|
||||||
export interface RawMessage extends MessageConfiguration {}
|
export interface RawMessage extends MessageConfiguration {}
|
||||||
|
|
||||||
|
export interface EnumMessage extends MessageConfiguration {
|
||||||
|
options: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface MessageConfiguration {
|
export interface MessageConfiguration {
|
||||||
type: MessageTypeEnum;
|
type: MessageTypeEnum;
|
||||||
}
|
}
|
||||||
@@ -76,17 +81,29 @@ export const ObjectMessage = (messageDefinition: ProtoMessage) => ({
|
|||||||
|
|
||||||
export const RawMessage = (): RawMessage => ({ type: MessageTypeEnum.Raw });
|
export const RawMessage = (): RawMessage => ({ type: MessageTypeEnum.Raw });
|
||||||
|
|
||||||
|
export const EnumMessage = (options: string[]) => ({
|
||||||
|
type: MessageTypeEnum.Enum,
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
|
||||||
export interface ProtoMessageField {
|
export interface ProtoMessageField {
|
||||||
name: string;
|
name: string;
|
||||||
configuration: MessageConfiguration;
|
configuration: MessageConfiguration;
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProtoMessage {
|
export interface ProtoBase {
|
||||||
name: string;
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProtoMessage extends ProtoBase {
|
||||||
values: ProtoMessageField[];
|
values: ProtoMessageField[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProtoEnum extends ProtoBase {
|
||||||
|
options: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export const UnknownProto = (name: string): ProtoMessage => ({
|
export const UnknownProto = (name: string): ProtoMessage => ({
|
||||||
name,
|
name,
|
||||||
values: [{ name: 'Raw JSON', configuration: RawMessage(), value: '' }],
|
values: [{ name: 'Raw JSON', configuration: RawMessage(), value: '' }],
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { TestBed } from '@angular/core/testing';
|
|||||||
|
|
||||||
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
|
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
|
EnumMessage,
|
||||||
ListMessage,
|
ListMessage,
|
||||||
MapMessage,
|
MapMessage,
|
||||||
MessageTypeEnum,
|
MessageTypeEnum,
|
||||||
@@ -27,12 +28,17 @@ message Test {
|
|||||||
map<string, string> hello8 = 8;
|
map<string, string> hello8 = 8;
|
||||||
ReferenceMessage hello9 = 9;
|
ReferenceMessage hello9 = 9;
|
||||||
NestedMessage hello10 = 10;
|
NestedMessage hello10 = 10;
|
||||||
|
EnumTest enum_test = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ReferenceMessage {
|
message ReferenceMessage {
|
||||||
string test = 1;
|
string test = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum EnumTest {
|
||||||
|
Hello = 0;
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
describe('TestService', () => {
|
describe('TestService', () => {
|
||||||
@@ -51,7 +57,7 @@ describe('TestService', () => {
|
|||||||
|
|
||||||
expect(converted.name).toBe('Test');
|
expect(converted.name).toBe('Test');
|
||||||
|
|
||||||
expect(converted.values.length).toBe(10);
|
expect(converted.values.length).toBe(11);
|
||||||
checkNameAndType(converted, 'hello', MessageTypeEnum.String);
|
checkNameAndType(converted, 'hello', MessageTypeEnum.String);
|
||||||
checkNameAndType(converted, 'hello2', MessageTypeEnum.Numeric);
|
checkNameAndType(converted, 'hello2', MessageTypeEnum.Numeric);
|
||||||
checkNameAndType(converted, 'hello3', MessageTypeEnum.Numeric);
|
checkNameAndType(converted, 'hello3', MessageTypeEnum.Numeric);
|
||||||
@@ -62,6 +68,7 @@ describe('TestService', () => {
|
|||||||
checkNameAndType(converted, 'hello8', MessageTypeEnum.Map);
|
checkNameAndType(converted, 'hello8', MessageTypeEnum.Map);
|
||||||
checkNameAndType(converted, 'hello9', MessageTypeEnum.Object);
|
checkNameAndType(converted, 'hello9', MessageTypeEnum.Object);
|
||||||
checkNameAndType(converted, 'hello10', MessageTypeEnum.Object);
|
checkNameAndType(converted, 'hello10', MessageTypeEnum.Object);
|
||||||
|
checkNameAndType(converted, 'enumTest', MessageTypeEnum.Enum);
|
||||||
|
|
||||||
const listMessage = converted.values[6].configuration as ListMessage;
|
const listMessage = converted.values[6].configuration as ListMessage;
|
||||||
expect(listMessage.subConfiguration.type).toBe(MessageTypeEnum.String);
|
expect(listMessage.subConfiguration.type).toBe(MessageTypeEnum.String);
|
||||||
@@ -86,7 +93,9 @@ describe('TestService', () => {
|
|||||||
expect(nestedNestedMessage.configuration.type).toBe(MessageTypeEnum.String);
|
expect(nestedNestedMessage.configuration.type).toBe(MessageTypeEnum.String);
|
||||||
expect(nestedNestedMessage.name).toBe('nested');
|
expect(nestedNestedMessage.name).toBe('nested');
|
||||||
|
|
||||||
// TODO: Enum type
|
const enumMessage = converted.values[10].configuration as EnumMessage;
|
||||||
|
expect(enumMessage.options.length).toBe(1);
|
||||||
|
expect(enumMessage.options[0]).toBe('Hello');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,16 @@ import { Injectable } from '@angular/core';
|
|||||||
import { FieldBase, MapField, ReflectionObject, parse } from 'protobufjs';
|
import { FieldBase, MapField, ReflectionObject, parse } from 'protobufjs';
|
||||||
import {
|
import {
|
||||||
BooleanMessage,
|
BooleanMessage,
|
||||||
|
EnumMessage,
|
||||||
ListMessage,
|
ListMessage,
|
||||||
MapMessage,
|
MapMessage,
|
||||||
MessageConfiguration,
|
MessageConfiguration,
|
||||||
MessageTypeEnum,
|
MessageTypeEnum,
|
||||||
NumericMessage,
|
NumericMessage,
|
||||||
ObjectMessage,
|
ObjectMessage,
|
||||||
|
ProtoBase,
|
||||||
|
ProtoEnum,
|
||||||
ProtoMessage,
|
ProtoMessage,
|
||||||
RawMessage,
|
|
||||||
StringMessage,
|
StringMessage,
|
||||||
UnknownProto,
|
UnknownProto,
|
||||||
} from './model/proto-message.model';
|
} from './model/proto-message.model';
|
||||||
@@ -24,35 +26,85 @@ export class ProtoDefinitionService {
|
|||||||
const definition = await parse(protoContents);
|
const definition = await parse(protoContents);
|
||||||
const messages = definition.root;
|
const messages = definition.root;
|
||||||
if (messages) {
|
if (messages) {
|
||||||
const messageObjects: ProtoMessage[] =
|
const messageObjects: ProtoBase[] = this.parseAllNestedMessages(messages);
|
||||||
this.parseAllNestedMessages(messages);
|
const standardMessages = messageObjects
|
||||||
// Now do another pass, where we populate each of the object fields based on their definitions... need a way to detect recursion? Maybe only go up to 5 layers deep?
|
.filter((messageObject) => 'values' in messageObject)
|
||||||
for (const messageObject of messageObjects) {
|
.map((messageObject) => messageObject as ProtoMessage);
|
||||||
|
for (const messageObject of standardMessages) {
|
||||||
for (const value of messageObject.values) {
|
for (const value of messageObject.values) {
|
||||||
if (value.configuration.type === MessageTypeEnum.Object) {
|
if (value.configuration.type === MessageTypeEnum.Object) {
|
||||||
this.populateNestedObject(
|
this.populateNestedObject(
|
||||||
value.configuration as ObjectMessage,
|
value.configuration as ObjectMessage,
|
||||||
messageObjects
|
standardMessages
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messageObjects;
|
const enumMessages = messageObjects
|
||||||
|
.filter((messageObject) => 'options' in messageObject)
|
||||||
|
.map((messageObject) => messageObject as ProtoEnum);
|
||||||
|
for (const messageObject of standardMessages) {
|
||||||
|
for (const value of messageObject.values) {
|
||||||
|
if (value.configuration.type === MessageTypeEnum.Object) {
|
||||||
|
value.configuration = this.populateEnumMessages(
|
||||||
|
value.configuration as ObjectMessage,
|
||||||
|
enumMessages
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return standardMessages;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private populateEnumMessages(
|
||||||
|
objectMessage: ObjectMessage,
|
||||||
|
enumMessages: ProtoEnum[]
|
||||||
|
): MessageConfiguration {
|
||||||
|
for (const enumMessage of enumMessages) {
|
||||||
|
if (enumMessage.name === objectMessage.messageDefinition.name) {
|
||||||
|
return EnumMessage(enumMessage.options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectMessage.messageDefinition.values =
|
||||||
|
objectMessage.messageDefinition.values.map((obj) =>
|
||||||
|
obj.configuration.type === MessageTypeEnum.Object
|
||||||
|
? {
|
||||||
|
...obj,
|
||||||
|
configuration: this.populateEnumMessages(
|
||||||
|
obj.configuration as ObjectMessage,
|
||||||
|
enumMessages
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: obj
|
||||||
|
);
|
||||||
|
return objectMessage;
|
||||||
|
}
|
||||||
|
|
||||||
private parseAllNestedMessages(obj: ReflectionObject) {
|
private parseAllNestedMessages(obj: ReflectionObject) {
|
||||||
const messageObjects: ProtoMessage[] = [];
|
const messageObjects: ProtoBase[] = [];
|
||||||
|
// Nested messages/enums
|
||||||
if ('nestedArray' in obj && obj.nestedArray) {
|
if ('nestedArray' in obj && obj.nestedArray) {
|
||||||
const nestedArray = obj.nestedArray as ReflectionObject[];
|
const nestedArray = obj.nestedArray as ReflectionObject[];
|
||||||
for (const nestedObj of nestedArray) {
|
for (const nestedObj of nestedArray) {
|
||||||
messageObjects.push(...this.parseAllNestedMessages(nestedObj));
|
messageObjects.push(...this.parseAllNestedMessages(nestedObj));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Message
|
||||||
if ('fieldsArray' in obj) {
|
if ('fieldsArray' in obj) {
|
||||||
messageObjects.push(this.convertProtoDefinitionToModel(obj));
|
messageObjects.push(this.convertProtoDefinitionToModel(obj));
|
||||||
}
|
}
|
||||||
|
// Enum
|
||||||
|
if ('values' in obj) {
|
||||||
|
messageObjects.push({
|
||||||
|
name: obj.name,
|
||||||
|
options: Object.entries(obj.values as Record<string, string>)
|
||||||
|
.sort((a, b) => Number(a[1]) - Number(b[1]))
|
||||||
|
.map(([key, _]) => key),
|
||||||
|
} as ProtoEnum);
|
||||||
|
}
|
||||||
return messageObjects;
|
return messageObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +136,7 @@ export class ProtoDefinitionService {
|
|||||||
|
|
||||||
private convertProtoDefinitionToModel(
|
private convertProtoDefinitionToModel(
|
||||||
messageDefinition?: ReflectionObject
|
messageDefinition?: ReflectionObject
|
||||||
): ProtoMessage {
|
): ProtoBase {
|
||||||
if (messageDefinition && 'fieldsArray' in messageDefinition) {
|
if (messageDefinition && 'fieldsArray' in messageDefinition) {
|
||||||
const convertedFields: ProtoMessage = {
|
const convertedFields: ProtoMessage = {
|
||||||
name: messageDefinition.name,
|
name: messageDefinition.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user