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:

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):

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

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

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

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

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

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

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

event: {
  record: {
    doc_type: 'Passport',
    doc_expire: '2020-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

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

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

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

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

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

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

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

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

  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

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

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!