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

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