strongmind-platform-sdk 3.15.0 → 3.19.9

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
  SHA256:
3
- metadata.gz: 6941d628845b176239f00bebe502b94f6d269fb0f38e08acec7793c446455ae7
4
- data.tar.gz: 30a19f5580c16199479cafffd74be056d766e1222c3ff3d6360147d1e7f5029e
3
+ metadata.gz: 3b4e9c7bfc3e7b93b4aabdfa138eaaa574725e36d087df67e15e239a4de595f4
4
+ data.tar.gz: 30acfbe7eb2487768dabe23ff6920b3b95f600e52039cde1f6ab5587cbdccc4e
5
5
  SHA512:
6
- metadata.gz: 633d8766b794a806932ff9a303c7ae68ff12b1ef16aab64f756dd4a3807f6dc86d72cf252d31bad964ab0a32b57dbef13e2d686ddb1c60937a63e779a7bb38fc
7
- data.tar.gz: 946088e561634d8e8e838f03d99af146f19b4c83140a4894b639502eb56a388af766612fd742668da4d13890d7526eb14a89e2798f732793c6c8ec2582715d4b
6
+ metadata.gz: 7539aa22ff674560101e36c8b668bd43a9d601ddec913865595cd9a8e81d19703b1482b2afd73dd1e44b25183ef1c80fd39701416e8460fd605817fc3676bb36
7
+ data.tar.gz: 1ca026d1104f79143f564c60c9b81473e2794b8501cb161e9df85c17e762ce8c5d5d339757deaecff9e4df623b3e228e2635da9f62779cbb27b7997c10487f0f
data/.rubocop.yml CHANGED
@@ -3,7 +3,7 @@ AllCops:
3
3
 
4
4
  Style/StringLiterals:
5
5
  Enabled: true
6
- EnforcedStyle: double_quotes
6
+ EnforcedStyle: single_quotes
7
7
 
8
8
  Style/StringLiteralsInInterpolation:
9
9
  Enabled: true
data/Gemfile CHANGED
@@ -8,16 +8,23 @@ gem "strongmind-oneroster-client", "~> 2.0.3"
8
8
  # Specify your gem's dependencies in platform_sdk-ruby-sdk.gemspec
9
9
  gemspec
10
10
 
11
- gem "factory_bot"
12
- gem "faker"
13
11
  gem "faraday"
14
12
  gem "faraday-retry"
15
13
  gem "jwt", "~> 1.5", ">= 1.5.4"
16
14
  gem "learnosity-sdk", "~> 0.2.2"
17
15
  gem "multi_json"
18
16
  gem "rake", "~> 13.0"
19
- gem "rspec", "~> 3.6", ">= 3.6.0"
17
+ gem "rspec"
20
18
  gem "rubocop"
21
19
  gem "sentry-ruby"
22
20
  gem "webmock"
21
+ gem 'activesupport', '~> 7.1', '>= 7.1.3.2'
22
+
23
+ group :development, :test do
24
+ gem "sqlite3", "~> 1.4"
25
+ gem 'rspec-rails', '~> 6.1.0'
26
+ gem 'faker'
27
+ gem "factory_bot"
28
+ gem 'timecop'
29
+ end
23
30
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- strongmind-platform-sdk (3.15.0)
4
+ strongmind-platform-sdk (3.19.9)
5
+ activesupport (~> 7.1)
5
6
  aws-sdk-secretsmanager (~> 1.66)
6
7
  faraday (~> 2.5, >= 2.5.2)
7
8
  faraday-retry
@@ -16,124 +17,301 @@ PATH
16
17
  GEM
17
18
  remote: https://rubygems.org/
18
19
  specs:
