jbr 3.12.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/CHANGELOG.md +59 -0
  4. data/README.md +155 -165
  5. data/lib/graphql/client.rb +2 -6
  6. data/lib/graphql/throttled.rb +0 -9
  7. data/lib/jbr/account.rb +44 -15
  8. data/lib/jbr/authorizing.rb +43 -0
  9. data/lib/jbr/collection.rb +14 -0
  10. data/lib/jbr/collections/customers.rb +54 -0
  11. data/lib/jbr/collections/invoices.rb +18 -0
  12. data/lib/jbr/{jobs.rb → collections/jobs.rb} +6 -6
  13. data/lib/jbr/collections/leads.rb +30 -0
  14. data/lib/jbr/collections/locations.rb +47 -0
  15. data/lib/jbr/collections/quotes.rb +18 -0
  16. data/lib/jbr/{visits.rb → collections/visits.rb} +8 -8
  17. data/lib/jbr/error.rb +1 -1
  18. data/lib/jbr/errors/throttled.rb +6 -0
  19. data/lib/jbr/includable.rb +6 -8
  20. data/lib/jbr/listable.rb +12 -34
  21. data/lib/jbr/mock/account.rb +25 -7
  22. data/lib/jbr/mock/invoice.rb +7 -11
  23. data/lib/jbr/mock/invoices.rb +7 -0
  24. data/lib/jbr/mock/jobs.rb +10 -6
  25. data/lib/jbr/mock/leads.rb +7 -0
  26. data/lib/jbr/mock/quote.rb +3 -8
  27. data/lib/jbr/mock/quotes.rb +7 -0
  28. data/lib/jbr/mock/visit.rb +3 -19
  29. data/lib/jbr/mock/visits.rb +9 -5
  30. data/lib/jbr/mock.rb +16 -3
  31. data/lib/jbr/phone.rb +5 -8
  32. data/lib/jbr/querying.rb +57 -0
  33. data/lib/jbr/reader.rb +9 -0
  34. data/lib/jbr/refreshing.rb +0 -8
  35. data/lib/jbr/resources/customer.rb +23 -0
  36. data/lib/jbr/resources/invoice.rb +29 -0
  37. data/lib/jbr/resources/job.rb +20 -0
  38. data/lib/jbr/resources/lead.rb +7 -0
  39. data/lib/jbr/resources/line.rb +15 -0
  40. data/lib/jbr/resources/location.rb +31 -0
  41. data/lib/jbr/resources/quote.rb +15 -0
  42. data/lib/jbr/resources/visit.rb +17 -0
  43. data/lib/jbr/version.rb +1 -1
  44. data/lib/jbr.rb +37 -46
  45. metadata +39 -28
  46. data/lib/jbr/client.rb +0 -98
  47. data/lib/jbr/cliental.rb +0 -14
  48. data/lib/jbr/invoice.rb +0 -43
  49. data/lib/jbr/itemized.rb +0 -9
  50. data/lib/jbr/job.rb +0 -40
  51. data/lib/jbr/line_item.rb +0 -36
  52. data/lib/jbr/mock/client.rb +0 -17
  53. data/lib/jbr/mock/job.rb +0 -31
  54. data/lib/jbr/mock/line_item.rb +0 -10
  55. data/lib/jbr/mock/oauth.rb +0 -22
  56. data/lib/jbr/mock/property.rb +0 -19
  57. data/lib/jbr/mock/request.rb +0 -12
  58. data/lib/jbr/mock/url.rb +0 -9
  59. data/lib/jbr/mocking.rb +0 -30
  60. data/lib/jbr/named.rb +0 -9
  61. data/lib/jbr/oauth.rb +0 -94
  62. data/lib/jbr/properted.rb +0 -17
  63. data/lib/jbr/property.rb +0 -93
  64. data/lib/jbr/quote.rb +0 -25
  65. data/lib/jbr/request.rb +0 -37
  66. data/lib/jbr/resource.rb +0 -26
  67. data/lib/jbr/retriable.rb +0 -20
  68. data/lib/jbr/url.rb +0 -16
  69. data/lib/jbr/visit.rb +0 -24
  70. /data/lib/jbr/{refused.rb → errors/refused.rb} +0 -0
