disco_app 0.15.2 → 0.18.1
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 +4 -4
- data/Rakefile +1 -5
- data/app/clients/disco_app/api_client.rb +25 -19
- data/app/clients/disco_app/graphql_client.rb +85 -0
- data/app/controllers/disco_app/admin/app_settings_controller.rb +2 -0
- data/app/controllers/disco_app/admin/application_controller.rb +1 -0
- data/app/controllers/disco_app/admin/concerns/app_settings_controller.rb +2 -1
- data/app/controllers/disco_app/admin/concerns/authenticated_controller.rb +5 -3
- data/app/controllers/disco_app/admin/concerns/plans_controller.rb +3 -2
- data/app/controllers/disco_app/admin/concerns/shops_controller.rb +1 -0
- data/app/controllers/disco_app/admin/concerns/sources_controller.rb +2 -1
- data/app/controllers/disco_app/admin/concerns/subscriptions_controller.rb +3 -2
- data/app/controllers/disco_app/admin/plans_controller.rb +2 -0
- data/app/controllers/disco_app/admin/resources/shops_controller.rb +2 -0
- data/app/controllers/disco_app/admin/shops_controller.rb +2 -0
- data/app/controllers/disco_app/admin/sources_controller.rb +2 -0
- data/app/controllers/disco_app/admin/subscriptions_controller.rb +2 -0
- data/app/controllers/disco_app/charges_controller.rb +6 -7
- data/app/controllers/disco_app/concerns/app_proxy_controller.rb +6 -6
- data/app/controllers/disco_app/concerns/authenticated_controller.rb +26 -27
- data/app/controllers/disco_app/concerns/carrier_request_controller.rb +22 -11
- data/app/controllers/disco_app/concerns/user_authenticated_controller.rb +1 -0
- data/app/controllers/disco_app/concerns/webhooks_controller.rb +13 -12
- data/app/controllers/disco_app/flow/actions_controller.rb +9 -0
- data/app/controllers/disco_app/flow/concerns/actions_controller.rb +23 -0
- data/app/controllers/disco_app/flow/concerns/trigger_usage_controller.rb +29 -0
- data/app/controllers/disco_app/flow/concerns/verifies_flow_payload.rb +39 -0
- data/app/controllers/disco_app/flow/trigger_usage_controller.rb +9 -0
- data/app/controllers/disco_app/frame_controller.rb +0 -1
- data/app/controllers/disco_app/install_controller.rb +3 -6
- data/app/controllers/disco_app/subscriptions_controller.rb +12 -4
- data/app/controllers/disco_app/user_sessions_controller.rb +1 -0
- data/app/controllers/disco_app/webhooks_controller.rb +2 -0
- data/app/controllers/sessions_controller.rb +5 -6
- data/app/helpers/disco_app/application_helper.rb +7 -7
- data/app/jobs/disco_app/app_installed_job.rb +2 -0
- data/app/jobs/disco_app/app_uninstalled_job.rb +2 -0
- data/app/jobs/disco_app/concerns/app_installed_job.rb +2 -3
- data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +2 -1
- data/app/jobs/disco_app/concerns/customers_data_request_job.rb +13 -0
- data/app/jobs/disco_app/concerns/customers_redact_job.rb +13 -0
- data/app/jobs/disco_app/concerns/render_asset_group_job.rb +1 -0
- data/app/jobs/disco_app/concerns/shop_redact_job.rb +13 -0
- data/app/jobs/disco_app/concerns/shop_update_job.rb +9 -2
- data/app/jobs/disco_app/concerns/subscription_changed_job.rb +2 -1
- data/app/jobs/disco_app/concerns/synchronise_carrier_service_job.rb +8 -7
- data/app/jobs/disco_app/concerns/synchronise_resources_job.rb +1 -0
- data/app/jobs/disco_app/concerns/synchronise_users_job.rb +6 -3
- data/app/jobs/disco_app/concerns/synchronise_webhooks_job.rb +24 -11
- data/app/jobs/disco_app/customers_data_request_job.rb +5 -0
- data/app/jobs/disco_app/customers_redact_job.rb +5 -0
- data/app/jobs/disco_app/flow/process_action_job.rb +11 -0
- data/app/jobs/disco_app/flow/process_trigger_job.rb +11 -0
- data/app/jobs/disco_app/render_asset_group_job.rb +2 -0
- data/app/jobs/disco_app/shop_job.rb +10 -10
- data/app/jobs/disco_app/shop_redact_job.rb +5 -0
- data/app/jobs/disco_app/shop_update_job.rb +2 -0
- data/app/jobs/disco_app/subscription_changed_job.rb +2 -0
- data/app/jobs/disco_app/synchronise_carrier_service_job.rb +2 -0
- data/app/jobs/disco_app/synchronise_resources_job.rb +2 -0
- data/app/jobs/disco_app/synchronise_users_job.rb +2 -0
- data/app/jobs/disco_app/synchronise_webhooks_job.rb +2 -0
- data/app/models/application_record.rb +2 -0
- data/app/models/disco_app/app_settings.rb +2 -0
- data/app/models/disco_app/application_charge.rb +1 -1
- data/app/models/disco_app/concerns/app_settings.rb +2 -0
- data/app/models/disco_app/concerns/can_be_liquified.rb +29 -14
- data/app/models/disco_app/concerns/has_metafields.rb +1 -2
- data/app/models/disco_app/concerns/plan.rb +3 -4
- data/app/models/disco_app/concerns/plan_code.rb +1 -2
- data/app/models/disco_app/concerns/renders_assets.rb +15 -21
- data/app/models/disco_app/concerns/shop.rb +20 -14
- data/app/models/disco_app/concerns/source.rb +2 -3
- data/app/models/disco_app/concerns/subscription.rb +3 -3
- data/app/models/disco_app/concerns/synchronises.rb +10 -15
- data/app/models/disco_app/concerns/taggable.rb +1 -0
- data/app/models/disco_app/concerns/user.rb +3 -2
- data/app/models/disco_app/flow/action.rb +9 -0
- data/app/models/disco_app/flow/concerns/action.rb +27 -0
- data/app/models/disco_app/flow/concerns/trigger.rb +28 -0
- data/app/models/disco_app/flow/concerns/trigger_usage.rb +17 -0
- data/app/models/disco_app/flow/trigger.rb +9 -0
- data/app/models/disco_app/flow/trigger_usage.rb +9 -0
- data/app/models/disco_app/plan.rb +2 -0
- data/app/models/disco_app/plan_code.rb +2 -0
- data/app/models/disco_app/session_storage.rb +5 -2
- data/app/models/disco_app/shop.rb +2 -0
- data/app/models/disco_app/source.rb +2 -0
- data/app/models/disco_app/subscription.rb +2 -0
- data/app/models/disco_app/user.rb +2 -0
- data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +12 -15
- data/app/resources/disco_app/admin/resources/shop_resource.rb +1 -0
- data/app/services/disco_app/carrier_request_service.rb +3 -3
- data/app/services/disco_app/charges_service.rb +26 -34
- data/app/services/disco_app/flow/create_action.rb +35 -0
- data/app/services/disco_app/flow/create_trigger.rb +34 -0
- data/app/services/disco_app/flow/process_action.rb +50 -0
- data/app/services/disco_app/flow/process_trigger.rb +72 -0
- data/app/services/disco_app/flow/update_trigger_usage.rb +42 -0
- data/app/services/disco_app/partner_app_service.rb +1 -1
- data/app/services/disco_app/proxy_service.rb +2 -2
- data/app/services/disco_app/request_validation_service.rb +2 -2
- data/app/services/disco_app/subscription_service.rb +60 -32
- data/app/services/disco_app/webhook_service.rb +9 -11
- data/app/views/layouts/admin.html.erb +1 -2
- data/app/views/layouts/application.html.erb +1 -2
- data/app/views/layouts/embedded_app.html.erb +2 -4
- data/app/views/layouts/embedded_app_modal.html.erb +2 -3
- data/app/views/shopify_app/sessions/new.html.erb +2 -4
- data/config/routes.rb +11 -3
- data/db/migrate/20150525000000_create_shops_if_not_existent.rb +80 -80
- data/db/migrate/20170315062548_create_disco_app_sources.rb +2 -0
- data/db/migrate/20170315062629_add_sources_to_shop_subscriptions.rb +3 -1
- data/db/migrate/20170327214540_create_disco_app_users.rb +2 -1
- data/db/migrate/20170606160751_fix_disco_app_users_index.rb +2 -0
- data/db/migrate/20181229100327_create_flow_actions_and_triggers.rb +32 -0
- data/db/migrate/20200405000000_create_flow_trigger_usages.rb +16 -0
- data/lib/disco_app/configuration.rb +12 -5
- data/lib/disco_app/constants.rb +4 -2
- data/lib/disco_app/engine.rb +1 -1
- data/lib/disco_app/session.rb +1 -0
- data/lib/disco_app/support/file_fixtures.rb +2 -1
- data/lib/disco_app/version.rb +3 -1
- data/lib/generators/disco_app/install/USAGE +5 -0
- data/lib/generators/disco_app/install/install_generator.rb +297 -0
- data/lib/generators/disco_app/{templates → install/templates}/assets/javascripts/application.js +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/assets/javascripts/components.js +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/assets/stylesheets/application.scss +0 -0
- data/lib/generators/disco_app/install/templates/config/appsignal.yml +12 -0
- data/lib/generators/disco_app/install/templates/config/cable.yml.tt +11 -0
- data/lib/generators/disco_app/{templates → install/templates}/config/database.yml.tt +6 -3
- data/lib/generators/disco_app/install/templates/config/environments/staging.rb +108 -0
- data/lib/generators/disco_app/{templates → install/templates}/config/puma.rb +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/controllers/home_controller.rb +1 -0
- data/lib/generators/disco_app/{templates → install/templates}/initializers/disco_app.rb +5 -0
- data/lib/generators/disco_app/install/templates/initializers/session_store.rb +2 -0
- data/lib/generators/disco_app/install/templates/initializers/shopify_app.rb +11 -0
- data/lib/generators/disco_app/{templates → install/templates}/initializers/shopify_session_repository.rb +0 -0
- data/lib/generators/disco_app/install/templates/initializers/timber.rb +4 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/.editorconfig +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/.env +7 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/.env.local +4 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/.gitignore +6 -0
- data/lib/generators/disco_app/install/templates/root/.rspec +1 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/.rubocop.yml +315 -217
- data/lib/generators/disco_app/install/templates/root/.tool-versions +2 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/CHECKS +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/Procfile +0 -0
- data/lib/generators/disco_app/{templates → install/templates}/root/README.md +0 -0
- data/lib/generators/disco_app/install/templates/root/package.json.tt +17 -0
- data/lib/generators/disco_app/install/templates/spec/rails_helper.rb +40 -0
- data/lib/generators/disco_app/install/templates/spec/spec_helper.rb +24 -0
- data/lib/generators/disco_app/install/templates/spec/support/active_job.rb +13 -0
- data/lib/generators/disco_app/install/templates/spec/support/coveralls.rb +3 -0
- data/lib/generators/disco_app/install/templates/spec/support/database_cleaner.rb +17 -0
- data/lib/generators/disco_app/install/templates/spec/support/factory_bot.rb +3 -0
- data/lib/generators/disco_app/install/templates/spec/support/helpers/json_helper.rb +13 -0
- data/lib/generators/disco_app/install/templates/spec/support/shared_examples/a_synchronise_job.rb +12 -0
- data/lib/generators/disco_app/install/templates/spec/support/shoulda.rb +6 -0
- data/lib/generators/disco_app/install/templates/spec/support/vcr.rb +14 -0
- data/lib/generators/disco_app/install/templates/spec/support/webmock.rb +8 -0
- data/lib/generators/disco_app/{templates → install/templates}/views/home/index.html.erb +0 -0
- data/lib/generators/disco_app/react/USAGE +5 -0
- data/lib/generators/disco_app/react/react_generator.rb +108 -0
- data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/base_controller.rb +18 -0
- data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/home_controller.rb +10 -0
- data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/shops_controller.rb +11 -0
- data/lib/generators/disco_app/react/templates/app/controllers/embedded/api/users_controller.rb +11 -0
- data/lib/generators/disco_app/react/templates/app/controllers/embedded/home_controller.rb +13 -0
- data/lib/generators/disco_app/react/templates/app/models/api_response.rb +107 -0
- data/lib/generators/disco_app/react/templates/app/serializers/disco_app/shop_serializer.rb +13 -0
- data/lib/generators/disco_app/react/templates/app/serializers/disco_app/user_serializer.rb +13 -0
- data/lib/generators/disco_app/react/templates/app/serializers/empty_serializer.rb +5 -0
- data/lib/generators/disco_app/react/templates/app/serializers/error_serializer.rb +76 -0
- data/lib/generators/disco_app/react/templates/app/views/embedded/home/index.html.erb +12 -0
- data/lib/generators/disco_app/react/templates/app/views/layouts/embedded.html.erb +10 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/App.jsx +77 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/HomePage.jsx +34 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/EmbeddedPage.jsx +70 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/ErrorBanner.jsx +58 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/PaginationWrapper.jsx +10 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/Shared/ScrollToTop.jsx +23 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/withApi.jsx +125 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/index.jsx +39 -0
- data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/utils.js +19 -0
- data/lib/generators/disco_app/react/templates/app/webpack/packs/embedded.js +2 -0
- data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/banners.scss +7 -0
- data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/busy.scss +3 -0
- data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/index.scss +3 -0
- data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded/shared/pagination.scss +5 -0
- data/lib/generators/disco_app/react/templates/app/webpack/stylesheets/embedded.scss +2 -0
- data/lib/generators/disco_app/react/templates/config/initializers/mime_types.rb +13 -0
- data/lib/generators/disco_app/react/templates/config/initializers/omniauth.rb +27 -0
- data/lib/generators/disco_app/react/templates/config/initializers/version.rb.tt +7 -0
- data/lib/generators/disco_app/react/templates/config/webpack/staging.js +5 -0
- data/lib/generators/disco_app/react/templates/config/webpack/test.js +5 -0
- data/lib/generators/disco_app/react/templates/config/webpacker.yml +96 -0
- data/lib/generators/disco_app/react/templates/root/.eslintignore +5 -0
- data/lib/generators/disco_app/react/templates/root/.eslintrc +69 -0
- data/lib/generators/disco_app/react/templates/root/.prettierrc +3 -0
- data/lib/generators/disco_app/react/templates/root/VERSION +1 -0
- data/lib/generators/disco_app/react/templates/root/babel.config.js +72 -0
- data/lib/generators/disco_app/react/templates/root/package.json.tt +84 -0
- data/lib/generators/disco_app/react/templates/root/postcss.config.js +14 -0
- data/lib/tasks/api.rake +0 -2
- data/lib/tasks/carrier_service.rake +0 -2
- data/lib/tasks/database.rake +1 -1
- data/lib/tasks/sessions.rake +0 -2
- data/lib/tasks/shops.rake +0 -2
- data/lib/tasks/users.rake +0 -2
- data/lib/tasks/webhooks.rake +0 -2
- data/test/clients/disco_app/api_client_test.rb +3 -3
- data/test/controllers/disco_app/admin/shops_controller_test.rb +1 -0
- data/test/controllers/disco_app/charges_controller_test.rb +8 -9
- data/test/controllers/disco_app/flow/trigger_usage_controller_test.rb +41 -0
- data/test/controllers/disco_app/install_controller_test.rb +2 -1
- data/test/controllers/disco_app/subscriptions_controller_test.rb +2 -1
- data/test/controllers/disco_app/webhooks_controller_test.rb +1 -0
- data/test/controllers/home_controller_test.rb +1 -1
- data/test/disco_app_test.rb +3 -1
- data/test/dummy/Rakefile +1 -1
- data/test/dummy/app/assets/config/manifest.js +2 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/carrier_request_controller.rb +1 -0
- data/test/dummy/app/controllers/disco_app/admin/shops_controller.rb +1 -0
- data/test/dummy/app/controllers/home_controller.rb +1 -0
- data/test/dummy/app/controllers/proxy_controller.rb +1 -0
- data/test/dummy/app/javascript/packs/application.js +18 -0
- data/test/dummy/app/jobs/disco_app/app_installed_job.rb +2 -3
- data/test/dummy/app/jobs/disco_app/app_uninstalled_job.rb +1 -0
- data/test/dummy/app/models/application_record.rb +2 -0
- data/test/dummy/app/models/cart.rb +4 -3
- data/test/dummy/app/models/disco_app/shop.rb +4 -5
- data/test/dummy/app/models/js_configuration.rb +1 -0
- data/test/dummy/app/models/product.rb +3 -2
- data/test/dummy/app/models/widget_configuration.rb +1 -0
- data/test/dummy/babel.config.js +72 -0
- data/test/dummy/bin/bundle +1 -1
- data/test/dummy/bin/rails +1 -1
- data/test/dummy/bin/setup +8 -8
- data/test/dummy/bin/webpack +19 -0
- data/test/dummy/bin/webpack-dev-server +19 -0
- data/test/dummy/config/application.rb +4 -3
- data/test/dummy/config/boot.rb +2 -2
- data/test/dummy/config/database.yml +3 -0
- data/test/dummy/config/environment.rb +1 -1
- data/test/dummy/config/environments/staging.rb +85 -0
- data/test/dummy/config/initializers/disco_app.rb +8 -1
- data/test/dummy/config/initializers/omniauth.rb +3 -4
- data/test/dummy/config/initializers/session_store.rb +1 -1
- data/test/dummy/config/initializers/shopify_app.rb +1 -0
- data/test/dummy/config/routes.rb +0 -2
- data/test/dummy/config/secrets.yml +3 -0
- data/test/dummy/config/webpack/development.js +5 -0
- data/test/dummy/config/webpack/environment.js +3 -0
- data/test/dummy/config/webpack/production.js +5 -0
- data/test/dummy/config/webpack/test.js +5 -0
- data/test/dummy/config/webpacker.yml +95 -0
- data/test/dummy/db/migrate/20160307182229_create_products.rb +3 -1
- data/test/dummy/db/migrate/20160530160739_create_asset_models.rb +3 -1
- data/test/dummy/db/migrate/20161105054746_create_carts.rb +2 -1
- data/test/dummy/db/schema.rb +45 -6
- data/test/dummy/package.json +8 -0
- data/test/dummy/postcss.config.js +12 -0
- data/test/dummy/yarn.lock +7244 -0
- data/test/fixtures/api/subscriptions/valid_request.json +1 -1
- data/test/fixtures/api/widget_store/empty_webhooks.json +3 -0
- data/test/fixtures/api/widget_store/existing_webhooks.json +43 -0
- data/test/fixtures/disco_app/shops.yml +3 -0
- data/test/fixtures/webhooks/flow/trigger_usage.json +7 -0
- data/test/integration/synchronises_test.rb +2 -1
- data/test/jobs/disco_app/app_installed_job_test.rb +28 -9
- data/test/jobs/disco_app/app_uninstalled_job_test.rb +5 -2
- data/test/jobs/disco_app/send_subscription_job_test.rb +3 -2
- data/test/jobs/disco_app/synchronise_carrier_service_job_test.rb +1 -0
- data/test/jobs/disco_app/synchronise_users_job_test.rb +1 -0
- data/test/jobs/disco_app/synchronise_webhooks_job_test.rb +70 -13
- data/test/models/disco_app/can_be_liquified_test.rb +3 -1
- data/test/models/disco_app/has_metafields_test.rb +37 -18
- data/test/models/disco_app/renders_assets_test.rb +2 -1
- data/test/models/disco_app/session_test.rb +2 -2
- data/test/models/disco_app/shop_test.rb +1 -1
- data/test/services/disco_app/charges_service_test.rb +7 -8
- data/test/services/disco_app/flow/create_action_test.rb +51 -0
- data/test/services/disco_app/flow/create_trigger_test.rb +56 -0
- data/test/services/disco_app/flow/process_action_test.rb +68 -0
- data/test/services/disco_app/flow/process_trigger_test.rb +94 -0
- data/test/services/disco_app/flow/update_trigger_usage_test.rb +87 -0
- data/test/services/disco_app/subscription_service_test.rb +3 -2
- data/test/support/test_file_fixtures.rb +2 -2
- data/test/support/test_shopify_api.rb +1 -1
- data/test/test_helper.rb +10 -7
- data/test/vcr/flow_trigger_invalid_title.yml +35 -0
- data/test/vcr/flow_trigger_valid.yml +38 -0
- data/test/vcr/webhook_failure.yml +13 -13
- metadata +282 -148
- data/app/clients/disco_app/disco_api_error.rb +0 -2
- data/app/clients/disco_app/rollbar_client.rb +0 -53
- data/app/clients/disco_app/rollbar_client_error.rb +0 -2
- data/lib/generators/disco_app/USAGE +0 -5
- data/lib/generators/disco_app/disco_app_generator.rb +0 -236
- data/lib/generators/disco_app/templates/config/newrelic.yml +0 -26
- data/lib/generators/disco_app/templates/initializers/rollbar.rb +0 -23
- data/lib/generators/disco_app/templates/initializers/session_store.rb +0 -2
- data/lib/generators/disco_app/templates/initializers/shopify_app.rb +0 -6
- data/lib/generators/disco_app/templates/root/.codeclimate.yml +0 -7
- data/lib/generators/disco_app/templates/root/.ruby-version +0 -1
- data/lib/tasks/rollbar.rake +0 -24
- data/test/fixtures/api/widget_store/webhooks.json +0 -1
data/lib/generators/disco_app/react/templates/app/webpack/javascripts/embedded/components/App.jsx
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
import * as PropTypes from 'prop-types';
|
3
|
+
import React from 'react';
|
4
|
+
import { Route, Switch } from 'react-router-dom';
|
5
|
+
import HomePage from './HomePage';
|
6
|
+
import withApi from './withApi';
|
7
|
+
|
8
|
+
const HomePageWithApi = withApi(HomePage);
|
9
|
+
|
10
|
+
class App extends React.Component {
|
11
|
+
static propTypes = {
|
12
|
+
api: PropTypes.func.isRequired,
|
13
|
+
parseApiResponse: PropTypes.func.isRequired
|
14
|
+
};
|
15
|
+
|
16
|
+
static childContextTypes = {
|
17
|
+
shop: PropTypes.shape({
|
18
|
+
id: PropTypes.string,
|
19
|
+
name: PropTypes.string,
|
20
|
+
shopifyDomain: PropTypes.string,
|
21
|
+
timeZone: PropTypes.string
|
22
|
+
}),
|
23
|
+
user: PropTypes.shape({
|
24
|
+
id: PropTypes.string,
|
25
|
+
email: PropTypes.string,
|
26
|
+
firstName: PropTypes.string,
|
27
|
+
initials: PropTypes.string,
|
28
|
+
lastName: PropTypes.string
|
29
|
+
})
|
30
|
+
};
|
31
|
+
|
32
|
+
state = {
|
33
|
+
shop: null,
|
34
|
+
user: null
|
35
|
+
};
|
36
|
+
|
37
|
+
getChildContext() {
|
38
|
+
return {
|
39
|
+
shop: this.state.shop,
|
40
|
+
user: this.state.user
|
41
|
+
};
|
42
|
+
}
|
43
|
+
|
44
|
+
componentWillMount() {
|
45
|
+
const { api, parseApiResponse } = this.props;
|
46
|
+
|
47
|
+
axios
|
48
|
+
.all([
|
49
|
+
api.get('/embedded/api/shop'),
|
50
|
+
api.get('/embedded/api/users/current')
|
51
|
+
])
|
52
|
+
.then(
|
53
|
+
axios.spread(
|
54
|
+
async (shopResponse, usersResponse) => {
|
55
|
+
this.setState({
|
56
|
+
shop: await parseApiResponse(shopResponse),
|
57
|
+
user: await parseApiResponse(usersResponse)
|
58
|
+
});
|
59
|
+
}
|
60
|
+
)
|
61
|
+
);
|
62
|
+
}
|
63
|
+
|
64
|
+
render() {
|
65
|
+
const { user } = this.state;
|
66
|
+
|
67
|
+
if (!user) return <div />;
|
68
|
+
|
69
|
+
return (
|
70
|
+
<Switch>
|
71
|
+
<Route exact path="/" component={HomePageWithApi} />
|
72
|
+
</Switch>
|
73
|
+
);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
export default App;
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { EmptyState, FooterHelp, Layout, Link } from '@shopify/polaris';
|
3
|
+
import EmbeddedPage from './Shared/EmbeddedPage';
|
4
|
+
|
5
|
+
const HomePage = props => (
|
6
|
+
<EmbeddedPage title="Welcome" {...props}>
|
7
|
+
<EmptyState
|
8
|
+
action={{
|
9
|
+
content: 'Polaris docs',
|
10
|
+
external: true,
|
11
|
+
url: 'https://polaris.shopify.com/'
|
12
|
+
}}
|
13
|
+
heading="Booyah!"
|
14
|
+
image="https://cdn.shopify.com/s/files/1/0757/9955/files/empty-state.svg"
|
15
|
+
>
|
16
|
+
<p>Time to build a killer UI</p>
|
17
|
+
</EmptyState>
|
18
|
+
<Layout.Section>
|
19
|
+
<FooterHelp>
|
20
|
+
Learn more about{' '}
|
21
|
+
<Link url="https://www.discolabs.com/" external>
|
22
|
+
Disco
|
23
|
+
</Link>
|
24
|
+
's{' '}
|
25
|
+
<Link url="https://www.discolabs.com/" external>
|
26
|
+
[Disco app]
|
27
|
+
</Link>
|
28
|
+
.
|
29
|
+
</FooterHelp>
|
30
|
+
</Layout.Section>
|
31
|
+
</EmbeddedPage>
|
32
|
+
);
|
33
|
+
|
34
|
+
export default HomePage;
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import _ from 'lodash';
|
2
|
+
import * as PropTypes from 'prop-types';
|
3
|
+
import React from 'react';
|
4
|
+
import ReactRouterPropTypes from 'react-router-prop-types';
|
5
|
+
import { History } from '@shopify/app-bridge/actions';
|
6
|
+
import { Page } from '@shopify/polaris';
|
7
|
+
|
8
|
+
class EmbeddedPage extends React.Component {
|
9
|
+
static contextTypes = {
|
10
|
+
polaris: PropTypes.object
|
11
|
+
};
|
12
|
+
|
13
|
+
static propTypes = {
|
14
|
+
children: PropTypes.node.isRequired,
|
15
|
+
history: ReactRouterPropTypes.history.isRequired,
|
16
|
+
location: ReactRouterPropTypes.location.isRequired,
|
17
|
+
title: PropTypes.string.isRequired
|
18
|
+
};
|
19
|
+
|
20
|
+
componentDidMount() {
|
21
|
+
window.addEventListener('message', this.handleMessage);
|
22
|
+
|
23
|
+
this.pushHistory();
|
24
|
+
}
|
25
|
+
|
26
|
+
componentDidUpdate(prevProps) {
|
27
|
+
if (prevProps.location.pathname !== this.props.location.pathname) {
|
28
|
+
this.pushHistory();
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
componentWillUnmount() {
|
33
|
+
window.removeEventListener('message', this.handleMessage);
|
34
|
+
}
|
35
|
+
|
36
|
+
pushHistory = () => {
|
37
|
+
const history = History.create(this.context.polaris.appBridge);
|
38
|
+
|
39
|
+
history.dispatch(History.Action.PUSH, this.props.history.location.pathname);
|
40
|
+
};
|
41
|
+
|
42
|
+
handleMessage = e => {
|
43
|
+
if (e.isTrusted) {
|
44
|
+
if (_.isString(e.data)) {
|
45
|
+
const json = JSON.parse(e.data);
|
46
|
+
|
47
|
+
if (json.message === 'Shopify.API.setWindowLocation') {
|
48
|
+
const url = new URL(json.data);
|
49
|
+
this.props.history.push(url.pathname);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
};
|
54
|
+
|
55
|
+
pushHistory = () => {
|
56
|
+
const history = History.create(this.context.polaris.appBridge);
|
57
|
+
|
58
|
+
history.dispatch(History.Action.PUSH, this.props.history.location.pathname);
|
59
|
+
};
|
60
|
+
|
61
|
+
render() {
|
62
|
+
return (
|
63
|
+
<Page title={this.props.title} {...this.props}>
|
64
|
+
{this.props.children}
|
65
|
+
</Page>
|
66
|
+
);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
export default EmbeddedPage;
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import * as PropTypes from 'prop-types';
|
2
|
+
import React from 'react';
|
3
|
+
import { Banner, TextStyle } from '@shopify/polaris';
|
4
|
+
|
5
|
+
const ErrorBanner = ({ errors, prologue }) => {
|
6
|
+
const errorKeys = () => Object.keys(errors).sort();
|
7
|
+
|
8
|
+
const errorCount = () => errorKeys().length;
|
9
|
+
|
10
|
+
const errorMessage = () => {
|
11
|
+
let msg = '1 field needs changes';
|
12
|
+
|
13
|
+
if (errorCount() > 1) {
|
14
|
+
msg = `${errorCount()} fields need changes`;
|
15
|
+
}
|
16
|
+
|
17
|
+
return msg;
|
18
|
+
};
|
19
|
+
|
20
|
+
const separator = index => {
|
21
|
+
if (index === 0) return ' ';
|
22
|
+
|
23
|
+
if (index === errorCount() - 1) return ' and ';
|
24
|
+
|
25
|
+
return ', ';
|
26
|
+
};
|
27
|
+
|
28
|
+
if (errorCount() === 0) return null;
|
29
|
+
|
30
|
+
return (
|
31
|
+
<Banner status="critical">
|
32
|
+
<p>
|
33
|
+
{prologue},{errorMessage()}:
|
34
|
+
{errorKeys().map((key, index) => (
|
35
|
+
<span>
|
36
|
+
{separator(index)}
|
37
|
+
<TextStyle key="error-{index}" variation="strong">
|
38
|
+
{key}
|
39
|
+
</TextStyle>
|
40
|
+
</span>
|
41
|
+
))}
|
42
|
+
.
|
43
|
+
</p>
|
44
|
+
</Banner>
|
45
|
+
);
|
46
|
+
};
|
47
|
+
|
48
|
+
ErrorBanner.defaultProps = {
|
49
|
+
errors: {},
|
50
|
+
prologue: 'To save this form'
|
51
|
+
};
|
52
|
+
|
53
|
+
ErrorBanner.propTypes = {
|
54
|
+
errors: PropTypes.shape({}),
|
55
|
+
prologue: PropTypes.string
|
56
|
+
};
|
57
|
+
|
58
|
+
export default ErrorBanner;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import * as PropTypes from 'prop-types';
|
2
|
+
import React from 'react';
|
3
|
+
import ReactRouterPropTypes from 'react-router-prop-types';
|
4
|
+
import { withRouter } from 'react-router-dom';
|
5
|
+
|
6
|
+
class ScrollToTop extends React.Component {
|
7
|
+
static propTypes = {
|
8
|
+
children: PropTypes.node.isRequired,
|
9
|
+
location: ReactRouterPropTypes.location.isRequired
|
10
|
+
};
|
11
|
+
|
12
|
+
componentDidUpdate(prevProps) {
|
13
|
+
if (this.props.location !== prevProps.location) {
|
14
|
+
window.scrollTo(0, 0);
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
render() {
|
19
|
+
return this.props.children;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
export default withRouter(ScrollToTop);
|
@@ -0,0 +1,125 @@
|
|
1
|
+
import axios from 'axios';
|
2
|
+
import { deserialise } from 'kitsu-core';
|
3
|
+
import _ from 'lodash';
|
4
|
+
import qs from 'qs';
|
5
|
+
import React from 'react';
|
6
|
+
|
7
|
+
function withApi(WrappedComponent) {
|
8
|
+
class WithApi extends React.Component {
|
9
|
+
constructor(props) {
|
10
|
+
super(props);
|
11
|
+
|
12
|
+
this.initApi();
|
13
|
+
}
|
14
|
+
|
15
|
+
getErrorsFor = (field, errors) => {
|
16
|
+
const fieldErrors = errors[field];
|
17
|
+
|
18
|
+
if (!fieldErrors) return '';
|
19
|
+
|
20
|
+
return fieldErrors.join(', ');
|
21
|
+
};
|
22
|
+
|
23
|
+
initApi = () => {
|
24
|
+
const csrfToken = document
|
25
|
+
.getElementsByName('csrf-token')[0]
|
26
|
+
.getAttribute('content');
|
27
|
+
|
28
|
+
this.api = axios.create({
|
29
|
+
headers: {
|
30
|
+
Accept: 'application/json',
|
31
|
+
'X-CSRF-Token': csrfToken,
|
32
|
+
'X-Key-Inflection': 'camel'
|
33
|
+
},
|
34
|
+
paramsSerializer: params => qs.stringify(params),
|
35
|
+
timeout: 5000
|
36
|
+
});
|
37
|
+
};
|
38
|
+
|
39
|
+
parseApiResponse = async (response, includeMeta) => {
|
40
|
+
const result = await deserialise(response.data);
|
41
|
+
|
42
|
+
return includeMeta ? result : result.data;
|
43
|
+
};
|
44
|
+
|
45
|
+
parseApiError = async errorResponse => {
|
46
|
+
const result = await deserialise(errorResponse.response.data);
|
47
|
+
|
48
|
+
const parsedErrors = {};
|
49
|
+
|
50
|
+
result.errors.forEach(error => {
|
51
|
+
if (
|
52
|
+
_.has(error, 'source.pointer') &&
|
53
|
+
error.source.pointer.startsWith('/data/attributes/')
|
54
|
+
) {
|
55
|
+
const attr = _.last(error.source.pointer.split('/'));
|
56
|
+
const msg = error.detail;
|
57
|
+
|
58
|
+
if (_.has(parsedErrors, attr)) {
|
59
|
+
parsedErrors[attr].push(msg);
|
60
|
+
} else {
|
61
|
+
parsedErrors[attr] = [msg];
|
62
|
+
}
|
63
|
+
}
|
64
|
+
});
|
65
|
+
|
66
|
+
return parsedErrors;
|
67
|
+
};
|
68
|
+
|
69
|
+
resourceListParams = state => {
|
70
|
+
const defaultPageSize = 25;
|
71
|
+
const params = {};
|
72
|
+
|
73
|
+
if (!state) return params;
|
74
|
+
|
75
|
+
if (state.filters) {
|
76
|
+
params.filter = {};
|
77
|
+
|
78
|
+
state.filters.forEach(
|
79
|
+
filter => (params.filter[filter.key] = filter.value)
|
80
|
+
);
|
81
|
+
}
|
82
|
+
|
83
|
+
if (state.pageNumber) {
|
84
|
+
params.page = {
|
85
|
+
number: state.pageNumber,
|
86
|
+
size: state.pageSize || defaultPageSize
|
87
|
+
};
|
88
|
+
}
|
89
|
+
|
90
|
+
if (state.searchQuery) {
|
91
|
+
params.search = state.searchQuery;
|
92
|
+
}
|
93
|
+
|
94
|
+
if (state.sortBy) {
|
95
|
+
const match = /(.*)_((?:a|de)sc)/.exec(state.sortBy);
|
96
|
+
|
97
|
+
if (match) {
|
98
|
+
const field = match[1];
|
99
|
+
const descSignifier = match[2] === 'desc' ? '-' : '';
|
100
|
+
|
101
|
+
params.sort = `${descSignifier}${field}`;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
return params;
|
106
|
+
};
|
107
|
+
|
108
|
+
render() {
|
109
|
+
return (
|
110
|
+
<WrappedComponent
|
111
|
+
api={this.api}
|
112
|
+
getErrorsFor={this.getErrorsFor}
|
113
|
+
parseApiError={this.parseApiError}
|
114
|
+
parseApiResponse={this.parseApiResponse}
|
115
|
+
resourceListParams={this.resourceListParams}
|
116
|
+
{...this.props}
|
117
|
+
/>
|
118
|
+
);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
return WithApi;
|
123
|
+
}
|
124
|
+
|
125
|
+
export default withApi;
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import bugsnag from 'bugsnag-js';
|
2
|
+
import createPlugin from 'bugsnag-react';
|
3
|
+
import React from 'react';
|
4
|
+
import ReactDOM from 'react-dom';
|
5
|
+
import { BrowserRouter as Router } from 'react-router-dom';
|
6
|
+
import { AppProvider } from '@shopify/polaris';
|
7
|
+
import App from './components/App';
|
8
|
+
import ScrollToTop from './components/Shared/ScrollToTop';
|
9
|
+
import withApi from './components/withApi';
|
10
|
+
|
11
|
+
const app = document.getElementById('app');
|
12
|
+
|
13
|
+
const bugsnagClient = bugsnag({
|
14
|
+
apiKey: app.dataset.bugsnagApiKey,
|
15
|
+
appVersion: app.dataset.version,
|
16
|
+
releaseStage: app.dataset.environment
|
17
|
+
});
|
18
|
+
|
19
|
+
const ErrorBoundary = bugsnagClient.use(createPlugin(React));
|
20
|
+
|
21
|
+
const AppWithApi = withApi(App);
|
22
|
+
|
23
|
+
ReactDOM.render(
|
24
|
+
<ErrorBoundary>
|
25
|
+
<Router>
|
26
|
+
<AppProvider
|
27
|
+
apiKey={app.dataset.apiKey}
|
28
|
+
debug={app.dataset.debug}
|
29
|
+
forceRedirect={app.dataset.forceRedirect}
|
30
|
+
shopOrigin={app.dataset.shopOrigin}
|
31
|
+
>
|
32
|
+
<ScrollToTop>
|
33
|
+
<AppWithApi bugsnagClient={bugsnagClient} />
|
34
|
+
</ScrollToTop>
|
35
|
+
</AppProvider>
|
36
|
+
</Router>
|
37
|
+
</ErrorBoundary>,
|
38
|
+
app
|
39
|
+
);
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { DateTime } from 'luxon';
|
2
|
+
import numeral from 'numeral';
|
3
|
+
|
4
|
+
export const numberToCurrency = amount => {
|
5
|
+
const format = '$0,0.00';
|
6
|
+
const floatAmount = parseFloat(amount);
|
7
|
+
|
8
|
+
if (floatAmount < 0) {
|
9
|
+
return `(${numeral(Math.abs(floatAmount)).format(format)})`;
|
10
|
+
}
|
11
|
+
|
12
|
+
return numeral(floatAmount).format(format);
|
13
|
+
};
|
14
|
+
|
15
|
+
export const formatTime = time => {
|
16
|
+
if (!time) return 'N/A';
|
17
|
+
|
18
|
+
return DateTime.fromISO(time).toLocaleString(DateTime.DATETIME_SHORT);
|
19
|
+
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
API_JSON = 'application/vnd.api+json'.freeze
|
2
|
+
|
3
|
+
Mime::Type.register(API_JSON, :jsonapi)
|
4
|
+
|
5
|
+
parsers = ActionDispatch::Request.parameter_parsers.merge(
|
6
|
+
Mime[:jsonapi].symbol => ->(body) { JSON.parse(body) }
|
7
|
+
)
|
8
|
+
ActionDispatch::Request.parameter_parsers = parsers
|
9
|
+
|
10
|
+
ActionController::Renderers.add :jsonapi do |obj, _options|
|
11
|
+
self.content_type ||= Mime[:jsonapi]
|
12
|
+
self.response_body = obj.to_json
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module OmniAuth::Strategies
|
2
|
+
class ShopifyUser < Shopify
|
3
|
+
|
4
|
+
def name
|
5
|
+
:shopify_user
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
SETUP_PROC = lambda do |env|
|
12
|
+
env['omniauth.strategy'].options[:per_user_permissions] = true
|
13
|
+
params = Rack::Utils.parse_query(env['QUERY_STRING'])
|
14
|
+
env['omniauth.strategy'].options[:client_options][:site] = "https://#{params['shop']}"
|
15
|
+
end
|
16
|
+
|
17
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
18
|
+
provider :shopify,
|
19
|
+
ShopifyApp.configuration.api_key,
|
20
|
+
ShopifyApp.configuration.secret,
|
21
|
+
scope: ShopifyApp.configuration.scope
|
22
|
+
provider :shopify_user,
|
23
|
+
ShopifyApp.configuration.api_key,
|
24
|
+
ShopifyApp.configuration.secret,
|
25
|
+
scope: ShopifyApp.configuration.scope,
|
26
|
+
setup: SETUP_PROC
|
27
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Note: You must restart bin/webpack-dev-server for changes to take effect
|
2
|
+
|
3
|
+
default: &default
|
4
|
+
source_path: app/webpack
|
5
|
+
source_entry_path: packs
|
6
|
+
public_root_path: public
|
7
|
+
public_output_path: packs
|
8
|
+
cache_path: tmp/cache/webpacker
|
9
|
+
check_yarn_integrity: false
|
10
|
+
webpack_compile_output: false
|
11
|
+
|
12
|
+
# Additional paths webpack should lookup modules
|
13
|
+
# ['app/assets', 'engine/foo/app/assets']
|
14
|
+
resolved_paths: []
|
15
|
+
|
16
|
+
# Reload manifest.json on all requests so we reload latest compiled packs
|
17
|
+
cache_manifest: false
|
18
|
+
|
19
|
+
# Extract and emit a css file
|
20
|
+
extract_css: true
|
21
|
+
|
22
|
+
static_assets_extensions:
|
23
|
+
- .jpg
|
24
|
+
- .jpeg
|
25
|
+
- .png
|
26
|
+
- .gif
|
27
|
+
- .tiff
|
28
|
+
- .ico
|
29
|
+
- .svg
|
30
|
+
- .eot
|
31
|
+
- .otf
|
32
|
+
- .ttf
|
33
|
+
- .woff
|
34
|
+
- .woff2
|
35
|
+
|
36
|
+
extensions:
|
37
|
+
- .jsx
|
38
|
+
- .mjs
|
39
|
+
- .js
|
40
|
+
- .sass
|
41
|
+
- .scss
|
42
|
+
- .css
|
43
|
+
- .module.sass
|
44
|
+
- .module.scss
|
45
|
+
- .module.css
|
46
|
+
- .png
|
47
|
+
- .svg
|
48
|
+
- .gif
|
49
|
+
- .jpeg
|
50
|
+
- .jpg
|
51
|
+
|
52
|
+
development:
|
53
|
+
<<: *default
|
54
|
+
compile: true
|
55
|
+
|
56
|
+
# Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
|
57
|
+
check_yarn_integrity: true
|
58
|
+
|
59
|
+
# Reference: https://webpack.js.org/configuration/dev-server/
|
60
|
+
dev_server:
|
61
|
+
https: false
|
62
|
+
host: localhost
|
63
|
+
port: 3035
|
64
|
+
public: localhost:3035
|
65
|
+
hmr: false
|
66
|
+
# Inline should be set to true if using HMR
|
67
|
+
inline: true
|
68
|
+
overlay: true
|
69
|
+
compress: true
|
70
|
+
disable_host_check: true
|
71
|
+
use_local_ip: false
|
72
|
+
quiet: false
|
73
|
+
headers:
|
74
|
+
'Access-Control-Allow-Origin': '*'
|
75
|
+
watch_options:
|
76
|
+
ignored: '**/node_modules/**'
|
77
|
+
|
78
|
+
|
79
|
+
test:
|
80
|
+
<<: *default
|
81
|
+
compile: true
|
82
|
+
|
83
|
+
# Compile test packs to a separate directory
|
84
|
+
public_output_path: packs-test
|
85
|
+
|
86
|
+
production:
|
87
|
+
<<: *default
|
88
|
+
|
89
|
+
# Production depends on precompilation of packs prior to booting for performance.
|
90
|
+
compile: false
|
91
|
+
|
92
|
+
# Extract and emit a css file
|
93
|
+
extract_css: true
|
94
|
+
|
95
|
+
# Cache manifest.json for performance
|
96
|
+
cache_manifest: true
|