From 50ee53a74f33914910ce5164c0d39f26c3dd30f2 Mon Sep 17 00:00:00 2001 From: abreckner Date: Thu, 30 Apr 2020 10:59:31 +1000 Subject: [PATCH 1/3] Add dry types gem and Job::Add schema --- Gemfile.lock | 23 +++++++++++++++++++++++ lib/xpm_ruby.rb | 1 + lib/xpm_ruby/schema/job/add.rb | 21 +++++++++++++++++++++ lib/xpm_ruby/types.rb | 7 +++++++ spec/xpm_ruby/schema/job/add_spec.rb | 27 +++++++++++++++++++++++++++ xpm_ruby.gemspec | 2 ++ 6 files changed, 81 insertions(+) create mode 100644 lib/xpm_ruby/schema/job/add.rb create mode 100644 lib/xpm_ruby/types.rb create mode 100644 spec/xpm_ruby/schema/job/add_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index f3b7ba6..9c5fd3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ PATH xpm_ruby (0.1.0) activesupport builder + dry-types faraday ox (~> 2.13) @@ -22,6 +23,28 @@ GEM coderay (1.1.2) concurrent-ruby (1.1.6) diff-lcs (1.3) + dry-configurable (0.11.5) + concurrent-ruby (~> 1.0) + dry-core (~> 0.4, >= 0.4.7) + dry-equalizer (~> 0.2) + dry-container (0.7.2) + concurrent-ruby (~> 1.0) + dry-configurable (~> 0.1, >= 0.1.3) + dry-core (0.4.9) + concurrent-ruby (~> 1.0) + dry-equalizer (0.3.0) + dry-inflector (0.2.0) + dry-logic (0.6.1) + concurrent-ruby (~> 1.0) + dry-core (~> 0.2) + dry-equalizer (~> 0.2) + dry-types (0.15.0) + concurrent-ruby (~> 1.0) + dry-container (~> 0.3) + dry-core (~> 0.4, >= 0.4.4) + dry-equalizer (~> 0.2, >= 0.2.2) + dry-inflector (~> 0.1, >= 0.1.2) + dry-logic (~> 0.5, >= 0.5) faraday (1.0.1) multipart-post (>= 1.2, < 3) i18n (1.8.2) diff --git a/lib/xpm_ruby.rb b/lib/xpm_ruby.rb index 491f2b7..bb1b63d 100644 --- a/lib/xpm_ruby.rb +++ b/lib/xpm_ruby.rb @@ -10,6 +10,7 @@ class Unauthorized < Error; end require "xpm_ruby/client" require "xpm_ruby/connection" require "xpm_ruby/job" +require "xpm_ruby/schema/job/add" require "xpm_ruby/staff" require "xpm_ruby/template" require "xpm_ruby/version" diff --git a/lib/xpm_ruby/schema/job/add.rb b/lib/xpm_ruby/schema/job/add.rb new file mode 100644 index 0000000..2ff2094 --- /dev/null +++ b/lib/xpm_ruby/schema/job/add.rb @@ -0,0 +1,21 @@ +require_relative "../../types" + +module XpmRuby + module Schema + module Job + Add = Types::Hash.schema( + Name: Types::String, + Description: Types::String, + ClientID: Types::Coercible::String, + ContactID?: Types::Coercible::String, + StartDate: Types::Coercible::String, + DueDate: Types::Coercible::String, + ClientNumber?: Types::Coercible::String, + ID?: Types::Coercible::String, + TemplateID?: Types::Coercible::String, + CategoryID?: Types::Coercible::String, + Budget?: Types::Coercible::String + ) + end + end +end diff --git a/lib/xpm_ruby/types.rb b/lib/xpm_ruby/types.rb new file mode 100644 index 0000000..c136ea3 --- /dev/null +++ b/lib/xpm_ruby/types.rb @@ -0,0 +1,7 @@ +require "dry-types" + +module XpmRuby + module Types + include Dry::Types.module + end +end \ No newline at end of file diff --git a/spec/xpm_ruby/schema/job/add_spec.rb b/spec/xpm_ruby/schema/job/add_spec.rb new file mode 100644 index 0000000..95ca859 --- /dev/null +++ b/spec/xpm_ruby/schema/job/add_spec.rb @@ -0,0 +1,27 @@ +require "spec_helper" + +module XpmRuby + module Schema + RSpec.describe(Job) do + context "with a valid Add schema" do + it "should not raise an error" do + hash = { Name: "Joe Bloggs", Description: "New Job", ClientID: 1234, StartDate: 20091023, DueDate: 20091023 } + expect { Job::Add[hash] }.not_to raise_error + end + + it "should coerce the types" do + hash = { Name: "Joe Bloggs", Description: "New Job", ClientID: 1234, StartDate: 20091023, DueDate: 20091023 } + add = Job::Add[hash] + expect(add[:ClientID]).to eql("1234") + end + end + + context "with an invalid Add schema" do + it "should raise an error" do + hash = { Name: "Joe Bloggs" } + expect { Job::Add[hash] }.to raise_error(Dry::Types::MissingKeyError, ":Description is missing in Hash input") + end + end + end + end +end diff --git a/xpm_ruby.gemspec b/xpm_ruby.gemspec index fad5f02..2e4a862 100644 --- a/xpm_ruby.gemspec +++ b/xpm_ruby.gemspec @@ -42,4 +42,6 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency("activesupport") spec.add_runtime_dependency("builder") + + spec.add_runtime_dependency("dry-types") end From 2ce75e08f5e9541e025e16e006841477cb8378c1 Mon Sep 17 00:00:00 2001 From: abreckner Date: Thu, 30 Apr 2020 11:40:42 +1000 Subject: [PATCH 2/3] Add function and test for add job --- lib/xpm_ruby/job.rb | 10 ++++ spec/vcr_cassettes/xpm_ruby/job/add.yml | 65 +++++++++++++++++++++++++ spec/xpm_ruby/job_spec.rb | 22 +++++++++ 3 files changed, 97 insertions(+) create mode 100644 spec/vcr_cassettes/xpm_ruby/job/add.yml diff --git a/lib/xpm_ruby/job.rb b/lib/xpm_ruby/job.rb index a29d9de..cc8e3b5 100644 --- a/lib/xpm_ruby/job.rb +++ b/lib/xpm_ruby/job.rb @@ -17,5 +17,15 @@ def current(access_token:, xero_tenant_id:) response["Jobs"]["Job"] end + + def add(access_token:, xero_tenant_id:, job:) + validated_job = XpmRuby::Schema::Job::Add[job] + + response = Connection + .new(access_token: access_token, xero_tenant_id: xero_tenant_id) + .post(endpoint: "job.api/add", data: validated_job.to_xml(root: "Job")) + + response["Job"] + end end end diff --git a/spec/vcr_cassettes/xpm_ruby/job/add.yml b/spec/vcr_cassettes/xpm_ruby/job/add.yml new file mode 100644 index 0000000..867a6cc --- /dev/null +++ b/spec/vcr_cassettes/xpm_ruby/job/add.yml @@ -0,0 +1,65 @@ +--- +http_interactions: +- request: + method: post + uri: https://api.xero.com/practicemanager/3.0/job.api/add + body: + encoding: UTF-8 + string: | + + + Joe Bloggs + New Job + 24097642 + 20091023 + 20091023 + + headers: + User-Agent: + - Faraday v1.0.1 + Authorization: + - Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFDQUY4RTY2NzcyRDZEQzAyOEQ2NzI2RkQwMjYxNTgxNTcwRUZDMTkiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJISy1PWm5jdGJjQW8xbkp2MENZVmdWY09fQmsifQ.eyJuYmYiOjE1ODgyMDg3MjksImV4cCI6MTU4ODIxMDUyOSwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS54ZXJvLmNvbSIsImF1ZCI6Imh0dHBzOi8vaWRlbnRpdHkueGVyby5jb20vcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoiNDkyMjZBNjIzMzY0NDVFM0FGQUM5QTQ4MkJGOUUyN0UiLCJzdWIiOiIwY2FmMWU4MWYyZWE1MzdkYWIxYjYzNTY3NTc2ZDk3ZSIsImF1dGhfdGltZSI6MTU4ODIwODU3NSwieGVyb191c2VyaWQiOiJmYzI5MDBjNy0wNjcyLTQzOGItOTNkMS1hOGMyNTBmZDg5MjkiLCJnbG9iYWxfc2Vzc2lvbl9pZCI6IjZmYWRiYTVmYWI3NTQ0NDliNjkwMGE1NzI3OGVlMjc5IiwianRpIjoiZmU2MjUyNzNhNmEwMTI4YjI2N2IxYmI1YWQzMGVkODgiLCJzY29wZSI6WyJlbWFpbCIsInByb2ZpbGUiLCJvcGVuaWQiLCJwcmFjdGljZW1hbmFnZXIiLCJvZmZsaW5lX2FjY2VzcyJdfQ.iL5C3Lcc1KuxTcIMUNUdKfqRoB13flB46dyQ2fUcQesF_lloofR9qWzt1uijc91qQPnkyrgb_Kv2aGvR3YvXXM1naK2Wy87wfAJwonkAGm6IcIIeukgvvJMt3bNZvow-lrk2eb9Wfcz7xt_nJSqL9lr8YV9T5f56IWJzZJzwAiNYz4F1lmqzKMwX5fUU9gYHucgIgQAwDaD3AGPdB-AC71ZNw2Zwsshebv89CbSbpw7rGU9gEhHnI7PjXXYwRn430Zh9k0ruD7IQWNRMh26oS_PR4zIEFDGBn5xBdnm9jLWl9juQUkn0OPQvAwkEuM54AXSWaAgyeasdOtDNjD8JMA + xero-tenant-id: + - '0791dc22-8611-4c1c-8df7-1c5453d0795b' + content_type: + - application/xml + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 200 + message: OK + headers: + content-type: + - text/xml; charset=utf-8 + server: + - Kestrel + x-daylimit-remaining: + - '4997' + x-minlimit-remaining: + - '58' + xero-correlation-id: + - 04e383a3-f80a-4076-b4fa-c5ac40eca5c8 + content-length: + - '412' + expires: + - Thu, 30 Apr 2020 01:14:38 GMT + cache-control: + - max-age=0, no-cache, no-store + pragma: + - no-cache + date: + - Thu, 30 Apr 2020 01:14:38 GMT + connection: + - keep-alive + x-client-tls-ver: + - tls1.3 + body: + encoding: UTF-8 + string: OKJ000031Joe + BloggsNew Job24097642ABC + CoPlanned2009-10-23T00:00:002009-10-23T00:00:0044385850 + http_version: null + recorded_at: Thu, 30 Apr 2020 01:14:38 GMT +recorded_with: VCR 5.1.0 diff --git a/spec/xpm_ruby/job_spec.rb b/spec/xpm_ruby/job_spec.rb index 553804a..089f09f 100644 --- a/spec/xpm_ruby/job_spec.rb +++ b/spec/xpm_ruby/job_spec.rb @@ -29,5 +29,27 @@ module XpmRuby expect(first_job["CompletedDate"]).to be_nil end end + + describe ".add" do + let(:xero_tenant_id) { "0791dc22-8611-4c1c-8df7-1c5453d0795b" } + let(:access_token) { "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFDQUY4RTY2NzcyRDZEQzAyOEQ2NzI2RkQwMjYxNTgxNTcwRUZDMTkiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJISy1PWm5jdGJjQW8xbkp2MENZVmdWY09fQmsifQ.eyJuYmYiOjE1ODgyMDg3MjksImV4cCI6MTU4ODIxMDUyOSwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS54ZXJvLmNvbSIsImF1ZCI6Imh0dHBzOi8vaWRlbnRpdHkueGVyby5jb20vcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoiNDkyMjZBNjIzMzY0NDVFM0FGQUM5QTQ4MkJGOUUyN0UiLCJzdWIiOiIwY2FmMWU4MWYyZWE1MzdkYWIxYjYzNTY3NTc2ZDk3ZSIsImF1dGhfdGltZSI6MTU4ODIwODU3NSwieGVyb191c2VyaWQiOiJmYzI5MDBjNy0wNjcyLTQzOGItOTNkMS1hOGMyNTBmZDg5MjkiLCJnbG9iYWxfc2Vzc2lvbl9pZCI6IjZmYWRiYTVmYWI3NTQ0NDliNjkwMGE1NzI3OGVlMjc5IiwianRpIjoiZmU2MjUyNzNhNmEwMTI4YjI2N2IxYmI1YWQzMGVkODgiLCJzY29wZSI6WyJlbWFpbCIsInByb2ZpbGUiLCJvcGVuaWQiLCJwcmFjdGljZW1hbmFnZXIiLCJvZmZsaW5lX2FjY2VzcyJdfQ.iL5C3Lcc1KuxTcIMUNUdKfqRoB13flB46dyQ2fUcQesF_lloofR9qWzt1uijc91qQPnkyrgb_Kv2aGvR3YvXXM1naK2Wy87wfAJwonkAGm6IcIIeukgvvJMt3bNZvow-lrk2eb9Wfcz7xt_nJSqL9lr8YV9T5f56IWJzZJzwAiNYz4F1lmqzKMwX5fUU9gYHucgIgQAwDaD3AGPdB-AC71ZNw2Zwsshebv89CbSbpw7rGU9gEhHnI7PjXXYwRn430Zh9k0ruD7IQWNRMh26oS_PR4zIEFDGBn5xBdnm9jLWl9juQUkn0OPQvAwkEuM54AXSWaAgyeasdOtDNjD8JMA" } + let(:job) { { Name: "Joe Bloggs", Description: "New Job", ClientID: "24097642", StartDate: "20091023", DueDate: "20091023" } } + + around(:each) do |example| + VCR.use_cassette("xpm_ruby/job/add") do + example.run + end + end + + it "lists adds a job" do + added_job = service.add(access_token: access_token, xero_tenant_id: xero_tenant_id, job: job) + + expect(added_job["Name"]).to eql(job[:Name]) + expect(added_job["Description"]).to eql(job[:Description]) + expect(added_job["Client"]["ID"]).to eql(job[:ClientID]) + expect(added_job["StartDate"]).to eql("2009-10-23T00:00:00") + expect(added_job["DueDate"]).to eql("2009-10-23T00:00:00") + end + end end end From 38c481b45da57b4be2091c3a529d925a1ad4ede0 Mon Sep 17 00:00:00 2001 From: abreckner Date: Thu, 30 Apr 2020 11:53:42 +1000 Subject: [PATCH 3/3] use string keys instead of symbol keys --- lib/xpm_ruby/schema/job/add.rb | 22 +++++++++++----------- lib/xpm_ruby/types.rb | 4 ++-- spec/xpm_ruby/job_spec.rb | 8 ++++---- spec/xpm_ruby/schema/job/add_spec.rb | 10 +++++----- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/xpm_ruby/schema/job/add.rb b/lib/xpm_ruby/schema/job/add.rb index 2ff2094..22757ca 100644 --- a/lib/xpm_ruby/schema/job/add.rb +++ b/lib/xpm_ruby/schema/job/add.rb @@ -4,17 +4,17 @@ module XpmRuby module Schema module Job Add = Types::Hash.schema( - Name: Types::String, - Description: Types::String, - ClientID: Types::Coercible::String, - ContactID?: Types::Coercible::String, - StartDate: Types::Coercible::String, - DueDate: Types::Coercible::String, - ClientNumber?: Types::Coercible::String, - ID?: Types::Coercible::String, - TemplateID?: Types::Coercible::String, - CategoryID?: Types::Coercible::String, - Budget?: Types::Coercible::String + "Name" => Types::String, + "Description" => Types::String, + "ClientID" => Types::Coercible::String, + "ContactID?" => Types::Coercible::String, + "StartDate" => Types::Coercible::String, + "DueDate" => Types::Coercible::String, + "ClientNumber?" => Types::Coercible::String, + "ID?" => Types::Coercible::String, + "TemplateID?" => Types::Coercible::String, + "CategoryID?" => Types::Coercible::String, + "Budget?" => Types::Coercible::String ) end end diff --git a/lib/xpm_ruby/types.rb b/lib/xpm_ruby/types.rb index c136ea3..8c95b30 100644 --- a/lib/xpm_ruby/types.rb +++ b/lib/xpm_ruby/types.rb @@ -2,6 +2,6 @@ module XpmRuby module Types - include Dry::Types.module + include Dry.Types() end -end \ No newline at end of file +end diff --git a/spec/xpm_ruby/job_spec.rb b/spec/xpm_ruby/job_spec.rb index 089f09f..34da5b7 100644 --- a/spec/xpm_ruby/job_spec.rb +++ b/spec/xpm_ruby/job_spec.rb @@ -33,7 +33,7 @@ module XpmRuby describe ".add" do let(:xero_tenant_id) { "0791dc22-8611-4c1c-8df7-1c5453d0795b" } let(:access_token) { "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFDQUY4RTY2NzcyRDZEQzAyOEQ2NzI2RkQwMjYxNTgxNTcwRUZDMTkiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJISy1PWm5jdGJjQW8xbkp2MENZVmdWY09fQmsifQ.eyJuYmYiOjE1ODgyMDg3MjksImV4cCI6MTU4ODIxMDUyOSwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS54ZXJvLmNvbSIsImF1ZCI6Imh0dHBzOi8vaWRlbnRpdHkueGVyby5jb20vcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoiNDkyMjZBNjIzMzY0NDVFM0FGQUM5QTQ4MkJGOUUyN0UiLCJzdWIiOiIwY2FmMWU4MWYyZWE1MzdkYWIxYjYzNTY3NTc2ZDk3ZSIsImF1dGhfdGltZSI6MTU4ODIwODU3NSwieGVyb191c2VyaWQiOiJmYzI5MDBjNy0wNjcyLTQzOGItOTNkMS1hOGMyNTBmZDg5MjkiLCJnbG9iYWxfc2Vzc2lvbl9pZCI6IjZmYWRiYTVmYWI3NTQ0NDliNjkwMGE1NzI3OGVlMjc5IiwianRpIjoiZmU2MjUyNzNhNmEwMTI4YjI2N2IxYmI1YWQzMGVkODgiLCJzY29wZSI6WyJlbWFpbCIsInByb2ZpbGUiLCJvcGVuaWQiLCJwcmFjdGljZW1hbmFnZXIiLCJvZmZsaW5lX2FjY2VzcyJdfQ.iL5C3Lcc1KuxTcIMUNUdKfqRoB13flB46dyQ2fUcQesF_lloofR9qWzt1uijc91qQPnkyrgb_Kv2aGvR3YvXXM1naK2Wy87wfAJwonkAGm6IcIIeukgvvJMt3bNZvow-lrk2eb9Wfcz7xt_nJSqL9lr8YV9T5f56IWJzZJzwAiNYz4F1lmqzKMwX5fUU9gYHucgIgQAwDaD3AGPdB-AC71ZNw2Zwsshebv89CbSbpw7rGU9gEhHnI7PjXXYwRn430Zh9k0ruD7IQWNRMh26oS_PR4zIEFDGBn5xBdnm9jLWl9juQUkn0OPQvAwkEuM54AXSWaAgyeasdOtDNjD8JMA" } - let(:job) { { Name: "Joe Bloggs", Description: "New Job", ClientID: "24097642", StartDate: "20091023", DueDate: "20091023" } } + let(:job) { { "Name" => "Joe Bloggs", "Description" => "New Job", "ClientID" => "24097642", "StartDate" => "20091023", "DueDate" => "20091023" } } around(:each) do |example| VCR.use_cassette("xpm_ruby/job/add") do @@ -44,9 +44,9 @@ module XpmRuby it "lists adds a job" do added_job = service.add(access_token: access_token, xero_tenant_id: xero_tenant_id, job: job) - expect(added_job["Name"]).to eql(job[:Name]) - expect(added_job["Description"]).to eql(job[:Description]) - expect(added_job["Client"]["ID"]).to eql(job[:ClientID]) + expect(added_job["Name"]).to eql(job["Name"]) + expect(added_job["Description"]).to eql(job["Description"]) + expect(added_job["Client"]["ID"]).to eql(job["ClientID"]) expect(added_job["StartDate"]).to eql("2009-10-23T00:00:00") expect(added_job["DueDate"]).to eql("2009-10-23T00:00:00") end diff --git a/spec/xpm_ruby/schema/job/add_spec.rb b/spec/xpm_ruby/schema/job/add_spec.rb index 95ca859..33cbff2 100644 --- a/spec/xpm_ruby/schema/job/add_spec.rb +++ b/spec/xpm_ruby/schema/job/add_spec.rb @@ -5,21 +5,21 @@ module Schema RSpec.describe(Job) do context "with a valid Add schema" do it "should not raise an error" do - hash = { Name: "Joe Bloggs", Description: "New Job", ClientID: 1234, StartDate: 20091023, DueDate: 20091023 } + hash = { "Name" => "Joe Bloggs", "Description" => "New Job", "ClientID" => 1234, "StartDate" => 20091023, "DueDate" => 20091023 } expect { Job::Add[hash] }.not_to raise_error end it "should coerce the types" do - hash = { Name: "Joe Bloggs", Description: "New Job", ClientID: 1234, StartDate: 20091023, DueDate: 20091023 } + hash = { "Name" => "Joe Bloggs", "Description" => "New Job", "ClientID" => 1234, "StartDate" => 20091023, "DueDate" => 20091023 } add = Job::Add[hash] - expect(add[:ClientID]).to eql("1234") + expect(add["ClientID"]).to eql("1234") end end context "with an invalid Add schema" do it "should raise an error" do - hash = { Name: "Joe Bloggs" } - expect { Job::Add[hash] }.to raise_error(Dry::Types::MissingKeyError, ":Description is missing in Hash input") + hash = { "Name" => "Joe Bloggs" } + expect { Job::Add[hash] }.to raise_error(Dry::Types::ConstraintError, '{"Name"=>"Joe Bloggs"} violates constraints (:Description is missing in Hash input failed)') end end end