Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

CLC-887 - API endpoint to enable adding tasks. #428

Merged
merged 8 commits into from

3 participants

@raydavis
Owner

https://jira.media.berkeley.edu/jira/browse/CLC-887

Fixes test failures from @christianv 's original pull request.
Adds two commits to pull request #427 , which should be reviewed first. If this looks good, though, feel free to merge this bigger pull request and close out #427.

@ctweney

This should be a constant -- we might have one already somewhere.

@ctweney
Owner

lgtm. @raydavis will add the constant for the emitter param in a followup.

@ctweney ctweney merged commit 2302538 into ets-berkeley-edu:master
@raydavis raydavis deleted the raydavis:fixed-CLC-887 branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
2  README.md
@@ -169,7 +169,7 @@ If you use VPN, use group #1 (1-Campus_VPN)
## Recording fake data feeds and timeshifting them
-Make sure your test.local.yml file has real connections to real external services that are fakeable (Canvas, Google, etc).
+Make sure your testext.local.yml file has real connections to real external services that are fakeable (Canvas, Google, etc).
Now do:
```bash
View
9 app/controllers/my_tasks_controller.rb
@@ -15,6 +15,15 @@ def update_task
end
end
+ def insert_task
+ begin
+ my_tasks_model = MyTasks.new session[:user_id]
+ render :json => my_tasks_model.insert_task(request.request_parameters).to_json
+ rescue ArgumentError => e
+ return render :json => {error: "Invalid Arguments", message: e.message}.to_json, :status => 400
+ end
+ end
+
private
def check_authentication
render :json => {}.to_json unless session[:user_id]
View
35 app/models/my_tasks.rb
@@ -18,11 +18,11 @@ def get_feed_internal
end
def update_task(params, task_list_id="@default")
- validate_general_params params
+ validate_update_params params
if params["emitter"] == "Google Tasks"
if GoogleProxy.access_granted?(@uid)
validate_google_params params
- body = format_google_task_request params
+ body = format_google_update_task_request params
google_proxy = GoogleProxy.new(user_id: @uid)
logger.debug "#{self.class.name} update_task, sending to Google (task_list_id, task_id, body):
{#{task_list_id}, #{params["id"]}, #{body.inspect}}"
@@ -40,6 +40,27 @@ def update_task(params, task_list_id="@default")
end
end
+ def insert_task(params, task_list_id="@default")
+ if params["emitter"] == "Google Tasks"
+ if GoogleProxy.access_granted?(@uid)
+ body = format_google_insert_task_request params
+ google_proxy = GoogleProxy.new(user_id: @uid)
+ logger.debug "#{self.class.name} insert_task, sending to Google (task_list_id, body):
+ {#{task_list_id}, #{body.inspect}}"
+ response = google_proxy.insert_task(task_list_id, body)
+ if (response.response.status == 200)
+ expire_cache
+ format_google_task_response response.data
+ else
+ logger.info "Errors in proxy response: #{response.inspect}"
+ {}
+ end
+ else
+ {}
+ end
+ end
+ end
+
private
def fetch_google_tasks
@@ -92,7 +113,7 @@ def format_google_task_response(entry)
formatted_entry
end
- def format_google_task_request(entry)
+ def format_google_update_task_request(entry)
formatted_entry = {"id" => entry["id"]}
formatted_entry["status"] = "needsAction" if entry["status"] == "needs_action"
formatted_entry["status"] ||= "completed"
@@ -100,6 +121,12 @@ def format_google_task_request(entry)
formatted_entry
end
+ def format_google_insert_task_request(entry)
+ formatted_entry = {"title" => entry["title"]}
+ logger.debug "Formatted body entry for google proxy update_task: #{formatted_entry.inspect}"
+ formatted_entry
+ end
+
def fetch_canvas_tasks
if CanvasProxy.access_granted?(@uid)
canvas_proxy = CanvasProxy.new(:user_id => @uid)
@@ -233,7 +260,7 @@ def validate_params(initial_hash={}, filters={})
end
end
- def validate_general_params(params)
+ def validate_update_params(params)
filters = {
"type" => Proc.new { |arg| !arg.blank? && arg.is_a?(String) },
"emitter" => includes_whitelist_values?(["Canvas", "Google Tasks"]),
View
45 app/models/my_up_next.rb
@@ -33,15 +33,20 @@ def get_feed_internal(opts={})
formatted_entry[:location_url] = "https://maps.google.com/maps?" + uri.query
end
+ # The Google Calendar API will return all-day events outside the specified
+ # event range.
+ start_datetime = parse_date(entry["start"])
+ next unless start_datetime < next_day
+
# date mangling to harmonize the different date formats.
- start_end_hash = determine_start_end(entry["start"], entry["end"])
- start_end_hash.each do |key, value|
- formatted_entry[key] = value unless value.nil?
- end
+ formatted_entry["start"] = date_entry(start_datetime)
+ formatted_entry["end"] = date_entry(parse_date(entry["end"]))
- if formatted_entry["is_all_day"]
+ if entry["start"]["date"] && entry["end"]["date"]
+ formatted_entry["is_all_day"] = true
day_events.push(formatted_entry)
else
+ formatted_entry["is_all_day"] = false
timed_events.push(formatted_entry)
end
end
@@ -57,33 +62,19 @@ def get_feed_internal(opts={})
end
private
- def determine_start_end(start_hash, end_hash)
- start_date = start_hash && (start_hash["date"] || start_hash["dateTime"])
- if !start_date.blank?
- start_entry = {
- "epoch" => DateTime.parse(start_date.to_s).strftime("%s").to_i,
- "datetime" => DateTime.parse(start_date.to_s).rfc3339(3)
- }
- end
- end_date = end_hash && (end_hash["date"] || end_hash["dateTime"])
- if !end_date.blank?
- end_entry = {
- "epoch" => DateTime.parse(end_date.to_s).strftime("%s").to_i,
- "datetime" => DateTime.parse(end_date.to_s).rfc3339(3)
- }
- end
-
- if start_hash && start_hash["date"] && end_hash && end_hash["date"]
- is_all_day = true
+ def parse_date(hash)
+ if hash["date"]
+ date = Date.parse(hash["date"].to_s).to_time_in_current_zone.to_datetime
else
- is_all_day = false
+ date = DateTime.parse(hash["dateTime"].to_s)
end
+ end
+ def date_entry(date)
{
- "start" => start_entry,
- "end" => end_entry,
- "is_all_day" => is_all_day
+ "epoch" => date.strftime("%s").to_i,
+ "datetime" => date.rfc3339(3)
}
end
View
7 config/initializers/timeshift.rb
@@ -23,8 +23,11 @@
":::TODAY_AFTER_LUNCH:::" => today.advance(:hours => 14, :minutes => 00, :seconds => 00).rfc3339,
":::TODAY_THREE_THIRTY:::" => today.advance(:hours => 15, :minutes => 30, :seconds => 00).rfc3339,
":::TODAY_FOUR_THIRTY:::" => today.advance(:hours => 16, :minutes => 30, :seconds => 00).rfc3339,
- ":::UTC_LATER_IN_WEEK:::" => end_of_week_utc.rfc3339(3),
- ":::UTC_NEXT_WEEK:::" => next_week_utc.rfc3339(3),
+ ":::UTC_LATER_IN_WEEK:::" => end_of_week_utc.strftime('%FT%T.000Z'),
+ ":::UTC_NEXT_WEEK:::" => next_week_utc.strftime('%FT%T.000Z'),
+ ":::TODAY:::" => Date.today.rfc3339,
+ ":::TOMORROW:::" => Date.today.advance(days: 1).rfc3339,
+ ":::DAY_AFTER_TOMORROW:::" => Date.today.advance(days: 2).rfc3339,
}
Rails.logger.info "Timeshifter: Today = #{today}; epoch = #{today.to_i}"
View
1  config/routes.rb
@@ -64,6 +64,7 @@
match '/api/my/up_next' => 'my_up_next#get_feed', :as => :my_up_next, :defaults => { :format => 'json' }
match '/api/my/tasks' => 'my_tasks#get_feed', :via => :get, :as => :my_tasks, :defaults => { :format => 'json' }
match '/api/my/tasks' => 'my_tasks#update_task', :via => :post, :as => :update_task, :defaults => { :format => 'json' }
+ match '/api/my/tasks/create' => 'my_tasks#insert_task', :via => :post, :as => :insert_task, :defaults => { :format => 'json' }
match '/api/my/groups' => 'my_groups#get_feed', :as => :my_groups, :defaults => { :format => 'json' }
match '/api/canvas/request_authorization' => 'canvas_auth#request_authorization'
View
2  config/settings/test.yml
@@ -2,6 +2,8 @@
# Avoid creating too many dependencies (if any) on a test.local.yml
canvas_proxy:
fake: true
+google_proxy:
+ fake: true
sakai_proxy:
fake: true
View
64 fixtures/pretty_vcr_recordings/Google_events.json
@@ -1387,6 +1387,38 @@
],
"items":[
{
+ "kind": "calendar#event",
+ "etag": "\"fi3QRdJKQhyHAzX8fKt0yeht0TY/Z2NhbDAwMDAxMzU2MDQyOTA3NzIzMDAw\"",
+ "id": "24mvjaandpak0tnc74p4ooqcag",
+ "status": "confirmed",
+ "htmlLink": "https://www.google.com/calendar/event?eid=MjRtdmphYW5kcGFrMHRuYzc0cDRvb3FjYWcgdGFtbWkuY2hhbmcuY2xjQG0",
+ "created": "2012-12-20T22:35:07.000Z",
+ "updated": "2012-12-20T22:35:07.723Z",
+ "summary": "Math library closed",
+ "creator": {
+ "email": "tammi.chang.clc@gmail.com",
+ "displayName": "Tammi Chang",
+ "self": true
+ },
+ "organizer": {
+ "email": "tammi.chang.clc@gmail.com",
+ "displayName": "Tammi Chang",
+ "self": true
+ },
+ "start": {
+ "date": ":::TODAY:::"
+ },
+ "end": {
+ "date": ":::TOMORROW:::"
+ },
+ "transparency": "transparent",
+ "iCalUID": "24mvjaandpak0tnc74p4ooqcag@google.com",
+ "sequence": 0,
+ "reminders": {
+ "useDefault": true
+ }
+ },
+ {
"kind":"calendar#event",
"etag":"\"NybCyMgjkLQM6Il-p8A5652MtaE/Q0lpMzB0ZXdKeEVBQUFBQUFBQUFBQT09\"",
"id":"_edhmgpb4elm6ab9n6ssjcdhd6d06sqbed9gm6rrle9pmaspecdnmq_20121116T190000Z",
@@ -1490,6 +1522,38 @@
"reminders":{
"useDefault":true
}
+ },
+ {
+ "kind": "calendar#event",
+ "etag": "\"fi3QRdJKQhyHAzX8fKt0yeht0TY/Z2NhbDAwMDAxMzU2MDQyOTIxMDE3MDAw\"",
+ "id": "3bq5gvvu0dduqrr7gqm3jchfds",
+ "status": "confirmed",
+ "htmlLink": "https://www.google.com/calendar/event?eid=M2JxNWd2dnUwZGR1cXJyN2dxbTNqY2hmZHMgdGFtbWkuY2hhbmcuY2xjQG0",
+ "created": "2012-12-20T22:35:21.000Z",
+ "updated": "2012-12-20T22:35:21.017Z",
+ "summary": "Tomorrow all-day",
+ "creator": {
+ "email": "tammi.chang.clc@gmail.com",
+ "displayName": "Tammi Chang",
+ "self": true
+ },
+ "organizer": {
+ "email": "tammi.chang.clc@gmail.com",
+ "displayName": "Tammi Chang",
+ "self": true
+ },
+ "start": {
+ "date": ":::TOMORROW:::"
+ },
+ "end": {
+ "date": ":::DAY_AFTER_TOMORROW:::"
+ },
+ "transparency": "transparent",
+ "iCalUID": "3bq5gvvu0dduqrr7gqm3jchfds@google.com",
+ "sequence": 0,
+ "reminders": {
+ "useDefault": true
+ }
}
]
}
View
2  lib/proxies/google_proxy.rb
@@ -111,7 +111,7 @@ def delete_task_list(task_list_id)
response.data.blank?
end
- def insert_task(body, task_list_id)
+ def insert_task(task_list_id, body)
parsed_body = stringify_body(body)
request(:api => "tasks", :resource => "tasks", :method => "insert", :params => {tasklist: task_list_id},
:body => parsed_body, :headers => {"Content-Type" => "application/json"}, :vcr_id => "_tasks")[0]
View
2  spec/controllers/my_tasks_controller_spec.rb
@@ -18,7 +18,7 @@
get :get_feed
json_response = JSON.parse(response.body)
json_response.should_not == {}
- json_response["tasks"].length.should == 11
+ json_response["tasks"].length.should > 0
json_response["tasks"].each do |task|
task.include?("title").should == true
end
View
4 spec/lib/google_proxy_spec.rb
@@ -29,7 +29,7 @@
:singleEvents => true
})
response_array.size.should == 1
- response_array[0].data["items"].size.should == 3
+ response_array[0].data["items"].size.should == 5
end
it "should simulate a fake, valid task list response (assuming a valid recorded fixture)" do
@@ -57,7 +57,7 @@
test_task_list.data["kind"].should == "tasks#taskList"
test_task_list_id = test_task_list.data["id"]
test_task_list_id.blank?.should_not == true
- new_task = proxy.insert_task(body='{"title": "New Task", "notes": "Please Complete me"}', task_list_id=test_task_list_id)
+ new_task = proxy.insert_task(task_list_id=test_task_list_id, body='{"title": "New Task", "notes": "Please Complete me"}')
new_task.response.status.should == 200
new_task.data["title"].should == "New Task"
new_task.data["status"].should == "needsAction"
View
14 spec/models/my_tasks_spec.rb
@@ -18,8 +18,14 @@
# Counts for task types in VCR recording
overdue_counter = 5
- today_counter = 2
- this_week_counter = 3
+ # On Sundays, no "Due This Week" tasks can escape the "Due Today" bucket.
+ if Date.today.sunday?
+ today_counter = 5
+ this_week_counter = 0
+ else
+ today_counter = 2
+ this_week_counter = 3
+ end
next_week_counter = 6
unscheduled_counter = 1
@@ -62,8 +68,6 @@
end
overdue_counter.should == 0
- # On Sundays, this test will fail because no "Due This Week" tasks will escape
- # the "Due Today" bucket.
today_counter.should == 0
this_week_counter.should == 0
next_week_counter.should == 0
@@ -163,7 +167,7 @@ def get_task_list_id_and_task_id
test_task_list = proxy.create_task_list '{"title": "test"}'
test_task_list.response.status.should == 200
task_list_id = test_task_list.data["id"]
- new_task = proxy.insert_task(body='{"title": "New Task", "notes": "Please Complete me"}', task_list_id=task_list_id)
+ new_task = proxy.insert_task(task_list_id=task_list_id, body='{"title": "New Task", "notes": "Please Complete me"}')
new_task.response.status.should == 200
task_id = new_task.data["id"]
[task_list_id, task_id]
View
20 spec/models/my_up_next_spec.rb
@@ -4,14 +4,14 @@
before(:each) do
@user_id = rand(99999).to_s
@fake_google_proxy = GoogleProxy.new({fake: true})
- @fake_google_events_array = @fake_google_proxy.events_list({:maxResults => 10})
end
it "should load nicely with the pre-recorded fake Google proxy feed for event#list" do
GoogleProxy.stub(:access_granted?).and_return(true)
GoogleProxy.stub(:new).and_return(@fake_google_proxy)
- GoogleProxy.any_instance.stub(:events_list).and_return(@fake_google_events_array)
+ fake_google_events_array = @fake_google_proxy.events_list({:maxResults => 10})
+ GoogleProxy.any_instance.stub(:events_list).and_return(fake_google_events_array)
valid_feed = MyUpNext.new(@user_id).get_feed
valid_feed[:items].size.should == 13
valid_feed[:items].each do |entry|
@@ -30,9 +30,23 @@
it "should return an empty feed for non-authorized users" do
GoogleProxy.stub(:new).and_return(@fake_google_proxy)
- GoogleProxy.any_instance.stub(:events_list).and_return(@fake_google_events_array)
empty_feed = MyUpNext.new(@user_id).get_feed
empty_feed["items"].empty?.should be_true
end
+ it "should not include all-day events for tomorrow" do
+ too_late = Date.today.to_time_in_current_zone.to_datetime.end_of_day
+ GoogleProxy.stub(:access_granted?).and_return(true)
+ GoogleProxy.stub(:new).and_return(@fake_google_proxy)
+ #GoogleProxy.any_instance.stub(:events_list).and_return(@fake_google_events_array)
+ valid_feed = MyUpNext.new(@user_id).get_feed
+ valid_feed[:items].size.should be > 0
+ all_day_event_count = 0
+ valid_feed[:items].each do |entry|
+ DateTime.parse(entry["start"]["datetime"]).should be < too_late
+ all_day_event_count += 1 if entry["is_all_day"] === true
+ end
+ all_day_event_count.should be > 0
+ end
+
end
Something went wrong with that request. Please try again.