diff --git a/jac-cloud/jac_cloud/jaseci/models/webhook.py b/jac-cloud/jac_cloud/jaseci/models/webhook.py index 96a98a6b20..275da663ec 100644 --- a/jac-cloud/jac_cloud/jaseci/models/webhook.py +++ b/jac-cloud/jac_cloud/jaseci/models/webhook.py @@ -11,7 +11,7 @@ @dataclass(kw_only=True) class Webhook: - """User Base Model.""" + """Webhook Base Model.""" id: ObjectId = field(default_factory=ObjectId) name: str @@ -23,9 +23,9 @@ class Webhook: class Collection(BaseCollection["Webhook"]): """ - User collection interface. + Webhook collection interface. - This interface is for User's Management. + This interface is for Webhook Credentials Management. You may override this if you wish to implement different structure """ @@ -40,7 +40,7 @@ def __document__(cls, doc: Mapping[str, Any]) -> "Webhook": """ Return parsed Webhook from document. - This the default User parser after getting a single document. + This the default Webhook parser after getting a single document. You may override this to specify how/which class it will be casted/based. """ doc = cast(dict, doc) diff --git a/jac-cloud/jac_cloud/tests/openapi_specs.yaml b/jac-cloud/jac_cloud/tests/openapi_specs.yaml index 382e967b5f..b45f78c029 100644 --- a/jac-cloud/jac_cloud/tests/openapi_specs.yaml +++ b/jac-cloud/jac_cloud/tests/openapi_specs.yaml @@ -533,7 +533,11 @@ components: securitySchemes: APIKeyHeader: in: header - name: X-API-KEY + name: test_key + type: apiKey + APIKeyQuery: + in: query + name: test_key type: apiKey HTTPBearer: scheme: bearer @@ -4069,7 +4073,6 @@ paths: summary: /visit_nested_node/{node} tags: - walker - - walker /walker/visit_sequence: post: operationId: api_root_walker_visit_sequence_post @@ -4086,7 +4089,6 @@ paths: summary: /visit_sequence tags: - walker - - walker /walker/visit_sequence/{node}: post: operationId: api_entry_walker_visit_sequence__node__post @@ -4118,4 +4120,303 @@ paths: summary: /visit_sequence/{node} tags: - walker - - walker + /webhook: + get: + description: Get keys API. + operationId: get_webhook_get + responses: + '200': + content: + application/json: + schema: {} + description: Successful Response + security: + - HTTPBearer: [] + summary: Get + tags: + - webhook + /webhook/delete: + delete: + description: Delete keys API. + operationId: delete_webhook_delete_delete + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KeyIDs' + required: true + responses: + '200': + content: + application/json: + schema: {} + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + security: + - HTTPBearer: [] + summary: Delete + tags: + - webhook + /webhook/extend/{id}: + patch: + description: Generate key API. + operationId: extend_webhook_extend__id__patch + parameters: + - in: path + name: id + required: true + schema: + title: Id + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Expiration' + required: true + responses: + '201': + content: + application/json: + schema: {} + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + security: + - HTTPBearer: [] + summary: Extend + tags: + - webhook + /webhook/generate-key: + post: + description: Generate key API. + operationId: generate_key_webhook_generate_key_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GenerateKey' + required: true + responses: + '201': + content: + application/json: + schema: {} + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + security: + - HTTPBearer: [] + summary: Generate Key + tags: + - webhook + /webhook/walker/webhook_by_body: + post: + operationId: api_root_webhook_walker_webhook_by_body_post + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Root Webhook Walker Webhook By Body Post + description: Successful Response + summary: /webhook_by_body + tags: + - webhook-walker + /webhook/walker/webhook_by_body/{node}: + post: + operationId: api_entry_webhook_walker_webhook_by_body__node__post + parameters: + - in: path + name: node + required: true + schema: + anyOf: + - type: string + - type: 'null' + title: Node + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Entry Webhook Walker Webhook By Body Node Post + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + summary: /webhook_by_body/{node} + tags: + - webhook-walker + /webhook/walker/webhook_by_header: + post: + operationId: api_root_webhook_walker_webhook_by_header_post + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Root Webhook Walker Webhook By Header Post + description: Successful Response + security: + - APIKeyHeader: [] + summary: /webhook_by_header + tags: + - webhook-walker + /webhook/walker/webhook_by_header/{node}: + post: + operationId: api_entry_webhook_walker_webhook_by_header__node__post + parameters: + - in: path + name: node + required: true + schema: + anyOf: + - type: string + - type: 'null' + title: Node + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Entry Webhook Walker Webhook By Header Node Post + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + security: + - APIKeyHeader: [] + summary: /webhook_by_header/{node} + tags: + - webhook-walker + /webhook/walker/webhook_by_path/{node}/{test_key}: + post: + operationId: api_entry_webhook_walker_webhook_by_path__node___test_key__post + parameters: + - in: path + name: node + required: true + schema: + anyOf: + - type: string + - type: 'null' + title: Node + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Entry Webhook Walker Webhook By Path Node Test + Key Post + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + summary: /webhook_by_path/{node}/{test_key} + tags: + - webhook-walker + /webhook/walker/webhook_by_path/{test_key}: + post: + operationId: api_root_webhook_walker_webhook_by_path__test_key__post + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Root Webhook Walker Webhook By Path Test Key Post + description: Successful Response + summary: /webhook_by_path/{test_key} + tags: + - webhook-walker + /webhook/walker/webhook_by_query: + post: + operationId: api_root_webhook_walker_webhook_by_query_post + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Root Webhook Walker Webhook By Query Post + description: Successful Response + security: + - APIKeyQuery: [] + summary: /webhook_by_query + tags: + - webhook-walker + /webhook/walker/webhook_by_query/{node}: + post: + operationId: api_entry_webhook_walker_webhook_by_query__node__post + parameters: + - in: path + name: node + required: true + schema: + anyOf: + - type: string + - type: 'null' + title: Node + responses: + '200': + content: + application/json: + schema: + anyOf: + - $ref: '#/components/schemas/ContextResponse_NoneType_' + - {} + title: Response Api Entry Webhook Walker Webhook By Query Node Post + description: Successful Response + '422': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + security: + - APIKeyQuery: [] + summary: /webhook_by_query/{node} + tags: + - webhook-walker \ No newline at end of file diff --git a/jac-cloud/jac_cloud/tests/simple_graph.jac b/jac-cloud/jac_cloud/tests/simple_graph.jac index ffec38156b..ba89a6080d 100644 --- a/jac-cloud/jac_cloud/tests/simple_graph.jac +++ b/jac-cloud/jac_cloud/tests/simple_graph.jac @@ -859,12 +859,58 @@ walker visit_sequence { } } -walker webhook { +################################################################# +# WEBHOOKS # +################################################################# + +walker webhook_by_header { can enter1 with `root entry { - report here; + report True; + } + + class __specs__ { + has webhook: dict = { + "type": "header", + "name": "test_key" + }; + } +} + +walker webhook_by_query { + can enter1 with `root entry { + report True; + } + + class __specs__ { + has webhook: dict = { + "type": "query", + "name": "test_key" + }; + } +} + +walker webhook_by_path { + can enter1 with `root entry { + report True; + } + + class __specs__ { + has webhook: dict = { + "type": "path", + "name": "test_key" + }, path: str = "/{test_key}"; + } +} + +walker webhook_by_body { + can enter1 with `root entry { + report True; } class __specs__ { - has webhook: dict = {}; + has webhook: dict = { + "type": "body", + "name": "test_key" + }; } } \ No newline at end of file diff --git a/jac-cloud/jac_cloud/tests/test_simple_graph.py b/jac-cloud/jac_cloud/tests/test_simple_graph.py index 7f5ae2a06a..319ed520a4 100644 --- a/jac-cloud/jac_cloud/tests/test_simple_graph.py +++ b/jac-cloud/jac_cloud/tests/test_simple_graph.py @@ -793,6 +793,42 @@ def trigger_visit_sequence(self) -> None: res["returns"], ) + def trigger_webhook_test(self) -> None: + """Test webhook.""" + res = post( + f"{self.host}/webhook/generate-key", + json={ + "name": "test", + "walkers": [], + "nodes": [], + "expiration": {"count": 60, "interval": "days"}, + }, + headers=self.users[0]["headers"], + ) + + res.raise_for_status() + key = res.json()["key"] + + self.assertEqual( + {"status": 200, "reports": [True], "returns": [None]}, + self.post_webhook("webhook_by_header", headers={"test_key": key}), + ) + + self.assertEqual( + {"status": 200, "reports": [True], "returns": [None]}, + self.post_webhook(f"webhook_by_query?test_key={key}"), + ) + + self.assertEqual( + {"status": 200, "reports": [True], "returns": [None]}, + self.post_webhook(f"webhook_by_path/{key}"), + ) + + self.assertEqual( + {"status": 200, "reports": [True], "returns": [None]}, + self.post_webhook("webhook_by_body", {"test_key": key}), + ) + def test_all_features(self) -> None: """Test Full Features.""" self.trigger_openapi_specs_test() @@ -909,3 +945,9 @@ def test_all_features(self) -> None: ################################################### self.trigger_visit_sequence() + + ################################################### + # WEBHOOK # + ################################################### + + self.trigger_webhook_test() diff --git a/jac-cloud/jac_cloud/tests/test_utils.py b/jac-cloud/jac_cloud/tests/test_utils.py index 6f50ecff2f..fb29f28cb8 100644 --- a/jac-cloud/jac_cloud/tests/test_utils.py +++ b/jac-cloud/jac_cloud/tests/test_utils.py @@ -112,3 +112,12 @@ def post_api( return res.json() else: return res.status_code + + def post_webhook( + self, api: str, json: dict | None = None, headers: dict | None = None + ) -> dict: + """Call walker post API.""" + res = post(f"{self.host}/webhook/walker/{api}", json=json, headers=headers) + + res.raise_for_status() + return res.json() diff --git a/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/jac_cloud_webhook.md b/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/jac_cloud_webhook.md index 0d0ea56cce..13c3d10618 100644 --- a/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/jac_cloud_webhook.md +++ b/jac/support/jac-lang.org/docs/learn/coders/jac-cloud/docs/jac_cloud_webhook.md @@ -113,3 +113,84 @@ walker webhook { "message": "Successfully Deleted!" } ``` +## WEBHOOK SAMPLES + +### `BY HEADER` (default) +```python +walker webhook_by_header { + can enter1 with `root entry { + report here; + } + + class __specs__ { + has webhook: dict = { + "type": "header", + "name": "test-key" + }; + } +} +``` +#### REQUEST +```bash +curl -X 'POST' 'http://localhost:8001/webhook/walker/webhook_by_header' \ + -H 'test-key: YOUR-GENERATED-KEY' +``` + +### `BY QUERY` +```python +walker webhook_by_query { + can enter1 with `root entry { + report here; + } + + class __specs__ { + has webhook: dict = { + "type": "query", + "name": "test_key" + }; + } +} +``` +#### REQUEST +```bash +curl -X 'POST' 'http://localhost:8001/webhook/walker/webhook_by_query?test_key=YOUR-GENERATED-KEY' +``` + +### `BY PATH` +```python +walker webhook_by_path { + can enter1 with `root entry { + report here; + } + + class __specs__ { + has webhook: dict = { + "type": "path", + "name": "test_key" # name and the path var should be the same + }, path: str = "/{test_key}"; + } +} +``` +#### REQUEST +```bash +curl -X 'POST' 'http://localhost:8001/webhook/walker/webhook_by_path/YOUR-GENERATED-KEY' +``` +### `BY BODY` +```python +walker webhook_by_body { + can enter1 with `root entry { + report here; + } + + class __specs__ { + has webhook: dict = { + "type": "body", + "name": "test_key" + }; + } +} +``` +#### REQUEST +```bash +curl -X 'POST' 'http://localhost:8001/webhook/walker/webhook_by_body' -d '{"test_key": "YOUR-GENERATED-KEY"}' +``` \ No newline at end of file