Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic types based on proto definition #581

Open
Joel-PeakMetrics opened this issue Aug 24, 2023 · 1 comment
Open

Dynamic types based on proto definition #581

Joel-PeakMetrics opened this issue Aug 24, 2023 · 1 comment

Comments

@Joel-PeakMetrics
Copy link

Hello,

First off, thank you for contributing this work to the open-source community using protobuf on the unofficially supported NodeJS / Typescript language.

I'm integrating this library with a schema registry that contains proto definitions. Is there an example of how to create a model class to use for serialization or deserialization on the fly?

Here's the code I've got working with protobufjs doing deserialization, but I don't like how this library handles bigints, etc.

Note: this is running with Deno. Your library works fine with Deno besides the extensions issue in the imports (which is easily fixed with the search/replace script).

import { default as protobuf } from "npm:protobufjs";

const protoDefinition = "syntax = \"proto3\";\n message ExampleMessage { \n    string id = 1; \n}";
const parsedMessage = protobuf.parse(protoDefinition);
const root = parsedMessage.root;
const protobufSchema = root.lookupType(
    getTypeName(
        parsedMessage, {
            messageName: props.schemaName,
        },
    ));

protobufSchema.decode(serializedMessage);

// helper functions
function getNestedTypeName(
    parent: { [k: string]: protobuf.ReflectionObject } | undefined,
): string {
    if (!parent) {
        throw new Error("Invalid parent");
    }
    const keys = Object.keys(parent);
    const reflection = parent[keys[0]];
    // Traverse down the nested Namespaces until we find a message Type instance (which extends Namespace)
    if (
        reflection instanceof protobuf.Namespace &&
        !(reflection instanceof protobuf.Type) && reflection.nested
    ) {
        return getNestedTypeName(reflection.nested);
    }
    return keys[0];
}
function getTypeName(
    parsedMessage: protobuf.IParserResult,
    opts: { messageName?: string } = {},
) {
    const root = parsedMessage.root;
    const pkg = parsedMessage.package;
    const name = opts && opts.messageName ? opts.messageName : getNestedTypeName(root.nested);
    return `${pkg ? pkg + "." : ""}.${name}`;
}

TYIA for your help, appreciate it!

@jcready
Copy link
Contributor

jcready commented Aug 24, 2023

Unlike protobuf.js, protobuf-ts depends on protoc for generating code. There is no way to directly convert the string content of a proto file into usable classes for encoding/decoding messages. You could potentially invoke protoc via a subprocess, but you'd still have to do everything via the file system (source files -> protoc -> out files -> await import('../some/out-file.ts')).

Additionally, protobuf-ts doesn't generate a single "root" (top-level proto package namespace) with messages indexed based on their package + message name. Instead it generates a number of files which match the file structure of the input proto files where each file exports message classes which match their proto message name (unless there is a naming conflict like having your proto file contain a message named const).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants