disco_app 0.16.0 → 0.16.1.pre.sidekiq.pre.6.pre.release

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/.DS_Store +0 -0
  3. data/app/clients/disco_app/graphql_client.rb +85 -0
  4. data/app/controllers/disco_app/flow/actions_controller.rb +7 -0
  5. data/app/controllers/disco_app/flow/concerns/actions_controller.rb +47 -0
  6. data/app/jobs/disco_app/flow/process_action_job.rb +11 -0
  7. data/app/jobs/disco_app/flow/process_trigger_job.rb +11 -0
  8. data/app/jobs/disco_app/shop_job.rb +8 -9
  9. data/app/models/disco_app/concerns/shop.rb +4 -5
  10. data/app/models/disco_app/flow/action.rb +7 -0
  11. data/app/models/disco_app/flow/concerns/action.rb +24 -0
  12. data/app/models/disco_app/flow/concerns/trigger.rb +24 -0
  13. data/app/models/disco_app/flow/trigger.rb +7 -0
  14. data/app/services/disco_app/flow/create_action.rb +35 -0
  15. data/app/services/disco_app/flow/create_trigger.rb +34 -0
  16. data/app/services/disco_app/flow/process_action.rb +50 -0
  17. data/app/services/disco_app/flow/process_trigger.rb +52 -0
  18. data/config/routes.rb +7 -1
  19. data/db/migrate/20181229100327_create_flow_actions_and_triggers.rb +32 -0
  20. data/lib/.DS_Store +0 -0
  21. data/lib/disco_app/configuration.rb +4 -0
  22. data/lib/disco_app/version.rb +1 -1
  23. data/lib/generators/disco_app/disco_app_generator.rb +2 -2
  24. data/lib/generators/disco_app/templates/config/appsignal.yml +12 -0
  25. data/lib/generators/disco_app/templates/root/.env +3 -0
  26. data/test/dummy/db/schema.rb +39 -11
  27. data/test/dummy/log/test.log +0 -0
  28. data/test/services/disco_app/flow/create_action_test.rb +51 -0
  29. data/test/services/disco_app/flow/create_trigger_test.rb +56 -0
  30. data/test/services/disco_app/flow/process_action_test.rb +68 -0
  31. data/test/services/disco_app/flow/process_trigger_test.rb +61 -0
  32. data/test/vcr/flow_trigger_invalid_title.yml +35 -0
  33. data/test/vcr/flow_trigger_valid.yml +38 -0
  34. metadata +188 -133
  35. data/app/clients/disco_app/rollbar_client.rb +0 -53
  36. data/app/clients/disco_app/rollbar_client_error.rb +0 -2
  37. data/lib/generators/disco_app/templates/initializers/rollbar.rb +0 -21
  38. data/lib/tasks/rollbar.rake +0 -24
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20170606160751) do
13
+ ActiveRecord::Schema.define(version: 2018_12_29_100327) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -24,12 +24,12 @@ ActiveRecord::Schema.define(version: 20170606160751) do
24
24
  t.index ["token"], name: "index_carts_on_token", unique: true
25
25
  end
26
26
 
27
- create_table "disco_app_app_settings", force: :cascade do |t|
27
+ create_table "disco_app_app_settings", id: :serial, force: :cascade do |t|
28
28
  t.datetime "created_at", null: false
29
29
  t.datetime "updated_at", null: false
30
30
  end
31
31
 
32
- create_table "disco_app_application_charges", force: :cascade do |t|
32
+ create_table "disco_app_application_charges", id: :serial, force: :cascade do |t|
33
33
  t.bigint "shop_id"
34
34
  t.bigint "subscription_id"
35
35
  t.integer "status", default: 0
@@ -39,7 +39,33 @@ ActiveRecord::Schema.define(version: 20170606160751) do
39
39
  t.string "confirmation_url"
40
40
  end
41
41
 