@@ -0,0 +1,7 @@
1
+ module Jbr
2
+ # The one quote an app under test asked {Jbr.mock} to answer with.
3
+ class Mock::Quotes
4
+ # @return [Mock::Quote, nil] quote the app named, whatever ID is asked for.
5
+ def find(_id) = (Mock::Quote.new node: Jbr.mock.quote if Jbr.mock.quote)
6
+ end
7
+ end
@@ -1,23 +1,7 @@
1
1
  module Jbr
2
- # A visit that reads from {Jbr.mock} instead of Jobber.
2
+ # A visit an app under test listed, keyed by the vocabulary's names rather than Jobber's.
3
3
  class Mock::Visit < Visit
4
- # @return [Object, nil] the values the app asked for.
5
- def id = @node[:id]
6
-
7
- def title = @node[:title]
8
-
9
- def job_id = @node[:job_id]
10
-
11
- def client = Mock::Client.new(node: @node.fetch(:client, {}))
12
-
13
- def property = Mock::Property.new(node: @node.fetch(:property, {}))
14
-
15
- def all_day? = @node[:all_day]
16
-
17
- def client_confirmed? = @node[:client_confirmed]
18
-
19
- def starts_at = @node[:starts_at]
20
-
21
- def ends_at = @node[:ends_at]
4
+ # The mock spells every key as the reader is named.
5
+ def self.keys = {}
22
6
  end
23
7
  end
@@ -2,17 +2,21 @@ module Jbr
2
2
  # The visits an app under test asked {Jbr.mock} to answer with. Only the walk is mocked:
3
3
  # narrowing a list, and reading it as records or as IDs, is the same code a real one runs.
4
4
  class Mock::Visits < Visits
5
- # @return [Mock::Visit] the visit the app listed under that ID.
6
- def find(id) = Mock::Visit.new node: listed(id)
5
+ # @param id [String] ID the app filed the visit under.
6
+ # @return [Mock::Visit, nil] visit the app listed under that ID, nil where it listed none.
7
+ def find(id)
8
+ node = Jbr.mock.visits.to_a.find { |visit| visit[:id] == id }
9
+ Mock::Visit.new node: node if node
10
+ end
7
11
 
8
12
  private
9
13
 
10
14
  def walk(_statement)
11
- Enumerator.new { |yielder| selected.each { |it| yielder << Mock::Visit.new(node: it) } }
15
+ Enumerator.new do |yielder|
16
+ selected.each { |node| yielder << Mock::Visit.new(node: node) }
17
+ end
12
18
  end
13
19
 
14
20
  def selected = Jbr.mock.visits.select { |visit| scheduled? visit[:starts_at] }
15
-
16
- def listed(id) = Jbr.mock.visits.to_a.find { |visit| visit[:id] == id }.to_h
17
21
  end
18
22
  end
data/lib/jbr/mock.rb CHANGED
@@ -1,8 +1,21 @@
1
1
  module Jbr
2
- # What an app under test wants Jobber to answer.
2
+ # What an app under test wants Jobber to answer, by the vocabulary's names.
3
3
  class Mock
4
- # The canned answers, each read by the matching Mock resource.
5
- attr_accessor :account, :quote, :job, :invoice, :request, :visits, :jobs, :oauth_url,
4
+ # The canned answers, each read by the matching mock collection.
5
+ attr_accessor :business, :quote, :job, :invoice, :lead, :jobs, :visits, :oauth_url,
6
6
  :oauth_error
7
7
  end
8
+
9
+ class << self
10
+ # Touching it once turns every account over to its mock counterpart.
11
+ # @return [Mock] answers this process gives, created on first use.
12
+ def mock = @mock ||= Mock.new
13
+
14
+ # @return [Boolean] whether an app under test has switched Jobber off.
15
+ def mocked? = !@mock.nil?
16
+
17
+ # Set to nil, hands the accounts back to Jobber.
18
+ # @return [Mock, nil] answers this process gives.
19
+ attr_writer :mock
20
+ end
8
21
  end
data/lib/jbr/phone.rb CHANGED
@@ -4,20 +4,17 @@ module Jbr
4
4
  # What Jobber is asked for wherever a client is read: the number, and what ranks it.
