Add enum message parsing
This commit is contained in:
@@ -6,6 +6,7 @@ export enum MessageTypeEnum {
|
||||
Map = 'map',
|
||||
Object = 'object',
|
||||
Raw = 'raw',
|
||||
Enum = 'enum',
|
||||
}
|
||||
|
||||
export interface StringMessage extends MessageConfiguration {
|
||||
@@ -34,6 +35,10 @@ export interface ObjectMessage extends MessageConfiguration {
|
||||
|
||||
export interface RawMessage extends MessageConfiguration {}
|
||||
|
||||
export interface EnumMessage extends MessageConfiguration {
|
||||
options: string[];
|
||||
}
|
||||
|
||||
export interface MessageConfiguration {
|
||||
type: MessageTypeEnum;
|
||||
}
|
||||
@@ -76,17 +81,29 @@ export const ObjectMessage = (messageDefinition: ProtoMessage) => ({
|
||||
|
||||
export const RawMessage = (): RawMessage => ({ type: MessageTypeEnum.Raw });
|
||||
|
||||
export const EnumMessage = (options: string[]) => ({
|
||||
type: MessageTypeEnum.Enum,
|
||||
options,
|
||||
});
|
||||
|
||||
export interface ProtoMessageField {
|
||||
name: string;
|
||||
configuration: MessageConfiguration;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export interface ProtoMessage {
|
||||
export interface ProtoBase {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ProtoMessage extends ProtoBase {
|
||||
values: ProtoMessageField[];
|
||||
}
|
||||
|
||||
export interface ProtoEnum extends ProtoBase {
|
||||
options: string[];
|
||||
}
|
||||
|
||||
export const UnknownProto = (name: string): ProtoMessage => ({
|
||||
name,
|
||||
values: [{ name: 'Raw JSON', configuration: RawMessage(), value: '' }],
|
||||
|
||||
@@ -2,6 +2,7 @@ import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
|
||||
import {
|
||||
EnumMessage,
|
||||
ListMessage,
|
||||
MapMessage,
|
||||
MessageTypeEnum,
|
||||
@@ -27,12 +28,17 @@ message Test {
|
||||
map<string, string> hello8 = 8;
|
||||
ReferenceMessage hello9 = 9;
|
||||
NestedMessage hello10 = 10;
|
||||
EnumTest enum_test = 11;
|
||||
}
|
||||
|
||||
message ReferenceMessage {
|
||||
string test = 1;
|
||||
}
|
||||
|
||||
enum EnumTest {
|
||||
Hello = 0;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
describe('TestService', () => {
|
||||
@@ -51,7 +57,7 @@ describe('TestService', () => {
|
||||
|
||||
expect(converted.name).toBe('Test');
|
||||
|
||||
expect(converted.values.length).toBe(10);
|
||||
expect(converted.values.length).toBe(11);
|
||||
checkNameAndType(converted, 'hello', MessageTypeEnum.String);
|
||||
checkNameAndType(converted, 'hello2', MessageTypeEnum.Numeric);
|
||||
checkNameAndType(converted, 'hello3', MessageTypeEnum.Numeric);
|
||||
@@ -62,6 +68,7 @@ describe('TestService', () => {
|
||||
checkNameAndType(converted, 'hello8', MessageTypeEnum.Map);
|
||||
checkNameAndType(converted, 'hello9', MessageTypeEnum.Object);
|
||||
checkNameAndType(converted, 'hello10', MessageTypeEnum.Object);
|
||||
checkNameAndType(converted, 'enumTest', MessageTypeEnum.Enum);
|
||||
|
||||
const listMessage = converted.values[6].configuration as ListMessage;
|
||||
expect(listMessage.subConfiguration.type).toBe(MessageTypeEnum.String);
|
||||
@@ -86,7 +93,9 @@ describe('TestService', () => {
|
||||
expect(nestedNestedMessage.configuration.type).toBe(MessageTypeEnum.String);
|
||||
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 {
|
||||
BooleanMessage,
|
||||
EnumMessage,
|
||||
ListMessage,
|
||||
MapMessage,
|
||||
MessageConfiguration,
|
||||
MessageTypeEnum,
|
||||
NumericMessage,
|
||||
ObjectMessage,
|
||||
ProtoBase,
|
||||
ProtoEnum,
|
||||
ProtoMessage,
|
||||
RawMessage,
|
||||
StringMessage,
|
||||
UnknownProto,
|
||||
} from './model/proto-message.model';
|
||||
@@ -24,35 +26,85 @@ export class ProtoDefinitionService {
|
||||
const definition = await parse(protoContents);
|
||||
const messages = definition.root;
|
||||
if (messages) {
|
||||
const messageObjects: ProtoMessage[] =
|
||||
this.parseAllNestedMessages(messages);
|
||||
// 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?
|
||||
for (const messageObject of messageObjects) {
|
||||
const messageObjects: ProtoBase[] = this.parseAllNestedMessages(messages);
|
||||
const standardMessages = messageObjects
|
||||
.filter((messageObject) => 'values' in messageObject)
|
||||
.map((messageObject) => messageObject as ProtoMessage);
|
||||
for (const messageObject of standardMessages) {
|
||||
for (const value of messageObject.values) {
|
||||
if (value.configuration.type === MessageTypeEnum.Object) {
|
||||
this.populateNestedObject(
|
||||
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 [];
|
||||
}
|
||||
|
||||
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) {
|
||||
const messageObjects: ProtoMessage[] = [];
|
||||
const messageObjects: ProtoBase[] = [];
|
||||
// Nested messages/enums
|
||||
if ('nestedArray' in obj && obj.nestedArray) {
|
||||
const nestedArray = obj.nestedArray as ReflectionObject[];
|
||||
for (const nestedObj of nestedArray) {
|
||||
messageObjects.push(...this.parseAllNestedMessages(nestedObj));
|
||||
}
|
||||
}
|
||||
// Message
|
||||
if ('fieldsArray' in 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;
|
||||
}
|
||||
|
||||
@@ -84,7 +136,7 @@ export class ProtoDefinitionService {
|
||||
|
||||
private convertProtoDefinitionToModel(
|
||||
messageDefinition?: ReflectionObject
|
||||
): ProtoMessage {
|
||||
): ProtoBase {
|
||||
if (messageDefinition && 'fieldsArray' in messageDefinition) {
|
||||
const convertedFields: ProtoMessage = {
|
||||
name: messageDefinition.name,
|
||||
|
||||
Reference in New Issue
Block a user