# RabbitMQ Barong Event API ## Overview of RabbitMQ details Barong submits all events into three exchanges depending on event category (read next). The exchange name consists of three parts: 1) application name (typically `barong`) 2) fixed keyword `events`. 3) category of event, like `system` (generic system event), `model` (the attributes of some record were updated) The routing key looks like `user.password.reset.token`, `user.created`. The event name matches the routing key but with event category appended at the beginning, like `system.user.password.reset.token`, `market.user.created`. ## Overview of RabbitMQ message Each produced message in `Event API` is JWT (complete format). This is very similar to `Management API`. The example below demonstrates both generation and verification of JWT: ```ruby require "jwt-multisig" require "securerandom" jwt_payload = { iss: 'barong', jti: SecureRandom.uuid, iat: Time.now.to_i, exp: Time.now.to_i + 60, event: event_payload } private_key = OpenSSL::PKey.read(Base64.urlsafe_decode64(private_key) algorithm = 'RS256' jwt = JWT::Multisig.generate_jwt jwt_payload, \ { barong: private_key }, { barong: algorithm } Kernel.puts "GENERATED JWT", jwt.to_json, "\n" verification_result = JWT::Multisig.verify_jwt jwt.deep_stringify_keys, \ { barong: public_key }, { verify_iss: true, iss: "barong", verify_jti: true } decoded_jwt_payload = verification_result[:payload] Kernel.puts "MATCH AFTER VERIFICATION: #{jwt_payload == decoded_jwt_payload}." ``` The RabbitMQ message is stored in JWT field called `event`. ## Overview of Event API message The typical event looks like (JSON): ```ruby event: { record: { foo: "ID30DD0DD986", bar: "example@barong.io", baz: "member", qux: 0 }, name: "model.user.created" } ``` The field `event[:name]` contains event name (same as routing key). The fields `foo`, `bar`, `baz`, `qux` (example) are fields which carry useful information. # Barong Event API messages ## Format of `model.user.created` event ```ruby event: { record: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 0, otp: false, state: "pending", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" }, name: "model.user.created" } ``` | Field | Description | | ---------- | ----------------------------------- | | `record` | Created user up-to-date attributes. | ## Format of `model.user.updated` event ```ruby event: { record: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 1, otp: false, state: "pending", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" }, changes: { level: 0 } name: "model.user.updated" } ``` | Field | Description | | ---------- | ----------------------------------- | | `record` | Created user up-to-date attributes. | | `changes` | The changed user attributes and their values. | ## Format of `model.user.created` event ```ruby event: { record: { address:"Illinois", city:"New Garfieldbury", country:"COD", dob:"1984-05-22", first_name:"Irina", last_name:"Heathcote", postcode:"10029", created_at:"2019-10-02T08:14:20Z", updated_at:"2019-10-02T08:14:20Z", user: { email: "clarisa_larkin@sawayn.info", level: 0, otp: false, referral_uid: nil, role: "member", state: "pending", uid: "IDEA819FB3F1", updated_at: "2019-10-02T08:14:20Z" created_at: "2019-10-02T08:14:20Z" } }, name: "model.profile.created" } ``` | Field | Description | | ---------- | -------------------------------------- | | `record` | Created profile up-to-date attributes. | ## Format of `model.user.updated` event ```ruby event: { record: { address:"Illinois", city:"New Garfieldbury", country:"COD", created_at:"2019-10-02T08:14:20Z", dob:"1984-05-22", first_name:"Irina", last_name:"Heathcote", postcode:"10029", updated_at:"2019-10-02T08:14:20Z", user: { email: "clarisa_larkin@sawayn.info", level: 0, otp: false, referral_uid: nil, role: "member", state: "pending", uid: "IDEA819FB3F1", updated_at: "2019-10-02T08:14:20Z" created_at: "2019-10-02T08:14:20Z" } }, changes: { first_name: "Vernell" }, name: "model.profile.updated" } ``` | Field | Description | | ---------- | ----------------------------------- | | `record` | Profile up-to-date attributes. | | `changes` | The changed profile attributes and their values. | ## Format of `model.label.created` event ```ruby event: { record: { id: 1, key: "email", value: "verified", user: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 2, otp: false, state: "active", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" } }, name: "model.label.created" } ``` | Field | Description | | ---------- | ----------------------------------- | | `record` | Created label up-to-date attributes. | ## Format of `model.label.updated` event ```ruby event: { record: { id: 1, key: "new_key", value: "verified", user: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 2, otp: false, state: "active", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" } }, changes: { key: "old_key" } name: "model.label.updated" } ``` | Field | Description | | ---------- | ----------------------------------- | | `record` | Created label up-to-date attributes. | | `changes` | The changed label attributes and their values. | ## Format of `model.document.created` event ```ruby event: { record: { doc_type: 'Passport', doc_expire: '3020-01-22', doc_number: 'AA1234BB', upload: [], updated_at:"2019-01-28T08:35:29Z", created_at:"2019-01-28T08:35:29ZZ", user: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 2, otp: false, state: "active", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" } } name: "model.document.created" } ``` | Field | Description | | ------------ | --------------------------------- | | `user` | The up-to-date user attributes. | | `doc_type` | Document type. | | `doc_expire` | Experation time for document. | | `doc_number` | Document number. | | `upload` | Array of updaded objects | | `updated_at` | Time of document object creation | | `created_at` | Time of last document update | ## Format of `system.user.email.confirmation.token` event ```ruby event: { record: { user: { uid: "ID739065AFD3", email: "example@barong.io", role: "member", level: 0, otp: false, state: "pending", created_at: "2019-01-28T09:03:50Z", updated_at: "2019-01-28T09:03:50Z" }, language: "EN", domain: "www.barong.io", token: "eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NDg2NjYyMzAsImV4cCI6MTU0ODY3MjIzMCwic3ViIjoiY29uZmlybWF0aW9uIiwiaXNzIjoiYmFyb25nIiwiYXVkIjpbInBlYXRpbyIsImJhcm9uZyJdLCJqdGkiOiI5OWJkNzFkMjU2NTdlMmI1YzI1MCIsImVtYWlsIjoiYWRtaW4xMjNAYmFyb25nLmlvIiwidWlkIjoiSUQ3MzkwNjVBRkQzIn0.OI5tL9kV6cA1JBAy7G5iqd3WplxcB-waHYKFjm83koMEpx2Hlw9fksq5lip5cIHTjR8i3ambFL40OaCwDNc1jAiDsHwuv2nLswgi88_M1G8KVFylboQdtgmH_cZiz-Y-51Fq2oqEID5QyJnsSMSJbfspb6A0JGT_V-SPK4WFZw43F_RKhlZBCrxojljMwd20rGqFPYirMgUpsfiW0_-mESXzQ7UK1eA8mYO7Id4y6JR2Yoo-JTloEnBL1M189tOz6LqmmQB0M_QjTiHG3y9I97Med3StgVziYo9qog9kJXyPuXbboddg__5WEhMcWbaToohoiT5UvpVJHKfgxEVaDg" }, name: "system.user.email.confirmation.token" } ``` | Field | Description | | ---------- | ------------------------------------------------ | | `user` | The up-to-date user attributes. | | `language` | The language. | | `domain` | The domain name of barong. | | `token` | Valid confirm-acc jwt token (mandatory param for user confirmation endpoint) `/identity/users/email/confirm_code`. | ## Format of `system.user.email.confirmed` event ```ruby event: { record: { user: { uid: "IDB1629BFE9E", email: "example@barong.io", role: "member", level: 0, otp: false, state: "active", created_at: "2019-01-28T10:17:27Z", updated_at: "2019-01-28T10:17:45Z" }, language: "EN", domain: "www.barong.io" }, name: "system.user.email.confirmed" } ``` | Field | Description | | ---------- | -------------------------------- | | `user` | The up-to-date user attributes. | | `language` | The language. | | `domain` | The domain name of barong. | ## Format of `system.user.password.reset.token` event ```ruby event: { record: { user: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 0, otp: false, state: "pending", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" }, language: "EN", domain: "www.barong.io", token: "eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1NDg2NjQ1OTUsImV4cCI6MTU0ODY3MDU5NSwic3ViIjoicmVzZXQiLCJpc3MiOiJiYXJvbmciLCJhdWQiOlsicGVhdGlvIiwiYmFyb25nIl0sImp0aSI6IjRhY2IzM2IzYmE2NDc0ZjY1YTI5IiwiZW1haWwiOiJhZG1pbjEyQGJhcm9uZy5pbyIsInVpZCI6IklEMzBERDBERDk4NiJ9.Rie4LCbkV0jVBbhMoceYx8a9uDA-ea9D1v790zlIqP_EY8Iue_OOKXYWiC1Y-55MPicFbknBILjZlPewvAF8ZrhqIt04ROsgBdDGEUGY_SnLWhXzqSx9-v_o_w2MVjLOUxvRBm6sD0RvL-_5LmOcLqhYtf7ZPUnPDwsvhDedqDfbXPEvI7OK2SZ-1uPAOg1IMOX1k7xaDt5I1Wp-Knr2DmEgwNYbIjaXraComYcMdtVSuYVJAufgA0kTADMeT3cV3jzGy9dNfs8heMCtf5tr72IbL0_N0VeUQj9uaPDUr4ntsYk7gOPmA3RSVrSismtYdBXA9oLA0b0YfOctiY9dqg" }, name: "system.user.password.reset.token" } ``` | Field | Description | | ---------- | ------------------------------------------------ | | `user` | The up-to-date user attributes. | | `language` | The language. | | `domain` | The domain name of barong. | | `token` | Valid reset-pass jwt token (mandatory param for password reset endpoint) `/identity/users/password/confirm_code`. | ## Format of `system.user.account.deleted` event ```ruby event: { record: { user: { uid: "IDB1629BFE9E", email: "example@barong.io", role: "member", level: 1, otp: false, state: "deleted", created_at: "2019-01-28T10:17:27Z", updated_at: "2019-01-28T10:17:45Z", } }, name: "system.user.account.deleted" } ``` | Field | Description | | ---------- | ------------------------------------------------ | | `user` | The up-to-date user attributes. | ## Format of `system.user.password.reset` event ```ruby event: { record: { user: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 0, otp: false, state: "pending", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T09:42:36Z" } }, name: "system.user.password.reset" } ``` | Field | Description | | ---------- | ------------------------------------------------ | | `user` | The up-to-date user attributes. | ## Format of `system.user.password.change` event ```ruby event: { record: { user: { uid: "IDC554ED1D0F", email: "example@barong.io", role: "member", level: 0, otp: false, state: "active", created_at: "2019-01-09T15:54:56Z", updated_at: "2019-01-28T09:59:03Z" } }, name: "system.user.password.change" } ``` | Field | Description | | --------- | ------------------------------------------------- | | `user` | The up-to-date user attributes. | ## Format of `system.document.verified` event ```ruby event: { record: { user: { uid: "IDC554ED1D0F", email: "example@barong.io", role: "member", level: 0, otp: false, state: "active", created_at: "2019-01-09T15:54:56Z", updated_at: "2019-01-28T09:59:03Z" }, id: 1, key: "something", value: "verified" }, name: "system.document.verified" } ``` | Field | Description | | --------- | ------------------------------------------------- | | `user` | The up-to-date user attributes. | ## Format of `system.document.rejected` event ```ruby event: { record: { user: { uid: "IDC554ED1D0F", email: "example@barong.io", role: "member", level: 0, otp: false, state: "active", created_at: "2019-01-09T15:54:56Z", updated_at: "2019-01-28T09:59:03Z" }, id: 1, key: "something", value: "rejected" }, name: "system.document.rejected" } ``` | Field | Description | | --------- | ------------------------------------------------- | | `user` | The up-to-date user attributes. | ## Format of `system.session.create` event ```ruby event: { record: { user: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 0, otp: false, state: "pending", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" }, user_ip: "127.0.0.1", user_agent: "Chrome" }, name: "system.session.create" } ``` ## Producing events using Ruby ```ruby require "bunny" def generate_jwt(jwt_payload) Kernel.abort "Please, see «Overview of RabbitMQ message» for implementation guide." end Bunny.run host: "localhost", port: 5672, username: "guest", password: "guest" do |session| channel = session.channel exchange = channel.direct("barong.events.model") jwt_payload = { iss: "barong", jti: SecureRandom.uuid, iat: Time.now.to_i, exp: Time.now.to_i + 60, event: { record: { uid: "ID30DD0DD986", email: "example@barong.io", role: "member", level: 0, otp: false, state: "pending", created_at: "2019-01-28T08:35:29Z", updated_at: "2019-01-28T08:35:29Z" }, name: "model.user.created" } } exchange.publish(generate_jwt(jwt_payload), routing_key: "user.created") end ``` IMPORTANT: Don't forget to implement the logic for JWT exception handling! ## Producing events using `rabbitmqadmin` `rabbitmqadmin publish routing_key=user.created payload=JWT exchange=barong.events.model` Don't forget to pass environment variable `JWT`. ## Consuming events using Ruby ```ruby require "bunny" def verify_jwt(jwt_payload) Kernel.abort "Please, see «Overview of RabbitMQ message» for implementation guide." end Bunny.run host: "localhost", port: 5672, username: "guest", password: "guest" do |session| channel = session.channel exchange = channel.direct("barong.events.model") queue = channel.queue("", auto_delete: true, durable: true, exclusive: true) .bind(exchange, routing_key: "user.created") queue.subscribe manual_ack: true, block: true do |delivery_info, metadata, payload| Kernel.puts verify_jwt(JSON.parse(payload)).fetch(:event) channel.ack(delivery_info.delivery_tag) rescue => e channel.nack(delivery_info.delivery_tag, false, true) end end ``` IMPORTANT: Don't forget to implement the logic for JWT exception handling!