0

We wish to build a repository of functions that a developer can assemble to build a complex program.

There can exist several versions of a function, each with its metadata. This, function metadata includes the developer's full name and email address, the language the function is implemented in and a set of keywords related to the functionality fulfilled by the function.

The versions of a function can be represented as a directed acyclic graph.

To manage the repository, we use a remote invocation based gRPC that allows a client to interact with a server and execute the following operations:

  1. add_new_fn: to add either a brand new function or a new version to an existing function;
  2. add_fns: to add multiple functions streamed by the client (note that multiple versions of a function are not allowed);
  3. delete_fn: to delete a function (this might require reordering the versions of the function);
  4. show_fn: to view a specific version of a function;
  5. show_all_fns: to view all versions of a function (the versions are streamed back by the server)
  6. show_all_with_criteria: to view all latest versions of functions implemented in a given language or related to a set of keywords (bi-directional streaming).
ThisaruG
  • 3,222
  • 7
  • 38
  • 60
Peter
  • 1
  • 3
  • 1
    The API (written as a protobufs) feels straightforward (famous last words!). You'd just need to determine the essential messages that your clients would need to ship to the server that are unique across the methods you summarized above. Is your issue that you want to then call the functions that are defined via gRPC too? I don't understand what you're seeking. – DazWilkin Oct 02 '21 at 23:04

1 Answers1

0

Assuming you just want the API defined:

syntax = "proto3";

package foo;

import "google/protobuf/any.proto";

// Convention is to name RPC method messages after the method
// Provides flexibility in evolving them distinctly too
service FnRepository {
  rpc AddFn(AddFnPackageRequest) returns (AddFnPackageResponse) {}
  rpc AddFns(stream
 AddFnPackageRequest) returns (stream AddFnPackageResponse) {}
  rpc DeleteFn(DelFnPackageRequest) returns (DelFnPackageResponse) {}
  ...
}

// Requests containing messages provides extensibility
message AddFnPackageRequest {
  FnPackage fn_package = 1;
}
// Unique ID returned here
// Would likely be used to delete
message AddFnPackageResponse {
  string id = 1;
}

message Developer {
  string name = 1;
  string email = 2;
}

// Enum assumes a predefined list of languages
message Fn {
  string name = 1;
  Signature signature = 2;
  enum Language {
    GOLANG = 0;
    RUST = 1;
  }
  Language language = 3;
}

// Conceptually cleaner to define subtypes for e.g. Developer
// This does disconnect e.g. Fn from Developer
message FnPackage {
  Fn fn = 1;
  Developer developer = 2;
  Metadata metadata = 3;
}

message Metadata {
  Developer developer = 1;
  repeated string keywords = 2;
}

// Any is a way to provide polymorphism
// Since Fn signatures are arbitrary types
message Signature {
  google.protobuf.Any params = 1;
  google.protobuf.Any return = 2;
}

...

This is written entirely here and so there's no warranty that it will actually compile

DazWilkin
  • 32,823
  • 5
  • 47
  • 88