42
- create_table "disco_app_plan_codes", force: :cascade do |t|
42
+ create_table "disco_app_flow_actions", force: :cascade do |t|
43
+ t.bigint "shop_id"
44
+ t.string "action_id"
45
+ t.string "action_run_id"
46
+ t.jsonb "properties", default: {}
47
+ t.integer "status", default: 0
48
+ t.datetime "processed_at"
49
+ t.jsonb "processing_errors", default: []
50
+ t.datetime "created_at", null: false
51
+ t.datetime "updated_at", null: false
52
+ t.index ["action_run_id"], name: "index_disco_app_flow_actions_on_action_run_id", unique: true
53
+ end
54
+
55
+ create_table "disco_app_flow_triggers", force: :cascade do |t|
56
+ t.bigint "shop_id"
57
+ t.string "title"
58
+ t.string "resource_name"
59
+ t.string "resource_url"
60
+ t.jsonb "properties", default: {}
61
+ t.integer "status", default: 0
62
+ t.datetime "processed_at"
63
+ t.jsonb "processing_errors", default: []
64
+ t.datetime "created_at", null: false
65
+ t.datetime "updated_at", null: false
66
+ end
67
+
68
+ create_table "disco_app_plan_codes", id: :serial, force: :cascade do |t|
43
69
  t.bigint "plan_id"
44
70
  t.string "code"
45
71
  t.integer "trial_period_days"
@@ -49,7 +75,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
49
75
  t.integer "status", default: 0
50
76
  end
51
77
 
52
- create_table "disco_app_plans", force: :cascade do |t|
78
+ create_table "disco_app_plans", id: :serial, force: :cascade do |t|
53
79
  t.integer "status", default: 0
54
80
  t.string "name"
55
81
  t.integer "plan_type", default: 0
@@ -62,7 +88,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
62
88
  t.integer "interval_count", default: 1
63
89
  end
64
90
 
65
- create_table "disco_app_recurring_application_charges", force: :cascade do |t|
91
+ create_table "disco_app_recurring_application_charges", id: :serial, force: :cascade do |t|
66
92
  t.bigint "shop_id"
67
93
  t.bigint "subscription_id"
68
94
  t.integer "status", default: 0
@@ -72,7 +98,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
72
98
  t.string "confirmation_url"
73
99
  end
74
100
 
75
- create_table "disco_app_sessions", force: :cascade do |t|
101
+ create_table "disco_app_sessions", id: :serial, force: :cascade do |t|
76
102
  t.string "session_id", null: false
77
103
  t.text "data"
78
104
  t.datetime "created_at", null: false
@@ -82,7 +108,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
82
108
  t.index ["updated_at"], name: "index_disco_app_sessions_on_updated_at"
83
109
  end
84
110
 
85
- create_table "disco_app_shops", force: :cascade do |t|
111
+ create_table "disco_app_shops", id: :serial, force: :cascade do |t|
86
112
  t.string "shopify_domain", null: false
87
113
  t.string "shopify_token", null: false
88
114
  t.datetime "created_at", null: false
@@ -95,7 +121,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
95
121
  t.index ["shopify_domain"], name: "index_disco_app_shops_on_shopify_domain", unique: true
96
122
  end
97
123
 
98
- create_table "disco_app_sources", force: :cascade do |t|
124
+ create_table "disco_app_sources", id: :serial, force: :cascade do |t|
99
125
  t.string "source"
100
126
  t.string "name"
101
127
  t.datetime "created_at", null: false
@@ -103,7 +129,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
103
129
  t.index ["source"], name: "index_disco_app_sources_on_source"
104
130
  end
105
131
 
106
- create_table "disco_app_subscriptions", force: :cascade do |t|
132
+ create_table "disco_app_subscriptions", id: :serial, force: :cascade do |t|
107
133
  t.integer "shop_id"
108
134
  t.integer "plan_id"
109
135
  t.integer "status"
