diff --git a/.github/workflows/test-spec.yml b/.github/workflows/test-spec.yml index 06a098265..a4c354c89 100644 --- a/.github/workflows/test-spec.yml +++ b/.github/workflows/test-spec.yml @@ -38,6 +38,8 @@ jobs: tests: snapshot - version: 2.17.0 tests: plugins/streaming + - version: 2.17.0 + tests: plugins/notifications - version: 2.18.0 hub: opensearchstaging ref: '@sha256:4445e195c53992038891519dc3be0d273cdaad1b047943d68921168ed243e7e9' diff --git a/.lycheeignore b/.lycheeignore index c859dd4da..caa2546d0 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -1 +1,2 @@ https://localhost:* +http://webhook:8080 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fb4ed38f..deb68e97b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added `/_plugins/_ml/agents/_register`, `/_plugins/_ml/connectors/_create`, `DELETE /_plugins/_ml/agents/{agent_id}`, `DELETE /_plugins/_ml/connectors/{connector_id}` ([#228](https://github.com/opensearch-project/opensearch-api-specification/issues/228)) - Added the `context` query param to the `put_script` APIs ([#586](https://github.com/opensearch-project/opensearch-api-specification/pull/586)) - Added `persian_stem` filter ([#592](https://github.com/opensearch-project/opensearch-api-specification/pull/592)) +- Added `config_id` and `config_id_list` to `/_plugins/_notifications/configs` query parameters ([#594](https://github.com/opensearch-project/opensearch-api-specification/pull/594)) ### Changed @@ -169,6 +170,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed create/delete/index operation in `_bulk` ([#582](https://github.com/opensearch-project/opensearch-api-specification/pull/582)) - Add `mode` and `compression` to k-NN index creation and search, and add `rescore` and `oversample_factor` to k-NN search ([#588](https://github.com/opensearch-project/opensearch-api-specification/pull/588)) - Fixed `/{index}/_search` with aggregations ([#576](https://github.com/opensearch-project/opensearch-api-specification/pull/576)) +- Fixed `RestStatus` responses in `DELETE /_plugins/_notifications/configs/{config_id}` ([#594](https://github.com/opensearch-project/opensearch-api-specification/pull/594)) ### Security diff --git a/spec/namespaces/notifications.yaml b/spec/namespaces/notifications.yaml index 00faf3941..811a2f21b 100644 --- a/spec/namespaces/notifications.yaml +++ b/spec/namespaces/notifications.yaml @@ -26,6 +26,8 @@ paths: parameters: - $ref: '#/components/parameters/notifications.get_configs::query.chime.url' - $ref: '#/components/parameters/notifications.get_configs::query.chime.url.keyword' + - $ref: '#/components/parameters/notifications.get_configs::query.config_id' + - $ref: '#/components/parameters/notifications.get_configs::query.config_id_list' - $ref: '#/components/parameters/notifications.get_configs::query.config_type' - $ref: '#/components/parameters/notifications.get_configs::query.created_time_ms' - $ref: '#/components/parameters/notifications.get_configs::query.description' @@ -318,6 +320,20 @@ components: in: query schema: type: string + notifications.get_configs::query.config_id: + name: config_id + in: query + description: Notification configuration ID. + schema: + type: string + notifications.get_configs::query.config_id_list: + name: config_id_list + in: query + description: Notification configuration IDs. + schema: + type: array + items: + type: string notifications.get_configs::query.config_type: name: config_type in: query diff --git a/spec/schemas/notifications._common.yaml b/spec/schemas/notifications._common.yaml index 55d1a0040..667eb0847 100644 --- a/spec/schemas/notifications._common.yaml +++ b/spec/schemas/notifications._common.yaml @@ -34,23 +34,14 @@ components: RestStatus: type: string enum: - - accepted - - continue - - created - - found - - moved_permanently - - multi_status - - multiple_choices - - no_content - - non_authoritative_information - - not_modified - - ok - - partial_content - - reset_content - - see_other - - switching_protocols - - temporary_redirect - - use_proxy + - ACCEPTED + - CREATED + - MULTI_STATUS + - NON_AUTHORITATIVE_INFORMATION + - NO_CONTENT + - OK + - PARTIAL_CONTENT + - RESET_CONTENT NotificationConfigType: type: string description: Type of notification configuration. diff --git a/tests/default/notifications/channels.yaml b/tests/default/notifications/channels.yaml new file mode 100644 index 000000000..d01ddd89e --- /dev/null +++ b/tests/default/notifications/channels.yaml @@ -0,0 +1,9 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test listing channels. +chapters: + - synopsis: Retrieve a list of all notification channels. + path: /_plugins/_notifications/channels + method: GET + response: + status: 200 diff --git a/tests/default/notifications/configs.yaml b/tests/default/notifications/configs.yaml new file mode 100644 index 000000000..1facbd51c --- /dev/null +++ b/tests/default/notifications/configs.yaml @@ -0,0 +1,97 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test listing notification configs. +version: '>= 2.0' +chapters: + - synopsis: Create a Slack channel configuration. + path: /_plugins/_notifications/configs + method: POST + request: + payload: + config_id: slack-configuration + config: + name: Notifications Slack Channel + description: Default notifications channel. + config_type: slack + is_enabled: true + slack: + url: https://example.org/webhook + response: + status: 200 + - synopsis: Update a Slack channel configuration. + path: /_plugins/_notifications/configs/{config_id} + method: PUT + parameters: + config_id: slack-configuration + request: + payload: + config: + name: Notifications Slack Channel + config_type: slack + is_enabled: false + slack: + url: https://example.org/webhook + response: + status: 200 + - synopsis: Retrieve a list of all notification configurations. + path: /_plugins/_notifications/configs + method: GET + retry: + count: 2 + response: + status: 200 + payload: + config_list: + - config_id: slack-configuration + - synopsis: Retrieve a list of all notification configurations filtered by config ID. + path: /_plugins/_notifications/configs + method: GET + parameters: + config_type: slack + response: + status: 200 + payload: + config_list: + - config_id: slack-configuration + - synopsis: Retrieve a notification configuration by config ID (path). + path: /_plugins/_notifications/configs/{config_id} + method: GET + parameters: + config_id: slack-configuration + response: + status: 200 + payload: + config_list: + - config_id: slack-configuration + - synopsis: Retrieve a notification configuration by config ID (query). + path: /_plugins/_notifications/configs + method: GET + parameters: + config_id: slack-configuration + response: + status: 200 + payload: + config_list: + - config_id: slack-configuration + - synopsis: Retrieve a notification configuration by config ID list (query). + path: /_plugins/_notifications/configs + method: GET + parameters: + config_id_list: + - slack-configuration + response: + status: 200 + payload: + config_list: + - config_id: slack-configuration + - synopsis: Delete a channel configuration. + path: /_plugins/_notifications/configs/{config_id} + method: DELETE + parameters: + config_id: slack-configuration + response: + status: 200 +epilogues: + - path: /_plugins/_notifications/configs/slack-configuration + method: DELETE + status: [200,404] diff --git a/tests/default/notifications/features.yaml b/tests/default/notifications/features.yaml new file mode 100644 index 000000000..71b7d3dc7 --- /dev/null +++ b/tests/default/notifications/features.yaml @@ -0,0 +1,9 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test listing supported channel configurations. +chapters: + - synopsis: Retrieve a list of all supported notification configuration types. + path: /_plugins/_notifications/features + method: GET + response: + status: 200 diff --git a/tests/plugins/notifications/docker-compose.yml b/tests/plugins/notifications/docker-compose.yml new file mode 100644 index 000000000..937846f3c --- /dev/null +++ b/tests/plugins/notifications/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3' + +services: + opensearch-cluster: + image: ${OPENSEARCH_DOCKER_HUB_PROJECT:-opensearchproject}/opensearch:${OPENSEARCH_VERSION:-latest}${OPENSEARCH_DOCKER_REF} + ports: + - 9200:9200 + - 9600:9600 + environment: + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_PASSWORD:-myStrongPassword123!} + - OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS} + - discovery.type=single-node + webhook: + image: python:latest + volumes: + - ./server.py:/server.py + ports: + - '8080:8080' + entrypoint: python server.py diff --git a/tests/plugins/notifications/notifications/feature/test.yaml b/tests/plugins/notifications/notifications/feature/test.yaml new file mode 100644 index 000000000..e889155e1 --- /dev/null +++ b/tests/plugins/notifications/notifications/feature/test.yaml @@ -0,0 +1,37 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test sending a notification. +version: '>= 2.0' +prologues: + - path: /_plugins/_notifications/configs + method: POST + request: + payload: + config_id: custom-webhook-configuration + config: + name: Notifications Channel + description: Default notifications channel. + config_type: webhook + is_enabled: true + webhook: + url: http://webhook:8080/ + status: [200] +chapters: + - synopsis: Test sending a notification. + path: /_plugins/_notifications/feature/test/{config_id} + method: GET + parameters: + config_id: custom-webhook-configuration + response: + status: 200 + payload: + status_list: + - config_id: custom-webhook-configuration + config_type: webhook + delivery_status: + status_code: '200' + status_text: '{"ok":"true"}' +epilogues: + - path: /_plugins/_notifications/configs/custom-webhook-configuration + method: DELETE + status: [200,404] diff --git a/tests/plugins/notifications/server.py b/tests/plugins/notifications/server.py new file mode 100644 index 000000000..a757ff741 --- /dev/null +++ b/tests/plugins/notifications/server.py @@ -0,0 +1,22 @@ +from http.server import BaseHTTPRequestHandler, HTTPServer + + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header("Content-type", "application_json") + self.end_headers() + self.wfile.write(b'{"ok":"true"}') + + def do_POST(self): + self.send_response(200) + self.send_header("Content-type", "application_json") + self.end_headers() + self.wfile.write(b'{"ok":"true"}') + + +if __name__ == "__main__": + server_address = ("", 8080) + httpd = HTTPServer(server_address, SimpleHTTPRequestHandler) + print("Server started on http://localhost:8080") + httpd.serve_forever() diff --git a/tools/src/tester/TestRunner.ts b/tools/src/tester/TestRunner.ts index 7b36af3b9..6317ef392 100644 --- a/tools/src/tester/TestRunner.ts +++ b/tools/src/tester/TestRunner.ts @@ -70,7 +70,7 @@ export default class TestRunner { #collect_story_files (folder: string, file: string, prefix: string): StoryFile[] { const path = file === '' ? folder : `${folder}/${file}` const next_prefix = prefix === '' ? file : `${prefix}/${file}` - if (file.startsWith('.') || file == 'docker-compose.yml' || file == 'Dockerfile') { + if (file.startsWith('.') || file == 'docker-compose.yml' || file == 'Dockerfile' || file.endsWith('.py')) { return [] } else if (fs.statSync(path).isFile()) { const story: Story = read_yaml(path) diff --git a/tools/tests/tester/integ/TestRunner.test.ts b/tools/tests/tester/integ/TestRunner.test.ts index 75253cd7c..a747f1b64 100644 --- a/tools/tests/tester/integ/TestRunner.test.ts +++ b/tools/tests/tester/integ/TestRunner.test.ts @@ -53,4 +53,10 @@ describe('story_files', () => { story_file => story_file.display_path )).not.toContain('nodes/plugins/docker-compose.yml') }) + + test('does not contain a python script', () => { + expect(test_runner.story_files('tests/plugins/notifications').map( + story_file => story_file.display_path + )).not.toContain('nodes/plugins/server.py') + }) })