This lives in the x_endpoint_spec.rb file.
RSpec.describe "Contacts Endpoint", :type => :feature do
describe "fetching the root collection" do
it "returns base collection response", :skip_auth do
get "/contacts"
expect(collection["href"]).to eq("http://example.org/v3/contacts")
end
end
describe "fetching specific objects" do
end
describe "creating objects" do
end
describe "updating objects" do
end
describe "destroying objects" do
end
describe "searching for objects" do
end
end
This lives in the x_endpoint_spec.rb file.
RSpec.describe "Contacts Endpoint", :type => :feature do
describe "fetching specific objects" do
it "returns 404 on missing" do
get "/contacts/1"
expect(last_response).to be_not_found
end
it "returns single requested object" do
team = create_team
member = create_auth_member(:team_id => team.id)
contact = create_contact(:member_id => member.id)
get "/contacts/#{contact.id}"
expect(items[0].id).to eq(contact.id)
end
end
end
rspec/support/object_creation_helpers.rb
This file defines all the helpers that you will be using to write specs.
i.e. create_team, create_contact, create_auth_member
If a specific function doesn't exist it will use meta programming to figure out the ruby object to create. i.e. create_sms_gateway
def create_team(attributes = {})
plan = attributes.delete(:plan) { FREE_PLAN }
defaults = {:sport_id => 1}
super(defaults.merge(attributes))
.tap { |t| assign_plan(t, :plan_id => plan.id) }
end
Collection+JSON is a JSON-based read/write hypermedia-type designed to support management and querying of simple collections.
It uses a JSON structure to contains things like collections, refs, templates, queries, and commands.
https://github.com/collection-json/spec
class ContactApp < BaseApp
include Omicron.curation(ContactCurator)
include Omicron.serialization(ContactSerializer)
get "/contacts" do
pipe(default_response, :through => [
:serialize
])
end
get "/contacts/search" do
response = curate(artifact, :action => :search)
serialize(response)
end
end
We use the include magic to
inject serialize into the app.
class Response
include Virtus.value_object
values do
attribute :status, Integer, :default => 200
attribute :headers, Hash, :default => {}
attribute :collection, Array, :default => []
attribute :original_collection, Array, :default => []
attribute :page_information, Hash
attribute :error_message, String
end
end
Omicron.serialization takes the response and will return one of three things:
class ContactApp < BaseApp
include Omicron.serialization(ContactSerializer)
get "/contacts/search" do
response = curate(artifact, :action => :search)
serialize(response)
end
end
All serializers inherit from Scribe which deals with the details of serialization. This allows you to only need a few functions in your serializer.
class ContactSerializer < Scribe
private
def search_url(*args)
search_contacts_url(*args)
end
def base_collection
...
end
def as_item(object)
...
end
end
class ContactSerializer < Scribe
private
def search_url(*args)
search_contacts_url(*args)
end
end
Returned for every request. Contains:
version -- the current version of the api
href -- href of this collection
rel -- unique identifier
template -- for creating/updating
links -- associated links
queries -- list of queries
commands -- list of commands
class ContactSerializer < Scribe
private
def base_collection
{
collection: {
version: API_VERSION,
href: contacts_url,
rel: "contacts",
template: {
data: [
{name: "label", value: nil},
{name: "type", value: Contact.type}
]
},
links: [
{rel: "contact_email_addresses", href: contact_email_addresses_url},
{rel: "self", href: request_url}
],
queries: [
{
rel: "search",
href: search_contacts_url,
data: [
{name: "team_id", value: nil},
{name: "can_receive_push_notifications", value: nil}
]
}
],
commands: [
{
rel: "create_bulk_contacts",
href: create_bulk_contacts_contacts_url,
data: [
{name: "team_id", value: nil},]
]
}
]
}
}
end
end
Called for every item in response.collection.
href -- href of this collection
data -- data for the item
links -- associated links
class ContactSerializer < Scribe
private
def as_item(object)
{
href: contact_url(object.id),
data: [
{name: "id", value: object.id},
{name: "type", value: object.type},
{name: "address_city", value: object.address_city},
{name: "address_country", value: object.address_country},
{name: "address_state", value: object.address_state},
{name: "address_street1", value: object.address_street1},
{name: "address_street2", value: object.address_street2},
{name: "address_zip", value: object.address_zip}
{name: "created_at", value: object.created_at, type: "DateTime"},
{name: "updated_at", value: object.updated_at, type: "DateTime"},
],
links: [
{
rel: "contact_email_addresses",
href: search_contact_email_addresses_url(:contact_id => object.id)
},
{
rel: "contact_phone_numbers",
href: search_contact_phone_numbers_url(:contact_id => object.id)
},
{rel: "member", href: member_url(object.member_id)},
]
}
end
end
{
"collection": {
"version": "3.454.0",
"href": "http://localhost:3000/contacts",
"rel": "contacts",
"template": {
"data": [
{
"name": "label",
"value": null
},
{
"name": "address_street1",
"value": null
},
...
{
"name": "type",
"value": "contact"
}
]
},
"links": [
{
"rel": "contact_email_addresses",
"href": "http://localhost:3000/contact_email_addresses"
},
....
{
"rel": "self",
"href": "http://localhost:3000/contacts"
}
],
"queries": [
{
"rel": "search",
"href": "http://localhost:3000/contacts/search",
"data": [
{
"name": "team_id",
"value": null
},
]
}
]
}
}
/contacts
{
"collection": {
"version": "3.454.0",
"href": "http://localhost:3000/contacts",
"rel": "contacts",
"template": {
...
},
"links": [
....
],
"queries": [
...
],
"items": [
{
"href": "http://localhost:3000/contacts/1",
"data": [
{
"name": "id",
"value": 1
},
{
"name": "type",
"value": "contact"
},
{
"name": "member_id",
"value": 63
},
{
"name": "created_at",
"value": "2017-05-10T17:28:05Z",
"type": "DateTime"
},
],
"links": [
{
"rel": "contact_email_addresses",
"href": "http://localhost:3000/contact_email_addresses/search?contact_id=1"
},
],
"rel": "contact-1"
}
]
}
}
/contacts/1
Responsibilities
Converts response into Collection JSON
https://github.com/collection-json/spec