@@ -121,7 +147,7 @@ ActiveRecord::Schema.define(version: 20170606160751) do
121
147
  t.index ["shop_id"], name: "index_disco_app_subscriptions_on_shop_id"
122
148
  end
123
149
 
124
- create_table "disco_app_users", force: :cascade do |t|
150
+ create_table "disco_app_users", id: :serial, force: :cascade do |t|
125
151
  t.bigint "shop_id"
126
152
  t.string "first_name"
127
153
  t.string "last_name"
@@ -154,6 +180,8 @@ ActiveRecord::Schema.define(version: 20170606160751) do
154
180
  add_foreign_key "carts", "disco_app_shops", column: "shop_id"
155
181
  add_foreign_key "disco_app_application_charges", "disco_app_shops", column: "shop_id"
156
182
  add_foreign_key "disco_app_application_charges", "disco_app_subscriptions", column: "subscription_id"
183
+ add_foreign_key "disco_app_flow_actions", "disco_app_shops", column: "shop_id", on_delete: :cascade
184
+ add_foreign_key "disco_app_flow_triggers", "disco_app_shops", column: "shop_id", on_delete: :cascade
157
185
  add_foreign_key "disco_app_plan_codes", "disco_app_plans", column: "plan_id"
158
186
  add_foreign_key "disco_app_recurring_application_charges", "disco_app_shops", column: "shop_id"
159
187
  add_foreign_key "disco_app_recurring_application_charges", "disco_app_subscriptions", column: "subscription_id"
