restforce-db 2.1.2 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
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