restforce-db 2.1.2 → 2.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 636dfb874caef4ef28885bf712c9dbe400f27105
4
- data.tar.gz: e409ddc6eff3798561bdfbbe83f6f3a5e7051c22
3
+ metadata.gz: fc23c3050b689f6b9b3c447c772bb8289fcaeb57
4
+ data.tar.gz: ebd65047805ff8e683d5e53a1db6574f2552ce8b
5
5
  SHA512:
6
- metadata.gz: 96b1901e99f411e68b711c7dd651f138df24434a8db50fbd98e934fa24763e067ba53994428757a6093b4f6ed360984539717fc7cc3aa2721e827bbc6b84362c
7
- data.tar.gz: 9812e06b5f5c3a465e2410b16ad5edfe2d695d54ad43068a0c75a575d39a0d645782e24669833f35c10f8cdbe661e70131a8d5527cb08f3f00e1720e4db19e28
6
+ metadata.gz: 28f57aaa66794e85dc0cfcf07ffca6728b229730a58df3b3d1c900b3d1aabe1ceab0c87dbdc7dd722b9c0705fdd923741c697a742cf69d7bd1effd9513d12c3f
7
+ data.tar.gz: aa32cae89906cf79f976e52d6c54ffd1b5a262131402d03ee75da5d0b3b0c045e7f9705ac3823262437681490407c9e9a2e9eb439fe7097b10084b7eba299c5d
@@ -3,9 +3,15 @@ module Restforce
3
3
  module DB
4
4
 
5
5
  # Restforce::DB::Cleaner is responsible for culling the matching database
6
- # records when a Salesforce record no longer meets the sync conditions.
6
+ # records when a Salesforce record is no longer available to synchronize
7
+ # for a specific mapping.
7
8
  class Cleaner
8
9
 
10
+ # Salesforce can take a few minutes to register record deletion. This
11
+ # buffer gives us a window of time (in seconds) to look back and see
12
+ # records which may not have been visible in previous runs.
13
+ DELETION_READ_BUFFER = 3 * 60
14
+
9
15
  # Public: Initialize a Restforce::DB::Cleaner.
10
16
  #
11
17
  # mapping - A Restforce::DB::Mapping.
@@ -20,18 +26,43 @@ module Restforce
20
26
  #
21
27
  # Returns nothing.
22
28
  def run
23
- return if @mapping.conditions.empty? || @strategy.passive?
24
- @mapping.database_record_type.destroy_all(invalid_salesforce_ids)
29
+ @mapping.database_record_type.destroy_all(dropped_salesforce_ids)
25
30
  end
26
31
 
27
32
  private
28
33
 
34
+ # Internal: Get a comprehensive list of Salesforce IDs corresponding to
35
+ # records which should be dropped from synchronization for this mapping.
36
+ #
37
+ # Returns an Array of IDs.
38
+ def dropped_salesforce_ids
39
+ deleted_salesforce_ids + invalid_salesforce_ids
40
+ end
41
+
42
+ # Internal: Get the IDs of records which have been removed from Salesforce
43
+ # for this mapping within the DELETION_BUFFER for this run.
44
+ #
45
+ # Returns an Array of IDs.
46
+ def deleted_salesforce_ids
47
+ return [] unless @runner.after
48
+
49
+ response = Restforce::DB.client.get_deleted_between(
50
+ @mapping.salesforce_model,
51
+ @runner.after - DELETION_READ_BUFFER,
52
+ @runner.before,
53
+ )
54
+
55
+ response.deletedRecords.map(&:id)
56
+ end
57
+
29
58
  # Internal: Get the IDs of records which are in the larger collection
30
59
  # of Salesforce records, but which do not meet the specific conditions for
31
60
  # this mapping.
32
61
  #
33
62
  # Returns an Array of IDs.
34
63
  def invalid_salesforce_ids
64
+ return [] if @mapping.conditions.empty? || @strategy.passive?
65
+
35
66
  all_salesforce_ids - valid_salesforce_ids
36
67
  end
37
68
 