19
- activesupport (6.1.7.7)
20
+ actioncable (7.1.3.2)
21
+ actionpack (= 7.1.3.2)
22
+ activesupport (= 7.1.3.2)
23
+ nio4r (~> 2.0)
24
+ websocket-driver (>= 0.6.1)
25
+ zeitwerk (~> 2.6)
26
+ actionmailbox (7.1.3.2)
27
+ actionpack (= 7.1.3.2)
28
+ activejob (= 7.1.3.2)
29
+ activerecord (= 7.1.3.2)
30
+ activestorage (= 7.1.3.2)
31
+ activesupport (= 7.1.3.2)
32
+ mail (>= 2.7.1)
33
+ net-imap
34
+ net-pop
35
+ net-smtp
36
+ actionmailer (7.1.3.2)
37
+ actionpack (= 7.1.3.2)
38
+ actionview (= 7.1.3.2)
39
+ activejob (= 7.1.3.2)
40
+ activesupport (= 7.1.3.2)
41
+ mail (~> 2.5, >= 2.5.4)
42
+ net-imap
43
+ net-pop
44
+ net-smtp
45
+ rails-dom-testing (~> 2.2)
46
+ actionpack (7.1.3.2)
47
+ actionview (= 7.1.3.2)
48
+ activesupport (= 7.1.3.2)
49
+ nokogiri (>= 1.8.5)
50
+ racc
51
+ rack (>= 2.2.4)
52
+ rack-session (>= 1.0.1)
53
+ rack-test (>= 0.6.3)
54
+ rails-dom-testing (~> 2.2)
55
+ rails-html-sanitizer (~> 1.6)
56
+ actiontext (7.1.3.2)
57
+ actionpack (= 7.1.3.2)
58
+ activerecord (= 7.1.3.2)
59
+ activestorage (= 7.1.3.2)
60
+ activesupport (= 7.1.3.2)
61
+ globalid (>= 0.6.0)
62
+ nokogiri (>= 1.8.5)
63
+ actionview (7.1.3.2)
64
+ activesupport (= 7.1.3.2)
65
+ builder (~> 3.1)
66
+ erubi (~> 1.11)
67
+ rails-dom-testing (~> 2.2)
68
+ rails-html-sanitizer (~> 1.6)
69
+ activejob (7.1.3.2)
70
+ activesupport (= 7.1.3.2)
71
+ globalid (>= 0.3.6)
72
+ activemodel (7.1.3.2)
73
+ activesupport (= 7.1.3.2)
74
+ activerecord (7.1.3.2)
75
+ activemodel (= 7.1.3.2)
76
+ activesupport (= 7.1.3.2)
77
+ timeout (>= 0.4.0)
78
+ activestorage (7.1.3.2)
79
+ actionpack (= 7.1.3.2)
80
+ activejob (= 7.1.3.2)
81
+ activerecord (= 7.1.3.2)
82
+ activesupport (= 7.1.3.2)
83
+ marcel (~> 1.0)
84
+ activesupport (7.1.3.2)
85
+ base64
86
+ bigdecimal
20
87
  concurrent-ruby (~> 1.0, >= 1.0.2)
88
+ connection_pool (>= 2.2.5)
89
+ drb
21
90
  i18n (>= 1.6, < 2)
22
91
  minitest (>= 5.1)
92
+ mutex_m
23
93
  tzinfo (~> 2.0)
24
- zeitwerk (~> 2.3)
25
94
  addressable (2.8.6)
26
95
  public_suffix (>= 2.0.2, < 6.0)
27
96
  ast (2.4.2)
28
97
  aws-eventstream (1.3.0)
29
- aws-partitions (1.925.0)
30
- aws-sdk-cloudwatch (1.90.0)
98
+ aws-partitions (1.934.0)
99
+ aws-sdk-cloudwatch (1.91.0)
31
100
  aws-sdk-core (~> 3, >= 3.193.0)
32
101
  aws-sigv4 (~> 1.1)
33
- aws-sdk-core (3.194.1)
102
+ aws-sdk-core (3.196.1)
34
103
  aws-eventstream (~> 1, >= 1.3.0)
35
104
  aws-partitions (~> 1, >= 1.651.0)
36
105
  aws-sigv4 (~> 1.8)
37
106
  jmespath (~> 1, >= 1.6.1)
38
- aws-sdk-secretsmanager (1.92.0)
107
+ aws-sdk-secretsmanager (1.95.0)
39
108
  aws-sdk-core (~> 3, >= 3.193.0)
40
109
  aws-sigv4 (~> 1.1)
41
110
  aws-sigv4 (1.8.0)
42
111
  aws-eventstream (~> 1, >= 1.0.2)
43
- bigdecimal (3.1.6)
112
+ base64 (0.2.0)
113
+ bigdecimal (3.1.8)
114
+ builder (3.2.4)
44
115
  concurrent-ruby (1.2.3)
45
116
  connection_pool (2.4.1)
46
117
  crack (1.0.0)
47
118
  bigdecimal
48
119
  rexml
49
- diff-lcs (1.5.0)
120
+ crass (1.0.6)
121
+ date (3.3.4)
122
+ diff-lcs (1.5.1)
123
+ drb (2.2.1)
124
+ erubi (1.12.0)
50
125
  ethon (0.16.0)
51
126
  ffi (>= 1.15.0)
52
- factory_bot (6.4.5)
127
+ factory_bot (6.4.6)
53
128
  activesupport (>= 5.0.0)
54
- faker (2.22.0)
129
+ faker (3.3.1)
55
130
  i18n (>= 1.8.11, < 2)
56
- faraday (2.7.7)
57
- faraday-net_http (>= 2.0, < 3.1)
58
- ruby2_keywords (>= 0.0.4)
59
- faraday-net_http (3.0.2)
60
- faraday-retry (2.1.0)
131
+ faraday (2.9.0)
132
+ faraday-net_http (>= 2.0, < 3.2)
133
+ faraday-net_http (3.1.0)
134
+ net-http
135
+ faraday-retry (2.2.1)
61
136
  faraday (~> 2.0)
62
- ffi (1.15.5)
137
+ ffi (1.16.3)
138
+ globalid (1.2.1)
139
+ activesupport (>= 6.1)
63
140
  hashdiff (1.1.0)
64
- i18n (1.14.1)
141
+ i18n (1.14.5)
65
142
  concurrent-ruby (~> 1.0)
143
+ io-console (0.7.2)
144
+ irb (1.13.1)
145
+ rdoc (>= 4.0.0)
146
+ reline (>= 0.4.2)
66
147
  jmespath (1.6.2)
