From 14aae3d70ec9aac0a07b38c517913eb778c0613b Mon Sep 17 00:00:00 2001 From: Randy Date: Wed, 29 Nov 2023 09:17:23 -0500 Subject: [PATCH] Added the outgoing webhook methods to scheduler.proto along with the corresponding mocked methods in AsyncScheduler. --- libs/gl-client-py/glclient/__init__.py | 18 +++ libs/gl-client-py/glclient/glclient.pyi | 5 + libs/gl-client-py/glclient/greenlight_pb2.py | 1 + libs/gl-client-py/glclient/scheduler_pb2.py | 107 +++++++------ libs/gl-client-py/glclient/scheduler_pb2.pyi | 142 +++++++++++++++++- .../glclient/scheduler_pb2_grpc.py | 132 ++++++++++++++++ libs/gl-client-py/src/scheduler.rs | 35 +++++ libs/gl-client/src/scheduler.rs | 48 ++++++ libs/gl-testing/gltesting/scheduler.py | 44 +++++- .../tests/test_outgoing_webhooks.py | 70 +++++++++ libs/proto/scheduler.proto | 45 ++++++ tools/glcli/glcli/cli.py | 2 +- 12 files changed, 601 insertions(+), 48 deletions(-) create mode 100644 libs/gl-testing/tests/test_outgoing_webhooks.py diff --git a/libs/gl-client-py/glclient/__init__.py b/libs/gl-client-py/glclient/__init__.py index 8374f4d47..b99751ea6 100644 --- a/libs/gl-client-py/glclient/__init__.py +++ b/libs/gl-client-py/glclient/__init__.py @@ -100,6 +100,24 @@ def node(self) -> "Node": def get_invite_codes(self) -> schedpb.ListInviteCodesResponse: cls = schedpb.ListInviteCodesResponse return cls.FromString(bytes(self.inner.get_invite_codes())) + + def add_outgoing_webhook(self, uri: str) -> schedpb.AddOutgoingWebhookResponse: + res = self.inner.add_outgoing_webhook(uri) + return schedpb.AddOutgoingWebhookResponse.FromString(bytes(res)) + + def list_outgoing_webhooks(self) -> schedpb.ListOutgoingWebhooksResponse: + res = self.inner.list_outgoing_webhooks() + return schedpb.ListOutgoingWebhooksResponse.FromString(bytes(res)) + + def delete_outgoing_webhook(self, webhook_id: int) -> None: + res = self.inner.delete_outgoing_webhooks([webhook_id]) + + def delete_outgoing_webhooks(self, webhook_ids: List[int]) -> None: + res = self.inner.delete_outgoing_webhooks(webhook_ids) + + def rotate_outgoing_webhook_secret(self, webhook_id: int) -> schedpb.WebhookSecretResponse: + res = self.inner.rotate_outgoing_webhook_secret(webhook_id) + return schedpb.WebhookSecretResponse.FromString(bytes(res)) class Node(object): diff --git a/libs/gl-client-py/glclient/glclient.pyi b/libs/gl-client-py/glclient/glclient.pyi index fdc08e341..8006c2800 100644 --- a/libs/gl-client-py/glclient/glclient.pyi +++ b/libs/gl-client-py/glclient/glclient.pyi @@ -40,6 +40,11 @@ class Scheduler: def schedule(self) -> bytes: ... def export_node(self) -> bytes: ... def get_invite_codes(self) -> bytes: ... + def add_outgoing_webhook(self, uri: str) -> bytes: ... + def list_outgoing_webhooks(self) -> bytes: ... + def delete_outgoing_webhook(self, webhook_id: int) -> bytes: ... + def delete_outgoing_webhooks(self, webhook_ids: List[int]) -> bytes: ... + def rotate_outgoing_webhook_secret(self, webhook_id: int) -> bytes: ... class Node: diff --git a/libs/gl-client-py/glclient/greenlight_pb2.py b/libs/gl-client-py/glclient/greenlight_pb2.py index 05d615c6d..677b56a2d 100644 --- a/libs/gl-client-py/glclient/greenlight_pb2.py +++ b/libs/gl-client-py/glclient/greenlight_pb2.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: greenlight.proto +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool diff --git a/libs/gl-client-py/glclient/scheduler_pb2.py b/libs/gl-client-py/glclient/scheduler_pb2.py index 3e0d84a25..e5ac908f1 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2.py +++ b/libs/gl-client-py/glclient/scheduler_pb2.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: scheduler.proto +# Protobuf Python Version: 4.25.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -14,55 +15,71 @@ from . import greenlight_pb2 as greenlight__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fscheduler.proto\x12\tscheduler\x1a\x10greenlight.proto\"M\n\x10\x43hallengeRequest\x12(\n\x05scope\x18\x01 \x01(\x0e\x32\x19.scheduler.ChallengeScope\x12\x0f\n\x07node_id\x18\x02 \x01(\x0c\"&\n\x11\x43hallengeResponse\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\"\xea\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x11\n\tbip32_key\x18\x02 \x01(\x0c\x12\x0f\n\x07network\x18\x04 \x01(\t\x12\x11\n\tchallenge\x18\x05 \x01(\x0c\x12\x11\n\tsignature\x18\x06 \x01(\x0c\x12\x14\n\x0csigner_proto\x18\x07 \x01(\t\x12\x10\n\x08init_msg\x18\x08 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\x12\x13\n\x0binvite_code\x18\n \x01(\t\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"?\n\x14RegistrationResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\"\"\n\x0fScheduleRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"0\n\x0fNodeInfoRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0c\n\x04wait\x18\x02 \x01(\x08\"5\n\x10NodeInfoResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x10\n\x08grpc_uri\x18\x02 \x01(\t\"U\n\x0fRecoveryRequest\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07node_id\x18\x03 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\";\n\x10RecoveryResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\"m\n\x0eUpgradeRequest\x12\x16\n\x0esigner_version\x18\x01 \x01(\t\x12\x13\n\x07initmsg\x18\x02 \x01(\x0c\x42\x02\x18\x01\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"&\n\x0fUpgradeResponse\x12\x13\n\x0bold_version\x18\x01 \x01(\t\"3\n\x0eStartupMessage\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x10\n\x08response\x18\x02 \x01(\x0c\"\x18\n\x16ListInviteCodesRequest\"J\n\x17ListInviteCodesResponse\x12/\n\x10invite_code_list\x18\x01 \x03(\x0b\x32\x15.scheduler.InviteCode\"/\n\nInviteCode\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x13\n\x0bis_redeemed\x18\x02 \x01(\x08\"\x13\n\x11\x45xportNodeRequest\"!\n\x12\x45xportNodeResponse\x12\x0b\n\x03url\x18\x01 \x01(\t\"\\\n\x0fSignerRejection\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\'\n\x07request\x18\x02 \x01(\x0b\x32\x16.greenlight.HsmRequest\x12\x13\n\x0bgit_version\x18\x03 \x01(\t*+\n\x0e\x43hallengeScope\x12\x0c\n\x08REGISTER\x10\x00\x12\x0b\n\x07RECOVER\x10\x01\x32\xf0\x04\n\tScheduler\x12M\n\x08Register\x12\x1e.scheduler.RegistrationRequest\x1a\x1f.scheduler.RegistrationResponse\"\x00\x12\x44\n\x07Recover\x12\x1a.scheduler.RecoveryRequest\x1a\x1b.scheduler.RecoveryResponse\"\x00\x12K\n\x0cGetChallenge\x12\x1b.scheduler.ChallengeRequest\x1a\x1c.scheduler.ChallengeResponse\"\x00\x12\x45\n\x08Schedule\x12\x1a.scheduler.ScheduleRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12H\n\x0bGetNodeInfo\x12\x1a.scheduler.NodeInfoRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12G\n\x0cMaybeUpgrade\x12\x19.scheduler.UpgradeRequest\x1a\x1a.scheduler.UpgradeResponse\"\x00\x12Z\n\x0fListInviteCodes\x12!.scheduler.ListInviteCodesRequest\x1a\".scheduler.ListInviteCodesResponse\"\x00\x12K\n\nExportNode\x12\x1c.scheduler.ExportNodeRequest\x1a\x1d.scheduler.ExportNodeResponse\"\x00\x32Q\n\x05\x44\x65\x62ug\x12H\n\x15ReportSignerRejection\x12\x1a.scheduler.SignerRejection\x1a\x11.greenlight.Empty\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fscheduler.proto\x12\tscheduler\x1a\x10greenlight.proto\"9\n\x19\x41\x64\x64OutgoingWebhookRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\"8\n\x1a\x41\x64\x64OutgoingWebhookResponse\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0e\n\x06secret\x18\x02 \x01(\t\".\n\x1bListOutgoingWebhooksRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"\"\n\x07Webhook\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0b\n\x03uri\x18\x02 \x01(\t\"M\n\x1cListOutgoingWebhooksResponse\x12-\n\x11outgoing_webhooks\x18\x01 \x03(\x0b\x32\x12.scheduler.Webhook\"=\n\x1d\x44\x65leteOutgoingWebhooksRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03ids\x18\x02 \x03(\x03\"I\n\"RotateOutgoingWebhookSecretRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x12\n\nwebhook_id\x18\x02 \x01(\x03\"\'\n\x15WebhookSecretResponse\x12\x0e\n\x06secret\x18\x01 \x01(\t\"M\n\x10\x43hallengeRequest\x12(\n\x05scope\x18\x01 \x01(\x0e\x32\x19.scheduler.ChallengeScope\x12\x0f\n\x07node_id\x18\x02 \x01(\x0c\"&\n\x11\x43hallengeResponse\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\"\xea\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x11\n\tbip32_key\x18\x02 \x01(\x0c\x12\x0f\n\x07network\x18\x04 \x01(\t\x12\x11\n\tchallenge\x18\x05 \x01(\x0c\x12\x11\n\tsignature\x18\x06 \x01(\x0c\x12\x14\n\x0csigner_proto\x18\x07 \x01(\t\x12\x10\n\x08init_msg\x18\x08 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\x12\x13\n\x0binvite_code\x18\n \x01(\t\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"?\n\x14RegistrationResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\"\"\n\x0fScheduleRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\"0\n\x0fNodeInfoRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0c\n\x04wait\x18\x02 \x01(\x08\"I\n\x10NodeInfoResponse\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x10\n\x08grpc_uri\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\x04\"U\n\x0fRecoveryRequest\x12\x11\n\tchallenge\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07node_id\x18\x03 \x01(\x0c\x12\x0b\n\x03\x63sr\x18\t \x01(\x0c\";\n\x10RecoveryResponse\x12\x13\n\x0b\x64\x65vice_cert\x18\x01 \x01(\t\x12\x12\n\ndevice_key\x18\x02 \x01(\t\"m\n\x0eUpgradeRequest\x12\x16\n\x0esigner_version\x18\x01 \x01(\t\x12\x13\n\x07initmsg\x18\x02 \x01(\x0c\x42\x02\x18\x01\x12.\n\x0bstartupmsgs\x18\x03 \x03(\x0b\x32\x19.scheduler.StartupMessage\"&\n\x0fUpgradeResponse\x12\x13\n\x0bold_version\x18\x01 \x01(\t\"3\n\x0eStartupMessage\x12\x0f\n\x07request\x18\x01 \x01(\x0c\x12\x10\n\x08response\x18\x02 \x01(\x0c\"\x18\n\x16ListInviteCodesRequest\"J\n\x17ListInviteCodesResponse\x12/\n\x10invite_code_list\x18\x01 \x03(\x0b\x32\x15.scheduler.InviteCode\"/\n\nInviteCode\x12\x0c\n\x04\x63ode\x18\x01 \x01(\t\x12\x13\n\x0bis_redeemed\x18\x02 \x01(\x08\"\x13\n\x11\x45xportNodeRequest\"!\n\x12\x45xportNodeResponse\x12\x0b\n\x03url\x18\x01 \x01(\t\"\\\n\x0fSignerRejection\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\'\n\x07request\x18\x02 \x01(\x0b\x32\x16.greenlight.HsmRequest\x12\x13\n\x0bgit_version\x18\x03 \x01(\t*+\n\x0e\x43hallengeScope\x12\x0c\n\x08REGISTER\x10\x00\x12\x0b\n\x07RECOVER\x10\x01\x32\x83\x08\n\tScheduler\x12M\n\x08Register\x12\x1e.scheduler.RegistrationRequest\x1a\x1f.scheduler.RegistrationResponse\"\x00\x12\x44\n\x07Recover\x12\x1a.scheduler.RecoveryRequest\x1a\x1b.scheduler.RecoveryResponse\"\x00\x12K\n\x0cGetChallenge\x12\x1b.scheduler.ChallengeRequest\x1a\x1c.scheduler.ChallengeResponse\"\x00\x12\x45\n\x08Schedule\x12\x1a.scheduler.ScheduleRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12H\n\x0bGetNodeInfo\x12\x1a.scheduler.NodeInfoRequest\x1a\x1b.scheduler.NodeInfoResponse\"\x00\x12G\n\x0cMaybeUpgrade\x12\x19.scheduler.UpgradeRequest\x1a\x1a.scheduler.UpgradeResponse\"\x00\x12Z\n\x0fListInviteCodes\x12!.scheduler.ListInviteCodesRequest\x1a\".scheduler.ListInviteCodesResponse\"\x00\x12K\n\nExportNode\x12\x1c.scheduler.ExportNodeRequest\x1a\x1d.scheduler.ExportNodeResponse\"\x00\x12\x63\n\x12\x41\x64\x64OutgoingWebhook\x12$.scheduler.AddOutgoingWebhookRequest\x1a%.scheduler.AddOutgoingWebhookResponse\"\x00\x12i\n\x14ListOutgoingWebhooks\x12&.scheduler.ListOutgoingWebhooksRequest\x1a\'.scheduler.ListOutgoingWebhooksResponse\"\x00\x12O\n\x0e\x44\x65leteWebhooks\x12(.scheduler.DeleteOutgoingWebhooksRequest\x1a\x11.greenlight.Empty\"\x00\x12p\n\x1bRotateOutgoingWebhookSecret\x12-.scheduler.RotateOutgoingWebhookSecretRequest\x1a .scheduler.WebhookSecretResponse\"\x00\x32Q\n\x05\x44\x65\x62ug\x12H\n\x15ReportSignerRejection\x12\x1a.scheduler.SignerRejection\x1a\x11.greenlight.Empty\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'scheduler_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _UPGRADEREQUEST.fields_by_name['initmsg']._options = None - _UPGRADEREQUEST.fields_by_name['initmsg']._serialized_options = b'\030\001' - _globals['_CHALLENGESCOPE']._serialized_start=1263 - _globals['_CHALLENGESCOPE']._serialized_end=1306 - _globals['_CHALLENGEREQUEST']._serialized_start=48 - _globals['_CHALLENGEREQUEST']._serialized_end=125 - _globals['_CHALLENGERESPONSE']._serialized_start=127 - _globals['_CHALLENGERESPONSE']._serialized_end=165 - _globals['_REGISTRATIONREQUEST']._serialized_start=168 - _globals['_REGISTRATIONREQUEST']._serialized_end=402 - _globals['_REGISTRATIONRESPONSE']._serialized_start=404 - _globals['_REGISTRATIONRESPONSE']._serialized_end=467 - _globals['_SCHEDULEREQUEST']._serialized_start=469 - _globals['_SCHEDULEREQUEST']._serialized_end=503 - _globals['_NODEINFOREQUEST']._serialized_start=505 - _globals['_NODEINFOREQUEST']._serialized_end=553 - _globals['_NODEINFORESPONSE']._serialized_start=555 - _globals['_NODEINFORESPONSE']._serialized_end=608 - _globals['_RECOVERYREQUEST']._serialized_start=610 - _globals['_RECOVERYREQUEST']._serialized_end=695 - _globals['_RECOVERYRESPONSE']._serialized_start=697 - _globals['_RECOVERYRESPONSE']._serialized_end=756 - _globals['_UPGRADEREQUEST']._serialized_start=758 - _globals['_UPGRADEREQUEST']._serialized_end=867 - _globals['_UPGRADERESPONSE']._serialized_start=869 - _globals['_UPGRADERESPONSE']._serialized_end=907 - _globals['_STARTUPMESSAGE']._serialized_start=909 - _globals['_STARTUPMESSAGE']._serialized_end=960 - _globals['_LISTINVITECODESREQUEST']._serialized_start=962 - _globals['_LISTINVITECODESREQUEST']._serialized_end=986 - _globals['_LISTINVITECODESRESPONSE']._serialized_start=988 - _globals['_LISTINVITECODESRESPONSE']._serialized_end=1062 - _globals['_INVITECODE']._serialized_start=1064 - _globals['_INVITECODE']._serialized_end=1111 - _globals['_EXPORTNODEREQUEST']._serialized_start=1113 - _globals['_EXPORTNODEREQUEST']._serialized_end=1132 - _globals['_EXPORTNODERESPONSE']._serialized_start=1134 - _globals['_EXPORTNODERESPONSE']._serialized_end=1167 - _globals['_SIGNERREJECTION']._serialized_start=1169 - _globals['_SIGNERREJECTION']._serialized_end=1261 - _globals['_SCHEDULER']._serialized_start=1309 - _globals['_SCHEDULER']._serialized_end=1933 - _globals['_DEBUG']._serialized_start=1935 - _globals['_DEBUG']._serialized_end=2016 + _globals['_UPGRADEREQUEST'].fields_by_name['initmsg']._options = None + _globals['_UPGRADEREQUEST'].fields_by_name['initmsg']._serialized_options = b'\030\001' + _globals['_CHALLENGESCOPE']._serialized_start=1742 + _globals['_CHALLENGESCOPE']._serialized_end=1785 + _globals['_ADDOUTGOINGWEBHOOKREQUEST']._serialized_start=48 + _globals['_ADDOUTGOINGWEBHOOKREQUEST']._serialized_end=105 + _globals['_ADDOUTGOINGWEBHOOKRESPONSE']._serialized_start=107 + _globals['_ADDOUTGOINGWEBHOOKRESPONSE']._serialized_end=163 + _globals['_LISTOUTGOINGWEBHOOKSREQUEST']._serialized_start=165 + _globals['_LISTOUTGOINGWEBHOOKSREQUEST']._serialized_end=211 + _globals['_WEBHOOK']._serialized_start=213 + _globals['_WEBHOOK']._serialized_end=247 + _globals['_LISTOUTGOINGWEBHOOKSRESPONSE']._serialized_start=249 + _globals['_LISTOUTGOINGWEBHOOKSRESPONSE']._serialized_end=326 + _globals['_DELETEOUTGOINGWEBHOOKSREQUEST']._serialized_start=328 + _globals['_DELETEOUTGOINGWEBHOOKSREQUEST']._serialized_end=389 + _globals['_ROTATEOUTGOINGWEBHOOKSECRETREQUEST']._serialized_start=391 + _globals['_ROTATEOUTGOINGWEBHOOKSECRETREQUEST']._serialized_end=464 + _globals['_WEBHOOKSECRETRESPONSE']._serialized_start=466 + _globals['_WEBHOOKSECRETRESPONSE']._serialized_end=505 + _globals['_CHALLENGEREQUEST']._serialized_start=507 + _globals['_CHALLENGEREQUEST']._serialized_end=584 + _globals['_CHALLENGERESPONSE']._serialized_start=586 + _globals['_CHALLENGERESPONSE']._serialized_end=624 + _globals['_REGISTRATIONREQUEST']._serialized_start=627 + _globals['_REGISTRATIONREQUEST']._serialized_end=861 + _globals['_REGISTRATIONRESPONSE']._serialized_start=863 + _globals['_REGISTRATIONRESPONSE']._serialized_end=926 + _globals['_SCHEDULEREQUEST']._serialized_start=928 + _globals['_SCHEDULEREQUEST']._serialized_end=962 + _globals['_NODEINFOREQUEST']._serialized_start=964 + _globals['_NODEINFOREQUEST']._serialized_end=1012 + _globals['_NODEINFORESPONSE']._serialized_start=1014 + _globals['_NODEINFORESPONSE']._serialized_end=1087 + _globals['_RECOVERYREQUEST']._serialized_start=1089 + _globals['_RECOVERYREQUEST']._serialized_end=1174 + _globals['_RECOVERYRESPONSE']._serialized_start=1176 + _globals['_RECOVERYRESPONSE']._serialized_end=1235 + _globals['_UPGRADEREQUEST']._serialized_start=1237 + _globals['_UPGRADEREQUEST']._serialized_end=1346 + _globals['_UPGRADERESPONSE']._serialized_start=1348 + _globals['_UPGRADERESPONSE']._serialized_end=1386 + _globals['_STARTUPMESSAGE']._serialized_start=1388 + _globals['_STARTUPMESSAGE']._serialized_end=1439 + _globals['_LISTINVITECODESREQUEST']._serialized_start=1441 + _globals['_LISTINVITECODESREQUEST']._serialized_end=1465 + _globals['_LISTINVITECODESRESPONSE']._serialized_start=1467 + _globals['_LISTINVITECODESRESPONSE']._serialized_end=1541 + _globals['_INVITECODE']._serialized_start=1543 + _globals['_INVITECODE']._serialized_end=1590 + _globals['_EXPORTNODEREQUEST']._serialized_start=1592 + _globals['_EXPORTNODEREQUEST']._serialized_end=1611 + _globals['_EXPORTNODERESPONSE']._serialized_start=1613 + _globals['_EXPORTNODERESPONSE']._serialized_end=1646 + _globals['_SIGNERREJECTION']._serialized_start=1648 + _globals['_SIGNERREJECTION']._serialized_end=1740 + _globals['_SCHEDULER']._serialized_start=1788 + _globals['_SCHEDULER']._serialized_end=2815 + _globals['_DEBUG']._serialized_start=2817 + _globals['_DEBUG']._serialized_end=2898 # @@protoc_insertion_point(module_scope) diff --git a/libs/gl-client-py/glclient/scheduler_pb2.pyi b/libs/gl-client-py/glclient/scheduler_pb2.pyi index 4d4126f27..706652858 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2.pyi +++ b/libs/gl-client-py/glclient/scheduler_pb2.pyi @@ -35,6 +35,143 @@ REGISTER: ChallengeScope.ValueType # 0 RECOVER: ChallengeScope.ValueType # 1 global___ChallengeScope = ChallengeScope +@typing_extensions.final +class AddOutgoingWebhookRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_ID_FIELD_NUMBER: builtins.int + URI_FIELD_NUMBER: builtins.int + node_id: builtins.bytes + uri: builtins.str + def __init__( + self, + *, + node_id: builtins.bytes = ..., + uri: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["node_id", b"node_id", "uri", b"uri"]) -> None: ... + +global___AddOutgoingWebhookRequest = AddOutgoingWebhookRequest + +@typing_extensions.final +class AddOutgoingWebhookResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + SECRET_FIELD_NUMBER: builtins.int + id: builtins.int + secret: builtins.str + def __init__( + self, + *, + id: builtins.int = ..., + secret: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "secret", b"secret"]) -> None: ... + +global___AddOutgoingWebhookResponse = AddOutgoingWebhookResponse + +@typing_extensions.final +class ListOutgoingWebhooksRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_ID_FIELD_NUMBER: builtins.int + node_id: builtins.bytes + def __init__( + self, + *, + node_id: builtins.bytes = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["node_id", b"node_id"]) -> None: ... + +global___ListOutgoingWebhooksRequest = ListOutgoingWebhooksRequest + +@typing_extensions.final +class Webhook(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + URI_FIELD_NUMBER: builtins.int + id: builtins.int + uri: builtins.str + def __init__( + self, + *, + id: builtins.int = ..., + uri: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "uri", b"uri"]) -> None: ... + +global___Webhook = Webhook + +@typing_extensions.final +class ListOutgoingWebhooksResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OUTGOING_WEBHOOKS_FIELD_NUMBER: builtins.int + @property + def outgoing_webhooks(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Webhook]: ... + def __init__( + self, + *, + outgoing_webhooks: collections.abc.Iterable[global___Webhook] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["outgoing_webhooks", b"outgoing_webhooks"]) -> None: ... + +global___ListOutgoingWebhooksResponse = ListOutgoingWebhooksResponse + +@typing_extensions.final +class DeleteOutgoingWebhooksRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_ID_FIELD_NUMBER: builtins.int + IDS_FIELD_NUMBER: builtins.int + node_id: builtins.bytes + @property + def ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: ... + def __init__( + self, + *, + node_id: builtins.bytes = ..., + ids: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ids", b"ids", "node_id", b"node_id"]) -> None: ... + +global___DeleteOutgoingWebhooksRequest = DeleteOutgoingWebhooksRequest + +@typing_extensions.final +class RotateOutgoingWebhookSecretRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NODE_ID_FIELD_NUMBER: builtins.int + WEBHOOK_ID_FIELD_NUMBER: builtins.int + node_id: builtins.bytes + webhook_id: builtins.int + def __init__( + self, + *, + node_id: builtins.bytes = ..., + webhook_id: builtins.int = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["node_id", b"node_id", "webhook_id", b"webhook_id"]) -> None: ... + +global___RotateOutgoingWebhookSecretRequest = RotateOutgoingWebhookSecretRequest + +@typing_extensions.final +class WebhookSecretResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SECRET_FIELD_NUMBER: builtins.int + secret: builtins.str + def __init__( + self, + *, + secret: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["secret", b"secret"]) -> None: ... + +global___WebhookSecretResponse = WebhookSecretResponse + @typing_extensions.final class ChallengeRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -224,15 +361,18 @@ class NodeInfoResponse(google.protobuf.message.Message): NODE_ID_FIELD_NUMBER: builtins.int GRPC_URI_FIELD_NUMBER: builtins.int + SESSION_ID_FIELD_NUMBER: builtins.int node_id: builtins.bytes grpc_uri: builtins.str + session_id: builtins.int def __init__( self, *, node_id: builtins.bytes = ..., grpc_uri: builtins.str = ..., + session_id: builtins.int = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["grpc_uri", b"grpc_uri", "node_id", b"node_id"]) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["grpc_uri", b"grpc_uri", "node_id", b"node_id", "session_id", b"session_id"]) -> None: ... global___NodeInfoResponse = NodeInfoResponse diff --git a/libs/gl-client-py/glclient/scheduler_pb2_grpc.py b/libs/gl-client-py/glclient/scheduler_pb2_grpc.py index 6f9332c6e..d33e5192c 100644 --- a/libs/gl-client-py/glclient/scheduler_pb2_grpc.py +++ b/libs/gl-client-py/glclient/scheduler_pb2_grpc.py @@ -88,6 +88,26 @@ def __init__(self, channel): request_serializer=scheduler__pb2.ExportNodeRequest.SerializeToString, response_deserializer=scheduler__pb2.ExportNodeResponse.FromString, ) + self.AddOutgoingWebhook = channel.unary_unary( + '/scheduler.Scheduler/AddOutgoingWebhook', + request_serializer=scheduler__pb2.AddOutgoingWebhookRequest.SerializeToString, + response_deserializer=scheduler__pb2.AddOutgoingWebhookResponse.FromString, + ) + self.ListOutgoingWebhooks = channel.unary_unary( + '/scheduler.Scheduler/ListOutgoingWebhooks', + request_serializer=scheduler__pb2.ListOutgoingWebhooksRequest.SerializeToString, + response_deserializer=scheduler__pb2.ListOutgoingWebhooksResponse.FromString, + ) + self.DeleteWebhooks = channel.unary_unary( + '/scheduler.Scheduler/DeleteWebhooks', + request_serializer=scheduler__pb2.DeleteOutgoingWebhooksRequest.SerializeToString, + response_deserializer=greenlight__pb2.Empty.FromString, + ) + self.RotateOutgoingWebhookSecret = channel.unary_unary( + '/scheduler.Scheduler/RotateOutgoingWebhookSecret', + request_serializer=scheduler__pb2.RotateOutgoingWebhookSecretRequest.SerializeToString, + response_deserializer=scheduler__pb2.WebhookSecretResponse.FromString, + ) class SchedulerServicer(object): @@ -270,6 +290,30 @@ def ExportNode(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def AddOutgoingWebhook(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListOutgoingWebhooks(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteWebhooks(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def RotateOutgoingWebhookSecret(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_SchedulerServicer_to_server(servicer, server): rpc_method_handlers = { @@ -313,6 +357,26 @@ def add_SchedulerServicer_to_server(servicer, server): request_deserializer=scheduler__pb2.ExportNodeRequest.FromString, response_serializer=scheduler__pb2.ExportNodeResponse.SerializeToString, ), + 'AddOutgoingWebhook': grpc.unary_unary_rpc_method_handler( + servicer.AddOutgoingWebhook, + request_deserializer=scheduler__pb2.AddOutgoingWebhookRequest.FromString, + response_serializer=scheduler__pb2.AddOutgoingWebhookResponse.SerializeToString, + ), + 'ListOutgoingWebhooks': grpc.unary_unary_rpc_method_handler( + servicer.ListOutgoingWebhooks, + request_deserializer=scheduler__pb2.ListOutgoingWebhooksRequest.FromString, + response_serializer=scheduler__pb2.ListOutgoingWebhooksResponse.SerializeToString, + ), + 'DeleteWebhooks': grpc.unary_unary_rpc_method_handler( + servicer.DeleteWebhooks, + request_deserializer=scheduler__pb2.DeleteOutgoingWebhooksRequest.FromString, + response_serializer=greenlight__pb2.Empty.SerializeToString, + ), + 'RotateOutgoingWebhookSecret': grpc.unary_unary_rpc_method_handler( + servicer.RotateOutgoingWebhookSecret, + request_deserializer=scheduler__pb2.RotateOutgoingWebhookSecretRequest.FromString, + response_serializer=scheduler__pb2.WebhookSecretResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'scheduler.Scheduler', rpc_method_handlers) @@ -492,6 +556,74 @@ def ExportNode(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def AddOutgoingWebhook(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/scheduler.Scheduler/AddOutgoingWebhook', + scheduler__pb2.AddOutgoingWebhookRequest.SerializeToString, + scheduler__pb2.AddOutgoingWebhookResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListOutgoingWebhooks(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/scheduler.Scheduler/ListOutgoingWebhooks', + scheduler__pb2.ListOutgoingWebhooksRequest.SerializeToString, + scheduler__pb2.ListOutgoingWebhooksResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def DeleteWebhooks(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/scheduler.Scheduler/DeleteWebhooks', + scheduler__pb2.DeleteOutgoingWebhooksRequest.SerializeToString, + greenlight__pb2.Empty.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def RotateOutgoingWebhookSecret(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/scheduler.Scheduler/RotateOutgoingWebhookSecret', + scheduler__pb2.RotateOutgoingWebhookSecretRequest.SerializeToString, + scheduler__pb2.WebhookSecretResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + class DebugStub(object): """A service to collect debugging information from clients. diff --git a/libs/gl-client-py/src/scheduler.rs b/libs/gl-client-py/src/scheduler.rs index 5844fd3a8..03bdf2092 100644 --- a/libs/gl-client-py/src/scheduler.rs +++ b/libs/gl-client-py/src/scheduler.rs @@ -98,6 +98,41 @@ impl Scheduler { fn get_invite_codes(&self) -> PyResult> { convert(exec(async move { self.inner.get_invite_codes().await })) } + + fn add_outgoing_webhook(&self, uri: String) -> PyResult> { + let outgoing_webhook_request = pb::scheduler::AddOutgoingWebhookRequest { + node_id: self.node_id.clone(), + uri + }; + + convert(exec(async move { self.inner.add_outgoing_webhook(outgoing_webhook_request).await })) + } + + fn list_outgoing_webhooks(&self) -> PyResult>{ + let list_outgoing_webhooks_request = pb::scheduler::ListOutgoingWebhooksRequest { + node_id: self.node_id.clone() + }; + + convert(exec(async move { self.inner.list_outgoing_webhooks(list_outgoing_webhooks_request).await })) + } + + fn delete_outgoing_webhooks(&self, webhook_ids: Vec) -> PyResult> { + let delete_outgoing_webhooks_request = pb::scheduler::DeleteOutgoingWebhooksRequest { + node_id: self.node_id.clone(), + ids: webhook_ids + }; + + convert(exec(async move { self.inner.delete_webhooks(delete_outgoing_webhooks_request).await })) + } + + fn rotate_outgoing_webhook_secret(&self, webhook_id: i64) -> PyResult> { + let rotate_outgoing_webhook_secret_request = pb::scheduler::RotateOutgoingWebhookSecretRequest { + node_id: self.node_id.clone(), + webhook_id + }; + + convert(exec(async move { self.inner.rotate_outgoing_webhook_secret(rotate_outgoing_webhook_secret_request).await })) + } } pub fn convert(r: Result) -> PyResult> { diff --git a/libs/gl-client/src/scheduler.rs b/libs/gl-client/src/scheduler.rs index d8b15f26a..d56db4164 100644 --- a/libs/gl-client/src/scheduler.rs +++ b/libs/gl-client/src/scheduler.rs @@ -234,4 +234,52 @@ impl Scheduler { .await?; Ok(res.into_inner()) } + + pub async fn add_outgoing_webhook( + &self, + outgoing_webhook_request: pb::scheduler::AddOutgoingWebhookRequest, + ) -> Result { + let res = self + .client + .clone() + .add_outgoing_webhook(outgoing_webhook_request) + .await?; + Ok(res.into_inner()) + } + + pub async fn list_outgoing_webhooks( + &self, + list_outgoing_webhooks_request: pb::scheduler::ListOutgoingWebhooksRequest, + ) -> Result { + let res = self + .client + .clone() + .list_outgoing_webhooks(list_outgoing_webhooks_request) + .await?; + Ok(res.into_inner()) + } + + pub async fn delete_webhooks( + &self, + delete_webhooks_request: pb::scheduler::DeleteOutgoingWebhooksRequest, + ) -> Result { + let res = self + .client + .clone() + .delete_webhooks(delete_webhooks_request) + .await?; + Ok(res.into_inner()) + } + + pub async fn rotate_outgoing_webhook_secret( + &self, + rotate_outgoing_webhook_secret_request: pb::scheduler::RotateOutgoingWebhookSecretRequest + ) -> Result { + let res = self + .client + .clone() + .rotate_outgoing_webhook_secret(rotate_outgoing_webhook_secret_request) + .await?; + Ok(res.into_inner()) + } } diff --git a/libs/gl-testing/gltesting/scheduler.py b/libs/gl-testing/gltesting/scheduler.py index f07cdfa9b..282d42eb0 100644 --- a/libs/gl-testing/gltesting/scheduler.py +++ b/libs/gl-testing/gltesting/scheduler.py @@ -1,8 +1,10 @@ import logging import os +import random import shutil import socket import subprocess +import string import tempfile import threading import time @@ -78,6 +80,8 @@ def enumerate_cln_versions(): logging.info(f"Found {len(versions)} versions: {versions}") return versions +def generate_secret(len=5): + return "".join(random.choices(string.ascii_uppercase, k=len)) class AsyncScheduler(schedgrpc.SchedulerServicer): """A mock scheduler to test against.""" @@ -95,8 +99,10 @@ def __init__( self.versions = enumerate_cln_versions() self.bitcoind = bitcoind self.invite_codes: List[str] = [] + self.next_webhook_id: int = 1 self.received_invite_code = None self.debugger = DebugServicer() + self.webhooks = [] if node_directory is not None: self.node_directory = node_directory @@ -122,7 +128,6 @@ def start(self): self.server.add_service(self.debugger.service) threading.Thread(target=anyio.run, args=(self.run,), daemon=True).start() - print("Hello") logging.info(f"Scheduler started on port {self.grpc_port}") def stop(self): @@ -358,6 +363,43 @@ async def ListInviteCodes(self, req) -> schedpb.ListInviteCodesResponse: codes = [schedpb.InviteCode(**c) for c in self.invite_codes] return schedpb.ListInviteCodesResponse(invite_code_list=codes) + async def add_outgoing_webhook(self, req) -> schedpb.AddOutgoingWebhookResponse: + n = self.get_node(req.node_id) + secret = generate_secret() + id = self.next_webhook_id + + self.webhooks.append({ + "id": id, + "node_id": n.node_id, + "uri": req.uri, + "secret": secret + }) + + self.next_webhook_id = self.next_webhook_id + 1 + return schedpb.AddOutgoingWebhookResponse(id=id, secret=secret) + + async def list_outgoing_webhooks(self, req) -> schedpb.ListOutgoingWebhooksResponse: + n = self.get_node(req.node_id) + webhooks = [schedpb.Webhook(**{"id": w["id"], "uri": w["uri"]}) for w in self.webhooks if w["node_id"] == n.node_id] + return schedpb.ListOutgoingWebhooksResponse(outgoing_webhooks=webhooks) + + async def delete_outgoing_webhooks(self, req) -> greenlightpb.Empty: + n = self.get_node(req.node_id) + self.webhooks = [w for w in self.webhooks if not (w["id"] in req.ids and w["node_id"] == n.node_id)] + return greenlightpb.Empty() + + async def rotate_outgoing_webhook_secret(self, req) -> schedpb.WebhookSecretResponse: + n = self.get_node(req.node_id) + webhook = next((w for w in self.webhooks if w["id"] == req.webhook_id and w["node_id"] == n.node_id), None) + + if webhook is None: + raise ValueError( + f"No webhook with id={webhook_id} found in gltesting scheduler" + ) + + secret = generate_secret() + webhook["secret"] = secret + return schedpb.WebhookSecretResponse(secret=secret) class DebugServicer(schedgrpc.DebugServicer): """Collects and analyzes rejected signer requests.""" diff --git a/libs/gl-testing/tests/test_outgoing_webhooks.py b/libs/gl-testing/tests/test_outgoing_webhooks.py new file mode 100644 index 000000000..9adde9ee8 --- /dev/null +++ b/libs/gl-testing/tests/test_outgoing_webhooks.py @@ -0,0 +1,70 @@ +"""Tests the outgoing webhook methods on the mock scheduler""" + +from gltesting.fixtures import * +from glclient import scheduler_pb2 as schedpb +import asyncio + +def test_add_outgoing_webhook(scheduler, clients): + c = clients.new() + r = c.register(configure=True) + n = c.find_node() + + asyncio.run(scheduler.add_outgoing_webhook(schedpb.AddOutgoingWebhookRequest(**{ + "node_id": n.node_id, + "uri": "https://blockstream.com" + }))) + + l = asyncio.run(scheduler.list_outgoing_webhooks(schedpb.ListOutgoingWebhooksRequest(**{ + "node_id": n.node_id + }))) + + assert len(l.outgoing_webhooks) == 1 + +def test_rotate_outgoing_webhook(scheduler, clients): + c = clients.new() + r = c.register(configure=True) + n = c.find_node() + + add_webhook_response = asyncio.run(scheduler.add_outgoing_webhook(schedpb.AddOutgoingWebhookRequest(**{ + "node_id": n.node_id, + "uri": "https://blockstream.com" + }))) + + l = asyncio.run(scheduler.list_outgoing_webhooks(schedpb.ListOutgoingWebhooksRequest(**{ + "node_id": n.node_id + }))) + + + rotate_response = asyncio.run(scheduler.rotate_outgoing_webhook_secret(schedpb.RotateOutgoingWebhookSecretRequest(**{ + "node_id": n.node_id, + "webhook_id": l.outgoing_webhooks[0].id + }))) + + assert add_webhook_response.secret != rotate_response.secret + +def test_delete_outgoing_webhook(scheduler, clients): + c = clients.new() + r = c.register(configure=True) + n = c.find_node() + + add_webhook_response = asyncio.run(scheduler.add_outgoing_webhook(schedpb.AddOutgoingWebhookRequest(**{ + "node_id": n.node_id, + "uri": "https://blockstream.com" + }))) + + l = asyncio.run(scheduler.list_outgoing_webhooks(schedpb.ListOutgoingWebhooksRequest(**{ + "node_id": n.node_id + }))) + + assert len(l.outgoing_webhooks) == 1 + + rotate_response = asyncio.run(scheduler.delete_outgoing_webhooks(schedpb.DeleteOutgoingWebhooksRequest(**{ + "node_id": n.node_id, + "ids": [l.outgoing_webhooks[0].id] + }))) + + l = asyncio.run(scheduler.list_outgoing_webhooks(schedpb.ListOutgoingWebhooksRequest(**{ + "node_id": n.node_id + }))) + + assert len(l.outgoing_webhooks) == 0 \ No newline at end of file diff --git a/libs/proto/scheduler.proto b/libs/proto/scheduler.proto index fab0afe90..1d797caec 100644 --- a/libs/proto/scheduler.proto +++ b/libs/proto/scheduler.proto @@ -148,8 +148,53 @@ service Scheduler { // being replayed) and loss of funds (see CLN Backups // documentation for more information) rpc ExportNode(ExportNodeRequest) returns (ExportNodeResponse) {} + + rpc AddOutgoingWebhook(AddOutgoingWebhookRequest) returns (AddOutgoingWebhookResponse) {} + + rpc ListOutgoingWebhooks(ListOutgoingWebhooksRequest) returns (ListOutgoingWebhooksResponse) {} + + rpc DeleteWebhooks(DeleteOutgoingWebhooksRequest) returns (greenlight.Empty) {} + + rpc RotateOutgoingWebhookSecret(RotateOutgoingWebhookSecretRequest) returns (WebhookSecretResponse) {} }; +message AddOutgoingWebhookRequest { + bytes node_id = 1; + string uri = 2; +} + +message AddOutgoingWebhookResponse { + int64 id = 1; + string secret = 2; +} + +message ListOutgoingWebhooksRequest { + bytes node_id = 1; +} + +message Webhook { + int64 id = 1; + string uri = 2; +} + +message ListOutgoingWebhooksResponse { + repeated Webhook outgoing_webhooks = 1; +} + +message DeleteOutgoingWebhooksRequest { + bytes node_id = 1; + repeated int64 ids = 2; +} + +message RotateOutgoingWebhookSecretRequest { + bytes node_id = 1; + int64 webhook_id = 2; +} + +message WebhookSecretResponse { + string secret = 1; +} + // A service to collect debugging information from clients. service Debug { // The signer is designed to fail closed, i.e., we reject requests diff --git a/tools/glcli/glcli/cli.py b/tools/glcli/glcli/cli.py index 0787da14d..9f79a917d 100644 --- a/tools/glcli/glcli/cli.py +++ b/tools/glcli/glcli/cli.py @@ -591,7 +591,7 @@ def listinvoices(ctx): @click.pass_context def listpays(ctx): node = ctx.obj.get_node() - res = node.list_payments() + res = node.list_pays() pbprint(res)