@@ -0,0 +1,52 @@
1
+ module Restforce
2
+
3
+ module DB
4
+
5
+ # Restforce::DB::Client is a thin abstraction on top of the default
6
+ # Restforce::Data::Client class, which adds support for an API endpoint
7
+ # not yet supported by the base gem.
8
+ class Client < ::Restforce::Data::Client
9
+
10
+ # Public: Get a list of Salesforce records which have been deleted between
11
+ # the specified times.
12
+ #
13
+ # sobject - The Salesforce object type to query against.
14
+ # start_time - A Time or Time-compatible object indicating the earliest
15
+ # time for which to find deleted records.
16
+ # end_time - A Time or Time-compatible object indicating the latest time
17
+ # for which to find deleted records. Defaults to the current
18
+ # time.
19
+ #
20
+ # Example
21
+ #
22
+ # Restforce::DB.client.get_deleted_between(
23
+ # "CustomObject__c",
24
+ # Time.now - 300,
25
+ # Time.now,
26
+ # )
27
+ #
28
+ # #=> #<Restforce::Mash
29
+ # latestDateCovered="2015-05-18T22:31:00.000+0000"
30
+ # earliestDateAvailable="2015-04-11T06:44:00.000+0000"
31
+ # deletedRecords=[
32
+ # #<Restforce::Mash
33
+ # deletedDate="2015-05-18T22:31:17.000+0000"
34
+ # id="a001a000001a5vOAAQ"
35
+ # >
36
+ # ]
37
+ # >
38
+ #
39
+ # Returns a Restforce::Mash with a `deletedRecords` key.
40
+ def get_deleted_between(sobject, start_time, end_time = Time.now)
41
+ api_get(
42
+ "sobjects/#{sobject}/deleted",
43
+ start: start_time.utc.iso8601,
44
+ end: end_time.utc.iso8601,
45
+ ).body
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -3,7 +3,7 @@ module Restforce
3
3
  # :nodoc:
4
4
  module DB
5
5
 
6
- VERSION = "2.1.2"
6
+ VERSION = "2.1.3"
7
7
 
8
8
  end
9
9
 
data/lib/restforce/db.rb CHANGED
@@ -4,6 +4,7 @@ require "restforce"
4
4
  require "restforce/extensions"
5
5
 
6
6
  require "restforce/db/version"
7
+ require "restforce/db/client"
7
8
  require "restforce/db/configuration"
8
9
  require "restforce/db/registry"
9
10
  require "restforce/db/strategy"
@@ -78,7 +79,7 @@ module Restforce
78
79
  #
79
80
  # Returns a Restforce::Data::Client instance.
80
81
  def self.client