67
- json (2.6.3)
148
+ json (2.7.2)
68
149
  jwt (1.5.6)
150
+ language_server-protocol (3.17.0.3)
69
151
  learnosity-sdk (0.2.2)
70
152
  sys-uname (>= 1.0)
71
- minitest (5.22.2)
153
+ loofah (2.22.0)
154
+ crass (~> 1.0.2)
155
+ nokogiri (>= 1.12.0)
156
+ mail (2.8.1)
157
+ mini_mime (>= 0.1.1)
158
+ net-imap
159
+ net-pop
160
+ net-smtp
161
+ marcel (1.0.4)
162
+ mini_mime (1.1.5)
163
+ mini_portile2 (2.8.6)
164
+ minitest (5.22.3)
72
165
  multi_json (1.15.0)
73
- parallel (1.23.0)
74
- parser (3.2.2.1)
166
+ mutex_m (0.2.0)
167
+ net-http (0.4.1)
168
+ uri
169
+ net-imap (0.4.11)
170
+ date
171
+ net-protocol
172
+ net-pop (0.1.2)
173
+ net-protocol
174
+ net-protocol (0.2.2)
175
+ timeout
176
+ net-smtp (0.5.0)
177
+ net-protocol
178
+ nio4r (2.7.3)
179
+ nokogiri (1.16.5)
180
+ mini_portile2 (~> 2.8.2)
181
+ racc (~> 1.4)
182
+ nokogiri (1.16.5-x86_64-darwin)
183
+ racc (~> 1.4)
184
+ nokogiri (1.16.5-x86_64-linux)
185
+ racc (~> 1.4)
186
+ parallel (1.24.0)
187
+ parser (3.3.1.0)
75
188
  ast (~> 2.4.1)
76
- public_suffix (5.0.4)
77
- rack (3.0.10)
189
+ racc
190
+ psych (5.1.2)
191
+ stringio
192
+ public_suffix (5.0.5)
193
+ racc (1.7.3)
194
+ rack (3.0.11)
195
+ rack-session (2.0.0)
196
+ rack (>= 3.0.0)
197
+ rack-test (2.1.0)
198
+ rack (>= 1.3)
199
+ rackup (2.1.0)
200
+ rack (>= 3)
201
+ webrick (~> 1.8)
202
+ rails (7.1.3.2)
203
+ actioncable (= 7.1.3.2)
204
+ actionmailbox (= 7.1.3.2)
205
+ actionmailer (= 7.1.3.2)
206
+ actionpack (= 7.1.3.2)
207
+ actiontext (= 7.1.3.2)
208
+ actionview (= 7.1.3.2)
209
+ activejob (= 7.1.3.2)
210
+ activemodel (= 7.1.3.2)
211
+ activerecord (= 7.1.3.2)
212
+ activestorage (= 7.1.3.2)
213
+ activesupport (= 7.1.3.2)
214
+ bundler (>= 1.15.0)
215
+ railties (= 7.1.3.2)
216
+ rails-dom-testing (2.2.0)
217
+ activesupport (>= 5.0.0)
218
+ minitest
219
+ nokogiri (>= 1.6)
220
+ rails-html-sanitizer (1.6.0)
221
+ loofah (~> 2.21)
222
+ nokogiri (~> 1.14)
223
+ railties (7.1.3.2)
224
+ actionpack (= 7.1.3.2)
225
+ activesupport (= 7.1.3.2)
226
+ irb
227
+ rackup (>= 1.0.0)
228
+ rake (>= 12.2)
229
+ thor (~> 1.0, >= 1.2.2)
230
+ zeitwerk (~> 2.6)
78
231
  rainbow (3.1.1)
79
- rake (13.0.6)
80
- redis-client (0.22.1)
232
+ rake (13.2.1)
233
+ rdoc (6.6.3.1)
234
+ psych (>= 4.0.0)
235
+ redis-client (0.22.2)
81
236
  connection_pool
82
- regexp_parser (2.8.0)
83
- rexml (3.2.5)
84
- rspec (3.12.0)
85
- rspec-core (~> 3.12.0)
86
- rspec-expectations (~> 3.12.0)
87
- rspec-mocks (~> 3.12.0)
88
- rspec-core (3.12.2)
89
- rspec-support (~> 3.12.0)
90
- rspec-expectations (3.12.3)
237
+ regexp_parser (2.9.1)
238
+ reline (0.5.7)
239
+ io-console (~> 0.5)
240
+ rexml (3.2.6)
241
+ rspec (3.13.0)
242
+ rspec-core (~> 3.13.0)
243
+ rspec-expectations (~> 3.13.0)
244
+ rspec-mocks (~> 3.13.0)
245
+ rspec-core (3.13.0)
246
+ rspec-support (~> 3.13.0)
247
+ rspec-expectations (3.13.0)
91
248
  diff-lcs (>= 1.2.0, < 2.0)
92
- rspec-support (~> 3.12.0)
93
- rspec-mocks (3.12.5)
249
+ rspec-support (~> 3.13.0)
250
+ rspec-mocks (3.13.1)
94
251
  diff-lcs (>= 1.2.0, < 2.0)
