Skip to content
This repository was archived by the owner on Dec 3, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
82e7d3c
Client for interacting with Airtable
hpjaj Apr 30, 2018
3376336
Service class to fetch mentor request data
hpjaj Apr 30, 2018
6bc4676
Specs for the happy path when fetching mentor request data
hpjaj Apr 30, 2018
6cb8168
No longer need two gems now that we have a client
hpjaj Apr 30, 2018
316ee35
Creates secrets and env vars for Airtable
hpjaj May 5, 2018
ebf3f96
Creates a custom Airtable error class
hpjaj May 5, 2018
61aec46
Tests the 429 rate limit exceeded case
hpjaj May 5, 2018
91321a8
Creates route, controller, and controller tests
hpjaj May 5, 2018
9edbc33
DRY’s up the similar calls to get new records
hpjaj May 6, 2018
40c0c9a
Calls HTTParty directly and cleans up URL
hpjaj May 6, 2018
fe5154b
Client method to POST a record to Airtable
hpjaj May 6, 2018
e850f3e
Client test coverage
hpjaj May 6, 2018
69210cc
Method to create a record a new Mentor Request in Airtable
hpjaj May 6, 2018
4a7bd99
Mentorship POSTing test coverage
hpjaj May 6, 2018
1498f07
Route and controller action to create new Mentor Requests in Airtable
hpjaj May 6, 2018
18c9be1
Controller test coverage for fetching and creating Airtable data
hpjaj May 6, 2018
a2cca36
Updates the :create action response to a 201
hpjaj May 6, 2018
e9e88d0
API docs for new Mentorship endpoints
hpjaj May 6, 2018
d2bae06
Removes unneeded instance variable assignment
hpjaj May 7, 2018
e25209e
Removes Operationcode::Airtable as it has been stale for 6 months
hpjaj May 12, 2018
487ee89
Fixes RuboCop offenses
hpjaj May 12, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ gem 'httparty'
gem 'jwt'
gem 'lograge'
gem 'logstash-event'
gem 'operationcode-airtable'
gem 'operationcode-slack'
gem 'pg', '~> 0.18'
gem 'puma', '~> 3.0'
Expand Down
6 changes: 0 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ GEM
activerecord (>= 4.2.8)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
airtable (0.0.9)
httparty
arbre (1.1.1)
activesupport (>= 3.0.0)
arel (7.1.4)
Expand Down Expand Up @@ -170,9 +168,6 @@ GEM
nio4r (2.0.0)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
operationcode-airtable (0.3.0)
activesupport
airtable
operationcode-slack (0.6.0)
activesupport
httparty
Expand Down Expand Up @@ -309,7 +304,6 @@ DEPENDENCIES
lograge
logstash-event
mocha
operationcode-airtable
operationcode-slack
pg (~> 0.18)
pry-rails
Expand Down
94 changes: 94 additions & 0 deletions apiary.apib
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,100 @@ API endpoints that Operation Code's Rails backend makes available to its React f
"bio": "incentivize vertical e-services"
}

## Mentorship | Collection [/api/v1/airtable/mentorships]

### Fetches all the mentor, services, and skillsets Airtables data [GET]

+ Response 200 (application/json)

{
"mentors" => [
{
"id" => "rec0SDZDK2DiW4PY9",
"name" => "Jack Smith"
},
...
],
"services" => [
{
"id" => "rec3ZQMCQsKPKlE2C",
"name" => "Mock Interview"
},
...
],
"skillsets" => [
{
"id" => "rec5FYXU6jTnXKPHg",
"name" => "Javascript"
},
...
]
}

+ Response 422 (application/json)

{
error: "Some error message"
}

## Mentorship | Create [/api/v1/airtable/mentorships{?slack_user,services,skillsets,additional_details,mentor_requested}]

+ Parameters

+ slack_user (string, required) - The requestee's Slack user handle
+ services (string, required) - A comma-separated list of Service ids
+ skillsets (string, required) - A comma-separated list of Skillset names
+ additional_details (text, optional) - Any additional details the requestee wants to share around their request
+ mentor_requested (string, optional) - The id of the mentor being requested

### Create a Mentor Request in Airtable (verified users only)[POST]

+ Request (application/json)

+ Headers

Authorization: Bearer Access-Token

+ Body

{
slack_user: "hpjaj",
services: "rec3ZQMCQsKPKlE2C",
skillsets: "Javascript",
additional_details: "Some additional description.",
mentor_requested: "rec0SDZDK2DiW4PY9"
}

+ Response 201 (application/json)

+ Body

{
"id" => "recTvgUmwkGMG2HNc",
"fields" => {
"Slack User" => "johnsmith",
"Service" => [
"rec3ZQMCQsKPKlE2C"
],
"Skillsets" => [
"Web (Frontend Development)",
"Java",
"Study Help"
],
"Additional Details" => "Some test description.",
"Mentor Requested" => [
"rec0SDZDK2DiW4PY9"
],
"Created At" => "2018-05-06T19:28:15.000Z"
},
"createdTime" => "2018-05-06T19:28:15.645Z"
}

+ Response 422 (application/json)

{
error: "User must be verified to request mentor services"
}

