Skip to content

Instantly share code, notes, and snippets.

@j4p3
Created February 3, 2016 18:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save j4p3/a4936de7c5b8ebc17d59 to your computer and use it in GitHub Desktop.
Save j4p3/a4936de7c5b8ebc17d59 to your computer and use it in GitHub Desktop.
DayWon API readme

DAYWON-API

API service provider for DayWon CRM system.

##Sections

##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 ids 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:

  1. GET /users/auth/google_oauth2. Client browser will be redirected to Google's auth page.

  2. 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:

  1. Install RVM and Bundler
  2. Install Ruby 2.0.0
  3. Install Redis
  4. Intall Postgres (mac) or (otherwise)
  5. Install the Heroku Toolbelt.
  6. Run bundle install in the application root
  7. Obtain .env file and place it in application root (can download from heroku application with heroku config:get when a heroku address has been added as a git remote repository)
  8. Obtail 'google_oath2.yml' file and place it in config folder
  9. Run redis-server
  10. Run postgres (depends on how postgres was installed)
  11. Run foreman start in the application root

###To deploy to Heroku:

  1. Run heroku login and enter Heroku account credentials with access to DW repositories
  2. 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
  3. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment