diff --git a/channel-sender/docs/swagger.yaml b/channel-sender/docs/swagger.yaml index f904f9f..dbacbe0 100644 --- a/channel-sender/docs/swagger.yaml +++ b/channel-sender/docs/swagger.yaml @@ -6,15 +6,11 @@ info: license: name: MIT url: https://github.com/bancolombia/async-dataflow-channel-sender/blob/master/LICENSE - version: 0.1.0 + version: 0.2.0 servers: - url: https://virtserver.swaggerhub.com/santitigaga/async-data-flow-channel-sender/0.1.0 description: SwaggerHub API Auto Mocking -tags: -- name: /ext/channel/ - description: Secured Admin-only calls -- name: channel - description: Available operations for channels + paths: /create: post: @@ -22,40 +18,37 @@ paths: - /ext/channel/ summary: Create Channel and session description: | - By passing in the appropriate options, you can search for - available inventory in the system + By passing in the appropriate options, you can regisster a new channel in the system operationId: createChannel requestBody: description: Channel to create content: application/json: schema: - $ref: '#/components/schemas/Channel' + $ref: '#/components/schemas/ChannelRequest' responses: "200": description: channel_ref and channel_secret content: application/json: schema: - type: object - properties: - channel_ref: - type: string - example: UNIQUE APPLICATION IDENTIFIER - channel_secret: - type: string - example: TOKEN - "412": - description: No application {application_ref} found + $ref: '#/components/schemas/Credentials' + "400": + description: Bad request due to invalid body or missing required fields + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidRequest' + /deliver_message: post: tags: - /ext/channel/ summary: Deliver an event from a message - description: Deliver an event from a message to a channel + description: Deliver an event message to a previusly registered channel_ref operationId: deliverMessage requestBody: - description: Inventory item to add + description: "Triggers internal workflow to deliver message. The message may not be delivered immediately, or not at all. Depends if the channel_ref was previusly registered. The message_data schema is not enforced, but its recommeded to use CloudEvents." content: application/json: schema: @@ -63,8 +56,20 @@ paths: responses: "202": description: Ok + content: + application/json: + schema: + type: object + properties: + result: + type: string + example: Ok "400": - description: Invalid request + description: Bad request due to invalid body or missing required fields + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidRequest' components: schemas: Message: @@ -88,11 +93,11 @@ components: example: d290f1ee-6c54-4b01-90e6-d701748f0851 message_data: type: object - example: '{}' + example: {"product_id": "1234", "product_name": "product name"} event_name: type: string example: event.productCreated - Channel: + ChannelRequest: required: - application_ref - user_ref @@ -100,7 +105,30 @@ components: properties: application_ref: type: string - example: UNIQUE APPLICATION IDENTIFIER + example: app01 user_ref: type: string - example: USER@DOMAIN + example: jhon.doe + Credentials: + required: + - channel_ref + - channel_secret + type: object + properties: + channel_ref: + type: string + example: beec634503c238f5b84f737275bfd4ba.855b8193bb6f419381eac6cc087aea3f + channel_secret: + type: string + example: SFMyNTY.g2gDaANtAAAAQWJlZWM2MzQ1MDNjMjM4ZjViODRmNzM3Mjc1YmZkNGJhLjg1NWI4MTkzYmI2ZjQxOTM4MWVhYzZjYzA4N2FlYTNmbQAAAAZ4eHh4eHhtAAAAB3h4eHh4eHhuBgDbcXMIlAFiAAFRgA....... + InvalidRequest: + required: + - error + - request + type: object + properties: + error: + type: string + example: Invalid request + request: + type: object diff --git a/channel-sender/lib/channel_sender_ex/transport/rest/rest_controller.ex b/channel-sender/lib/channel_sender_ex/transport/rest/rest_controller.ex index 41304f1..c45b69c 100644 --- a/channel-sender/lib/channel_sender_ex/transport/rest/rest_controller.ex +++ b/channel-sender/lib/channel_sender_ex/transport/rest/rest_controller.ex @@ -29,17 +29,27 @@ defmodule ChannelSenderEx.Transport.Rest.RestController do route_create(conn.body_params, conn) end - defp route_create(_message = %{ + defp route_create(message = %{ application_ref: application_ref, user_ref: user_ref }, conn ) do - {channel_ref, channel_secret} = ChannelAuthenticator.create_channel(application_ref, user_ref) - response = %{channel_ref: channel_ref, channel_secret: channel_secret} - conn - |> put_resp_header("content-type", "application/json") - |> send_resp(200, Jason.encode!(response)) + is_valid = message + |> Enum.all?(fn {_, value} -> is_binary(value) and value != "" end) + + case is_valid do + true -> + {channel_ref, channel_secret} = ChannelAuthenticator.create_channel(application_ref, user_ref) + response = %{channel_ref: channel_ref, channel_secret: channel_secret} + + conn + |> put_resp_header("content-type", "application/json") + |> send_resp(200, Jason.encode!(response)) + + false -> + invalid_body(conn) + end end defp route_create(_body, conn) do @@ -60,13 +70,31 @@ defmodule ChannelSenderEx.Transport.Rest.RestController do }, conn ) do - Task.start(fn -> PubSubCore.deliver_to_channel(channel_ref, - Map.drop(message, [:channel_ref]) |> ProtocolMessage.to_protocol_message) + # assert that minimum required fields are present + is_valid = message + |> Enum.all?(fn {key, value} -> + case key do + :message_data -> + not is_nil(value) + :correlation_id -> + true + _ -> + is_binary(value) and value != "" + end end) - conn - |> put_resp_header("content-type", "application/json") - |> send_resp(202, Jason.encode!(%{result: "Ok"})) + case is_valid do + true -> + Task.start(fn -> PubSubCore.deliver_to_channel(channel_ref, + Map.drop(message, [:channel_ref]) |> ProtocolMessage.to_protocol_message) + end) + conn + |> put_resp_header("content-type", "application/json") + |> send_resp(202, Jason.encode!(%{result: "Ok"})) + + false -> + invalid_body(conn) + end end defp route_deliver(_, conn), do: invalid_body(conn) @@ -75,6 +103,6 @@ defmodule ChannelSenderEx.Transport.Rest.RestController do defp invalid_body(conn = %{body_params: invalid_body}) do conn |> put_resp_header("content-type", "application/json") - |> send_resp(400, Jason.encode!(%{error: "Invalid request #{inspect(invalid_body)}"})) + |> send_resp(400, Jason.encode!(%{error: "Invalid request", request: invalid_body})) end end diff --git a/channel-sender/test/channel_sender_ex/core/channel_integration_test.exs b/channel-sender/test/channel_sender_ex/core/channel_integration_test.exs index 7b041b3..ee03e63 100644 --- a/channel-sender/test/channel_sender_ex/core/channel_integration_test.exs +++ b/channel-sender/test/channel_sender_ex/core/channel_integration_test.exs @@ -132,6 +132,12 @@ defmodule ChannelSenderEx.Core.ChannelIntegrationTest do Process.sleep(500) assert Process.alive?(channel_pid) == false + + on_exit(fn -> + Application.delete_env(:channel_sender_ex, :channel_shutdown_on_clean_close) + Application.delete_env(:channel_sender_ex, :channel_shutdown_on_disconnection) + Helper.compile(:channel_sender_ex) + end) end test "Should not restart channel when terminated normal (Waiting timeout)" do @@ -145,7 +151,11 @@ defmodule ChannelSenderEx.Core.ChannelIntegrationTest do Process.sleep(300) assert :noproc == ChannelRegistry.lookup_channel_addr(channel) - Helper.compile(:channel_sender_ex) + on_exit(fn -> + Application.delete_env(:channel_sender_ex, :channel_shutdown_on_clean_close) + Application.delete_env(:channel_sender_ex, :channel_shutdown_on_disconnection) + Helper.compile(:channel_sender_ex) + end) end test "Should send pending messages to twin process when terminated by supervisor merge (name conflict)" do diff --git a/channel-sender/test/channel_sender_ex/transport/rest/rest_controller_test.exs b/channel-sender/test/channel_sender_ex/transport/rest/rest_controller_test.exs index 16dbd43..5ccee15 100644 --- a/channel-sender/test/channel_sender_ex/transport/rest/rest_controller_test.exs +++ b/channel-sender/test/channel_sender_ex/transport/rest/rest_controller_test.exs @@ -56,6 +56,17 @@ defmodule ChannelSenderEx.Transport.Rest.RestControllerTest do end + test "Should not create channel on bad request due to missing fields" do + body = Jason.encode!(%{application_ref: "some_application", user_ref: nil}) + + conn = conn(:post, "/ext/channel/create", body) + |> put_req_header("content-type", "application/json") + + conn = RestController.call(conn, @options) + + assert conn.status == 400 + end + test "Should send message on request" do body = @@ -97,7 +108,27 @@ defmodule ChannelSenderEx.Transport.Rest.RestControllerTest do assert conn.status == 400 assert %{"error" => "Invalid request" <> _rest} = Jason.decode!(conn.resp_body) + end + test "Should fail on invalid body due to invalid values" do + body = + Jason.encode!(%{ + channel_ref: nil, + message_id: "message_id", + correlation_id: "correlation_id", + message_data: "message_data", + event_name: "event_name" + }) + + conn = conn(:post, "/ext/channel/deliver_message", body) + |> put_req_header("content-type", "application/json") + + conn = RestController.call(conn, @options) + + assert conn.status == 400 + + assert %{"error" => "Invalid request" <> _rest} = Jason.decode!(conn.resp_body) end + end