Skip to content

Commit

Permalink
uDiscovery redesign (#242)
Browse files Browse the repository at this point in the history
* uDiscovery redesign

The following PR is a reboot of uDiscovery to be able to take all the lessons learned from existing implementations in production.

#137

---------

Co-authored-by: Kai Hudalla <sophokles.kh@gmail.com>
  • Loading branch information
stevenhartley and sophokles73 authored Nov 14, 2024
1 parent 4835ba1 commit d5091c4
Show file tree
Hide file tree
Showing 20 changed files with 1,103 additions and 1,925 deletions.
350 changes: 55 additions & 295 deletions up-core-api/uprotocol/core/udiscovery/v3/udiscovery.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
syntax = "proto3";

package uprotocol.core.udiscovery.v3;
import "google/protobuf/timestamp.proto";
import "uprotocol/uoptions.proto";
import "uprotocol/v1/uri.proto";

Expand All @@ -25,332 +24,93 @@ option java_multiple_files = true;
// Enables generation of generic service attributes in C++
option cc_generic_services = true;

// Platform USE Discovery Service Interface
// Client facing APIs to the uProtocol Discovery Service.
//
// The uDiscovery service is used to store information about devices and services namely their
// addresses (in URI format), and properties of topics. This API is used by clients (uEs) to
// access the information within the database. Clients talk to their local uDiscovery service
// which, if unable to find what it is looking for, queries the central database to find the information.
// The UDiscovery service instances form a hierarchy where the local uDiscovery service is the first point of contact
// and the domain level service (for information about devices within a domain), and then central service
// is the last point of contact.
//
// NOTE: the internal uDiscovery communication protocol (for how data is replicated between the local, domain,
// and central) is NOT covered in this interface as this is ONLY the client APIs.
service uDiscovery {
option (uprotocol.service_name) = "core.udiscovery"; // Service name
option (uprotocol.service_version_major) = 3;
option (uprotocol.service_version_minor) = 0;
option (uprotocol.service_id) = 1;

// uDiscovery Node Change Notification that sends the Update message
option (uprotocol.notification_topic) = {
id: 0x8000,
name: "NodeChange",
message: "Notification"
};



// Used by any uProtocol application or service to find service instances
// locations. The passed UUri contains valid UEntity, UResource, and UAuthority information
// for a query.
// What is returned is a list of UUris that match the query.
// If lookup fails, the response will be a UStatus with
// 2. code.NOT_FOUND: No matching UUris were found
// 3. code.INVALID_ARGUMENT: The passed UUri is invalid
// 4. code.PERMISSION_DENIED: The caller does not have permission to perform the query
rpc LookupUri(LookupUriRequest) returns (LookupUriResponse) {
// Find Services.
//
// Discover authorities, instances, and versions of services based on the passed URI.
rpc FindServices(FindServicesRequest) returns (FindServicesResponse) {
option (uprotocol.method_id) = 1;
}

// Update a node in the database.
// **NOTE:** You MUST have write permission to the node in the database
rpc UpdateNode(UpdateNodeRequest) returns (UpdateNodeResponse) {
option (uprotocol.method_id) = 2;
}

// Query the database to find Node(s). What is passed is the search criterial in
// the FindNodeRequest message. the returned FindNodeResponse contains the
// resulting query node(s) and their child nodes. Below are some example queries:
// 1. uDomain: `//*.device/`
// 2. uDevice: `//device`
// 3. uService: `//device.domain/body.access/`
// 4. uResource: `//device.domain/body.access//door.door_front_left`
// **NOTE:** You MUST have read permission to the node in the database
rpc FindNodes(FindNodesRequest) returns (FindNodesResponse) {
option (uprotocol.method_id) = 3;
}

// Query the database to fetch a list of 1 or more properties for a given node.
rpc FindNodeProperties(FindNodePropertiesRequest) returns (FindNodePropertiesResponse) {
option (uprotocol.method_id) = 4;
}

// Remove one or more nodes (and all its children nodes) in the database.
// **NOTE:** You MUST have write permission to the deleted all the nodes passed,
// all the children nodes, as well as write permission to the parent otherwise
// you will get PERMISSION_DENIED and no nodes will be deleted.
rpc DeleteNodes(DeleteNodesRequest) returns (DeleteNodesResponse) {
option (uprotocol.method_id) = 5;
}


// Add one of more nodes to a parent node. If one of the nodes already exists, this RPC will fail
// with a UStatus containing an ALREADY_EXISTS UCode and none of the nodes shall be added to the parent.
// **NOTE:** You MUST have write permission to the parent node
rpc AddNodes(AddNodesRequest) returns (AddNodesResponse) {
option (uprotocol.method_id) = 6;
}


// Update a property in a node
// **NOTE:** You MUST have write permission to the node who's property you
// are updating
rpc UpdateProperty(UpdatePropertyRequest) returns (UpdatePropertyResponse) {
option (uprotocol.method_id) = 7;
}


// Register to receive Notifications to changes to one of more Nodes. The changes
// are published on the notification topic: '/core.udiscovery/3/nodes#Notification'
rpc RegisterForNotifications(NotificationsRequest) returns (NotificationsResponse) {
option (uprotocol.method_id) = 8;
}


// Unregister for Node change notifications
rpc UnregisterForNotifications(NotificationsRequest) returns (NotificationsResponse) {
option (uprotocol.method_id) = 9;
}


// Resolve a UUri filling in the missing names/numbers from the Discovery database.
// If the resolution was successful, the resolved UUri containing names and numbers
// is returned.
// If resolving fails, the response will be a UStatus with
// - code.NOT_FOUND: Unable to find the missing names or numbers for the passed UUri
// - code.INVALID_ARGUMENT: The passed UUri is invalid (missing names or numbers)
// - code.PERMISSION_DENIED: The caller does not have permission to perform the resolution
rpc ResolveUri(ResolveUriRequest) returns (ResolveUriResponse) {
option (uprotocol.method_id) = 10;
}
}


// Typedef for a node properties. A node property can be any one of the uProtocol ("u_") types
// defined below
message PropertyValue {
oneof attr {
bool u_boolean = 1; // Boolean
int32 u_integer = 2; // Integer
string u_string = 3; // String
bytes u_bytes = 4; // Raw Bytes
string u_uri = 5; // A URI
google.protobuf.Timestamp u_timestamp = 6; // Timestamp
double u_double = 7; // Double
int64 u_integer_64 = 8; // 64 bit Integer
// Get information about one or more topics that is being served by a service
//
// The API is used to fetch metadata about one or more topics (depending on what is passed in the UUri).
// Wildcard ue_id and/or resource_id can be used to fetch multiple metadata for multiple topics.
// The UServiceTopic stores topic metadata such as the permission level, message name, payload format
// (how the message is encoded) and more.
//
rpc GetServiceTopics(GetServiceTopicsRequest) returns (GetServiceTopicsResponse) {
option (uprotocol.method_id) = 2;
}
}


// Node can be domain, device, service, resource, method, etc...
message Node {
// uProtocol long form URI pointing to this node
string uri = 1;

// List of child nodes under this node
repeated Node nodes = 2;

// List of node properties
map <string, PropertyValue> properties = 3;

// The node type
Type type = 4;


// What is the uThing (stored in Node) type. This is used to more easily
// identify the Node rather than parsing from uri and inferring the type
enum Type {
UNSPECIFIED = 0; // Unspecified node type
DOMAIN = 1; // uDomain
DEVICE = 2; // uDevice
ENTITY = 3; // uEntity (uE)
VERSION = 9; // uEntity version
TOPIC = 4; // uE Topic
METHOD = 5; // uE Method
MESSAGE = 6; // uE Message
RESOURCE = 7; // uE Resource
USER = 8; // User Information
}
}


// Message passed to the UpdateNode() RPC call
message UpdateNodeRequest {
// Node to be updated in the database
Node node = 1;

// Time-to-live: How long (ms) the information should be live for in the database.
// -1 means lives forever
optional int32 ttl = 2;
}

// The (empty) response message representing the successful execution of the UpdateNode operation.
message UpdateNodeResponse {}
// Request to get information about a topic
// Example might be to get information about a specific topic, all topics for a service (using wildcard resource_id),
// or all topics for all services for a given device (using wildcard ue_id and resource_id)
message GetServiceTopicsRequest {
// The URI of the topics that we would like to query.
uprotocol.v1.UUri topic = 1;

// Delete one or more nodes request
message DeleteNodesRequest {
repeated string uris = 1; // uProtocol formatted URI
// Recursively search up to the root node of the uDiscovery tree
bool recursive = 2;
}

// The (empty) response message representing the successful execution of the DeleteNodes operation.
message DeleteNodesResponse {}

// FindNodesRequest passed to FindNodes()
message FindNodesRequest {
// uProtocol formatted URI for the node to search, ex. '//vcu.VIN/core.app.hartley'
// shall return the 'core.app.hartley' node
string uri = 1;

// How deep in the node tree should the results return. A value of -1 or
// the field is not present means all child nodes are returned, value of 0
// returns only the parent node, any other value is the depth of child nodes
optional int32 depth = 2;
// Response to GetTopicInfoRequest
message GetServiceTopicsResponse {
repeated ServiceTopicInfo topics = 1;
}


// FindNodesResponse that is returned from the FindNodes() API
message FindNodesResponse {

// List of node information
repeated Node nodes = 1;

// Time-to-live: How long (ms) the information should be live for in the database.
// -1 means lives forever
optional int32 ttl = 2;
}


// Find 1 or more properties for a given node passed to FindNodeProperties()
message FindNodePropertiesRequest {
// the uri for the node in the database
string uri = 1;

// List of 1 or more properties names to fetch from the database
// **NOTE:** When this is not populated with any property name, all properties are returned
repeated string properties = 2;
}


// Returned from FindNodeProperties()
message FindNodePropertiesResponse {
// a list of property name/value pairs
map <string, PropertyValue> properties = 1;
}


// Passed to AddNodes() API containing the parent node URI and a list of nodes
message AddNodesRequest {
// the URI of the parent node that you would like to add nodes to
string parent_uri = 1;

// One or more nodes that you would like to add to the parent node.
// **NOTE:** The node uri field MAY be unqualified meaning it does not include the parent_uri
// authority & path). Support for unqualified URIs allows for the same node to be inserted into
// multiple parent nodes (ex. installing a uE node to multiple uDevice parent nodes)
repeated Node nodes = 2;
}

// The (empty) response message representing the successful execution of the AddNodes operation.
message AddNodesResponse {}

// Message passed to UpdateProperty() to update a property in a Node
message UpdatePropertyRequest {
// the uri for the node whos property will be updated
string uri = 1;

// The name of the property that is to be updated
string property = 2;

// The value to set in the property
PropertyValue value = 3;
}

// The (empty) response message representing the successful execution of the UpdateProperty operation.
message UpdatePropertyResponse {}

// Node Change Notification Message.
// When uEs call RegisterForNotifications(), a Notification message is sent when the node either
// changes, added, or removed.
message Notification {
// The URI to the node that has changed
string uri = 1;

// the URI of the parent node (if it was affected)
optional string parent_uri = 2;

// The operation performed on said Node
Operation operation = 3;

// Operation
enum Operation {
INVALID = 0; // Invalid
UPDATE = 1; // Updated
ADD = 2; // Added to the parent
REMOVE = 3; // Removed
}

// Time-to-live: How long (ms) the information should be live for in the database.
// A value of -1 means lives forever.
optional int32 ttl = 4;

// uDiscovery resource that it serves (per SDV-202 definition): database
enum Resources { nodes = 0; }
}


// Observer Identification Information
message ObserverInfo {
// Fully qualified URI for the Observer who is registering to receive the
// notifications from uDiscovery ex. `//vcu.VIN/com.gm.app.hartley`
string uri = 1;
}


// Passed to the RegisterForNotifications() and UnregisterForNotifications()
// APIs this message includes the list of one or more node addresses we would like
// to receive updates for as well as information about the caller so the notification
// can be routed to the right destination.
message NotificationsRequest {
// A list of one or more Node URIs to receive notifications for
repeated string uris = 1;

// Observer's identification information
ObserverInfo observer = 2;
// Message that stores the metadata about a topic and the topic URI so
// it can be passed back in GetServiceTopicsResponse.
message ServiceTopicInfo {
// The URI of the topic
uprotocol.v1.UUri topic = 1;

// The topic metadata
UServiceTopic info = 2;

// How deep in the node tree should the notifications be sent for. A value of -1 or if
// the field is not present in the message, signifies that changes to any child nodes will trigger
// a Notification event. A value of 0 returns only the parent node. Any other value specified is
// the depth of child nodes to receive notifications for.
optional int32 depth = 3;
// How long the topic metadata is valid for in seconds.
// If the metadata is older than this value, the client SHOULD re-fetch the metadata.
// Metadata lives forever if the value is 0 (default).
uint32 ttl = 3;
}

// The (empty) response message representing the successful execution of the
// RegisterForNotifications and UnregisterForNotifications operations.
message NotificationsResponse {}

// Request message passed to ResolveUri() API to resolve the missing names or numbers.
message ResolveUriRequest {
// The URI to resolve
// The URI containing the service that we would like to find.
message FindServicesRequest {
// The Uri to look up
uprotocol.v1.UUri uri = 1;
}


// Response message returned from ResolveUri() API containing the resolved UUri
message ResolveUriResponse {
// Resolved URI
uprotocol.v1.UUri uri = 1;
// Recursively search up to the root node of the uDiscovery tree
bool recursive = 2;
}

// Request message passed to LookupUri() API.
message LookupUriRequest {
// The Uri to look up
uprotocol.v1.UUri uri = 1;
}

// Return value from LookupUri() API that contains the batch of Uris for the
// lookup
message LookupUriResponse {
// Return value from FindService API that contains a batch of all services that match the search criteria
message FindServicesResponse {
// Batch of URIs
uprotocol.v1.UUriBatch uris = 1;
}
Loading

0 comments on commit d5091c4

Please sign in to comment.