5
5
  SELECTION = 'phones { normalizedPhoneNumber smsAllowed primary }'
6
6
 
7
- # Ten digits whose area code and exchange open with 2 through 9, behind the E.164 +1.
8
- NORTH_AMERICAN = /\A\+1([2-9]\d{2}[2-9]\d{6})\z/
9
-
10
- # The number to reach a client on, without its country code.
11
- # @param phones [Array<Hash>, nil] the numbers as Jobber answered them.
12
- # @return [String, nil] the ten digits to call, or nil where none can be dialed.
7
+ # The primary number first, then one that takes texts, then the rest as filed; the first
8
+ # of them that can be dialed is the one.
9
+ # @param phones [Array<Hash>, nil] numbers as Jobber answered them.
10
+ # @return [String, nil] ten digits to call, or nil where none can be dialed.
13
11
  def self.from(phones)
14
12
  # The index breaks a tie because sort_by does not: two equally ranked numbers would
15
13
  # otherwise swap between runs, and the client would answer a different phone each time.
16
14
  ranked = Array(phones).each_with_index.sort_by do |phone, index|
17
15
  [ phone['primary'] ? 0 : 1, phone['smsAllowed'] ? 0 : 1, index ]
18
16
  end
19
- numbers = ranked.lazy.map { |phone, _| phone['normalizedPhoneNumber'].to_s }
20
- numbers.filter_map { |number| number[NORTH_AMERICAN, 1] }.first
17
+ ranked.lazy.filter_map { |phone, _| Company::Phone.from phone['normalizedPhoneNumber'] }.first
21
18
  end
22
19
  end
23
20
  end
@@ -0,0 +1,57 @@
1
+ module Jbr
2
+ # What credentials do with a statement: post it to Jobber, refresh a stale token once, and
3
+ # raise every refusal as a {Error} so a caller rescues one class.
4
+ module Querying
5
+ # Where every query and mutation is posted.
6
+ ENDPOINT = 'https://api.getjobber.com/api/graphql'
7
+
8
+ # The version of the schema every statement is written against.
9
+ HEADERS = { 'X-JOBBER-GRAPHQL-VERSION' => '2026-04-22' }
10
+
11
+ # The mutation that revokes the app on the account.
12
+ DISCONNECT = <<~GRAPHQL
13
+ mutation Disconnect {
14
+ appDisconnect {
15
+ app { name author }
16
+ userErrors { message }
17
+ }
18
+ }
19
+ GRAPHQL
20
+
21
+ # Nothing here ever sleeps: where Jobber holds the app to a limit it says so, and a caller
22
+ # asking from a background job has a queue that will bring the whole job back later.
23
+ # @param statement [String] query or mutation to run.
24
+ # @param variables [Hash] what the statement takes.
25
+ # @return [Hash] data Jobber answered, or empty when the credentials are dead.
26
+ # @raise [Throttled] where Jobber refused the statement for what it costs.
27
+ # @raise [Error] where Jobber refused the statement, or took a mutation and would not act.
28
+ def query(statement, variables: {})
29
+ data = client.query statement, variables: variables
30
+ refusals = user_errors(data).map { |error| error['message'] }
31
+ raise Error, refusals.join('; ') if refusals.any?
32
+
33
+ data
34
+ rescue GraphQL::Unauthorized
35
+ refresh ? retry : {}
36
+ rescue GraphQL::Throttled => error
37
+ raise Throttled, error.message
38
+ rescue GraphQL::Error => error
39
+ raise Error, error.message
40
+ end
41
+
42
+ # Revoke the credentials on the account. Dead ones have nothing left to revoke.
43
+ def delete
44
+ client.query DISCONNECT
45
+ rescue GraphQL::Unauthorized
46
+ end
47
+
48
+ private
49
+
50
+ def client = GraphQL::Client.new endpoint: ENDPOINT, token: @access_token, headers: HEADERS
51
+
52
+ def user_errors(data)
53
+ fields = data.each_value.select { |field| field.is_a? Hash }
54
+ fields.flat_map { |field| Array(field['userErrors']) }
55
+ end
56
+ end
57
+ end
data/lib/jbr/reader.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Jbr
2
+ # What every kind reached through the credentials shares: the credentials.
3
+ class Reader
4
+ # @param account [Account] credentials to reach Jobber with.
5
+ def initialize(account:)
6
+ @account = account
7
+ end
8
+ end
9
+ end
@@ -5,18 +5,12 @@ module Jbr
5
5
  module Refreshing