95
- rspec-support (~> 3.12.0)
96
- rspec-support (3.12.0)
97
- rubocop (1.51.0)
252
+ rspec-support (~> 3.13.0)
253
+ rspec-rails (6.1.2)
254
+ actionpack (>= 6.1)
255
+ activesupport (>= 6.1)
256
+ railties (>= 6.1)
257
+ rspec-core (~> 3.13)
258
+ rspec-expectations (~> 3.13)
259
+ rspec-mocks (~> 3.13)
260
+ rspec-support (~> 3.13)
261
+ rspec-support (3.13.1)
262
+ rubocop (1.63.5)
98
263
  json (~> 2.3)
264
+ language_server-protocol (>= 3.17.0)
99
265
  parallel (~> 1.10)
100
- parser (>= 3.2.0.0)
266
+ parser (>= 3.3.0.2)
101
267
  rainbow (>= 2.2.2, < 4.0)
102
268
  regexp_parser (>= 1.8, < 3.0)
103
269
  rexml (>= 3.2.5, < 4.0)
104
- rubocop-ast (>= 1.28.0, < 2.0)
270
+ rubocop-ast (>= 1.31.1, < 2.0)
105
271
  ruby-progressbar (~> 1.7)
106
272
  unicode-display_width (>= 2.4.0, < 3.0)
107
- rubocop-ast (1.28.1)
108
- parser (>= 3.2.1.0)
273
+ rubocop-ast (1.31.3)
274
+ parser (>= 3.3.1.0)
109
275
  ruby-progressbar (1.13.0)
110
- ruby2_keywords (0.0.5)
111
- sentry-ruby (5.16.1)
276
+ sentry-ruby (5.17.3)
277
+ bigdecimal
112
278
  concurrent-ruby (~> 1.0, >= 1.0.2)
113
279
  sidekiq (7.2.4)
114
280
  concurrent-ruby (< 2)
115
281
  connection_pool (>= 2.3.0)
116
282
  rack (>= 2.2.4)
117
283
  redis-client (>= 0.19.0)
284
+ sqlite3 (1.7.3)
285
+ mini_portile2 (~> 2.8.0)
286
+ sqlite3 (1.7.3-x86_64-darwin)
287
+ sqlite3 (1.7.3-x86_64-linux)
288
+ stringio (3.1.0)
118
289
  strongmind-oneroster-client (2.0.3)
119
290
  json (~> 2.1, >= 2.1.0)
120
291
  typhoeus (~> 1.0, >= 1.0.1)
121
- strongmind-sidekiq-cloudwatchmetrics (2.6.3)
292
+ strongmind-sidekiq-cloudwatchmetrics (2.6.4)
122
293
  aws-sdk-cloudwatch (~> 1.6)
123
294
  sidekiq (>= 5.0, < 8.0)
124
295
  sys-uname (1.2.3)
125
296
  ffi (~> 1.1)
126
- typhoeus (1.4.0)
297
+ thor (1.3.1)
298
+ timecop (0.9.8)
299
+ timeout (0.4.1)
300
+ typhoeus (1.4.1)
127
301
  ethon (>= 0.9.0)
128
302
  tzinfo (2.0.6)
129
303
  concurrent-ruby (~> 1.0)
130
- unicode-display_width (2.4.2)
304
+ unicode-display_width (2.5.0)
131
305
  uri (0.13.0)
132
- webmock (3.22.0)
306
+ webmock (3.23.0)
133
307
  addressable (>= 2.8.0)
134
308
  crack (>= 0.3.2)
135
309
  hashdiff (>= 0.4.0, < 2.0.0)
136
- zeitwerk (2.6.13)
310
+ webrick (1.8.1)
311
+ websocket-driver (0.7.6)
312
+ websocket-extensions (>= 0.1.0)
313
+ websocket-extensions (0.1.5)
314
+ zeitwerk (2.6.14)
137
315
 
138
316
  PLATFORMS
139
317
  ruby
@@ -141,6 +319,7 @@ PLATFORMS
141
319
  x86_64-linux
142
320
 
143
321
  DEPENDENCIES
322
+ activesupport (~> 7.1, >= 7.1.3.2)
144
323
  factory_bot
145
324
  faker
146
325
  faraday
@@ -148,12 +327,16 @@ DEPENDENCIES
148
327
  jwt (~> 1.5, >= 1.5.4)
149
328
  learnosity-sdk (~> 0.2.2)
150
329
  multi_json
330
+ rails (~> 7.1)
151
331
  rake (~> 13.0)
152
- rspec (~> 3.6, >= 3.6.0)
332
+ rspec
333
+ rspec-rails (~> 6.1.0)
153
334
  rubocop
154
335
  sentry-ruby
336
+ sqlite3 (~> 1.4)
155
337
  strongmind-oneroster-client (~> 2.0.3)
156
338
  strongmind-platform-sdk!
339
+ timecop
157
340
  webmock
158
341
 
159
342
  BUNDLED WITH
