Skip to content

Commit

Permalink
feat(protobuf): load definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
szymonlesisz committed Oct 23, 2024
1 parent 9b811e8 commit d3628bf
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/protobuf/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './encode';
export * as Messages from './messages';
export * from './types';
export { parseConfigure, createMessageFromName, createMessageFromType } from './utils';
export { loadProtobuf } from './load';
export * as MessagesSchema from './messages-schema';
// It's problem to reexport enums when they are under MessagesSchema namespace, check packages/connect/src/types/device.ts
export { DeviceModelInternal } from './messages-schema';
39 changes: 39 additions & 0 deletions packages/protobuf/src/load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Root } from 'protobufjs/light';

type Data = Record<string, unknown>;

export const loadProtobuf = async (
messages: Root,
packageName: string,
packageLoader: () => Data | Promise<Data>,
) => {
// check if messages already exists
try {
const pkg = messages.lookup(packageName);
if (pkg) {
return;
}
} catch {}

// get current MessageType enum
let enumType;
try {
enumType = messages.lookupEnum('MessageType');
} catch {}

// load definitions
const packageMessages = await packageLoader();
const pkg = messages.define(packageName, packageMessages);
// get package MessageType enum
let packageEnumType;
try {
packageEnumType = pkg.lookupEnum('MessageType');
} catch {}

// merge both enums
if (enumType && packageEnumType) {
Object.keys(packageEnumType.values).forEach(key => {
enumType.add(key, packageEnumType.values[key]);
});
}
};
98 changes: 98 additions & 0 deletions packages/protobuf/tests/load.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as ProtoBuf from 'protobufjs/light';

import { loadProtobuf } from '../src/load';

describe('protocol-thp/loadProtobuf', () => {
const createProtobufRoot = () => {
return ProtoBuf.Root.fromJSON({
nested: {
MessageType: {
values: {
Initialize: 0,
},
},
},
});
};

it('merge MessageType enum', async () => {
const root = createProtobufRoot();
await loadProtobuf(root, 'bitcoin', () => {
return Promise.resolve({
MessageType: {
values: {
GetAddress: 29,
},
},
});
});

const keys = root.lookupEnum('MessageType')?.values;
expect(keys).toEqual({
Initialize: 0,
GetAddress: 29,
});
});

it('throw on merge MessageType enum', async () => {
await expect(
loadProtobuf(createProtobufRoot(), 'bitcoin', () => {
return Promise.resolve({
MessageType: {
values: {
GetAddress: 0,
},
},
});
}),
).rejects.toThrow('duplicate id 0');

await expect(
loadProtobuf(createProtobufRoot(), 'bitcoin', () => {
return Promise.resolve({
MessageType: {
values: {
Initialize: 1,
},
},
});
}),
).rejects.toThrow('duplicate name');
});

it('create MessageType enum', async () => {
const root = createProtobufRoot();
root.remove(root.lookupEnum('MessageType'));

await loadProtobuf(root, 'bitcoin', () => {
return Promise.resolve({
MessageType: {
values: {
GetAddress: 29,
},
},
});
});

const keys = root.lookupEnum('MessageType')?.values;
expect(keys).toEqual({
GetAddress: 29,
});
});

it('already loaded', async () => {
const root = createProtobufRoot();
root.define('bitcoin', {
MessageType: {
values: {
GetAddress: 29,
},
},
});

const spy = jest.fn();
await loadProtobuf(root, 'bitcoin', spy);

expect(spy).toHaveBeenCalledTimes(0);
});
});

0 comments on commit d3628bf

Please sign in to comment.