6
6
  private
7
7
 
8
- # With no store there is nobody to compare against: refresh, and leave what we end up
9
- # holding for the caller to persist.
10
8
  def refresh
11
9
  return exchange unless @store
12
10
 
13
11
  @store.exclusively { |stored| renew stored }
14
12
  end
15
13
 
16
- # Under the store's lock, holding what it says right now. A token that is no longer the one
17
- # we tried is one somebody else has already replaced, and adopting it asks Jobber nothing —
18
- # which is what keeps a queue of workers from refreshing a hundred times over, each with a
19
- # refresh token the first of them has already spent.
20
14
  def renew(stored)
21
15
  return adopt stored if stored[:access_token] != @access_token
22
16
 
@@ -37,8 +31,6 @@ module Jbr
37
31
  true
38
32
  end
39
33
 
40
- # Refused while holding the freshest refresh token there is, so the grant itself is dead
41
- # rather than our copy being behind.
42
34
  def refused
43
35
  @invalid_at = Time.now
44
36
  @store&.write self
@@ -0,0 +1,23 @@
1
+ module Jbr
2
+ # A person the business works for, who Jobber calls a client.
3
+ class Customer < Company::Customer
4
+ # The node keys Jobber spells otherwise than the vocabulary.
5
+ def self.keys = { name: :firstName, surname: :lastName }
6
+
7
+ # What Jobber calls each flat field of a client. The phones nest: see {Phone}.
8
+ FIELDS = 'id firstName lastName companyName email'
9
+
10
+ # What to ask for wherever a record names its client.
11
+ SELECTION = "client { #{FIELDS} #{Phone::SELECTION} }"
12
+
13
+ # Jobber files nobody without one name or the other, so there is always one to call them.
14
+ # @return [String, nil] first name, or the business's name where a person has none.
15
+ def name = super.presence || attribute(:companyName).presence
16
+
17
+ # @return [String, nil] ten digits they are reached on, nil where none can be dialed.
18
+ def phone = Phone.from @node[:phones]
19
+
20
+ # @return [Array<Location>] places on their file, where the query asked for them.
21
+ def locations = @node.dig(:clientProperties, :nodes).to_a.map { |node| Location.new node: node }
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ module Jbr
2
+ # A bill the business issued for finished work, which Jobber alone answers for.
3
+ class Invoice < Company::Resource
4
+ # What every invoice reads.
5
+ def self.attributes = %i[id amount completed_at issued_at]
6
+
7
+ # The node keys Jobber spells otherwise than the readers.
8
+ def self.keys = { amount: :total, issued_at: :issuedDate }
9
+
10
+ # Jobber lists the jobs an invoice bills, and one it bills one.
11
+ # @return [Job, nil] job the invoice bills, where Jobber listed one.
12
+ def job
13
+ node = @node.dig :jobs, :nodes, 0
14
+ Job.new node: node if node
15
+ end
16
+
17
+ # @return [BigDecimal, nil] what the invoice comes to, in dollars.
18
+ def amount = decimal :amount
19
+
20
+ # @return [Time, nil] moment the billed work was finished, or the bill issued where undated.
21
+ def fulfilled_at = completed_at || issued_at
22
+
23
+ private
24
+
25
+ def completed_at = job&.completed_at
26
+
27
+ def issued_at = time :issued_at
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ module Jbr
2
+ # Work the business accepted and scheduled.
3
+ class Job < Company::Job
4
+ # The node keys Jobber spells otherwise than the vocabulary.
5
+ def self.keys
6
+ { description: :title, notes: :instructions, created_at: :createdAt,
7
+ scheduled_at: :startAt, completed_at: :completedAt, amount: :total, }
8
+ end
9
+
10
+ # @return [Quote, nil] quote the job was won with, where Jobber filed one beside it.
11
+ def quote = record Quote, :quote
12
+
13
+ # @return [Array<Line>] lines the job is made of, empty where the query never asked for
14
+ # them: a page costs what it carries, so nothing nested arrives unasked.
15
+ def lines = @node.dig(:lineItems, :nodes).to_a.map { |node| Line.new node: node }
16
+
17
+ # @return [Location, nil] where the work happens, where it came back beside the job.
18
+ def location = record Location, :property
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Jbr
2
+ # Somebody who asked the business for work, which Jobber files as a request.
3
+ class Lead < Company::Lead
4
+ # @return [Customer, nil] client the request was opened against.
5
+ def customer = record Customer, :client
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module Jbr
2
+ # One line of the work a job is made of.
3
+ class Line < Company::Line
4
+ # The node keys Jobber spells otherwise than the vocabulary.
5
+ def self.keys = { amount: :totalPrice }
6
+
7
+ # The most lines to read off one record. Bounded because Jobber prices a connection by the
8
+ # page it is asked for and prices an unbounded one at its own maximum, so the lines of a
9
+ # page of jobs were charged for as though every job had the largest job's worth of them.
10
+ PAGE = 20
11
+
12
+ # What to ask for wherever a record lists the lines it is made of.
13
+ SELECTION = "lineItems(first: #{PAGE}) { nodes { #{node_keys.join ' '} } }"
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ module Jbr
2
+ # Where work happens, which Jobber calls a property: one address on a client's file.
3
+ class Location < Company::Location
4
+ # The node keys Jobber spells otherwise than the vocabulary.
5
+ def self.keys = { street: :street1, zip: :postalCode }
6
+
7
+ # The address as Jobber answers it, the coordinates tucked inside.
8
+ ADDRESS = 'street1 city postalCode coordinates { latitude longitude }'
9
+
10
+ # What to ask for wherever a record names the place the work happens at.
11
+ # @param customer [Boolean] whether whose place it is comes back beside it, which spares a
12
+ # second query for somebody Jobber already knows the place belongs to.
13
+ # @return [String] selection of a property.
14
+ def self.selection(customer: false)
15
+ [ 'property { id address {', ADDRESS, '}', (Customer::SELECTION if customer), '}' ].
16
+ compact.join ' '
17
+ end
18
+
19
+ # Jobber answers a field it holds nothing for with an empty string as readily as with
20
+ # null, and the two arrive as the same nothing.
21
+ # @param node [Hash] property as Jobber answered it, the address and its coordinates nested.
22
+ def initialize(node: {})
23
+ node = node.with_indifferent_access
24
+ address, coordinates = node[:address].to_h, node.dig(:address, :coordinates).to_h
25
+ super node: node.merge(address, coordinates).compact_blank
26
+ end
27
+
28
+ # @return [Customer, nil] whose place it is, where they came back beside the location.
29
+ def customer = record Customer, :client
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module Jbr
2
+ # A price the business sent to answer a lead, which Jobber calls a request.
3
+ class Quote < Company::Quote
4
+ # Jobber alone files the request a quote answers beside it; the vocabulary knows no such link.
5
+ # @return [Lead, nil] request the quote answers, where Jobber filed one beside it.
6
+ def lead = record Lead, :request
7
+
8
+ # Jobber files the total among the quote's amounts.
9
+ # @return [BigDecimal, nil] what the quote comes to, in dollars.
10
+ def amount
11
+ total = @node.dig :amounts, :total
12
+ BigDecimal total.to_s if total.present?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Jbr
2
+ # One stop of a job: when the work is scheduled to happen, where the job does.
3
+ class Visit < Company::Visit
4
+ # What every visit reads: the vocabulary's names, and whether the client confirmed it,
5
+ # which Jobber alone asks.
6
+ def self.attributes = super + %i[confirmed]
7
+
8
+ # The node keys Jobber spells otherwise than the vocabulary.
9
+ def self.keys
10
+ { description: :title, starts_at: :startAt, ends_at: :endAt, anytime: :allDay,
11
+ confirmed: :clientConfirmed, }
12
+ end
13
+
14
+ # @return [Boolean, nil] whether the client confirmed the visit.
15
+ def confirmed? = attribute :confirmed
16
+ end
17
+ end
data/lib/jbr/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # A Ruby client for the Jobber API.
2
2
  module Jbr
3
3
  # The version of this gem.
4
- VERSION = '3.12.0'
4
+ VERSION = '4.0.0'
5
5
  end
data/lib/jbr.rb CHANGED
@@ -1,68 +1,59 @@
1
1
  require 'json'
2
2
  require 'net/http'
3
3
 
4
- # Only the three Active Support files whose methods are used, rather than the whole of it:
5
- # Jobber answers a field it holds nothing for with an empty string as readily as with null,
6
- # and a caller who validates presence needs those to arrive as the same nothing. The third is
7
- # for the sentence a job's lines read as.
8
- require 'active_support/core_ext/object/blank'
4
+ # The vocabulary every record here answers in, and the Active Support it brings along. One
5
+ # more file of it: an address is sent without the fields a caller left blank.
6
+ require 'company'
9
7
  require 'active_support/core_ext/enumerable'
10
- require 'active_support/core_ext/array/conversions'
11
8
 
12
9
  require 'graphql/error'
13
10
  require 'graphql/unauthorized'
14
11
  require 'graphql/throttled'
15
12
  require 'graphql/client'
16
13
 
17
- require 'jbr/mock'
18
-
19
- require 'jbr/url'
14
+ require 'jbr/version'
20
15
  require 'jbr/error'
21
- require 'jbr/refused'
22
- require 'jbr/retriable'
16
+ require 'jbr/errors/refused'
17
+ require 'jbr/errors/throttled'
23
18
  require 'jbr/token'
24
19
  require 'jbr/refreshing'
25
- # Phone before Cliental, and Cliental before the records that include it: what each asks
26
- # Jobber for about a client is built as they load.
27
- require 'jbr/phone'
28
- require 'jbr/cliental'
29
- require 'jbr/named'
30
- require 'jbr/resource'
31
- require 'jbr/request'
32
- require 'jbr/oauth'
20
+ require 'jbr/querying'
21
+ require 'jbr/authorizing'
22
+ require 'jbr/mock'
33
23
 
34
- require 'jbr/account'
35
- # Property comes before Client and Visit: their queries read its fields as they load.
36
- require 'jbr/property'
37
- require 'jbr/properted'
24
+ # Phone before Customer, and Location before Customers: what each asks Jobber for is built as
25
+ # it loads. Every record before the collection that reads it, for the same reason.
26
+ require 'jbr/phone'
27
+ require 'jbr/resources/customer'
28
+ require 'jbr/resources/location'
29
+ require 'jbr/resources/line'
30
+ require 'jbr/resources/job'
31
+ require 'jbr/resources/visit'
32
+ require 'jbr/resources/quote'
33
+ require 'jbr/resources/invoice'
34
+ require 'jbr/resources/lead'
35
+
36
+ require 'jbr/reader'
37
+ require 'jbr/collection'
38
38
  require 'jbr/includable'
39
39
  require 'jbr/listable'
40
- # LineItem before Itemized, and both before Job: the lines a job is made of are asked for
41
- # by a constant the include reads as it loads.
42
- require 'jbr/line_item'
43
- require 'jbr/itemized'
44
- require 'jbr/client'
45
- require 'jbr/invoice'
46
- require 'jbr/job'
47
- require 'jbr/jobs'
48
- require 'jbr/quote'
49
- require 'jbr/visit'
50
- require 'jbr/visits'
40
+ require 'jbr/collections/jobs'
41
+ require 'jbr/collections/visits'
42
+ require 'jbr/collections/quotes'
43
+ require 'jbr/collections/invoices'
44
+ require 'jbr/collections/customers'
45
+ require 'jbr/collections/locations'
46
+ require 'jbr/collections/leads'
47
+ require 'jbr/account'
51
48
 
52
- require 'jbr/mock/oauth'
53
- require 'jbr/mock/client'
54
- require 'jbr/mock/property'
55
- require 'jbr/mock/quote'
56
- require 'jbr/mock/line_item'
57
- require 'jbr/mock/job'
58
49
  require 'jbr/mock/jobs'
