From b12e4f1bf66122be204b4e245bd47bd84ca19b23 Mon Sep 17 00:00:00 2001 From: David Steinberg Date: Mon, 6 Mar 2017 12:01:23 -0800 Subject: [PATCH] Peer service (#806) * Peer service Allows services to notify each other about their existence * Add peer service to protocol * Change URI to URL * Empty protos should still be able to pass --- .../ga4gh/schemas/ga4gh/peer_service_pb2.py | 339 ++++++++++++++++++ python/ga4gh/schemas/protocol.py | 1 + src/main/proto/ga4gh/peer_service.proto | 142 ++++++++ tests/test_protocol.py | 3 +- 4 files changed, 484 insertions(+), 1 deletion(-) create mode 100644 python/ga4gh/schemas/ga4gh/peer_service_pb2.py create mode 100644 src/main/proto/ga4gh/peer_service.proto diff --git a/python/ga4gh/schemas/ga4gh/peer_service_pb2.py b/python/ga4gh/schemas/ga4gh/peer_service_pb2.py new file mode 100644 index 00000000..8d84b7d4 --- /dev/null +++ b/python/ga4gh/schemas/ga4gh/peer_service_pb2.py @@ -0,0 +1,339 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ga4gh/schemas/ga4gh/peer_service.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from ga4gh.schemas.ga4gh import common_pb2 as ga4gh_dot_schemas_dot_ga4gh_dot_common__pb2 +from ga4gh.schemas.google.api import annotations_pb2 as ga4gh_dot_schemas_dot_google_dot_api_dot_annotations__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='ga4gh/schemas/ga4gh/peer_service.proto', + package='ga4gh.schemas.ga4gh', + syntax='proto3', + serialized_pb=_b('\n&ga4gh/schemas/ga4gh/peer_service.proto\x12\x13ga4gh.schemas.ga4gh\x1a ga4gh/schemas/ga4gh/common.proto\x1a*ga4gh/schemas/google/api/annotations.proto\"9\n\x10ListPeersRequest\x12\x11\n\tpage_size\x18\x01 \x01(\x05\x12\x12\n\npage_token\x18\x02 \x01(\t\"V\n\x11ListPeersResponse\x12(\n\x05peers\x18\x01 \x03(\x0b\x32\x19.ga4gh.schemas.ga4gh.Peer\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\">\n\x13\x41nnouncePeerRequest\x12\'\n\x04peer\x18\x01 \x01(\x0b\x32\x19.ga4gh.schemas.ga4gh.Peer\"\\\n\x14\x41nnouncePeerResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x33\n\nattributes\x18\x02 \x01(\x0b\x32\x1f.ga4gh.schemas.ga4gh.Attributes\"\x10\n\x0eGetInfoRequest\"`\n\x0fGetInfoResponse\x12\x18\n\x10protocol_version\x18\x01 \x01(\t\x12\x33\n\nattributes\x18\x02 \x01(\x0b\x32\x1f.ga4gh.schemas.ga4gh.Attributes\"H\n\x04Peer\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x33\n\nattributes\x18\x02 \x01(\x0b\x32\x1f.ga4gh.schemas.ga4gh.Attributes2\xfa\x02\n\x0bPeerService\x12|\n\tListPeers\x12%.ga4gh.schemas.ga4gh.ListPeersRequest\x1a&.ga4gh.schemas.ga4gh.ListPeersResponse\" \x82\xd3\xe4\x93\x02\x1a\"\x15/v0.6.0a10/peers/list:\x01*\x12\x80\x01\n\x0c\x41nnouncePeer\x12(.ga4gh.schemas.ga4gh.AnnouncePeerRequest\x1a).ga4gh.schemas.ga4gh.AnnouncePeerResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\"\x13/v0.6.0a10/announce\x12j\n\x04Info\x12#.ga4gh.schemas.ga4gh.GetInfoRequest\x1a$.ga4gh.schemas.ga4gh.GetInfoResponse\"\x17\x82\xd3\xe4\x93\x02\x11\x12\x0f/v0.6.0a10/infob\x06proto3') + , + dependencies=[ga4gh_dot_schemas_dot_ga4gh_dot_common__pb2.DESCRIPTOR,ga4gh_dot_schemas_dot_google_dot_api_dot_annotations__pb2.DESCRIPTOR,]) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_LISTPEERSREQUEST = _descriptor.Descriptor( + name='ListPeersRequest', + full_name='ga4gh.schemas.ga4gh.ListPeersRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='page_size', full_name='ga4gh.schemas.ga4gh.ListPeersRequest.page_size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='page_token', full_name='ga4gh.schemas.ga4gh.ListPeersRequest.page_token', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=141, + serialized_end=198, +) + + +_LISTPEERSRESPONSE = _descriptor.Descriptor( + name='ListPeersResponse', + full_name='ga4gh.schemas.ga4gh.ListPeersResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='peers', full_name='ga4gh.schemas.ga4gh.ListPeersResponse.peers', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='next_page_token', full_name='ga4gh.schemas.ga4gh.ListPeersResponse.next_page_token', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=200, + serialized_end=286, +) + + +_ANNOUNCEPEERREQUEST = _descriptor.Descriptor( + name='AnnouncePeerRequest', + full_name='ga4gh.schemas.ga4gh.AnnouncePeerRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='peer', full_name='ga4gh.schemas.ga4gh.AnnouncePeerRequest.peer', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=288, + serialized_end=350, +) + + +_ANNOUNCEPEERRESPONSE = _descriptor.Descriptor( + name='AnnouncePeerResponse', + full_name='ga4gh.schemas.ga4gh.AnnouncePeerResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='success', full_name='ga4gh.schemas.ga4gh.AnnouncePeerResponse.success', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='attributes', full_name='ga4gh.schemas.ga4gh.AnnouncePeerResponse.attributes', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=352, + serialized_end=444, +) + + +_GETINFOREQUEST = _descriptor.Descriptor( + name='GetInfoRequest', + full_name='ga4gh.schemas.ga4gh.GetInfoRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=446, + serialized_end=462, +) + + +_GETINFORESPONSE = _descriptor.Descriptor( + name='GetInfoResponse', + full_name='ga4gh.schemas.ga4gh.GetInfoResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='protocol_version', full_name='ga4gh.schemas.ga4gh.GetInfoResponse.protocol_version', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='attributes', full_name='ga4gh.schemas.ga4gh.GetInfoResponse.attributes', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=464, + serialized_end=560, +) + + +_PEER = _descriptor.Descriptor( + name='Peer', + full_name='ga4gh.schemas.ga4gh.Peer', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='url', full_name='ga4gh.schemas.ga4gh.Peer.url', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='attributes', full_name='ga4gh.schemas.ga4gh.Peer.attributes', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=562, + serialized_end=634, +) + +_LISTPEERSRESPONSE.fields_by_name['peers'].message_type = _PEER +_ANNOUNCEPEERREQUEST.fields_by_name['peer'].message_type = _PEER +_ANNOUNCEPEERRESPONSE.fields_by_name['attributes'].message_type = ga4gh_dot_schemas_dot_ga4gh_dot_common__pb2._ATTRIBUTES +_GETINFORESPONSE.fields_by_name['attributes'].message_type = ga4gh_dot_schemas_dot_ga4gh_dot_common__pb2._ATTRIBUTES +_PEER.fields_by_name['attributes'].message_type = ga4gh_dot_schemas_dot_ga4gh_dot_common__pb2._ATTRIBUTES +DESCRIPTOR.message_types_by_name['ListPeersRequest'] = _LISTPEERSREQUEST +DESCRIPTOR.message_types_by_name['ListPeersResponse'] = _LISTPEERSRESPONSE +DESCRIPTOR.message_types_by_name['AnnouncePeerRequest'] = _ANNOUNCEPEERREQUEST +DESCRIPTOR.message_types_by_name['AnnouncePeerResponse'] = _ANNOUNCEPEERRESPONSE +DESCRIPTOR.message_types_by_name['GetInfoRequest'] = _GETINFOREQUEST +DESCRIPTOR.message_types_by_name['GetInfoResponse'] = _GETINFORESPONSE +DESCRIPTOR.message_types_by_name['Peer'] = _PEER + +ListPeersRequest = _reflection.GeneratedProtocolMessageType('ListPeersRequest', (_message.Message,), dict( + DESCRIPTOR = _LISTPEERSREQUEST, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.ListPeersRequest) + )) +_sym_db.RegisterMessage(ListPeersRequest) + +ListPeersResponse = _reflection.GeneratedProtocolMessageType('ListPeersResponse', (_message.Message,), dict( + DESCRIPTOR = _LISTPEERSRESPONSE, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.ListPeersResponse) + )) +_sym_db.RegisterMessage(ListPeersResponse) + +AnnouncePeerRequest = _reflection.GeneratedProtocolMessageType('AnnouncePeerRequest', (_message.Message,), dict( + DESCRIPTOR = _ANNOUNCEPEERREQUEST, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.AnnouncePeerRequest) + )) +_sym_db.RegisterMessage(AnnouncePeerRequest) + +AnnouncePeerResponse = _reflection.GeneratedProtocolMessageType('AnnouncePeerResponse', (_message.Message,), dict( + DESCRIPTOR = _ANNOUNCEPEERRESPONSE, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.AnnouncePeerResponse) + )) +_sym_db.RegisterMessage(AnnouncePeerResponse) + +GetInfoRequest = _reflection.GeneratedProtocolMessageType('GetInfoRequest', (_message.Message,), dict( + DESCRIPTOR = _GETINFOREQUEST, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.GetInfoRequest) + )) +_sym_db.RegisterMessage(GetInfoRequest) + +GetInfoResponse = _reflection.GeneratedProtocolMessageType('GetInfoResponse', (_message.Message,), dict( + DESCRIPTOR = _GETINFORESPONSE, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.GetInfoResponse) + )) +_sym_db.RegisterMessage(GetInfoResponse) + +Peer = _reflection.GeneratedProtocolMessageType('Peer', (_message.Message,), dict( + DESCRIPTOR = _PEER, + __module__ = 'ga4gh.schemas.ga4gh.peer_service_pb2' + # @@protoc_insertion_point(class_scope:ga4gh.schemas.ga4gh.Peer) + )) +_sym_db.RegisterMessage(Peer) + + +# @@protoc_insertion_point(module_scope) diff --git a/python/ga4gh/schemas/protocol.py b/python/ga4gh/schemas/protocol.py index 64ac5af8..b94d7b03 100644 --- a/python/ga4gh/schemas/protocol.py +++ b/python/ga4gh/schemas/protocol.py @@ -31,6 +31,7 @@ from ga4gh.genotype_phenotype_service_pb2 import * # noqa from ga4gh.rna_quantification_pb2 import * # noqa from ga4gh.rna_quantification_service_pb2 import * # noqa +from ga4gh.peer_service_pb2 import * # noqa import ga4gh.common_pb2 as common diff --git a/src/main/proto/ga4gh/peer_service.proto b/src/main/proto/ga4gh/peer_service.proto new file mode 100644 index 00000000..67c3e0a2 --- /dev/null +++ b/src/main/proto/ga4gh/peer_service.proto @@ -0,0 +1,142 @@ +syntax = "proto3"; + +package ga4gh; + +import "ga4gh/common.proto"; +import "google/api/annotations.proto"; + +/* +This interface provides a peer listing functionality for servers. +Clients can create listings of GA4GH services by requesting peer +lists from known network participants. + +Network membership is voluntary, in that each peer is free to +manage its peer list. +*/ + +service PeerService { + // Gets a list of `Peer` messages that are being managed by the + // service. If the peer list becomes very long it is spread + // across multiple pages. + // + // `POST /peers/list` must accept a JSON version of + // `ListPeersRequest` as the post body and will return a JSON + // version of `ListPeersResponse`. + rpc ListPeers(ListPeersRequest) + returns (ListPeersResponse) { + option (google.api.http) = { + post: "/v0.6.0a10/peers/list" + body: "*" + }; + }; + + // Allows a client to notify a service of a potential peer. + // Services are expected to log these requests and implement + // policies for adding peers to their peer lists as desired. + // + // The AnnouncePeerResponse only notifies the requester whether + // the request was valid. To find if their announce request + // has been accepted they must make a `ListPeersRequest`. + // `POST /peers/announce` must accept a JSON version of + // `AnnouncePeerRequest` as the post body and will return a JSON + // version of `AnnouncePeerResponse`. + rpc AnnouncePeer(AnnouncePeerRequest) + returns (AnnouncePeerResponse) { + option (google.api.http) = { + post: "/v0.6.0a10/announce" + }; + }; + + // Provides peers with a way to identify the protocol version + // of a peer. Other information describing the service can + // be included in the `info` field of the `GetInfoResponse`. + // + // `GET /info` will return a JSON version of `GetInfoResponse`. + // and does not require any parameters. + rpc Info(GetInfoRequest) + returns (GetInfoResponse) { + option (google.api.http) = { + get: "/v0.6.0a10/info" + }; + }; + +} + +// This request maps to the body of `POST /peers/list`. +message ListPeersRequest { + // Specifies the maximum number of results to return in a single page. If + // unspecified, a system default will be used. + int32 page_size = 1; + + // The continuation token, which is used to page through large result sets. + // To get the next page of results, set this parameter to the value of + // `next_page_token` from the previous response. + string page_token = 2; +} + +// This is the response from `POST /peers/list` expressed as +// JSON. +message ListPeersResponse { + // The list of `Peer` messages presented by the server. They + // are not expected in any particular order. + repeated Peer peers = 1; + + // The continuation token, which is used to page through large result sets. + // Provide this value in a subsequent request to return the next page of + // results. This field will be empty if there aren't any additional results. + string next_page_token = 2; +} + +// This is the request sent to `POST /peers/announce`. +message AnnouncePeerRequest { + // This message contains information that can be used to connect + // to a possible peer. + Peer peer = 1; +} + +// This is the response from `POST /peers/announce`. +message AnnouncePeerResponse { + // This message notifies the client whether the AnnouncePeerRequest + // was well formed. + bool success = 1; + + // Other information regarding an AnnouncePeerRequest can be sent in + // the attributes field. + Attributes attributes = 2; +} + +message GetInfoRequest { + +} + +// This is the response from `GET /info`. +message GetInfoResponse { + // The string of the protocol version offered by the service. For + // example, "0.6.0a10". + string protocol_version = 1; + + // A map of additional information about the service can be included. + Attributes attributes = 2; +} + +// Peers allow clients to represent services to each other so +// ad-hoc networks can be easily constructed. +message Peer { + // This is the base URL where the service can be accessed from. It is + // expected to be fully formed and to include the port number if the + // port in use is not standard (http 80, https 443). + // + // For example, the peer at 1kgenomes would appear as: + // http://1kgenomes.ga4gh.org + // + // Trailing slashes should be ignored when constructing client + // requests based on this peer, and so shouldn't be included. + // + // This example shows a peer that has specified both a base path + // and a port. + // http://myapp.mycloudservice.com:8080/data/ga4gh + string url = 1; + + // A map of additional information about the service can be included. + Attributes attributes = 2; +} diff --git a/tests/test_protocol.py b/tests/test_protocol.py index dab58136..b3a71ab3 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -29,7 +29,8 @@ class TestProtocol(unittest.TestCase): def testGetValueListName(self): for clazz in protocol.getProtocolClasses(): - self.assertIsInstance(protocol.getValueListName(clazz), str) + if len(clazz.DESCRIPTOR.fields_by_number) > 0: + self.assertIsInstance(protocol.getValueListName(clazz), str) def testConvertDatetime(self): datetime_ = datetime.datetime.fromordinal(1234567)