## Request | Request a Mentor [/api/v1/requests{?id,user_id,details,language,service_id,requested_mentor_id,status,assigned_mentor_id}]
+ Parameters
Expand Down
43 changes: 43 additions & 0 deletions app/controllers/api/v1/airtable/mentorships_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Api
module V1
module Airtable
class MentorshipsController < ApiController
before_action :authenticate_user!

def index
mentorship_data = ::Airtable::Mentorship.new.mentor_request_data

render json: mentorship_data, status: :ok
rescue ::Airtable::Error => e
render json: { error: e.message }, status: :unprocessable_entity
end

def create
verify_user!

mentor_request = ::Airtable::Mentorship.new.create_mentor_request(mentorship_params)

render json: mentor_request, status: :created
rescue ::Airtable::Error => e
render json: { error: e.message }, status: :unprocessable_entity
end

private

def mentorship_params
params.permit(
:slack_user,
:services,
:skillsets,
:additional_details,
:mentor_requested
)
end

def verify_user!
raise ::Airtable::Error, 'User must be verified to request mentor services' unless current_user.verified?
end
end
end
end
end
35 changes: 0 additions & 35 deletions app/jobs/add_user_to_airtables_job.rb

This file was deleted.

80 changes: 80 additions & 0 deletions app/lib/airtable/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module Airtable
class Client
include HTTParty

attr_reader :base_id, :headers, :base_url

def initialize
api_key = Rails.application.secrets.airtable_api_key
base_id = Rails.application.secrets.airtable_base_id
@headers = { 'Authorization' => "Bearer #{api_key}" }
@base_url = "https://api.airtable.com/v0/#{base_id}/"
end

# Fetches all of the records for the passed Airtable
#
# @param table [String] Name of the table in Airtable
# @return [Hash] The parsed_response from Airtable
#
def get_records_for(table)
response = HTTParty.get(url_for(table), headers: headers)

return_value_for(response)
end

# Creates a new record in the passed Airtable
#
# @param table [String] Name of the table in Airtable
# @param body [JSON] JSON string of request body attributes
# @return [Hash] The parsed_response from Airtable
#
def post_record(table, body)
response = HTTParty.post(
url_for(table),
headers: update_headers,
body: body
)

return_value_for(response)
end

private

def url_for(table)
base_url + escaped(table)
end

# URL-encodes the passed string for cases where the string has spaces
#
# @param url_string [String] a URL string
# @return [String] String where any spaces are URL-encoded
# @example escaped('Mentor Request') => 'Mentor%20Request'
# @see http://ruby-doc.org/stdlib-2.3.3/libdoc/uri/rdoc/URI/Escape.html
#
# rubocop:disable Lint/UriEscapeUnescape
def escaped(url_string)
URI.escape url_string
end
# rubocop:enable Lint/UriEscapeUnescape

# If the Airtable rate limit is reached, a 429 status code is returned
# @see https://airtable.com/appSqQz7spgg0I1jQ/api/docs#curl/ratelimits
#
def return_value_for(response)
case response.code
when 200
response.parsed_response
when 429
raise Airtable::Error, 'Exceeded Airtable rate limit. Wait 30 seconds for subsequent requests.'
else
raise Airtable::Error, response.parsed_response
end
end

def update_headers
headers.merge(
'Content-Type' => 'application/json'
)
end
end
end
4 changes: 4 additions & 0 deletions app/lib/airtable/error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Airtable
class Error < StandardError
end
end
63 changes: 63 additions & 0 deletions app/lib/airtable/mentorship.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module Airtable
class Mentorship
attr_reader :client

def initialize
@client = Airtable::Client.new
end

# Fetches all the records from the mentors, services, and skillsets Airtables,
# and re-formats the data into a custom hash
#
# @return [Hash] Hash of mentor, services, and skillsets data
#
def mentor_request_data
{
mentors: data_for('Mentors', name_key: 'Full Name'),
services: data_for('Services'),
skillsets: data_for('Skillsets')
}
end

# Creates a new mentor request record in the Mentor Request Airtable
#
# @param body [Hash] Hash of mentor request attributes
# @return [Hash] Hash of the newly created mentor request Airtable record
#
def create_mentor_request(body)
request_body = {
'fields' => {
'Slack User' => body[:slack_user],
'Service' => format_for_posting(body[:services]),
'Skillsets' => format_for_posting(body[:skillsets]),
'Additional Details' => body[:additional_details],
'Mentor Requested' => format_for_posting(body[:mentor_requested])
}
}.to_json

client.post_record('Mentor Request', request_body)
end

private

def data_for(table, name_key: 'Name')
records = client.get_records_for(table)

records['records'].map do |record|
{
id: record['id'],
name: record.dig('fields', name_key)
}
end
end

# Converts a comman separated string into an array of strings
#
# @param data_string [String] Comma separated strings of data
# @return [Array] An array of stripped strings
#
def format_for_posting(data_string)
data_string.split(',').map(&:strip)
end
end
end
5 changes: 0 additions & 5 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,13 @@ def name

def welcome_user
invite_to_slack
add_to_airtables
add_to_send_grid
end

def invite_to_slack
SlackJobs::InviterJob.perform_later(email)
end

def add_to_airtables
AddUserToAirtablesJob.perform_later(self)
end

def add_to_send_grid
AddUserToSendGridJob.perform_later(self)
end
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
namespace :users do
post '/passwords/reset', to: 'passwords#reset'
end

namespace :airtable do
resources :mentorships, only: [:index, :create]
end
end
end
end
Loading