59
- require 'jbr/mock/invoice'
60
- require 'jbr/mock/request'
61
- require 'jbr/mock/account'
62
- require 'jbr/mock/url'
63
50
  require 'jbr/mock/visit'
64
51
  require 'jbr/mock/visits'
65
-
66
- require 'jbr/mocking'
52
+ require 'jbr/mock/quote'
53
+ require 'jbr/mock/quotes'
54
+ require 'jbr/mock/invoice'
55
+ require 'jbr/mock/invoices'
56
+ require 'jbr/mock/leads'
57
+ require 'jbr/mock/account'
67
58
 
68
59
  require 'jbr/event'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jbr
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.12.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: company
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: minitest
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -110,48 +124,45 @@ files:
110
124
  - lib/graphql/unauthorized.rb
111
125
  - lib/jbr.rb
112
126
  - lib/jbr/account.rb
113
- - lib/jbr/client.rb
114
- - lib/jbr/cliental.rb
127
+ - lib/jbr/authorizing.rb
128
+ - lib/jbr/collection.rb
129
+ - lib/jbr/collections/customers.rb
130
+ - lib/jbr/collections/invoices.rb
131
+ - lib/jbr/collections/jobs.rb
132
+ - lib/jbr/collections/leads.rb
133
+ - lib/jbr/collections/locations.rb
134
+ - lib/jbr/collections/quotes.rb
135
+ - lib/jbr/collections/visits.rb
115
136
  - lib/jbr/error.rb
137
+ - lib/jbr/errors/refused.rb
138
+ - lib/jbr/errors/throttled.rb
116
139
  - lib/jbr/event.rb
117
140
  - lib/jbr/includable.rb
118
- - lib/jbr/invoice.rb
119
- - lib/jbr/itemized.rb
120
- - lib/jbr/job.rb
121
- - lib/jbr/jobs.rb
122
- - lib/jbr/line_item.rb
123
141
  - lib/jbr/listable.rb
124
142
  - lib/jbr/mock.rb
125
143
  - lib/jbr/mock/account.rb
126
- - lib/jbr/mock/client.rb
127
144
  - lib/jbr/mock/invoice.rb
128
- - lib/jbr/mock/job.rb
145
+ - lib/jbr/mock/invoices.rb
129
146
  - lib/jbr/mock/jobs.rb
130
- - lib/jbr/mock/line_item.rb
131
- - lib/jbr/mock/oauth.rb
132
- - lib/jbr/mock/property.rb
147
+ - lib/jbr/mock/leads.rb
133
148
  - lib/jbr/mock/quote.rb
134
- - lib/jbr/mock/request.rb
135
- - lib/jbr/mock/url.rb
149
+ - lib/jbr/mock/quotes.rb
136
150
  - lib/jbr/mock/visit.rb
137
151
  - lib/jbr/mock/visits.rb
138
- - lib/jbr/mocking.rb
139
- - lib/jbr/named.rb
140
- - lib/jbr/oauth.rb
141
152
  - lib/jbr/phone.rb
142
- - lib/jbr/properted.rb
143
- - lib/jbr/property.rb
144
- - lib/jbr/quote.rb
153
+ - lib/jbr/querying.rb
154
+ - lib/jbr/reader.rb
145
155
  - lib/jbr/refreshing.rb
146
- - lib/jbr/refused.rb
147
- - lib/jbr/request.rb
148
- - lib/jbr/resource.rb
149
- - lib/jbr/retriable.rb
156
+ - lib/jbr/resources/customer.rb
157
+ - lib/jbr/resources/invoice.rb
158
+ - lib/jbr/resources/job.rb
159
+ - lib/jbr/resources/lead.rb
160
+ - lib/jbr/resources/line.rb
161
+ - lib/jbr/resources/location.rb
162
+ - lib/jbr/resources/quote.rb
163
+ - lib/jbr/resources/visit.rb
150
164
  - lib/jbr/token.rb
151
- - lib/jbr/url.rb
152
165
  - lib/jbr/version.rb
153
- - lib/jbr/visit.rb
154
- - lib/jbr/visits.rb
155
166
  homepage: https://github.com/HouseAccountEng/jbr
156
167
  licenses:
157
168
  - MIT