data/README.md CHANGED
@@ -11,7 +11,7 @@ First [configure your GitHub credentials](#configure-github-credentials)
11
11
  Install the gem and add to the application's Gemfile by executing:
12
12
 
13
13
  ```
14
- gem "strongmind-platform-sdk", "~> 3.9.2"
14
+ gem "strongmind-platform-sdk", "~> 3.19.1"
15
15
  ```
16
16
 
17
17
  If bundler is not being used to manage dependencies, install the gem by executing:
@@ -67,9 +67,15 @@ gem push --key github --host https://rubygems.pkg.github.com/StrongMind platform
67
67
 
68
68
  ## Development
69
69
 
70
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
70
+ After checking out the repo, run `bin/setup` to install dependencies.
71
71
 
72
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
72
+ Migrate the database for the dummy app:
73
+ ```
74
+ cd spec/dummy
75
+ RAILS_ENV=test rails db:migrate
76
+ ```
77
+
78
+ Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
73
79
 
74
80
  ## Contributing
75
81
 
@@ -83,10 +89,123 @@ The gem is available as open source under the terms of the [MIT License](https:/
83
89
 
84
90
  Everyone interacting in the Platform::Ruby::Sdk project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/platform-ruby-sdk/blob/main/CODE_OF_CONDUCT.md).
85
91
 
86
- ## Setting up IRB for OneRoster Calls
92
+ ## Data Pipeline Concern
93
+
94
+ The `DataPipelineable` concern is a shared concern that can be included in models
95
+ automatically to send data to the Data Pipeline on creates, updates, and destroys.
96
+
97
+ ### Installation
98
+ Ensure that `platform-sdk` is required in the application.
99
+
100
+ One way to do this is to require the SDK via the `Gemfile`:
101
+
102
+ ```ruby
103
+ gem 'strongmind-platform-sdk', require: 'platform_sdk'
104
+ ```
105
+
106
+ This provides the application access to the `DataPipelineable` concern and RSpec shared examples.
107
+
108
+ ### Usage
109
+
110
+ To use the `DataPipelineable` concern in a model, include the concern in the model:
111
+
112
+ ```ruby
113
+ class MyModel < ApplicationRecord
114
+ include PlatformSdk::ActiveRecord::DataPipelineable
115
+ end
116
+ ```
117
+
118
+ For the concern to function, the model must have both a `pipeline_noun` and a `pipeline_meta` class method that returns the noun and metadata of the model, respectively.
119
+
120
+ This can be done once by defining the method(s) in the ApplicationRecord class:
121
+
122
+ ```ruby
123
+ class ApplicationRecord < ActiveRecord::Base
124
+ self.abstract_class = true
125
+
126
+ def self.pipeline_noun
127
+ "StrongMind.Central.#{name}}"
128
+ end
129
+
130
+ def self.pipeline_meta
131
+ {
132
+ "source": "strongmind-central"
133
+ }
134
+ end
135
+ end
136
+ ```
137
+
138
+ Or it can be defined in each model:
139
+
140
+ ```ruby
141
+ class MyModel < ApplicationRecord
142
+ include PlatformSdk::ActiveRecord::DataPipelineable
143
+
144
+ def self.pipeline_noun
145
+ "StrongMind.Central.#{name}}"
146
+ end
147
+
148
+ def self.pipeline_meta
149
+ {
150
+ "source": "strongmind-central"
151
+ }
152
+ end
153
+ end
154
+ ```
155
+
156
+ You can override the following methods in the model to customize the noun data:
157
+
158
+ ```ruby
159
+ class MyModel < ApplicationRecord
160
+ include PlatformSdk::ActiveRecord::DataPipelineable
161
+
162
+ def pipeline_excluded_attributes
163
+ [:column1, :column2]
164
+ end
165
+
166
+ def pipeline_additional_attributes
167
+ {
168
+ "foo": "important data",
169
+ "bar": "more important data"
170
+ }
171
+ end
172
+ end
173
+ ```
174
+
175
+ ### Configuration
176
+ You will need the following ENV variables set in your application to send data to the Data Pipeline (found in bitwarden):
177
+ * DATA_PIPELINE_HOST
178
+ * DATA_PIPELINE_USERNAME
179
+ * DATA_PIPELINE_PASSWORD
180
+
181
+
182
+ ### Testing
183
+
184
+ To test the `DataPipelineable` concern for a model, include the common shared examples in the model's spec file:
185
+
186
+ ```ruby
187
+ RSpec.describe MyModel, type: :model do
188
+ describe 'sends to the Data Pipeline' do
189
+ it_behaves_like 'DataPipelineable'
190
+ end
191
+ end
192
+ ```
193
+
194
+ Make sure to stub the call to the Data Pipeline in `rails_helper.rb`
195
+
196
+ ```ruby
197
+ config.before(:each) do
198
+ allow_any_instance_of(PlatformSdk::DataPipeline::Client).to receive(:post)
199
+ end
200
+ ```
201
+
202
+
203
+ ## Setting up IRB for API Client Testing
87
204
  Require needed dependencies:
88
205
  `require 'platform_sdk'`
89
206
 
207
+
208
+ ### OneRoster API Client Initialization Example
90
209
  Open IRB and initialize dependencies:
91
210
  * IDENTITY_CLIENT_ID
92
211
  * IDENTITY_CLIENT_SECRET
@@ -102,6 +221,7 @@ Create Identity Auth Client:
102
221
  Create OneRoster Client:
103
222
  `one_roster_client = PlatformSdk::OneRoster::Client.new(ONEROSTER_BASE_URL, auth_client)`
104
223
 
224
+ ### Additional Client Initialization Examples
105
225
  Create Aws Client:
106
226
  `aws_client = PlatformSdk::Aws::SecretsManagerClient.new(access_key_id: 'ACCESS_KEY_ID', secret_access_key: 'SECRET_ACCESS_KEY', region: 'REGION')`
107
227
 
@@ -0,0 +1,74 @@
1
+ require 'active_support/concern'
2
+
3
+ module PlatformSdk
4
+ module ActiveRecord
5
+
6
+ module DataPipelineable
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ after_create -> { send_to_pipeline('created') }
11
+ before_destroy -> { send_to_pipeline('destroyed') }
12
+ after_update -> { send_to_pipeline('modified') }
13
+ end
14
+
15
+ def pipeline_payload(action)
16
+ {
17
+ "noun": self.class.pipeline_noun,
18
+ "identifiers": pipeline_identifiers,
19
+ "meta": self.class.pipeline_meta,
20
+ "data": pipeline_data(action),
21
+ "envelope_version": '1.0.0',
22
+ "message_timestamp": Time.current.utc.iso8601
23
+ }
24
+ end
25
+
26
+ def self.pipeline_noun
27
+ raise NotImplementedError, 'You must implement the pipeline_noun class method'
28
+ end
29
+
30
+ def self.pipeline_meta
31
+ raise NotImplementedError, 'You must implement the pipeline_meta class method, an example value is { "source": "strongmind-central" }'
32
+ end
33
+
34
+ def pipeline_excluded_attributes
35
+ []
36
+ end
37
+
38
+ def pipeline_identifiers
39
+ {
40
+ "id": id
41
+ }
42
+ end
43
+
44
+ def pipeline_data(action)
45
+ data_hash = { action: }.merge!(attributes.symbolize_keys)
46
+
47
+ data_hash.merge!(pipeline_additional_attributes)
48
+
49
+ data_hash.except(*pipeline_excluded_attributes)
50
+ end
51
+
52
+ def pipeline_additional_attributes
53
+ {}
54
+ end
55
+
56
+ def one_roster_pipeline_payload(deleted: false)
57
+ {}
58
+ end
59
+
60
+ def send_to_pipeline(action)
61
+ credentials = {
62
+ pipeline_host: ENV.fetch('DATA_PIPELINE_HOST', 'stage-di-data-pipeline-api.strongmind.com'),
63
+ pipeline_username: ENV.fetch('DATA_PIPELINE_USERNAME', 'canvas_prod'),
64
+ pipeline_password: ENV.fetch('DATA_PIPELINE_PASSWORD', '')
65
+ }
66
+ client = PlatformSdk::DataPipeline::Client.new(credentials)
67
+ client.post(pipeline_payload(action)) unless Rails.env.development?
68
+ return unless respond_to?(:one_roster_data_type) && !Rails.env.development?
69
+
70
+ client.post(one_roster_pipeline_payload(deleted: action == "destroyed"))
71
+ end
72
+ end
73
+ end
74
+ end
@@ -17,6 +17,7 @@ module PlatformSdk
17
17
  faraday.request(:retry, retry_options)
18
18
  faraday.request(:authorization, :basic, @credentials[:pipeline_username], @credentials[:pipeline_password])
19
19
  faraday.headers = headers
20
+ faraday.response :raise_error
20
21
  faraday.adapter(:net_http)
21
22
  end
22
23
  end
@@ -39,6 +39,8 @@ module PlatformSdk
39
39
 
40
40
  def with_rescue
41
41
  yield
42
+ rescue Faraday::TimeoutError => e
43
+ raise TimeoutError, e
42
44
  rescue Faraday::ServerError => e
43
45
  raise_error_with_payload(ServerError, e)
44
46
  rescue Faraday::ClientError => e
@@ -77,20 +79,6 @@ module PlatformSdk
77
79
  Time.at(JWT.decode(jwt, nil, false)[0]["exp"])
78
80
  end
79
81
 
80
- def refresh_token_if_expired(jwt:, refresh_token:)
81
- raise ArgumentError if refresh_token.nil? || jwt.nil?
82
- return unless token_expired?(jwt)
83
-
84
- refresh_token(refresh_token)
85
- end
86
-
87
- def refresh_token(refresh_token)
88
- raise ArgumentError if refresh_token.nil?
89
-
90
- post_payload("/connect/token", request_body(
91
- grant_type: "refresh_token", refresh_token:))
92
- end
93
-
94
82
  def refresh_session(session: {})
95
83
  raise ArgumentError if session[:access_token].nil? || session[:refresh_token].nil?
96
84
 
@@ -103,6 +91,19 @@ module PlatformSdk
103
91
  session[:refresh_token] = refreshed_tokens[:refresh_token]
104
92
  end
105
93
 
94
+ def refresh_token_if_expired(jwt:, refresh_token:)
95
+ raise ArgumentError if refresh_token.nil? || jwt.nil?
96
+ return unless token_expired?(jwt)
97
+
98
+ new_refresh_token(refresh_token)
99
+ end
100
+
101
+ def new_refresh_token(refresh_token)
102
+ raise ArgumentError if refresh_token.nil?
103
+
104
+ post_payload("/connect/token", request_body(grant_type: "refresh_token", refresh_token:))
105
+ end
106
+
106
107
  def raise_error_with_payload(exception_class, error)
107
108
  json_log = {
108
109
  exception: exception_class.new.class.name.demodulize,
@@ -110,7 +111,7 @@ module PlatformSdk
110
111
  response_body: error.response[:body],
111
112
  status: error.response[:status]
112
113
  }.compact
113
- Rails.logger.info json_log.to_json if respond_to?(:Rails)
114
+ Rails.logger.info json_log.to_json if defined?(Rails)
114
115
  raise exception_class, error.response
115
116
  end
116
117
 
@@ -13,5 +13,6 @@ module PlatformSdk
13
13
  class ResourceNotFound < Faraday::ResourceNotFound; end
14
14
  class ServerError < Faraday::ServerError; end
15
15
  class ClientError < Faraday::ClientError; end
16
+ class TimeoutError < Faraday::TimeoutError; end
16
17
  end
17
18
  end
@@ -0,0 +1,90 @@
1
+ module PlatformSdk
2
+ module SpecSupport
3
+ RSpec.shared_examples "DataPipelineable" do
4
+ let(:data) do
5
+ { action: }
6
+ .merge(record.attributes.symbolize_keys)
7
+ .merge(record.pipeline_additional_attributes)
8
+ .except(*record.pipeline_excluded_attributes)
9
+ end
10
+
11
+ let(:pipeline_payload) do
12
+ {
13
+ "noun": described_class.pipeline_noun,
14
+ "identifiers": { "id": record.id },
15
+ "meta": described_class.pipeline_meta,
16
+ "data": data,
17
+ "envelope_version": '1.0.0',
18
+ "message_timestamp": Time.current.utc.iso8601
19
+ }
20
+ end
21
+
22
+ let(:record) { build(described_class.to_s.underscore.to_sym) }
23
+ let(:data_pipeline_client) { double(PlatformSdk::DataPipeline::Client)}
24
+
25
+ before do
26
+ allow(PlatformSdk::DataPipeline::Client).to receive(:new).and_return(data_pipeline_client)
27
+ allow(data_pipeline_client).to receive(:post)
28
+ Timecop.freeze(DateTime.parse('2024-06-09 04:20:00'))
29
+ end
30
+
31
+ after do
32
+ Timecop.return
33
+ end
34
+
35
+ context 'after_create callbacks' do
36
+ let(:action) { 'created' }
37
+
38
+ it "defines an after_create callback" do
39
+ record.save!
40
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload)
41
+ end
42
+ end
43
+
44
+ context 'before_destroy callbacks' do
45
+ let(:action) { 'destroyed' }
46
+
47
+ it "defines an after_destroy callback" do
48
+ record.save!
49
+ record.destroy
50
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload)
51
+ end
52
+ end
53
+
54
+ context 'after_update callbacks' do
55
+ let(:action) { 'modified' }
56
+
57
+ it "defines an after_update callback" do
58
+ record.save!
59
+ column = column_to_update(record)
60
+ update_record(record, column)
61
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload)
62
+ end
63
+ end
64
+
65
+ def column_to_update(record)
66
+ [:string, :integer, :boolean].each do |type|
67
+ if record.class.columns.select { |c| c.sql_type_metadata.type == type && !c.name.match?(/_type$/) }.any?
68
+ return record.class.columns.select { |c| c.sql_type_metadata.type == type && !c.name.match?(/_type$/) }.first.name
69
+ end
70
+ end
71
+ end
72
+
73
+ def update_record(record, column)
74
+ new_record = build(described_class.to_s.underscore.to_sym)
75
+
76
+ column_class = record[column]
77
+ case column_class
78
+ when String
79
+ record.update!(column => new_record[column])
80
+ when Integer
81
+ record.update!(column => 69)
82
+ when Boolean
83
+ record.update!(column => !record[column])
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PlatformSdk
4
+ module SpecSupport
5
+ RSpec.shared_examples "OneRosterDataPipelineable" do |record_keys|
6
+ let(:data) do
7
+ record_keys.each_with_object({}) do |key, hash|
8
+ if key == "_type"
9
+ hash[:type] = record.try(key)
10
+ next
11
+ end
12
+ hash[key.to_sym] = record.try(key)
13
+ end
14
+ end
15
+ let(:pipeline_payload) do
16
+ {
17
+ "noun": "StrongMind.Platform.OneRoster.#{record.one_roster_data_type.capitalize}",
18
+ "identifiers": { "sourcedId": record.roster_id },
19
+ "meta": {
20
+ "version": "3",
21
+ "source": "Central OneRoster API"
22
+ },
23
+ "data": one_roster_common_data.merge!(data),
24
+ "envelope_version": "1.0.0",
25
+ "message_timestamp": Time.current.utc.iso8601
26
+ }
27
+ end
28
+ let(:record) { build(described_class.to_s.underscore.to_sym) }
29
+ let(:one_roster_common_data) do
30
+ {
31
+ "sourcedId": record.roster_id,
32
+ "status": record.status,
33
+ "dateLastModified": record.updated_at,
34
+ "metadata": record.metadata
35
+ }
36
+ end
37
+ let(:data_pipeline_client) { double(PlatformSdk::DataPipeline::Client) }
38
+
39
+ before do
40
+ allow(PlatformSdk::DataPipeline::Client).to receive(:new).and_return(data_pipeline_client)
41
+ allow(data_pipeline_client).to receive(:post)
42
+ Timecop.freeze(DateTime.parse("2024-06-09 04:20:00"))
43
+ end
44
+
45
+ after do
46
+ Timecop.return
47
+ end
48
+
49
+ context "after_create callbacks" do
50
+ let(:action) { "created" }
51
+
52
+ it "defines an after_create callback" do
53
+ record.save!
54
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload)
55
+ end
56
+ end
57
+
58
+ context "before_destroy callbacks" do
59
+ let(:action) { "destroyed" }
60
+ let(:one_roster_common_data_deleted) do
61
+ one_roster_common_data.merge(
62
+ "status": "tobedeleted"
63
+ )
64
+ end
65
+
66
+ let(:pipeline_payload_deleted) do
67
+ pipeline_payload.merge("data": one_roster_common_data_deleted)
68
+ end
69
+
70
+ it "defines an after_destroy callback" do
71
+ record.save!
72
+ record.destroy
73
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload)
74
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload_deleted)
75
+ end
76
+ end
77
+
78
+ context "after_update callbacks" do
79
+ let(:action) { "modified" }
80
+
81
+ it "defines an after_update callback" do
82
+ record.save!
83
+ column = column_to_update(record)
84
+ update_record(record, column)
85
+ expect(data_pipeline_client).to have_received(:post).with(pipeline_payload)
86
+ end
87
+ end
88
+
89
+ def column_to_update(record)
90
+ %i[string integer boolean].each do |type|
91
+ if record.class.columns.select { |c| c.sql_type_metadata.type == type && !c.name.match?(/_type$/) }.any?
92
+ return record.class.columns.select { |c| c.sql_type_metadata.type == type && !c.name.match?(/_type$/) }.first.name
93
+ end
94
+ end
95
+ end
96
+
97
+ def update_record(record, column)
98
+ column_class = record[column]
99
+ case column_class
100
+ when String
101
+ record.update!(column => "new value")
102
+ when Integer
103
+ record.update!(column => 69)
104
+ when Boolean
105
+ record.update!(column => !record[column])
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core'
4
+ require 'platform_sdk/spec_support/shared_examples/data_pipelineable_examples'
5
+ require 'platform_sdk/spec_support/shared_examples/one_roster_data_pipelineable_examples'
6
+
7
+ module PlatformSdk
8
+ module SpecSupport
9
+ end
10
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PlatformSdk
4
- VERSION = "3.15.0"
4
+ VERSION = "3.19.9"
5
5
  end
