API service provider for DayWon CRM system.
##Sections
- Resources
- Search
- Events
- Tasks
- Contacts
- Tags
- Users
- Orphans
- Dates
- Authorization
- Deployment
- Custom Rake Tasks
##Resources Each has CRUD actions through POST GET PATCH/PUT DELETE. All requests to resources are expected to come with a header parameters for authentication:
headers: {
'X-AUTHENTICATION-TOKEN': 'CLByUrJEwP-J-sgCFaFs',
'X-AUTHENTICATION-EMAIL': 'bonner.jp@gmail.com'
}
###Credential files (not in version control)
- config/google_oauth2.yml
- .env
###Instances
- development: daywon-api-development-2zhbdbnpsp.elasticbeanstalk.com
- staging: staging-krqwhjugxs.elasticbeanstalk.com
- alpha production: daywon-api-production-3efm3ryaf2.elasticbeanstalk.com
###Search /search?show=&include[]=&sort=<asc/desc> /search?show=contacts&include[]=tags&sort=desc /search?show=events&include[]=tags&include[]=contacts&sort=asc
The search endpoint allows for returning nested arrays of objects and their relations. Its parameters are:
show
: the type for the root array (contacts
,events
,tasks
,tags
)include[]
: the type(s) for the root array's subordinate nodes (contacts
,events
,tasks
,tags
)sort
: the sorting direction for root array nodes. Due date for tasks, start datetime for events, and created date for contacts (asc
,desc
)
The result is an array of primary objects, each containing an array of ids for subordinate objects e.g. task_ids
.
###Events /events /events/#{event_id} Event object:
"event": {
"id": integer, #required
"title": string(255),
"calendar_title": string(255),
"recurring": boolean,
"updated_at": datetime(RFC-3339),
"description": string(255),
"location": string(255),
"link": string(255),
"start_datetime": datetime(RFC-3339),
"end_datetime": datetime(RFC-3339),
"start_date": date(RFC-3339),
"end_date": date(RFC-3339),
"is_orphan": boolean,
"is_all_day": boolean,
"subscribed": boolean,
"recurring_event_id": string(255),
"tags": integer
}
#####Dates and Datetimes
An DateTime: 2014-03-14T15:56:02-04:00
A Date: 2014-03-14T00:00:00+00:00
RFC-3339 expects the same format, minus the h-m-s attributes. The difference is the event
attribute it's attached to: Date will be converted to ActiveRecord's :date
, while DateTime will be converted to :datetime
. The only events with a start_date
or end_date
attribute should be all-day events with a "is_all_day": true
attribute.
####Updating Recurring events
There is a special case where an update is meant to be applied to all subsequent events in the series. In this case, a boolean apply_to_following
parameter should be included in the event object. A working example is below.
{
"event": {
"id":373,
"title":"Cecilia Birthday",
"calendar_title":"#{calendar.id}",
"recurring":true,
"apply_to_following":true,
"updated_at":"2014-02-10T03:53:41.367Z",
"description":null,
"link":null,
"location":null,
"start_datetime":"2015-10-20T00:00:00.000Z",
"end_datetime":"2015-10-21T00:00:00.000Z"
}
}
####Deleting Recurring events
The same boolean apply_to_following
parameter, if attached to the body of a DELETE
request, will apply the depetion to all events in the series.
####Creating Recurring Events
DW represents all events as single instances, but it is convenient to be able to create a series of events all at once. To do this, attach a recurrence
attribute to your event, as shown below:
{
"event": {
"title": string(255),
"recurring":true,
recurrence: {
frequency: variable,
ends_after: {
occurrences: integer,
date: date(RFC-3339)
}
}
}
}
The frequency
attribute can have a value of:
day
week
month
year
weekday
MWF
TTH
(INTEGER)
, where the integer given is the number of days before the event will occur again.
The ends_after
attribute should contain either an occurrences
integer or a date
string.
###Contacts /contacts /contacts/#{contact_id} Contact object:
"contact": {
"id": integer, # required
"name": string(255),
"emails": [
{"email": string(255)},
...
]
"notes": text,
"organization": string(255),
"phones": [
{"number": string(255)},
...
]
"address": string(255),
"updated_at": datetime(RFC-3339),
"created_at": datetime(RFC-3339),
"tags": integer
}
####Extended Properties
Contacts can have arbitrary, user-defined properties like birthday
or favorite_ice_cream_flavor
. To set these properties, nest them as key-value objects inside an array node titled extended_properties
, as seen below:
{
"contact": {
"name": "A POSTed DW contact",
"email": "foo@bar.com",
"notes": "No Notes",
"extended_properties": [
{"key": "birthday", "value": "today"},
{"key": "today's news:", "value": "HELL YES MOTHERHUGGERS"}
]
}
}
###Tasks /tasks /tasks/#{task_id} Task object:
"task": {
"id": integer, #required
"title": string(255),
"updated_at": datetime(RFC-3339),
"self_link": string(255),
"notes": string(255),
"status": boolean,
"priority": integer
"due": datetime(RFC-3339),
"is_orphan": boolean,
"created_at": datetime(RFC-3339),
"tags": integer
}
###Tags /tags /tags/#{tag_id} Tag object:
"tag": {
"id": integer, #required
"name": string(255),
"count": integer #read-only
}
count
represents the number of objects attached to that particular tag. The My Contacts
tag is special in that it is an autogenerated Google group that is pulled into DayWon as a tag, and that adding this tag to a contact will cause the contact to join that group within Google.
###Reports /reports_admin Administrator Report:
{
"report": [
{"id":1,"name":"Ms. Carissa Collier","email":"may@deckow.net","active":true},
{"id":3,"name":"JP Bonner","email":"bonner.jp@gmail.com","active":true}, ...
]
}
Must be called by a user with administrative access, or will return 401 unauthorized. "Active" indicates activity within the last week.
Tooltips:
/reports/tooltip
/tooltip
Tooltip Index
GET /tooltips
{
"tooltip": {
{
"id":1,
"prompt": string(255),
"content": string(255)
}
}
}
Tooltip create/update
PUT/POST /tooltips
{
"prompt": string(255),
"content": string(255)
}
Automatically finds the tooltip with a matching prompt and updates its content
The prompt
attribute is expected to be unique, and is used as a flag for the specific tooltip that is being referenced. Possible values might include strings like entrance
, navigation
, filtering
, or other topics that users are being prompted to notice.
###Users
User Info:
GET /users/info
{
"name": string(255),
"email": string(255),
"avatar": string(255), (complete URL)
}
User Settings:
PUT/POST /users/settings
{
"user": {
"sort_by_last_name": boolean,
"show_new_user_popups": boolean,
"display_contact_notes": boolean,
"push_info_to_webmail": boolean,
"receive_email": boolean,
"time_zone": string
}
}
Timezones should be one of the strings listed in the IANA timezone database.
User Sync Status:
GET /users/syncing
{
"syncing": boolean
}
Describes current push/pull status from Google. If true
, application currently has workers running to push/pull from external DBs.
User Deletion:
DELETE /users
Destroys all records for the currently authenticated user, triggering 'goodbye' email. Long response time.
###Orphans /orphans Orphans Index: (non-associated events and tasks in the application)
{
"orphans": {
"events":[
{
"id":92,
"title":"Options",
"updated_at":"2014-02-13T04:01:19.477Z",
"self_link":"https://www.googleapis.com/tasks/v1/lists/MTU0OTk4MjkwMzMwMDUzNzEyNjc6Njk4NDYwMjc5OjA/tasks/MTU0OTk4MjkwMzMwMDUzNzEyNjc6Njk4NDYwMjc5OjE0Mjg5MTc3OTg",
"notes":"Task notes",
"status":false,
"due":null,
"created_at":"2014-02-13T03:41:56.349Z"
}, ...
],
"tasks":[
{
"id":92,
"title":"Options",
"updated_at":"2014-02-13T04:01:19.477Z",
"self_link":"https://www.googleapis.com/tasks/v1/lists/MTU0OTk4MjkwMzMwMDUzNzEyNjc6Njk4NDYwMjc5OjA/tasks/MTU0OTk4MjkwMzMwMDUzNzEyNjc6Njk4NDYwMjc5OjE0Mjg5MTc3OTg",
"notes":"Task notes",
"status":false,
"due":null,
"created_at":"2014-02-13T03:41:56.349Z"
}, ...
],
"tags":[
{
"id": integer,
"name": string(255)
}, ...
]
}
}
####Associating Objects
Objects in the application (Event
, Task
, Contact
, and Tag
), can all be associated with one another. During a create
or update
action, attach an array of the id
s to associate the element with. For instance, when updating a contact:
{
"contact": {
"name": "Name Update",
"tag_ids":[1,3,4]
}
}
This will update the given contact's name and associate it with tags 1, 3, and 4. The same structure works when updating other objects and other associations (task_ids
, contact_ids
, etc).
###Dates
Some objects (contacts, tags) do not have any date/datetime properties. To receive an array of these objects' associated dates, pass the query parameter ?dates=true
at their index level view.
Example:
/contacts?dates=true
"contact": {
"id": integer, # required
"name": string(255),
"dates" [
datetime(RFC-3339),
datetime(RFC-3339)
]
###Auth /users/auth/google_oauth2
####Auth Process for clients:
-
GET /users/auth/google_oauth2. Client browser will be redirected to Google's auth page.
-
Client approves on Google. Client browser will be redirected to the client application root with a query string containing authentication_token and user_email. These must be attached as a header to every subsequent request.
headers: { 'X-AUTHENTICATION-TOKEN': 'CLByUrJEwP-J-sgCFaFs', 'X-AUTHENTICATION-EMAIL': 'bonner.jp@gmail.com' }
(Note: in order for this to be secure, we’ll have to force HTTPS.)
##Deployment
###To run this application locally:
- Install RVM and Bundler
- Install Ruby 2.0.0
- Install Redis
- Intall Postgres (mac) or (otherwise)
- Install the Heroku Toolbelt.
- Run
bundle install
in the application root - Obtain
.env
file and place it in application root (can download from heroku application withheroku config:get
when a heroku address has been added as a git remote repository) - Obtail 'google_oath2.yml' file and place it in
config
folder - Run
redis-server
- Run postgres (depends on how postgres was installed)
- Run
foreman start
in the application root
###To deploy to Heroku:
- Run
heroku login
and enter Heroku account credentials with access to DW repositories - Add the following remotes to your local git repository:
git@heroku.com:daywon-api-staging.git
for staging &git@heroku.com:daywon-api-prod.git
for production - Run
git push [REMOTE]
with one of these remotes to deploy.
####Rake
A custom rake task exists for resetting the database without deleting users. To make rake execute the task, enter rake admin:dump_all
. To run this command on heroku, the input would look like heroku run rake admin:dump_all -a daywon-api-staging
on staging.
A custom rake tasks exists for promoting users to admin. To execute the task, enter rake admin:make_admin[user_email@domain.com]
, where the text in brackets is the email address of the User to be recieve admin access.
###Additional Information This Rails application requires the presence of
- Ruby (RVM satisfies this)
- various Ruby libraries (Bundler satisfies this)
- a Redis key-value store running on its default port (6379)
- a Postgres database running on its default port (5432)
- a valid Google application registration with authorization for:
- Calendar API
- Contacts API
- Google+ API
- Tasks API
The Google application registration is managed through Google's Developer Console and the client_id
and client_secret
variables must be copied from that console into the config/google_oath2.yml
file. Different instances of the application running on different URLs must be registered seperately with Google.
Other useful Heroku commands for logging, scaling can be found in the guides. Heroku also provides config var tools, which we use to generate the .env file.