81
- @client ||= Restforce.new(
82
+ @client ||= DB::Client.new(
82
83
  username: configuration.username,
83
84
  password: configuration.password,
84
85
  security_token: configuration.security_token,
@@ -0,0 +1,120 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://<host>/services/oauth2/token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=password&client_id=<client_id>&client_secret=<client_secret>&username=<username>&password=<password><security_token>
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.9.1
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Wed, 20 May 2015 22:04:31 GMT
25
+ Set-Cookie:
26
+ - BrowserId=bSV2iUXwSSOWJFCXI7O_RA;Path=/;Domain=.salesforce.com;Expires=Sun,
27
+ 19-Jul-2015 22:04:31 GMT
28
+ Expires:
29
+ - Thu, 01 Jan 1970 00:00:00 GMT
30
+ Pragma:
31
+ - no-cache
32
+ Cache-Control:
33
+ - no-cache, no-store
34
+ Content-Type:
35
+ - application/json;charset=UTF-8
36
+ Transfer-Encoding:
37
+ - chunked
38
+ body:
39
+ encoding: ASCII-8BIT
40
+ string: '{"id":"https://login.salesforce.com/id/00D1a000000H3O9EAK/0051a000000UGT8AAO","issued_at":"1432159471623","token_type":"Bearer","instance_url":"https://<host>","signature":"vlqOCnMqVU2ShrK2bLIsZTK24INW4Z4LpDdILzgxrFs=","access_token":"00D1a000000H3O9!AQ4AQPRrI9oLwB7KrYUUG5M8JsqJ4YAQepwPNs0vuNDy1phuqeS5ZoO0WecEoXrGBflaKK9LegZ0cFfz2FZwe9v4MZFSUlc6"}'
41
+ http_version:
42
+ recorded_at: Wed, 20 May 2015 22:04:31 GMT
43
+ - request:
44
+ method: post
45
+ uri: https://<host>/services/data/<api_version>/sobjects/CustomObject__c
46
+ body:
47
+ encoding: UTF-8
48
+ string: '{"Name":"Are you going to Scarborough Fair?","Example_Field__c":"Parsley,
49
+ Sage, Rosemary, and Thyme."}'
50
+ headers:
51
+ User-Agent:
52
+ - Faraday v0.9.1
53
+ Content-Type:
54
+ - application/json
55
+ Authorization:
56
+ - OAuth 00D1a000000H3O9!AQ4AQPRrI9oLwB7KrYUUG5M8JsqJ4YAQepwPNs0vuNDy1phuqeS5ZoO0WecEoXrGBflaKK9LegZ0cFfz2FZwe9v4MZFSUlc6
57
+ Accept-Encoding:
58
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
59
+ Accept:
60
+ - "*/*"
61
+ response:
62
+ status:
63
+ code: 201
64
+ message: Created
65
+ headers:
66
+ Date:
67
+ - Wed, 20 May 2015 22:04:31 GMT
68
+ Set-Cookie:
69
+ - BrowserId=iTltKbJMRMS2WMP0BxPVvg;Path=/;Domain=.salesforce.com;Expires=Sun,
70
+ 19-Jul-2015 22:04:31 GMT
71
+ Expires:
72
+ - Thu, 01 Jan 1970 00:00:00 GMT
73
+ Sforce-Limit-Info:
74
+ - api-usage=9/15000
75
+ Location:
76
+ - "/services/data/<api_version>/sobjects/CustomObject__c/a001a000001aHLuAAM"
77
+ Content-Type:
78
+ - application/json;charset=UTF-8
79
+ Transfer-Encoding:
80
+ - chunked
81
+ body:
82
+ encoding: ASCII-8BIT
83
+ string: '{"id":"a001a000001aHLuAAM","success":true,"errors":[]}'
84
+ http_version:
85
+ recorded_at: Wed, 20 May 2015 22:04:31 GMT
86
+ - request:
87
+ method: delete
88
+ uri: https://<host>/services/data/<api_version>/sobjects/CustomObject__c/a001a000001aHLuAAM
89
+ body:
90
+ encoding: US-ASCII
91
+ string: ''
92
+ headers:
93
+ User-Agent:
94
+ - Faraday v0.9.1
95
+ Authorization:
96
+ - OAuth 00D1a000000H3O9!AQ4AQPRrI9oLwB7KrYUUG5M8JsqJ4YAQepwPNs0vuNDy1phuqeS5ZoO0WecEoXrGBflaKK9LegZ0cFfz2FZwe9v4MZFSUlc6
97
+ Accept-Encoding:
98
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
99
+ Accept:
100
+ - "*/*"
101
+ response:
102
+ status:
103
+ code: 204
104
+ message: No Content
105
+ headers:
106
+ Date:
107
+ - Wed, 20 May 2015 22:04:32 GMT
108
+ Set-Cookie:
109
+ - BrowserId=6WNubRIVSxG4Hy8WSNOcaw;Path=/;Domain=.salesforce.com;Expires=Sun,
110
+ 19-Jul-2015 22:04:32 GMT
111
+ Expires:
112
+ - Thu, 01 Jan 1970 00:00:00 GMT
113
+ Sforce-Limit-Info:
114
+ - api-usage=9/15000
115
+ body:
116
+ encoding: UTF-8
117
+ string: ''
118
+ http_version:
119
+ recorded_at: Wed, 20 May 2015 22:04:32 GMT
120
+ recorded_with: VCR 2.9.3
@@ -73,6 +73,28 @@ describe Restforce::DB::Cleaner do
73
73
  end
74
74
  end
75
75
  end
76
+
77
+ describe "when the record has been deleted in Salesforce" do
78
+ let(:runner) { Restforce::DB::Runner.new(0, Time.now - 300) }
79
+ let(:cleaner) { Restforce::DB::Cleaner.new(mapping, runner) }
80
+ let(:dummy_response) do
81
+ Struct.new(:deletedRecords).new([
82
+ Restforce::Mash.new(id: salesforce_id),
83
+ ])
84
+ end
85
+
86
+ before do
87
+ runner.tick!
88
+ end
89
+
90
+ it "drops the synchronized database record" do
91
+ Restforce::DB::Client.stub_any_instance(:get_deleted_between, dummy_response) do
92
+ cleaner.run
93
+ end
94
+
95
+ expect(database_model.last).to_be_nil
96
+ end
97
+ end
76
98
  end
77
99
  end
78
100
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restforce-db
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-05-21 00:00:00.000000000 Z
11
+ date: 2015-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -219,6 +219,7 @@ files:
219
219
  - lib/restforce/db/associator.rb
220
220
  - lib/restforce/db/attribute_map.rb
221
221
  - lib/restforce/db/cleaner.rb
222
+ - lib/restforce/db/client.rb
222
223
  - lib/restforce/db/collector.rb
223
224
  - lib/restforce/db/command.rb
224
225
  - lib/restforce/db/configuration.rb
@@ -280,6 +281,7 @@ files:
280
281
  - test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_mapping_has_no_conditions/does_not_drop_the_synchronized_database_record.yml
281
282
  - test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_record_does_not_meet_the_mapping_conditions/but_meets_conditions_for_a_parallel_mapping/does_not_drop_the_synchronized_database_record.yml
282
283
  - test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_record_does_not_meet_the_mapping_conditions/drops_the_synchronized_database_record.yml
284
+ - test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_record_has_been_deleted_in_Salesforce/drops_the_synchronized_database_record.yml
283
285
  - test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_record_meets_the_mapping_conditions/does_not_drop_the_synchronized_database_record.yml
284
286
  - test/cassettes/Restforce_DB_Collector/_run/given_a_Salesforce_record_with_an_associated_database_record/returns_the_attributes_from_both_records.yml
285
287
  - test/cassettes/Restforce_DB_Collector/_run/given_an_existing_Salesforce_record/returns_the_attributes_from_the_Salesforce_record.yml