File without changes
@@ -0,0 +1,51 @@
1
+ require 'test_helper'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class CreateActionTest < ActiveSupport::TestCase
6
+
7
+ include ActiveJob::TestHelper
8
+
9
+ def setup
10
+ @shop = disco_app_shops(:widget_store)
11
+ end
12
+
13
+ def teardown
14
+ @shop = nil
15
+ end
16
+
17
+ test 'call to create flow action creates model' do
18
+ result = CreateAction.call(shop: @shop, action_id: action_id, action_run_id: action_run_id, properties: properties)
19
+ assert result.success?
20
+ assert result.action.persisted?
21
+ assert result.action.pending?
22
+ assert_equal action_id, result.action.action_id
23
+ assert_equal action_run_id, result.action.action_run_id
24
+ assert_equal properties, result.action.properties
25
+ end
26
+
27
+ test 'call to create flow action enqueues processing job' do
28
+ assert_enqueued_with(job: ProcessActionJob) do
29
+ CreateAction.call(shop: @shop, action_id: action_id, action_run_id: action_run_id, properties: properties)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def action_id
36
+ 'test_action_id'
37
+ end
38
+
39
+ def action_run_id
40
+ 'bdb15e45-4f9d-4c80-88c8-7b43a24edaac-30892-cc8eb62a-14db-43fc-bc33-d6dea41ae623'
41
+ end
42
+
43
+ def properties
44
+ {
45
+ 'customer_email' => 'name@example.com'
46
+ }
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class CreateTriggerTest < ActiveSupport::TestCase
6
+
7
+ include ActiveJob::TestHelper
8
+
9
+ def setup
10
+ @shop = disco_app_shops(:widget_store)
11
+ end
12
+
13
+ def teardown
14
+ @shop = nil
15
+ end
16
+
17
+ test 'call to create flow trigger creates model' do
18
+ result = CreateTrigger.call(shop: @shop, title: title, resource_name: resource_name, resource_url: resource_url, properties: properties)
19
+ assert result.success?
20
+ assert result.trigger.persisted?
21
+ assert result.trigger.pending?
22
+ assert_equal title, result.trigger.title
23
+ assert_equal resource_name, result.trigger.resource_name
24
+ assert_equal resource_url, result.trigger.resource_url
25
+ assert_equal properties, result.trigger.properties
26
+ end
27
+
28
+ test 'call to create flow trigger enqueues processing job' do
29
+ assert_enqueued_with(job: ProcessTriggerJob) do
30
+ CreateTrigger.call(shop: @shop, title: title, resource_name: resource_name, resource_url: resource_url, properties: properties)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def title
37
+ 'Test trigger'
38
+ end
39
+
40
+ def resource_name
41
+ 'test_resource_name'
42
+ end
43
+
44
+ def resource_url
45
+ 'https://example.com/test-resource-url'
46
+ end
47
+
48
+ def properties
49
+ {
50
+ 'Customer email' => 'name@example.com'
51
+ }
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,68 @@
1
+ require 'test_helper'
2
+ require 'interactor'
3
+
4
+ class TestActionId
5
+
6
+ include Interactor
7
+
8
+ def call
9
+ # noop
10
+ end
11
+
12
+ end
13
+
14
+ module DiscoApp
15
+ module Flow
16
+ class ProcessActionTest < ActiveSupport::TestCase
17
+
18
+ include ActiveJob::TestHelper
19
+
20
+ def setup
21
+ @shop = disco_app_shops(:widget_store)
22
+ @action = @shop.flow_actions.create(
23
+ action_id: 'test_action_id',
24
+ action_run_id: 'bdb15e45-4f9d-4c80-88c8-7b43a24edaac-30892-cc8eb62a-14db-43fc-bc33-d6dea41ae623',
25
+ properties: { 'customer_email' => 'name@example.com' }
26
+ )
27
+ @now = Time.parse('2018-12-29T00:00:00Z')
28
+ Timecop.freeze(@now)
29
+ end
30
+
31
+ def teardown
32
+ @shop = nil
33
+ @action = nil
34
+ @now = nil
35
+ Timecop.return
36
+ end
37
+
38
+ test 'call to process action that has already succeeded fails' do
39
+ @action.succeeded!
40
+ result = ProcessAction.call(action: @action)
41
+ assert_not result.success?
42
+ end
43
+
44
+ test 'call to process trigger that has already failed fails' do
45
+ @action.failed!
46
+ result = ProcessAction.call(action: @action)
47
+ assert_not result.success?
48
+ end
49
+
50
+ test 'processing pending action with existing service class succeeds' do
51
+ result = ProcessAction.call(action: @action)
52
+ assert result.success?
53
+ assert @action.succeeded?
54
+ assert_equal @now, @action.processed_at
55
+ end
56
+
57
+ test 'processing pending action with non-existent service class does not succeed' do
58
+ @action.update!(action_id: 'unknown_test_action_id')
59
+ result = ProcessAction.call(action: @action)
60
+ assert_not result.success?
61
+ assert @action.failed?
62
+ assert_equal @now, @action.processed_at
63
+ assert_equal ['Could not find service class for unknown_test_action_id'], @action.processing_errors
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,61 @@
1
+ require 'test_helper'
2
+
3
+ module DiscoApp
4
+ module Flow
5
+ class ProcessTriggerTest < ActiveSupport::TestCase
6
+
7
+ include ActiveJob::TestHelper
8
+
9
+ def setup
10
+ @shop = disco_app_shops(:widget_store)
11
+ @trigger = @shop.flow_triggers.create(
12
+ title: 'Test trigger',
13
+ resource_name: 'test_resource_name',
14
+ resource_url: 'https://example.com/test-resource-url',
15
+ properties: { 'Customer email' => 'name@example.com' }
16
+ )
17
+ @now = Time.parse('2018-12-29T00:00:00Z')
18
+ Timecop.freeze(@now)
19
+ end
20
+
21
+ def teardown
22
+ @shop = nil
23
+ @trigger = nil
24
+ @now = nil
25
+ Timecop.return
26
+ end
27
+
28
+ test 'call to process trigger that has already succeeded fails' do
29
+ @trigger.succeeded!
30
+ result = ProcessTrigger.call(trigger: @trigger)
31
+ assert_not result.success?
32
+ end
33
+
34
+ test 'call to process trigger that has already failed fails' do
35
+ @trigger.failed!
36
+ result = ProcessTrigger.call(trigger: @trigger)
37
+ assert_not result.success?
38
+ end
39
+
40
+ test 'processing valid pending trigger succeeds and makes the expected api call' do
41
+ VCR.use_cassette('flow_trigger_valid') do
42
+ result = ProcessTrigger.call(trigger: @trigger)
43
+ assert result.success?
44
+ assert @trigger.succeeded?
45
+ assert_equal @now, @trigger.processed_at
46
+ end
47
+ end
48
+
49
+ test 'processing invalid pending trigger makes the expected api call with errors logged' do
50
+ VCR.use_cassette('flow_trigger_invalid_title') do
51
+ result = ProcessTrigger.call(trigger: @trigger)
52
+ assert_not result.success?
53
+ assert @trigger.failed?
54
+ assert_equal @now, @trigger.processed_at
55
+ assert_equal 1, @trigger.processing_errors.size
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://widgets.myshopify.com/admin/api/graphql.json
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"query":"\n mutation {\n flowTriggerReceive(body: \"{\\\"trigger_title\\\":\\\"Test trigger\\\",\\\"resources\\\":[{\\\"name\\\":\\\"test_resource_name\\\",\\\"url\\\":\\\"https://example.com/test-resource-url\\\"}],\\\"properties\\\":{\\\"Customer email\\\":\\\"name@example.com\\\"}}\") {\n userErrors {\n field,\n message\n }\n }\n }\n "}'
9
+ headers:
10
+ Accept:
11
+ - "*/*"
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ Content-Type:
15
+ - application/json
16
+ X-Shopify-Access-Token:
17
+ - 00000000000000000000000000000000
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - nginx
25
+ Date:
26
+ - Sat, 29 Dec 2018 11:44:11 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ body:
30
+ encoding: ASCII-8BIT
31
+ string: '{"data":{"flowTriggerReceive":{"userErrors":[{"field":["body"],"message":"Errors
32
+ validating schema:\n Invalid trigger_title ''Test trigger''.\n"}]}},"extensions":{"cost":{"requestedQueryCost":1,"actualQueryCost":1,"throttleStatus":{"maximumAvailable":2000.0,"currentlyAvailable":1999,"restoreRate":100.0}}}}'
33
+ http_version:
34
+ recorded_at: Sat, 29 Dec 2018 11:44:11 GMT
35
+ recorded_with: VCR 3.0.3
@@ -0,0 +1,38 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://widgets.myshopify.com/admin/api/graphql.json
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"query":"\n mutation {\n flowTriggerReceive(body: \"{\\\"trigger_title\\\":\\\"Test trigger\\\",\\\"resources\\\":[{\\\"name\\\":\\\"test_resource_name\\\",\\\"url\\\":\\\"https://example.com/test-resource-url\\\"}],\\\"properties\\\":{\\\"Customer email\\\":\\\"name@example.com\\\"}}\") {\n userErrors {\n field,\n message\n }\n }\n }\n "}'
9
+ headers:
10
+ Accept:
11
+ - "*/*"
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ Content-Type:
15
+ - application/json
16
+ X-Shopify-Access-Token:
17
+ - 00000000000000000000000000000000
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - nginx
25
+ Date:
26
+ - Sat, 29 Dec 2018 11:46:27 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Connection:
32
+ - keep-alive
33
+ body:
34
+ encoding: ASCII-8BIT
35
+ string: '{"data":{"flowTriggerReceive":{"userErrors":[]}},"extensions":{"cost":{"requestedQueryCost":1,"actualQueryCost":1,"throttleStatus":{"maximumAvailable":2000.0,"currentlyAvailable":1999,"restoreRate":100.0}}}}'
36
+ http_version:
37
+ recorded_at: Sat, 29 Dec 2018 11:46:27 GMT
38
+ recorded_with: VCR 3.0.3