data/lib/platform_sdk.rb CHANGED
@@ -16,6 +16,8 @@ require "platform_sdk/canvas_api"
16
16
  require "platform_sdk/bynder"
17
17
  require "platform_sdk/events"
18
18
  require "platform_sdk/central"
19
+ require "platform_sdk/active_record/data_pipelineable"
20
+ require "platform_sdk/spec_support"
19
21
 
20
22
  module PlatformSdk
21
23
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strongmind-platform-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.15.0
4
+ version: 3.19.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Platform Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-13 00:00:00.000000000 Z
11
+ date: 2024-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -156,6 +156,34 @@ dependencies:
156
156
  - - ">="
157
157
  - !ruby/object:Gem::Version
158
158
  version: '0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: activesupport
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '7.1'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '7.1'
173
+ - !ruby/object:Gem::Dependency
174
+ name: rails
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '7.1'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '7.1'
159
187
  description: Includes a wrapper for the swagger generated OneRoster management clients
160
188
  email:
161
189
  - horseshoes@strongmind.com
@@ -177,6 +205,7 @@ files:
177
205
  - Rakefile
178
206
  - docs/identity_installation_in_rails.md
179
207
  - lib/platform_sdk.rb
208
+ - lib/platform_sdk/active_record/data_pipelineable.rb
180
209
  - lib/platform_sdk/aws.rb
181
210
  - lib/platform_sdk/aws/secrets_manager_client.rb
182
211
  - lib/platform_sdk/bynder.rb
@@ -215,6 +244,9 @@ files:
215
244
  - lib/platform_sdk/power_school/client.rb
216
245
  - lib/platform_sdk/power_school/special_program.rb
217
246
  - lib/platform_sdk/sentry.rb
247
+ - lib/platform_sdk/spec_support.rb
248
+ - lib/platform_sdk/spec_support/shared_examples/data_pipelineable_examples.rb
249
+ - lib/platform_sdk/spec_support/shared_examples/one_roster_data_pipelineable_examples.rb
218
250
  - lib/platform_sdk/version.rb
219
251
  - sig/platform_sdk/identity/auth_client.rbs
220
252
  - sig/platform_sdk/learnosity_api/client.rbs