├── .env.example ├── .formatter.exs ├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── Procfile ├── README.md ├── config ├── config.exs ├── dev.exs ├── integration.exs ├── prod.exs └── test.exs ├── coveralls.json ├── elixir_buildpack.config ├── lib ├── blue_jet.ex ├── blue_jet │ ├── app │ │ ├── balance.ex │ │ ├── balance │ │ │ ├── card.ex │ │ │ ├── card │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── default_service.ex │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── crm_service.ex │ │ │ │ ├── identity_service.ex │ │ │ │ ├── oauth_client.ex │ │ │ │ └── stripe_client.ex │ │ │ ├── payment.ex │ │ │ ├── payment │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── policy.ex │ │ │ ├── refund.ex │ │ │ ├── refund │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── service.ex │ │ │ ├── settings.ex │ │ │ └── settings │ │ │ │ └── proxy.ex │ │ ├── catalogue.ex │ │ ├── catalogue │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── file_storage_service.ex │ │ │ │ ├── goods_service.ex │ │ │ │ └── identity_service.ex │ │ │ ├── policy.ex │ │ │ ├── price.ex │ │ │ ├── price │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── product.ex │ │ │ ├── product │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── product_collection.ex │ │ │ ├── product_collection │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── product_collection_membership.ex │ │ │ ├── product_collection_membership │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ └── service.ex │ │ ├── crm.ex │ │ ├── crm │ │ │ ├── customer.ex │ │ │ ├── customer │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── file_storage_service.ex │ │ │ │ └── identity_service.ex │ │ │ ├── point_account.ex │ │ │ ├── point_account │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── point_transaction.ex │ │ │ ├── point_transaction │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── policy.ex │ │ │ └── service.ex │ │ ├── data_trading.ex │ │ ├── data_trading │ │ │ ├── data_import.ex │ │ │ ├── data_import │ │ │ │ └── query.ex │ │ │ ├── default_service.ex │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── catalogue_service.ex │ │ │ │ ├── crm_service.ex │ │ │ │ ├── goods_service.ex │ │ │ │ └── identity_service.ex │ │ │ ├── policy.ex │ │ │ └── service.ex │ │ ├── file_storage.ex │ │ ├── file_storage │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── cloudfront_client.ex │ │ │ │ ├── identity_service.ex │ │ │ │ └── s3_client.ex │ │ │ ├── file.ex │ │ │ ├── file │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── file_collection.ex │ │ │ ├── file_collection │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── file_collection_membership.ex │ │ │ ├── file_collection_membership │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── policy.ex │ │ │ └── service.ex │ │ ├── fulfillment.ex │ │ ├── fulfillment │ │ │ ├── default_service.ex │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── crm_service.ex │ │ │ │ ├── goods_service.ex │ │ │ │ └── identity_service.ex │ │ │ ├── fulfillment_item.ex │ │ │ ├── fulfillment_item │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── fulfillment_package.ex │ │ │ ├── fulfillment_package │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── policy.ex │ │ │ ├── return_item.ex │ │ │ ├── return_item │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── return_package.ex │ │ │ ├── return_package │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── service.ex │ │ │ ├── unlock.ex │ │ │ └── unlock │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ ├── goods.ex │ │ ├── goods │ │ │ ├── depositable.ex │ │ │ ├── depositable │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ ├── file_storage_service.ex │ │ │ │ └── identity_service.ex │ │ │ ├── policy.ex │ │ │ ├── service.ex │ │ │ ├── stockable.ex │ │ │ ├── stockable │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── unlockable.ex │ │ │ └── unlockable │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ ├── identity.ex │ │ ├── identity │ │ │ ├── account.ex │ │ │ ├── account │ │ │ │ ├── query.ex │ │ │ │ └── service.ex │ │ │ ├── account_membership.ex │ │ │ ├── account_membership │ │ │ │ ├── proxy.ex │ │ │ │ ├── query.ex │ │ │ │ └── service.ex │ │ │ ├── authentication │ │ │ │ └── service.ex │ │ │ ├── jwt.ex │ │ │ ├── password.ex │ │ │ ├── password │ │ │ │ └── query.ex │ │ │ ├── phone_verification_code.ex │ │ │ ├── phone_verification_code │ │ │ │ └── query.ex │ │ │ ├── policy.ex │ │ │ ├── refresh_token.ex │ │ │ ├── refresh_token │ │ │ │ ├── query.ex │ │ │ │ └── service.ex │ │ │ ├── service.ex │ │ │ ├── user.ex │ │ │ └── user │ │ │ │ ├── proxy.ex │ │ │ │ ├── query.ex │ │ │ │ └── service.ex │ │ ├── notification.ex │ │ ├── notification │ │ │ ├── email.ex │ │ │ ├── email │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── email_template.ex │ │ │ ├── email_template │ │ │ │ ├── factory.ex │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── email_templates │ │ │ │ ├── email_verification.html │ │ │ │ ├── email_verification.txt │ │ │ │ ├── order_confirmation.html │ │ │ │ ├── order_confirmation.txt │ │ │ │ ├── password_reset.html │ │ │ │ ├── password_reset.txt │ │ │ │ ├── password_reset_not_registered.html │ │ │ │ └── password_reset_not_registered.txt │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ │ └── identity_service.ex │ │ │ ├── policy.ex │ │ │ ├── service.ex │ │ │ ├── sms.ex │ │ │ ├── sms │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── sms_template.ex │ │ │ ├── sms_template │ │ │ │ ├── factory.ex │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ │ ├── trigger.ex │ │ │ └── trigger │ │ │ │ ├── factory.ex │ │ │ │ ├── proxy.ex │ │ │ │ └── query.ex │ │ ├── storefront.ex │ │ └── storefront │ │ │ ├── default_service.ex │ │ │ ├── event_handler.ex │ │ │ ├── external │ │ │ ├── balance_service.ex │ │ │ ├── catalogue_service.ex │ │ │ ├── crm_service.ex │ │ │ ├── fulfillment_service.ex │ │ │ ├── goods_service.ex │ │ │ └── identity_service.ex │ │ │ ├── order.ex │ │ │ ├── order │ │ │ ├── proxy.ex │ │ │ └── query.ex │ │ │ ├── order_line_item.ex │ │ │ ├── order_line_item │ │ │ ├── proxy.ex │ │ │ └── query.ex │ │ │ ├── policy.ex │ │ │ └── service.ex │ ├── application.ex │ ├── core │ │ ├── context.ex │ │ ├── context_request.ex │ │ ├── context_response.ex │ │ ├── control_flow.ex │ │ ├── data.ex │ │ ├── event │ │ │ ├── event_emitter.ex │ │ │ └── event_handler.ex │ │ ├── event_bus.ex │ │ ├── policy │ │ │ ├── authorized_request.ex │ │ │ └── common.ex │ │ ├── proxy │ │ │ ├── common.ex │ │ │ └── option.ex │ │ ├── query.ex │ │ ├── query │ │ │ ├── filter.ex │ │ │ ├── helper.ex │ │ │ ├── preloads.ex │ │ │ └── search.ex │ │ ├── remote_csv.ex │ │ ├── service │ │ │ ├── default.ex │ │ │ ├── helper.ex │ │ │ ├── option.ex │ │ │ └── preload.ex │ │ ├── translation.ex │ │ ├── utils.ex │ │ └── validation.ex │ └── vendors │ │ ├── aws │ │ ├── cloudfront_client.ex │ │ └── s3_client.ex │ │ ├── mailer │ │ ├── account_mailer.ex │ │ └── global_mailer.ex │ │ ├── oauth │ │ ├── client.ex │ │ └── http_client.ex │ │ ├── repo.ex │ │ └── stripe │ │ ├── client.ex │ │ └── http_client.ex ├── blue_jet_web.ex ├── blue_jet_web │ ├── channels │ │ └── user_socket.ex │ ├── controller.ex │ ├── controllers │ │ ├── balance_settings_controller.ex │ │ ├── card_controller.ex │ │ ├── catalogue │ │ │ ├── price_controller.ex │ │ │ ├── product_collection_controller.ex │ │ │ ├── product_collection_membership_controller.ex │ │ │ └── product_controller.ex │ │ ├── crm │ │ │ ├── customer_controller.ex │ │ │ └── point_transaction_controller.ex │ │ ├── data_import_controller.ex │ │ ├── fallback_controller.ex │ │ ├── file_storage │ │ │ ├── file_collection_controller.ex │ │ │ ├── file_collection_membership_controller.ex │ │ │ └── file_controller.ex │ │ ├── fulfillment_item_controller.ex │ │ ├── fulfillment_package_controller.ex │ │ ├── goods │ │ │ ├── depositable_controller.ex │ │ │ ├── stockable_controller.ex │ │ │ └── unlockable_controller.ex │ │ ├── identity │ │ │ ├── account_controller.ex │ │ │ ├── account_membership_controller.ex │ │ │ ├── account_reset_controller.ex │ │ │ ├── email_verification_controller.ex │ │ │ ├── email_verification_token_controller.ex │ │ │ ├── password_controller.ex │ │ │ ├── password_reset_token_controller.ex │ │ │ ├── phone_verification_code_controller.ex │ │ │ ├── refresh_token_controller.ex │ │ │ ├── token_controller.ex │ │ │ └── user_controller.ex │ │ ├── notification │ │ │ ├── email_controller.ex │ │ │ ├── email_template_controller.ex │ │ │ ├── notification_trigger_controller.ex │ │ │ ├── sms_controller.ex │ │ │ └── sms_template_controller.ex │ │ ├── payment_controller.ex │ │ ├── refund_controller.ex │ │ ├── return_item_controller.ex │ │ ├── return_package_controller.ex │ │ ├── storefront │ │ │ ├── order_controller.ex │ │ │ └── order_line_item_controller.ex │ │ ├── unlock_controller.ex │ │ └── welcome_controller.ex │ ├── endpoint.ex │ ├── gettext.ex │ ├── plugs │ │ ├── authentication.ex │ │ ├── content_type_negotiation.ex │ │ ├── cors.ex │ │ ├── fields.ex │ │ ├── filter.ex │ │ ├── include.ex │ │ ├── locale.ex │ │ └── pagination.ex │ ├── router.ex │ └── views │ │ ├── account_membership_view.ex │ │ ├── account_view.ex │ │ ├── billing_settings_view.ex │ │ ├── card_view.ex │ │ ├── changeset_view.ex │ │ ├── customer_view.ex │ │ ├── data_import_view.ex │ │ ├── depositable_view.ex │ │ ├── email_template_view.ex │ │ ├── email_view.ex │ │ ├── error_helpers.ex │ │ ├── error_view.ex │ │ ├── file_collection_membership_view.ex │ │ ├── file_collection_view.ex │ │ ├── file_view.ex │ │ ├── fulfillment_item_view.ex │ │ ├── fulfillment_package_view.ex │ │ ├── identifier_view.ex │ │ ├── notification_trigger_view.ex │ │ ├── order_line_item_view.ex │ │ ├── order_view.ex │ │ ├── password_reset_token_view.ex │ │ ├── password_view.ex │ │ ├── payment_view.ex │ │ ├── phone_verification_code_view.ex │ │ ├── point_account_view.ex │ │ ├── point_transaction_view.ex │ │ ├── price_view.ex │ │ ├── product_collection_membership_view.ex │ │ ├── product_collection_view.ex │ │ ├── product_view.ex │ │ ├── refresh_token_view.ex │ │ ├── refund_view.ex │ │ ├── return_item_view.ex │ │ ├── return_package_view.ex │ │ ├── sms_template_view.ex │ │ ├── sms_view.ex │ │ ├── stockable_view.ex │ │ ├── unlock_view.ex │ │ ├── unlockable_view.ex │ │ └── user_view.ex └── mix │ └── tasks │ ├── blue_jet.db.init.ex │ └── blue_jet.db.sample.ex ├── mix.exs ├── mix.lock ├── old └── test │ └── blue_jet_web │ └── controllers │ ├── customer_controller_test.exs │ ├── email_controller_test.exs │ ├── email_template_controller_test.exs │ ├── file_collection_controller_test.exs │ ├── file_collection_membership_controller_test.exs │ ├── file_controller_test.exs │ ├── helpers_test.exs │ ├── notification_trigger_controller_test.exs │ ├── order_controller_test.exs │ ├── password_reset_token_controller_test.exs │ ├── price_controller_test.exs │ ├── product_controller_test.exs │ ├── sku_controller_test.exs │ ├── unlockable_controller_test.exs │ └── user_controller_test.exs ├── priv ├── gettext │ ├── en │ │ └── LC_MESSAGES │ │ │ └── errors.po │ └── errors.pot └── repo │ ├── migrations │ ├── 001_create_account.exs │ ├── 002_create_user.exs │ ├── 003_create_account_memberships.exs │ ├── 004_create_customer.exs │ ├── 005_create_refresh_token.exs │ ├── 006_create_file.exs │ ├── 007_create_stockable.exs │ ├── 008_create_unlockable.exs │ ├── 009_create_product.exs │ ├── 011_create_file_collection.exs │ ├── 012_create_file_collection_membership.exs │ ├── 013_create_price.exs │ ├── 014_create_order.exs │ ├── 015_create_order_line_item.exs │ ├── 016_create_payment.exs │ ├── 017_create_unlock.exs │ ├── 018_create_refund.exs │ ├── 019_create_card.exs │ ├── 020_create_balance_settings.exs │ ├── 021_create_data_import.exs │ ├── 022_create_point_account.exs │ ├── 023_create_point_transaction.exs │ ├── 024_create_depositable.exs │ ├── 025_create_product_collections.exs │ ├── 026_create_product_collection_membership.exs │ ├── 027_create_fulfillment_package.exs │ ├── 028_create_fulfillment_item.exs │ ├── 029_create_email_template.exs │ ├── 030_create_notification_trigger.exs │ ├── 031_create_email.exs │ ├── 032_create_return_package.exs │ ├── 033_create_return_item.exs │ ├── 034_create_sms_template.exs │ ├── 035_create_sms.exs │ ├── 036_create_phone_verification_code.exs │ ├── 037_add_is_ready_for_live_transaction_to_account.exs │ ├── 038_update_account_id_nullable_for_customer.exs │ ├── 039_add_ppi_to_product.exs │ └── 040_add_is_owner_to_account_membership.exs │ └── seeds.exs └── test ├── blue_jet ├── balance │ ├── balance_test.exs │ ├── card_test.exs │ ├── default_service_test.exs │ ├── payment_test.exs │ ├── refund_test.exs │ └── settings_test.exs ├── catalogue │ ├── price_test.exs │ ├── product_test.exs │ └── service_test.exs ├── crm │ ├── customer_test.exs │ └── service_test.exs ├── data_trading │ └── data_trading_test.exs ├── file_storage │ ├── file_test.exs │ └── service_test.exs ├── fulfillment │ ├── default_service_test.exs │ ├── fulfillment_item_test.exs │ ├── fulfillment_package_test.exs │ └── fulfillment_test.exs ├── goods │ ├── depositable_test.exs │ ├── goods_test.exs │ ├── service_test.exs │ ├── stockable_test.exs │ └── unlockable_test.exs ├── identity │ ├── account_membership_test.exs │ ├── account_test.exs │ ├── identity_test.exs │ ├── jwt_test.exs │ ├── phone_verification_code_test.exs │ ├── refresh_token_test.exs │ ├── service_test.exs │ └── user_test.exs ├── notification │ ├── service_test.exs │ └── trigger_test.exs ├── storefront │ ├── default_service_test.exs │ ├── order_line_item_test.exs │ ├── order_test.exs │ └── storefront_test.exs ├── support │ ├── channel_case.ex │ ├── context_case.ex │ ├── data_case.ex │ ├── event_handler_case.ex │ └── mocks.ex ├── test_helper.exs ├── translation_test.exs └── validation_test.exs ├── blue_jet_web ├── controllers │ ├── catalogue │ │ ├── price_controller_test.exs │ │ ├── product_collection_controller_test.exs │ │ ├── product_collection_membership_controller_test.exs │ │ └── product_controller_test.exs │ ├── crm │ │ ├── customer_controller_test.exs │ │ └── point_transaction_controller_test.exs │ ├── file_storage │ │ ├── file_collection_controller_test.exs │ │ ├── file_collection_membership_controller_test.exs │ │ └── file_controller_test.exs │ ├── goods │ │ ├── depositable_controller_test.exs │ │ ├── stockable_controller_test.exs │ │ └── unlockable_controller_test.exs │ ├── identity │ │ ├── account_controller_test.exs │ │ ├── account_membership_controller_test.exs │ │ ├── account_reset_controller_test.exs │ │ ├── email_verification_controller_test.exs │ │ ├── email_verification_token_controller_test.exs │ │ ├── password_controller_test.exs │ │ ├── password_reset_token_controller_test.exs │ │ ├── phone_verification_code_controller_test.exs │ │ ├── refresh_token_controller_test.exs │ │ ├── token_controller_test.exs │ │ └── user_controller_test.exs │ ├── notification │ │ ├── email_controller_test.exs │ │ ├── email_template_controller_test.exs │ │ ├── notification_trigger_controller_test.exs │ │ ├── sms_controler_test.exs │ │ └── sms_template_controller_test.exs │ └── storefront │ │ └── order_controller_test.exs ├── support │ └── conn_case.ex └── test_helper.exs └── support ├── catalogue_helper.ex ├── crm_helper.ex ├── file_storage_helper.ex ├── goods_helper.ex ├── identity_helper.ex └── notification_helper.ex /.env.example: -------------------------------------------------------------------------------- 1 | export AWS_ACCESS_KEY_ID="" 2 | export AWS_SECRET_ACCESS_KEY="" 3 | export AWS_S3_BUCKET_NAME="" 4 | export AWS_REGION="" 5 | export CLOUDFRONT_PRIVATE_KEY="" 6 | export CLOUDFRONT_ACCESS_KEY_ID="" 7 | export CDN_ROOT_URL="" 8 | export JWT_PRIVATE_KEY="$(cat keys/dev/jwt_private.pem)" 9 | export JWT_PUBLIC_KEY="$(cat keys/dev/jwt_public.pem)" 10 | export STRIPE_TEST_SECRET_KEY="" 11 | export STRIPE_LIVE_SECRET_KEY="" 12 | export POSTMARK_API_KEY="" 13 | export SMTP_SERVER="" 14 | export SMTP_USERNAME="" 15 | export SMTP_PASSWORD="" 16 | export SMTP_PORT="" 17 | export DB_USERNAME="" 18 | export DB_HOSTNAME="" 19 | export DOMAIN="" 20 | export MARKETING_WEBSITE_URL="" 21 | export SUPPORT_WEBSITE_URL="" 22 | export SIGNUP_URL="" 23 | export FORGOT_PASSWORD_URL="" 24 | export RESET_PASSWORD_URL="" 25 | export VERIFY_EMAIL_URL="" 26 | export COMPANY_NAME="" 27 | export COMPANY_ADDRESS="" 28 | export GLOBAL_EMAIL_SENDER="" 29 | export GLOBAL_EMAIL_SENDER_NAME="" 30 | export RELEASE_LEVEL="" 31 | export SENTRY_DSN="" 32 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | # functions to let allow the no parens like def print value 3 | locals_without_parens: [ 4 | field: 2, 5 | field: 3, 6 | belongs_to: 2, 7 | belongs_to: 3, 8 | has_many: 2, 9 | has_many: 3, 10 | has_one: 2, 11 | has_one: 3 12 | ], 13 | 14 | # files to format 15 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 16 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # App artifacts 2 | /_build 3 | /db 4 | /deps 5 | /*.ez 6 | 7 | # Generated on crash by the VM 8 | erl_crash.dump 9 | 10 | # The config/prod.secret.exs file by default contains sensitive 11 | # data and you should not commit it into version control. 12 | # 13 | # Alternatively, you may comment the line below and commit the 14 | # secrets file as long as you replace its contents by environment 15 | # variables. 16 | /config/prod.secret.exs 17 | 18 | /.env 19 | /keys 20 | /doc 21 | /cover -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: elixir:1.6 2 | 3 | services: 4 | - postgres:10 5 | 6 | variables: 7 | MIX_ENV: "test" 8 | DB_USERNAME: "postgres" 9 | DB_HOSTNAME: "postgres" 10 | GLOBAL_MAIL_SENDER: "support@freshcom.io" 11 | 12 | before_script: 13 | - apt-get update 14 | - apt-get install -y postgresql-client 15 | - mix local.hex --force 16 | - mix local.rebar --force 17 | - mix deps.get --only test 18 | - mix ecto.reset 19 | 20 | test: 21 | only: 22 | - develop 23 | script: 24 | - mix coveralls test/blue_jet 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Freshcom Technology Inc. and other contributors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of Freshcom Technology Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: MIX_ENV=prod mix phx.server -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Freshcom API 2 | 3 | Freshcom API is undergoing some architectural changes. This repo will be slowly migrated to [freshcom](https://github.com/freshcom/freshcom) and [freshcom_web](https://github.com/freshcom/freshcom_web). 4 | 5 | This repo is deprecated. 6 | 7 | The Web API will not change and the docs in [https://docs.freshcom.io/](https://docs.freshcom.io/) and [freshcom-api-reference](https://github.com/freshcom/freshcom-api-reference) 8 | will stay as is and and serve as a reference to what will be available in the beta version. However development for the [Freshcom Dashboard](https://github.com/freshcom/freshcom-dashboard) 9 | will be slowed down for a bit. 10 | -------------------------------------------------------------------------------- /config/integration.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # We don't run a server during test. If one is required, 4 | # you can enable the server option below. 5 | config :blue_jet, BlueJetWeb.Endpoint, 6 | http: [port: 4001], 7 | server: false 8 | 9 | # Print only warnings and errors during test 10 | config :logger, level: :warn 11 | 12 | # Configure your database 13 | config :blue_jet, BlueJet.Repo, 14 | adapter: Ecto.Adapters.Postgres, 15 | database: "blue_jet_test", 16 | hostname: System.get_env("DB_HOSTNAME"), 17 | username: System.get_env("DB_USERNAME"), 18 | pool: Ecto.Adapters.SQL.Sandbox 19 | 20 | config :comeonin, :bcrypt_log_rounds, 4 21 | 22 | config :blue_jet, BlueJet.GlobalMailer, 23 | adapter: Bamboo.TestAdapter 24 | 25 | config :blue_jet, BlueJet.AccountMailer, 26 | adapter: Bamboo.TestAdapter 27 | -------------------------------------------------------------------------------- /coveralls.json: -------------------------------------------------------------------------------- 1 | { 2 | "skip_files": [ 3 | "test/support", 4 | "lib/mix/tasks", 5 | 6 | "lib/blue_jet.ex", 7 | "lib/blue_jet/application.ex", 8 | "lib/blue_jet/repo.ex", 9 | "lib/blue_jet/endpoint.ex", 10 | 11 | "lib/blue_jet_web", 12 | "lib/blue_jet/crm", 13 | "lib/blue_jet/data_trading", 14 | "lib/blue_jet/distribution", 15 | "lib/blue_jet/mailer", 16 | "lib/blue_jet/notification", 17 | "lib/blue_jet/oauth", 18 | "lib/blue_jet/registration", 19 | "lib/blue_jet/stripe", 20 | "lib/blue_jet/context_helper.ex", 21 | "lib/blue_jet/custom_data.ex", 22 | "lib/blue_jet/remote_csv.ex", 23 | "lib/blue_jet/request.ex", 24 | "lib/blue_jet/response.ex", 25 | "lib/blue_jet/utils.ex", 26 | ] 27 | } -------------------------------------------------------------------------------- /elixir_buildpack.config: -------------------------------------------------------------------------------- 1 | elixir_version=1.6.2 -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/card/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.Card.Query do 2 | use BlueJet, :query 3 | use BlueJet.Query.Search, for: [] 4 | 5 | use BlueJet.Query.Filter, 6 | for: [ 7 | :id, 8 | :name, 9 | :status, 10 | :label, 11 | :last_four_digit, 12 | :owner_id, 13 | :owner_type, 14 | :primary 15 | ] 16 | 17 | alias BlueJet.Balance.Card 18 | 19 | def default() do 20 | from(c in Card) 21 | end 22 | 23 | def not_primary(query) do 24 | from(c in query, where: c.primary != true) 25 | end 26 | 27 | def except_id(query, id) do 28 | from(c in query, where: c.id != ^id) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.EventHandler do 2 | alias BlueJet.Balance.Service 3 | 4 | @behaviour BlueJet.EventHandler 5 | 6 | def handle_event("identity:account.reset.success", %{account: account = %{mode: "test"}}) do 7 | Task.start(fn -> 8 | Service.delete_all_card(%{account: account}) 9 | Service.delete_all_payment(%{account: account}) 10 | end) 11 | 12 | Task.start(fn -> 13 | Service.delete_settings(%{account: account}) 14 | Service.create_settings(%{account: account}) 15 | end) 16 | 17 | {:ok, nil} 18 | end 19 | 20 | def handle_event("identity.account.create.success", %{ 21 | account: account, 22 | test_account: test_account 23 | }) do 24 | {:ok, _} = Service.create_settings(%{account: account}) 25 | {:ok, _} = Service.create_settings(%{account: test_account}) 26 | 27 | {:ok, nil} 28 | end 29 | 30 | def handle_event(_, _), do: {:ok, nil} 31 | end 32 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/external/crm_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.CRMService do 2 | alias BlueJet.CRM.Customer 3 | 4 | @crm_service Application.get_env(:blue_jet, :balance)[:crm_service] 5 | 6 | @callback get_customer(map, map) :: Customer.t() | nil 7 | @callback update_customer(Customer.t(), map, map) :: {:ok, Customer.t()} | {:error, any} 8 | 9 | defdelegate get_customer(identifiers, opts), to: @crm_service 10 | defdelegate update_customer(customer, fields, opts), to: @crm_service 11 | end 12 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.IdentityService do 2 | @identity_service Application.get_env(:blue_jet, :balance)[:identity_service] 3 | 4 | @callback put_vas_data(map) :: map 5 | @callback get_account(String.t() | map) :: map 6 | @callback update_account(map, map, map) :: map 7 | 8 | @callback get_user(map, map) :: map 9 | 10 | defdelegate put_vas_data(request), to: @identity_service 11 | defdelegate get_account(id_or_struct), to: @identity_service 12 | defdelegate update_account(id_or_struct, fields, opts \\ %{}), to: @identity_service 13 | defdelegate get_user(identifiers, options), to: @identity_service 14 | end 15 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/external/oauth_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.OauthClient do 2 | @oauth_client Application.get_env(:blue_jet, :balance)[:oauth_client] 3 | 4 | @callback get(String.t()) :: {:ok | :error, map} 5 | @callback post(String.t(), map) :: {:ok | :error, map} 6 | 7 | defdelegate get(path), to: @oauth_client 8 | defdelegate post(path, fields), to: @oauth_client 9 | end 10 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/external/stripe_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.StripeClient do 2 | @stripe_client Application.get_env(:blue_jet, :balance)[:stripe_client] 3 | 4 | @callback get(String.t(), list) :: {:ok | :error, map} 5 | @callback post(String.t(), map, list) :: {:ok | :error, map} 6 | @callback delete(String.t(), list) :: {:ok | :error, map} 7 | 8 | defdelegate get(path, opts), to: @stripe_client 9 | defdelegate post(path, fields, opts), to: @stripe_client 10 | defdelegate delete(path, opts), to: @stripe_client 11 | end 12 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/payment/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.Payment.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Search, 5 | for: [ 6 | :code 7 | ] 8 | 9 | use BlueJet.Query.Filter, 10 | for: [ 11 | :id, 12 | :target_type, 13 | :target_id, 14 | :owner_id, 15 | :owner_type, 16 | :status, 17 | :gateway, 18 | :method, 19 | :label 20 | ] 21 | 22 | alias BlueJet.Balance.{Payment, Refund} 23 | 24 | def default() do 25 | from(p in Payment) 26 | end 27 | 28 | def for_target(query, target_type, target_id) do 29 | from( 30 | p in query, 31 | where: p.target_type == ^target_type, 32 | where: p.target_id == ^target_id 33 | ) 34 | end 35 | 36 | def preloads({:refunds, refund_preloads}, options) do 37 | [refunds: {Refund.Query.default(), Refund.Query.preloads(refund_preloads, options)}] 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/blue_jet/app/balance/refund/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Balance.Refund.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Search, 5 | for: [ 6 | :code 7 | ] 8 | 9 | use BlueJet.Query.Filter, 10 | for: [ 11 | :status, 12 | :gateway, 13 | :processor, 14 | :method, 15 | :label, 16 | :owner_id, 17 | :owner_type, 18 | :target_id, 19 | :target_type 20 | ] 21 | 22 | alias BlueJet.Balance.Refund 23 | 24 | def default() do 25 | from(r in Refund) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.EventHandler do 2 | alias BlueJet.Catalogue.Service 3 | 4 | @behaviour BlueJet.EventHandler 5 | 6 | def handle_event("identity:account.reset.success", %{account: account = %{mode: "test"}}) do 7 | Task.start(fn -> 8 | Service.delete_all_product_collection(%{account: account}) 9 | Service.delete_all_product(%{account: account}) 10 | end) 11 | 12 | {:ok, nil} 13 | end 14 | 15 | def handle_event(_, _) do 16 | {:ok, nil} 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/external/file_storage_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.FileStorageService do 2 | @moduledoc false 3 | 4 | @file_storage_service Application.get_env(:blue_jet, :catalogue)[:file_storage_service] 5 | 6 | @callback delete_file(String.t(), map) :: nil 7 | @callback list_file_collection(map, map) :: list 8 | @callback get_file(map, map) :: map 9 | 10 | defdelegate delete_file(id, opts), to: @file_storage_service 11 | defdelegate list_file_collection(fields, opts), to: @file_storage_service 12 | defdelegate get_file(identifiers, opts), to: @file_storage_service 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/external/goods_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.GoodsService do 2 | @moduledoc false 3 | 4 | alias BlueJet.Goods.{Stockable, Unlockable, Depositable} 5 | 6 | @goods_service Application.get_env(:blue_jet, :catalogue)[:goods_service] 7 | 8 | @callback get_stockable(map, map) :: Stockable.t() | nil 9 | @callback get_unlockable(map, map) :: Unlockable.t() | nil 10 | @callback get_depositable(map, map) :: Depositable.t() | nil 11 | 12 | defdelegate get_stockable(fileds, opts), to: @goods_service 13 | defdelegate get_unlockable(fileds, opts), to: @goods_service 14 | defdelegate get_depositable(fileds, opts), to: @goods_service 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.IdentityService do 2 | @moduledoc false 3 | 4 | @identity_service Application.get_env(:blue_jet, :catalogue)[:identity_service] 5 | 6 | @callback get_vad(map) :: map 7 | @callback get_role(map) :: String.t 8 | @callback get_account(String.t | map) :: map 9 | 10 | defdelegate get_vad(vas), to: @identity_service 11 | defdelegate get_role(vad), to: @identity_service 12 | defdelegate get_account(id_or_struct), to: @identity_service 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/price/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.Price.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(price, _, _), do: price 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/product_collection/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.ProductCollection.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.Catalogue.FileStorageService 5 | 6 | def delete_avatar(%{avatar_id: nil}), do: {:ok, nil} 7 | 8 | def delete_avatar(product_collection) do 9 | account = get_account(product_collection) 10 | FileStorageService.delete_file(%{id: product_collection.avatar_id}, %{account: account}) 11 | end 12 | 13 | def put(%{avatar_id: nil} = collection, {:avatar, nil}, _), do: collection 14 | 15 | def put(collection, {:avatar, nil}, opts) do 16 | avatar = FileStorageService.get_file(%{id: collection.avatar_id}, opts) 17 | 18 | %{collection | avatar: avatar} 19 | end 20 | 21 | def put(product_collection, _, _), do: product_collection 22 | end 23 | -------------------------------------------------------------------------------- /lib/blue_jet/app/catalogue/product_collection_membership/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Catalogue.ProductCollectionMembership.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(membership, _, _), do: membership 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM do 2 | use BlueJet, :context 3 | 4 | alias BlueJet.CRM.{Policy, Service} 5 | 6 | def list_customer(req), do: default(req, :list, :customer, Policy, Service) 7 | def create_customer(req), do: default(req, :create, :customer, Policy, Service) 8 | def get_customer(req), do: default(req, :get, :customer, Policy, Service) 9 | def update_customer(req), do: default(req, :update, :customer, Policy, Service) 10 | def delete_customer(req), do: default(req, :delete, :customer, Policy, Service) 11 | 12 | def list_point_transaction(req), do: default(req, :list, :point_transaction, Policy, Service) 13 | def create_point_transaction(req), do: default(req, :create, :point_transaction, Policy, Service) 14 | def get_point_transaction(req), do: default(req, :get, :point_transaction, Policy, Service) 15 | def update_point_transaction(req), do: default(req, :update, :point_transaction, Policy, Service) 16 | def delete_point_transaction(req), do: default(req, :delete, :point_transaction, Policy, Service) 17 | end 18 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/customer/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.Customer.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.CRM.{FileStorageService, IdentityService} 5 | 6 | def sync_to_user(customer, opts \\ %{}) 7 | 8 | def sync_to_user(%{user_id: nil}, _), do: {:ok, nil} 9 | 10 | def sync_to_user(%{user_id: user_id} = customer, opts) do 11 | identifiers = %{id: user_id} 12 | fields = 13 | Map.take(customer, [ 14 | :email, 15 | :phone_number, 16 | :phone_verification_code, 17 | :name, 18 | :first_name, 19 | :last_name 20 | ]) 21 | opts = %{ 22 | account: get_account(customer), 23 | bypass_pvc_validation: !!opts[:bypass_user_pvc_validation] 24 | } 25 | 26 | IdentityService.update_user(identifiers, fields, opts) 27 | end 28 | 29 | def delete_user(%{user_id: nil}), do: {:ok, nil} 30 | 31 | def delete_user(%{user_id: user_id} = customer) do 32 | identifiers = %{id: user_id} 33 | opts = %{account: get_account(customer)} 34 | 35 | IdentityService.delete_user(identifiers, opts) 36 | end 37 | 38 | def put(customer, {:file_collections, collection_paths}, opts) do 39 | preload = %{paths: collection_paths, opts: opts} 40 | opts = Map.put(opts, :preload, preload) 41 | filter = %{owner_id: customer.id, owner_type: "Customer"} 42 | 43 | collections = FileStorageService.list_file_collection(%{filter: filter}, opts) 44 | 45 | %{customer | file_collections: collections} 46 | end 47 | 48 | def put(customer, _, _), do: customer 49 | end 50 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/customer/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.Customer.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.CRM.{Customer, PointAccount} 7 | 8 | def identifiable_fields, do: [:id, :code, :email, :status, :user_id] 9 | def filterable_fields, do: [:id, :status, :label] 10 | def searchable_fields, do: [:id, :code, :name, :email, :phone_number] 11 | 12 | def default(), do: from(c in Customer) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), Customer.translatable_fields()) 17 | 18 | def preloads({:point_account, point_account_preloads}, options) do 19 | [ 20 | point_account: 21 | {PointAccount.Query.default(), 22 | PointAccount.Query.preloads(point_account_preloads, options)} 23 | ] 24 | end 25 | 26 | def preloads(_, _) do 27 | [] 28 | end 29 | 30 | def with_id_or_code(query, id_or_code) do 31 | case Ecto.UUID.dump(id_or_code) do 32 | :error -> from(c in query, where: c.code == ^id_or_code) 33 | _ -> from(c in query, where: c.id == ^id_or_code or c.code == ^id_or_code) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.EventHandler do 2 | @moduledoc false 3 | 4 | @behaviour BlueJet.EventHandler 5 | 6 | alias BlueJet.CRM.Service 7 | 8 | @account_reset "identity:account.reset.success" 9 | @user_updated "identity:user.update.success" 10 | 11 | def handle_event(@account_reset, %{account: account = %{mode: "test"}}) do 12 | Task.start(fn -> Service.delete_all_customer(%{account: account}) end) 13 | 14 | {:ok, nil} 15 | end 16 | 17 | def handle_event(@user_updated, %{changeset: changeset, account: account}) do 18 | identifires = %{user_id: changeset.data.id} 19 | fields = Map.take(changeset.changes, [:email, :phone_number, :name, :first_name, :last_name]) 20 | opts = %{account: account} 21 | 22 | case Service.update_customer(identifires, fields, opts) do 23 | {:error, :not_found} -> 24 | {:ok, nil} 25 | 26 | {:ok, customer} -> 27 | {:ok, customer} 28 | end 29 | end 30 | 31 | def handle_event(_, _) do 32 | {:ok, nil} 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/external/file_storage_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.FileStorageService do 2 | @moduledoc false 3 | 4 | @file_storage_service Application.get_env(:blue_jet, :crm)[:file_storage_service] 5 | 6 | @callback list_file_collection(map, map) :: list 7 | 8 | defdelegate list_file_collection(fields, opts), to: @file_storage_service 9 | end 10 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.IdentityService do 2 | @moduledoc false 3 | 4 | alias BlueJet.Identity.{User} 5 | 6 | @identity_service Application.get_env(:blue_jet, :crm)[:identity_service] 7 | 8 | @callback get_vad(map) :: map 9 | @callback get_role(map) :: String.t 10 | @callback get_account(String.t | map) :: map 11 | @callback create_user(map, map) :: {:ok, User.t()} | {:error, any} 12 | @callback update_user(String.t() | User.t(), map, map) :: {:ok, User.t()} | {:error, any} 13 | @callback delete_user(String.t(), map) :: {:ok, User.t()} | {:error, any} 14 | 15 | defdelegate get_vad(vas), to: @identity_service 16 | defdelegate get_role(vad), to: @identity_service 17 | defdelegate get_account(id_or_struct), to: @identity_service 18 | defdelegate create_user(fields, opts), to: @identity_service 19 | defdelegate update_user(id_or_user, fields, opts), to: @identity_service 20 | defdelegate delete_user(id, opts), to: @identity_service 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/point_account.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.PointAccount do 2 | use BlueJet, :data 3 | 4 | alias BlueJet.CRM.{PointTransaction, Customer} 5 | 6 | schema "point_accounts" do 7 | field :account_id, UUID 8 | field :account, :map, virtual: true 9 | 10 | field :status, :string, default: "active" 11 | field :balance, :integer, default: 0 12 | 13 | field :custom_data, :map, default: %{} 14 | field :translations, :map, default: %{} 15 | 16 | timestamps() 17 | 18 | belongs_to :customer, Customer 19 | has_many :transactions, PointTransaction 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/point_account/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.PointAccount.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(point_account, _, _), do: point_account 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/point_account/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.PointAccount.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.CRM.{PointAccount, PointTransaction} 7 | 8 | def identifiable_fields, do: [:id, :customer_id] 9 | def filterable_fields, do: [:id, :status] 10 | def searchable_fields, do: [] 11 | 12 | def default(), do: from(pa in PointAccount) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), []) 17 | 18 | def preloads({:transactions, transaction_preloads}, options) do 19 | query = 20 | PointTransaction.Query.default() 21 | |> PointTransaction.Query.filter_by(%{status: "committed"}) 22 | |> BlueJet.Query.paginate(size: 10, number: 1) 23 | 24 | [transactions: {query, PointTransaction.Query.preloads(transaction_preloads, options)}] 25 | end 26 | 27 | def preloads(_, _) do 28 | [] 29 | end 30 | 31 | def for_customer(query, customer_id) do 32 | from(pa in query, where: pa.customer_id == ^customer_id) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/point_transaction/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.PointTransaction.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(point_transaction, _, _), do: point_transaction 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/crm/point_transaction/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.PointTransaction.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.CRM.{PointTransaction, PointAccount} 7 | 8 | def identifiable_fields, do: [:id, :status, :point_account_id] 9 | def filterable_fields, do: [:id, :status, :label, :point_account_id, :reason_label] 10 | def searchable_fields, do: [:name, :caption] 11 | 12 | def default(), do: from(pt in PointTransaction) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), PointTransaction.translatable_fields()) 17 | 18 | def preloads({:point_account, point_account_preloads}, options) do 19 | [ 20 | point_account: 21 | {PointAccount.Query.default(), 22 | PointAccount.Query.preloads(point_account_preloads, options)} 23 | ] 24 | end 25 | 26 | def preloads(_, _) do 27 | [] 28 | end 29 | 30 | def for_point_account(query, point_account_id) do 31 | from(pt in query, where: pt.point_account_id == ^point_account_id) 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading do 2 | use BlueJet, :context 3 | 4 | def create_data_import(req), do: create("data_import", req, __MODULE__) 5 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/data_import/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.DataImport.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Filter, 5 | for: [ 6 | :id, 7 | :status 8 | ] 9 | 10 | alias BlueJet.DataTrading.DataImport 11 | 12 | def default() do 13 | from(di in DataImport) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/default_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.DefaultService do 2 | use BlueJet, :service 3 | 4 | alias BlueJet.DataTrading.DataImport 5 | 6 | @behaviour BlueJet.DataTrading.Service 7 | 8 | def create_data_import(fields, opts) do 9 | account_id = opts[:account_id] || opts[:account].id 10 | 11 | changeset = 12 | %DataImport{account_id: account_id, account: opts[:account]} 13 | |> DataImport.changeset(:insert, fields) 14 | 15 | case Repo.insert(changeset) do 16 | {:ok, data_import} -> 17 | Task.start(fn -> DataImport.process(data_import, changeset) end) 18 | 19 | other -> 20 | other 21 | end 22 | end 23 | 24 | def delete_all_data_import(opts) do 25 | delete_all(DataImport, opts) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.EventHandler do 2 | alias BlueJet.DataTrading.Service 3 | 4 | @behaviour BlueJet.EventHandler 5 | 6 | def handle_event("identity:account.reset.success", %{account: account = %{mode: "test"}}) do 7 | Task.start(fn -> 8 | Service.delete_all_data_import(%{account: account}) 9 | end) 10 | 11 | {:ok, nil} 12 | end 13 | 14 | def handle_event(_, _), do: {:ok, nil} 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/external/crm_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.CRMService do 2 | alias BlueJet.CRM.Customer 3 | 4 | @crm_service Application.get_env(:blue_jet, :data_trading)[:crm_service] 5 | 6 | @callback get_customer(map, map) :: Customer.t() | nil 7 | @callback create_customer(map, map) :: {:ok, Customer.t()} | {:error, any} 8 | @callback update_customer(String.t(), map, map) :: {:ok, Customer.t()} | {:error, any} 9 | 10 | defdelegate get_customer(identifiers, opts), to: @crm_service 11 | defdelegate create_customer(fields, opts), to: @crm_service 12 | defdelegate update_customer(id, fields, opts), to: @crm_service 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/external/goods_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.GoodsService do 2 | alias BlueJet.Goods.Unlockable 3 | 4 | @goods_service Application.get_env(:blue_jet, :data_trading)[:goods_service] 5 | 6 | @callback get_unlockable(map, map) :: Unlockable.t() | nil 7 | @callback create_unlockable(map, map) :: {:ok, Unlockable.t()} | {:error, any} 8 | @callback update_unlockable(map, map, map) :: {:ok, Unlockable.t()} | {:error, any} 9 | 10 | defdelegate get_unlockable(identifiers, opts), to: @goods_service 11 | defdelegate create_unlockable(fields, opts), to: @goods_service 12 | defdelegate update_unlockable(id, fields, opts), to: @goods_service 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.IdentityService do 2 | @identity_service Application.get_env(:blue_jet, :data_trading)[:identity_service] 3 | 4 | @callback put_vas_data(map) :: map 5 | 6 | defdelegate put_vas_data(request), to: @identity_service 7 | end 8 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/policy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.Policy do 2 | use BlueJet, :policy 3 | 4 | def authorize(request = %{role: role}, "create_data_import") 5 | when role in ["developer", "administrator"] do 6 | {:ok, from_access_request(request, :create)} 7 | end 8 | 9 | def authorize(_, _) do 10 | {:error, :access_denied} 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet/app/data_trading/service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTrading.Service do 2 | @service Application.get_env(:blue_jet, :data_trading)[:service] 3 | 4 | @callback create_data_import(map, map) :: {:ok, DataImport.t()} | {:error, any} 5 | @callback delete_all_data_import(map) :: :ok 6 | 7 | defdelegate create_data_import(fields, opts), to: @service 8 | defdelegate delete_all_data_import(opts), to: @service 9 | end 10 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.EventHandler do 2 | alias BlueJet.FileStorage.Service 3 | 4 | @behaviour BlueJet.EventHandler 5 | 6 | def handle_event("identity:account.reset.success", %{account: account = %{mode: "test"}}) do 7 | Task.start(fn -> 8 | Service.delete_all_file_collection(%{account: account}) 9 | Service.delete_all_file(%{account: account}) 10 | end) 11 | 12 | {:ok, nil} 13 | end 14 | 15 | def handle_event(_, _), do: {:ok, nil} 16 | end 17 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/external/cloudfront_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.CloudfrontClient do 2 | @moduledoc false 3 | 4 | @cloudfront_client Application.get_env(:blue_jet, :file_storage)[:cloudfront_client] 5 | 6 | @callback get_presigned_url(String.t()) :: String.t() 7 | 8 | defdelegate get_presigned_url(key), to: @cloudfront_client 9 | end 10 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.IdentityService do 2 | @moduledoc false 3 | 4 | @identity_service Application.get_env(:blue_jet, :file_storage)[:identity_service] 5 | 6 | @callback get_vad(map) :: map 7 | @callback get_role(map) :: String.t 8 | @callback get_account(String.t | map) :: map 9 | 10 | defdelegate get_vad(vas), to: @identity_service 11 | defdelegate get_role(vad), to: @identity_service 12 | 13 | defdelegate get_account(id_or_struct), to: @identity_service 14 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/external/s3_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.S3Client do 2 | @moduledoc false 3 | 4 | @s3_client Application.get_env(:blue_jet, :file_storage)[:s3_client] 5 | 6 | @callback get_presigned_url(String.t(), String.t()) :: String.t() 7 | @callback delete_object(String.t() | [String.t()]) :: :ok 8 | 9 | defdelegate get_presigned_url(key, method), to: @s3_client 10 | defdelegate delete_object(key), to: @s3_client 11 | end 12 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/file/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.File.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.FileStorage.S3Client 5 | alias BlueJet.FileStorage.File 6 | 7 | def delete_s3_object(file_or_files) do 8 | File.get_s3_key(file_or_files) 9 | |> S3Client.delete_object() 10 | 11 | {:ok, file_or_files} 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/file/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.File.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.FileStorage.File 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :label, :content_type] 10 | def searchable_fields, do: [:id, :name, :code, :content_type] 11 | 12 | def default() do 13 | from(f in File) 14 | end 15 | 16 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 17 | 18 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 19 | 20 | def search(q, k, l, d), 21 | do: search(q, k, l, d, searchable_fields(), File.translatable_fields()) 22 | end 23 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/file_collection/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.FileCollection.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(file_collection, _, _), do: file_collection 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/file_collection/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.FileCollection.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.FileStorage.{FileCollection, File, FileCollectionMembership} 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :label, :owner_id, :owner_type, :content_type] 10 | def searchable_fields, do: [:id, :name, :content_type, :code] 11 | 12 | def default() do 13 | from(fc in FileCollection) 14 | end 15 | 16 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 17 | 18 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 19 | 20 | def search(q, k, l, d), 21 | do: search(q, k, l, d, searchable_fields(), FileCollection.translatable_fields()) 22 | 23 | def preloads({:files, ef_preloads}, options) do 24 | query = File.Query.default() |> File.Query.filter_by(%{status: "uploaded"}) 25 | [files: {query, File.Query.preloads(ef_preloads, options)}] 26 | end 27 | 28 | def preloads({:memberships, membership_preloads}, options) do 29 | query = 30 | FileCollectionMembership.Query.default() 31 | |> FileCollectionMembership.Query.with_file_status("uploaded") 32 | |> BlueJet.Query.paginate(size: 10, number: 1) 33 | |> order_by(desc: :sort_index, desc: :inserted_at) 34 | 35 | [memberships: {query, FileCollectionMembership.Query.preloads(membership_preloads, options)}] 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/file_collection_membership/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.FileCollectionMembership.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(fcm, _, _), do: fcm 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/file_storage/file_collection_membership/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.FileCollectionMembership.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | def identifiable_fields, do: [:id] 7 | def filterable_fields, do: [:id, :collection_id, :file_id] 8 | def searchable_fields, do: [] 9 | 10 | alias BlueJet.FileStorage.{FileCollectionMembership, File} 11 | 12 | def default() do 13 | from(fcm in FileCollectionMembership) 14 | end 15 | 16 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 17 | 18 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 19 | 20 | def search(q, k, l, d), 21 | do: search(q, k, l, d, searchable_fields(), []) 22 | 23 | def preloads({:files, ef_preloads}, options) do 24 | query = File.Query.default() |> File.Query.filter_by(%{status: "uploaded"}) 25 | [files: {query, File.Query.preloads(ef_preloads, options)}] 26 | end 27 | 28 | def preloads({:file, ef_preloads}, options) do 29 | query = File.Query.default() 30 | [file: {query, File.Query.preloads(ef_preloads, options)}] 31 | end 32 | 33 | def for_collection(query, collection_id) do 34 | from(fcm in query, where: fcm.collection_id == ^collection_id) 35 | end 36 | 37 | def with_file_status(query, nil) do 38 | query 39 | end 40 | 41 | def with_file_status(query, status) do 42 | from(fcm in query, join: f in File, on: f.id == fcm.file_id, where: f.status == ^status) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment do 2 | use BlueJet, :context 3 | 4 | def list_fulfillment_package(req), do: list("fulfillment_package", req, __MODULE__) 5 | def get_fulfillment_package(req), do: get("fulfillment_package", req, __MODULE__) 6 | def delete_fulfillment_package(req), do: delete("fulfillment_package", req, __MODULE__) 7 | 8 | def list_fulfillment_item(req), do: list("fulfillment_item", req, __MODULE__) 9 | def create_fulfillment_item(req), do: create("fulfillment_item", req, __MODULE__) 10 | def update_fulfillment_item(req), do: update("fulfillment_item", req, __MODULE__) 11 | 12 | def list_return_package(req), do: list("return_package", req, __MODULE__) 13 | 14 | def create_return_item(req), do: create("return_item", req, __MODULE__) 15 | 16 | def list_unlock(req), do: list("unlock", req, __MODULE__) 17 | def create_unlock(req), do: create("unlock", req, __MODULE__) 18 | def get_unlock(req), do: get("unlock", req, __MODULE__) 19 | def delete_unlock(req), do: delete("unlock", req, __MODULE__) 20 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.EventHandler do 2 | alias BlueJet.Fulfillment.Service 3 | 4 | @behaviour BlueJet.EventHandler 5 | 6 | def handle_event("identity:account.reset.success", %{account: account = %{mode: "test"}}) do 7 | Task.start(fn -> 8 | Service.delete_all_fulfillment_package(%{account: account}) 9 | Service.delete_all_return_package(%{account: account}) 10 | end) 11 | 12 | {:ok, nil} 13 | end 14 | 15 | def handle_event(_, _), do: {:ok, nil} 16 | end 17 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/external/crm_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.CRMService do 2 | alias BlueJet.CRM.{Customer, PointAccount, PointTransaction} 3 | 4 | @crm_service Application.get_env(:blue_jet, :fulfillment)[:crm_service] 5 | 6 | @callback get_customer(map, map) :: Customer.t() | nil 7 | @callback get_point_account(map, map) :: PointAccount.t() | nil 8 | @callback create_point_transaction(map, map) :: {:ok, PointTransaction.t()} | {:error, any} 9 | @callback get_point_transaction(map, map) :: PointTransaction.t() | nil 10 | @callback update_point_transaction(String.t(), map, map) :: 11 | {:ok, PointTransaction.t()} | {:error, any} 12 | 13 | defdelegate get_customer(fields, opts), to: @crm_service 14 | defdelegate get_point_account(fields, opts), to: @crm_service 15 | defdelegate create_point_transaction(fields, opts), to: @crm_service 16 | defdelegate get_point_transaction(fields, opts), to: @crm_service 17 | defdelegate update_point_transaction(id, fields, opts), to: @crm_service 18 | end 19 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/external/goods_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.GoodsService do 2 | alias BlueJet.Goods.{Stockable, Unlockable, Depositable} 3 | 4 | @goods_service Application.get_env(:blue_jet, :fulfillment)[:goods_service] 5 | 6 | @callback get_stockable(map, map) :: Stockable.t() | nil 7 | @callback get_unlockable(map, map) :: Unlockable.t() | nil 8 | @callback get_depositable(map, map) :: Depositable.t() | nil 9 | 10 | defdelegate get_stockable(fields, opts), to: @goods_service 11 | defdelegate get_unlockable(fields, opts), to: @goods_service 12 | defdelegate get_depositable(fields, opts), to: @goods_service 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.IdentityService do 2 | @identity_service Application.get_env(:blue_jet, :fulfillment)[:identity_service] 3 | 4 | @callback put_vas_data(map) :: map 5 | @callback get_account(String.t() | map) :: map 6 | 7 | defdelegate put_vas_data(request), to: @identity_service 8 | defdelegate get_account(id_or_struct), to: @identity_service 9 | end 10 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/fulfillment_item/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.FulfillmentItem.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(fulfillment_item, _, _), do: fulfillment_item 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/fulfillment_item/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.FulfillmentItem.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Search, 5 | for: [ 6 | :name, 7 | :caption 8 | ] 9 | 10 | use BlueJet.Query.Filter, 11 | for: [ 12 | :id, 13 | :source_type, 14 | :source_id, 15 | :order_line_item_id, 16 | :fulfillment_id 17 | ] 18 | 19 | alias BlueJet.Fulfillment.FulfillmentItem 20 | 21 | def default() do 22 | from(fi in FulfillmentItem) 23 | end 24 | 25 | def preloads(_, _) do 26 | [] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/fulfillment_package/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.FulfillmentPackage.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(package, _, _), do: package 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/fulfillment_package/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.FulfillmentPackage.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Search, 5 | for: [ 6 | :name, 7 | :caption 8 | ] 9 | 10 | use BlueJet.Query.Filter, 11 | for: [ 12 | :id, 13 | :status, 14 | :label, 15 | :customer_id, 16 | :order_id 17 | ] 18 | 19 | alias BlueJet.Fulfillment.{FulfillmentPackage, FulfillmentItem} 20 | 21 | def default() do 22 | from(f in FulfillmentPackage) 23 | end 24 | 25 | def preloads({:items, item_preloads}, options) do 26 | query = FulfillmentItem.Query.default() 27 | [items: {query, FulfillmentItem.Query.preloads(item_preloads, options)}] 28 | end 29 | 30 | def preloads(_, _) do 31 | [] 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/return_item/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.ReturnItem.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(return_item, _, _), do: return_item 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/return_item/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.ReturnItem.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Filter, 5 | for: [ 6 | :id, 7 | :source_type, 8 | :source_id, 9 | :order_line_item_id, 10 | :fulfillment_item_id, 11 | :package_id, 12 | :status 13 | ] 14 | 15 | alias BlueJet.Fulfillment.ReturnItem 16 | 17 | def default() do 18 | from(fli in ReturnItem) 19 | end 20 | 21 | def preloads(_, _) do 22 | [] 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/return_package/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.ReturnPackage.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(package, _, _), do: package 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/return_package/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.ReturnPackage.Query do 2 | use BlueJet, :query 3 | 4 | use BlueJet.Query.Search, 5 | for: [ 6 | :name, 7 | :caption 8 | ] 9 | 10 | use BlueJet.Query.Filter, 11 | for: [ 12 | :id, 13 | :order_id 14 | ] 15 | 16 | alias BlueJet.Fulfillment.{ReturnPackage, ReturnItem} 17 | 18 | def default() do 19 | from(fp in ReturnPackage) 20 | end 21 | 22 | def preloads({:items, item_preloads}, options) do 23 | query = ReturnItem.Query.default() 24 | [items: {query, ReturnItem.Query.preloads(item_preloads, options)}] 25 | end 26 | 27 | def preloads(_, _) do 28 | [] 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/unlock/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.Unlock.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.Fulfillment.{GoodsService, CRMService} 5 | 6 | def put(order, {:customer, customer_path}, opts) do 7 | preloads = %{path: customer_path, opts: opts} 8 | 9 | opts = 10 | opts 11 | |> Map.take([:account, :account_id]) 12 | |> Map.merge(%{preloads: preloads}) 13 | 14 | customer = CRMService.get_customer(%{id: order.customer_id}, opts) 15 | %{order | customer: customer} 16 | end 17 | 18 | def put(order, {:unlockable, unlockable_path}, opts) do 19 | preloads = %{path: unlockable_path, opts: opts} 20 | 21 | opts = 22 | opts 23 | |> Map.take([:account, :account_id]) 24 | |> Map.merge(%{preloads: preloads}) 25 | 26 | unlockable = GoodsService.get_unlockable(%{id: order.unlockable_id}, opts) 27 | %{order | unlockable: unlockable} 28 | end 29 | 30 | def put(unlock, _, _), do: unlock 31 | end 32 | -------------------------------------------------------------------------------- /lib/blue_jet/app/fulfillment/unlock/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Fulfillment.Unlock.Query do 2 | use BlueJet, :query 3 | use BlueJet.Query.Search, for: [] 4 | 5 | use BlueJet.Query.Filter, 6 | for: [ 7 | :id, 8 | :customer_id, 9 | :unlockable_id 10 | ] 11 | 12 | alias BlueJet.Fulfillment.Unlock 13 | 14 | def default() do 15 | from(u in Unlock) 16 | end 17 | 18 | def preloads(_, _) do 19 | [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/goods.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods do 2 | use BlueJet, :context 3 | 4 | alias BlueJet.Goods.{Policy, Service} 5 | 6 | def list_stockable(req), do: default(req, :list, :stockable, Policy, Service) 7 | def create_stockable(req), do: default(req, :create, :stockable, Policy, Service) 8 | def get_stockable(req), do: default(req, :get, :stockable, Policy, Service) 9 | def update_stockable(req), do: default(req, :update, :stockable, Policy, Service) 10 | def delete_stockable(req), do: default(req, :delete, :stockable, Policy, Service) 11 | 12 | def list_unlockable(req), do: default(req, :list, :unlockable, Policy, Service) 13 | def create_unlockable(req), do: default(req, :create, :unlockable, Policy, Service) 14 | def get_unlockable(req), do: default(req, :get, :unlockable, Policy, Service) 15 | def update_unlockable(req), do: default(req, :update, :unlockable, Policy, Service) 16 | def delete_unlockable(req), do: default(req, :delete, :unlockable, Policy, Service) 17 | 18 | def list_depositable(req), do: default(req, :list, :depositable, Policy, Service) 19 | def create_depositable(req), do: default(req, :create, :depositable, Policy, Service) 20 | def get_depositable(req), do: default(req, :get, :depositable, Policy, Service) 21 | def update_depositable(req), do: default(req, :update, :depositable, Policy, Service) 22 | def delete_depositable(req), do: default(req, :delete, :depositable, Policy, Service) 23 | end 24 | -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/depositable/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.Depositable.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.Goods.FileStorageService 5 | 6 | def put(%{avatar_id: nil} = depositable, {:avatar, nil}, _), do: depositable 7 | 8 | def put(depositable, {:avatar, nil}, opts) do 9 | avatar = FileStorageService.get_file(%{id: depositable.avatar_id}, opts) 10 | 11 | %{depositable | avatar: avatar} 12 | end 13 | 14 | def put(depositable, {:file_collections, collection_paths}, opts) do 15 | preload = %{paths: collection_paths, opts: opts} 16 | opts = Map.put(opts, :preload, preload) 17 | filter = %{owner_id: depositable.id, owner_type: "Depositable"} 18 | 19 | collections = FileStorageService.list_file_collection(%{filter: filter}, opts) 20 | 21 | %{depositable | file_collections: collections} 22 | end 23 | 24 | def put(depositable, _, _), do: depositable 25 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/depositable/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.Depositable.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Goods.Depositable 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :label] 10 | def searchable_fields, do: [:code, :name] 11 | 12 | def default(), do: from s in Depositable 13 | 14 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 15 | 16 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 17 | 18 | def search(q, k, l, d), 19 | do: search(q, k, l, d, searchable_fields(), Depositable.translatable_fields()) 20 | 21 | def preloads(_, _), do: [] 22 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.EventHandler do 2 | @behaviour BlueJet.EventHandler 3 | 4 | alias BlueJet.Goods.Service 5 | 6 | def handle_event("identity:account.reset.success", %{account: account = %{mode: "test"}}) do 7 | Task.start(fn -> 8 | Service.delete_all_stockable(%{account: account}) 9 | Service.delete_all_unlockable(%{account: account}) 10 | Service.delete_all_depositable(%{account: account}) 11 | end) 12 | 13 | {:ok, nil} 14 | end 15 | 16 | def handle_event(_, _) do 17 | {:ok, nil} 18 | end 19 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/external/file_storage_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.FileStorageService do 2 | @moduledoc false 3 | 4 | @file_storage_service Application.get_env(:blue_jet, :goods)[:file_storage_service] 5 | 6 | @callback delete_file(String.t, map) :: File.t | nil 7 | @callback get_file(map, map) :: File.t | nil 8 | @callback list_file_collection(map, map) :: list 9 | 10 | defdelegate delete_file(id, opts), to: @file_storage_service 11 | defdelegate get_file(fields, opts), to: @file_storage_service 12 | defdelegate list_file_collection(fields, opts), to: @file_storage_service 13 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.IdentityService do 2 | @moduledoc false 3 | 4 | @identity_service Application.get_env(:blue_jet, :goods)[:identity_service] 5 | 6 | @callback get_vad(map) :: map 7 | @callback get_role(map) :: String.t 8 | @callback get_account(String.t | map) :: map 9 | 10 | defdelegate get_vad(vas), to: @identity_service 11 | defdelegate get_role(vad), to: @identity_service 12 | 13 | defdelegate get_account(id_or_struct), to: @identity_service 14 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/stockable/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.Stockable.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.Goods.FileStorageService 5 | 6 | def put(%{avatar_id: nil} = stockable, {:avatar, nil}, _), do: stockable 7 | 8 | def put(stockable, {:avatar, nil}, opts) do 9 | avatar = FileStorageService.get_file(%{id: stockable.avatar_id}, opts) 10 | 11 | %{stockable | avatar: avatar} 12 | end 13 | 14 | def put(stockable, {:file_collections, collection_paths}, opts) do 15 | preload = %{paths: collection_paths, opts: opts} 16 | opts = Map.put(opts, :preload, preload) 17 | filter = %{owner_id: stockable.id, owner_type: "Stockable"} 18 | 19 | collections = FileStorageService.list_file_collection(%{filter: filter}, opts) 20 | 21 | %{stockable | file_collections: collections} 22 | end 23 | 24 | def put(stockable, _, _), do: stockable 25 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/stockable/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.Stockable.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Goods.Stockable 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :label] 10 | def searchable_fields, do: [:code, :name] 11 | 12 | def default(), do: from s in Stockable 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), Stockable.translatable_fields()) 17 | 18 | def preloads(_, _), do: [] 19 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/unlockable/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.Unlockable.Proxy do 2 | use BlueJet, :proxy 3 | 4 | alias BlueJet.Goods.FileStorageService 5 | 6 | def put(%{avatar_id: nil} = unlockable, {:avatar, nil}, _), do: unlockable 7 | 8 | def put(unlockable, {:avatar, nil}, opts) do 9 | avatar = FileStorageService.get_file(%{id: unlockable.avatar_id}, opts) 10 | 11 | %{unlockable | avatar: avatar} 12 | end 13 | 14 | def put(unlockable = %{file_id: file_id}, {:file, nil}, opts) when not is_nil(file_id) do 15 | file = FileStorageService.get_file(%{id: file_id}, opts) 16 | 17 | %{unlockable | file: file} 18 | end 19 | 20 | def put(unlockable, {:file_collections, collection_paths}, opts) do 21 | preload = %{paths: collection_paths, opts: opts} 22 | opts = Map.put(opts, :preload, preload) 23 | filter = %{owner_id: unlockable.id, owner_type: "Unlockable"} 24 | 25 | collections = FileStorageService.list_file_collection(%{filter: filter}, opts) 26 | 27 | %{unlockable | file_collections: collections} 28 | end 29 | 30 | def put(unlockable, _, _), do: unlockable 31 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/goods/unlockable/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Goods.Unlockable.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Goods.Unlockable 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :label] 10 | def searchable_fields, do: [:code, :name] 11 | 12 | def default(), do: from s in Unlockable 13 | 14 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 15 | 16 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 17 | 18 | def search(q, k, l, d), 19 | do: search(q, k, l, d, searchable_fields(), Unlockable.translatable_fields()) 20 | 21 | def preloads(_, _), do: [] 22 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/account/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.Account.Query do 2 | use BlueJet, :query 3 | 4 | alias BlueJet.Identity.{Account, AccountMembership} 5 | 6 | def default() do 7 | from(a in Account) 8 | end 9 | 10 | def has_member(query, user_id) do 11 | from( 12 | a in query, 13 | join: ac in AccountMembership, 14 | on: ac.account_id == a.id, 15 | where: ac.user_id == ^user_id 16 | ) 17 | end 18 | 19 | def live(query) do 20 | from(a in query, where: a.mode == "live") 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/account_membership/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.AccountMembership.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(am, _, _), do: am 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/account_membership/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.AccountMembership.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Identity.{Account, User, AccountMembership} 7 | 8 | def filterable_fields, do: [:id, :role, :user_id, :account_id] 9 | def identifiable_fields, do: [:id, :user_id, :account_id] 10 | 11 | def default() do 12 | from(am in AccountMembership) 13 | end 14 | 15 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 16 | 17 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 18 | 19 | def search(query, nil), do: query 20 | def search(query, ""), do: query 21 | 22 | def search(query, keyword) do 23 | keyword = "%#{keyword}%" 24 | 25 | from(am in query, 26 | join: u in User, 27 | on: am.user_id == u.id, 28 | or_where: ilike(fragment("?::varchar", u.name), ^keyword), 29 | or_where: ilike(fragment("?::varchar", u.email), ^keyword), 30 | or_where: ilike(fragment("?::varchar", u.username), ^keyword) 31 | ) 32 | end 33 | 34 | def search(q, k, _, _), do: search(q, k) 35 | 36 | def preloads({:account, _}, _) do 37 | [account: Account.Query.default()] 38 | end 39 | 40 | def preloads({:user, _}, _) do 41 | [user: User.Query.default()] 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/jwt.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.Jwt do 2 | def sign_token(claims) do 3 | {_, signed} = 4 | System.get_env("JWT_PRIVATE_KEY") 5 | |> JOSE.JWK.from_pem() 6 | |> JOSE.JWT.sign(%{"alg" => "RS256"}, claims) 7 | |> JOSE.JWS.compact() 8 | 9 | signed 10 | end 11 | 12 | def verify_token(signed_token) do 13 | with {true, %JOSE.JWT{fields: claims}, _} <- 14 | JOSE.JWK.from_pem(System.get_env("JWT_PUBLIC_KEY")) 15 | |> JOSE.JWT.verify_strict(["RS256"], signed_token) do 16 | {true, claims} 17 | else 18 | {:error, _} -> {false, nil} 19 | {false, _} -> {false, nil} 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/password/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.Password.Query do 2 | import Ecto.Query 3 | 4 | alias BlueJet.Identity.{Password} 5 | 6 | def default() do 7 | from(p in Password) 8 | end 9 | 10 | def standard(query) do 11 | from(p in query, where: is_nil(p.account_id)) 12 | end 13 | 14 | def with_valid_reset_token(query) do 15 | now = Timex.now() 16 | from(p in query, where: p.reset_token_expires_at > ^now) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/phone_verification_code/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.PhoneVerificationCode.Query do 2 | import Ecto.Query 3 | import BlueJet.Query 4 | 5 | alias BlueJet.Identity.PhoneVerificationCode 6 | 7 | def default() do 8 | from(pvc in PhoneVerificationCode) 9 | end 10 | 11 | def filter_by(q, f), do: filter_by(q, f, [:phone_number, :value]) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/refresh_token/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.RefreshToken.Query do 2 | import Ecto.Query 3 | 4 | alias BlueJet.Identity.RefreshToken 5 | 6 | def default() do 7 | from(rt in RefreshToken) 8 | end 9 | 10 | def for_user(user_id) do 11 | from(rt in RefreshToken, where: rt.user_id == ^user_id) 12 | end 13 | 14 | def publishable() do 15 | from(rt in RefreshToken, where: is_nil(rt.user_id)) 16 | end 17 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/refresh_token/service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.RefreshToken.Service do 2 | @moduledoc false 3 | 4 | use BlueJet, :service 5 | 6 | alias BlueJet.Identity.RefreshToken 7 | 8 | @spec create_refresh_token!(map, map) :: RefreshToken.t() 9 | def create_refresh_token!(fields \\ %{}, opts) do 10 | account = extract_account(opts) 11 | 12 | Repo.insert!(%RefreshToken{ 13 | account_id: account.id, 14 | user_id: fields["user_id"] || fields[:user_id] 15 | }) 16 | end 17 | 18 | @spec get_refresh_token(map) :: RefreshToken.t() | nil 19 | def get_refresh_token(opts) do 20 | account = extract_account(opts) 21 | 22 | RefreshToken.Query.publishable() 23 | |> Repo.get_by!(account_id: account.id) 24 | |> RefreshToken.put_prefixed_id() 25 | end 26 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/user/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.User.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(user, _, _), do: user 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/identity/user/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.User.Query do 2 | import Ecto.Query 3 | import BlueJet.Query 4 | 5 | alias BlueJet.Identity.{User, AccountMembership} 6 | 7 | def identifiable_fields, do: [:id, :username, :password_reset_token, :email_verification_token] 8 | 9 | def default() do 10 | from(u in User) 11 | end 12 | 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | 15 | def filter_by(q, f), do: filter_by(q, f, [:id, :code, :email]) 16 | 17 | def standard(query) do 18 | from(u in query, where: is_nil(u.account_id)) 19 | end 20 | 21 | def member_of_account(query, account_id) do 22 | from( 23 | u in query, 24 | join: ac in AccountMembership, 25 | on: ac.user_id == u.id, 26 | where: ac.account_id == ^account_id 27 | ) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.Email.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(email, _, _), do: email 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.Email.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Notification.Email 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status] 10 | def searchable_fields, do: [:to, :from, :subject, :reply_to] 11 | 12 | def default(), do: from(e in Email) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), []) 17 | 18 | def preloads(_, _) do 19 | [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email_template/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.EmailTemplate.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(email_template, _, _), do: email_template 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email_template/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.EmailTemplate.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Notification.EmailTemplate 7 | 8 | def identifiable_fields, do: [:id, :from] 9 | def filterable_fields, do: [:id, :from] 10 | def searchable_fields, do: [:name, :subject, :to, :reply_to] 11 | 12 | def default(), do: from(et in EmailTemplate) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), EmailTemplate.translatable_fields()) 17 | 18 | def preloads(_, _) do 19 | [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email_templates/email_verification.txt: -------------------------------------------------------------------------------- 1 | Please verify your email so that we can safely send any order receipt or critical account information to you. 2 | 3 | {{account.name}} ( https://example.com ) 4 | 5 | Please verify your email so that we can safely send any order receipt or critical account information to you. 6 | 7 | Confirm email address ( http://example.com/email-verification/new?token={{user.email_verification_token}} ) 8 | 9 | Thanks, 10 | {{account.name}} Team 11 | 12 | If you’re having trouble with the button above, copy and paste the URL below into your web browser. 13 | 14 | http://example.com/email-verification/new?token={{user.email_verification_token}} 15 | 16 | © 2018 {{account.name}}. All rights reserved. 17 | 18 | [Company Name, LLC] 19 | 20 | 1234 Street Rd. 21 | 22 | Suite 1234 -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email_templates/order_confirmation.txt: -------------------------------------------------------------------------------- 1 | This is a receipt for your recent purchase on {{order.opened_date}}. 2 | 3 | {{account.name}} (http://example.com) 4 | 5 | ************ 6 | Hi {{order.name}}, 7 | ************ 8 | 9 | Thanks for using {{account.name}}. This email is the receipt for your order. 10 | 11 | This purchase will appear as “[Credit Card Statement Name]” on your credit card statement for your {{credit_card_brand}} ending in {{credit_card_last_four}}. Need to update your payment information ( {{ billing_url }} )? 12 | 13 | {{order_number}} 14 | -------------- 15 | 16 | {{order.opened_date}} 17 | -------- 18 | 19 | Description 20 | 21 | Amount 22 | 23 | {{#line_items}} 24 | 25 | {{name}} 26 | ${{sub_total}} 27 | 28 | {{/line_items}} 29 | 30 | Tax 31 | ${{order.tax_total}} 32 | 33 | Total 34 | 35 | ${{order.grand_total}} 36 | 37 | If you have any questions about this receipt, simply reply to this email or reach out to our support team (http://support.example.com) for help. 38 | 39 | Cheers, 40 | The {{account.name}} Team 41 | 42 | © 2018 {{account.name}}. All rights reserved. 43 | 44 | {{account.company_name}} 45 | 46 | 1234 Street Rd. 47 | 48 | Suite 1234 -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email_templates/password_reset.txt: -------------------------------------------------------------------------------- 1 | Use this link to reset your password. The link is only valid for 24 hours. 2 | 3 | {{account.name}} ( {{account.url}} ) 4 | 5 | ************ 6 | Hi {{user.name}}, 7 | ************ 8 | 9 | You recently requested to reset your password for your {{account.name}} account. Use the button below to reset it. This password reset is only valid for the next 24 hours. 10 | 11 | Reset your password ( http://example.com/password/new?token={{user.password_reset_token}} ) 12 | 13 | If you did not request a password reset, please ignore this email or contact support ( http://support.example.com ) if you have questions. 14 | 15 | Thanks, 16 | The {{account.name}} Team 17 | 18 | If you’re having trouble with the button above, copy and paste the URL below into your web browser. 19 | 20 | http://example.com/password/new?token={{user.password_reset_token}} 21 | 22 | © 2018 {{account.name}}. All rights reserved. 23 | 24 | [Company Name, LLC] 25 | 26 | 1234 Street Rd. 27 | 28 | Suite 1234 -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/email_templates/password_reset_not_registered.txt: -------------------------------------------------------------------------------- 1 | You have attempted to reset password for {{account.name}} using this email, but this email is not registered with {{account.name}} 2 | 3 | {{account.name}} ( {{account.url}} ) 4 | 5 | ************ 6 | Hi there, 7 | ************ 8 | 9 | You recently requested to reset your password for your {{account.name}} account, but your email is not registered with {{account.name}}. 10 | 11 | Click here ( http://example.com/forgot-password ) to go to forgot password page and try a different email, or click the link below to sign up for {{account.name}}. 12 | 13 | Sign up for {{account.name}} ( http://example.com/signup ) 14 | 15 | If you did not request a password reset, you can safely ignore this email, or contact support ( http://support.example.com ) if you have questions. 16 | 17 | Thanks, 18 | The {{account.name}} Team 19 | 20 | If you’re having trouble with the button above, copy and paste the URL below into your web browser. 21 | 22 | {{http://example.com/signup}} 23 | 24 | © 2018 {{account.name}}. All rights reserved. 25 | 26 | Company Name 27 | 28 | Suite 1234 29 | 1234 Street Rd. 30 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.IdentityService do 2 | @moduledoc false 3 | 4 | @identity_service Application.get_env(:blue_jet, :notification)[:identity_service] 5 | 6 | @callback get_vad(map) :: map 7 | @callback get_role(map) :: String.t 8 | @callback get_account(String.t | map) :: map 9 | 10 | defdelegate get_vad(vas), to: @identity_service 11 | defdelegate get_role(vad), to: @identity_service 12 | defdelegate get_account(id_or_struct), to: @identity_service 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/sms.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.SMS do 2 | use BlueJet, :data 3 | 4 | alias BlueJet.Identity.User 5 | alias BlueJet.Notification.{Trigger, SMSTemplate} 6 | 7 | schema "sms" do 8 | field :account_id, UUID 9 | field :account, :map, virtual: true 10 | 11 | field :status, :string, default: "sent" 12 | 13 | field :to, :string 14 | field :body, :string 15 | field :locale, :string 16 | 17 | timestamps() 18 | 19 | belongs_to :recipient, User 20 | belongs_to :trigger, Trigger 21 | belongs_to :template, SMSTemplate 22 | end 23 | 24 | @type t :: Ecto.Schema.t() 25 | 26 | @system_fields [ 27 | :id, 28 | :account_id, 29 | :inserted_at, 30 | :updated_at 31 | ] 32 | 33 | def writable_fields do 34 | __MODULE__.__schema__(:fields) -- @system_fields 35 | end 36 | 37 | def translatable_fields do 38 | [] 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/sms/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.SMS.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(sms, _, _), do: sms 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/sms/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.SMS.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Notification.SMS 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :to] 10 | def searchable_fields, do: [:to, :body] 11 | 12 | def default(), do: from(s in SMS) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), []) 17 | 18 | def preloads(_, _) do 19 | [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/sms_template/factory.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.SMSTemplate.Factory do 2 | alias BlueJet.Notification.SMSTemplate 3 | 4 | def phone_verification(account) do 5 | %SMSTemplate{ 6 | account_id: account.id, 7 | system_label: "default", 8 | name: "Phone Verification Code", 9 | to: "{{phone_number}}", 10 | body: "Your {{account.name}} verification code: {{code}}" 11 | } 12 | end 13 | 14 | def tfa(account) do 15 | %SMSTemplate{ 16 | account_id: account.id, 17 | system_label: "default", 18 | name: "TFA Code", 19 | to: "{{user.phone_number}}", 20 | body: "Your {{account.name}} verification code: {{code}}" 21 | } 22 | end 23 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/sms_template/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.SMSTemplate.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(sms_template, _, _), do: sms_template 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/sms_template/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.SMSTemplate.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Notification.SMSTemplate 7 | 8 | def identifiable_fields, do: [:id] 9 | def filterable_fields, do: [:id] 10 | def searchable_fields, do: [:name, :to] 11 | 12 | def default(), do: from(st in SMSTemplate) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), SMSTemplate.translatable_fields()) 17 | 18 | def preloads(_, _) do 19 | [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/trigger/proxy.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.Trigger.Proxy do 2 | use BlueJet, :proxy 3 | 4 | def put(trigger, _, _), do: trigger 5 | end 6 | -------------------------------------------------------------------------------- /lib/blue_jet/app/notification/trigger/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.Trigger.Query do 2 | @behaviour BlueJet.Query 3 | 4 | use BlueJet, :query 5 | 6 | alias BlueJet.Notification.Trigger 7 | 8 | def identifiable_fields, do: [:id, :status] 9 | def filterable_fields, do: [:id, :status, :event, :action_target, :action_type] 10 | def searchable_fields, do: [:name, :event] 11 | 12 | def default(), do: from(t in Trigger) 13 | def get_by(q, i), do: filter_by(q, i, identifiable_fields()) 14 | def filter_by(q, f), do: filter_by(q, f, filterable_fields()) 15 | def search(q, k, l, d), 16 | do: search(q, k, l, d, searchable_fields(), []) 17 | 18 | def preloads(_, _) do 19 | [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront do 2 | use BlueJet, :context 3 | 4 | def list_order(req), do: list("order", req, __MODULE__) 5 | def create_order(req), do: create("order", req, __MODULE__) 6 | def get_order(req), do: get("order", req, __MODULE__) 7 | def update_order(req), do: update("order", req, __MODULE__) 8 | def delete_order(req), do: delete("order", req, __MODULE__) 9 | 10 | def create_order_line_item(req), do: create("order_line_item", req, __MODULE__) 11 | def update_order_line_item(req), do: update("order_line_item", req, __MODULE__) 12 | def delete_order_line_item(req), do: delete("order_line_item", req, __MODULE__) 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/external/balance_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.BalanceService do 2 | @balance_service Application.get_env(:blue_jet, :storefront)[:balance_service] 3 | 4 | @callback list_payment(map, map) :: list(map) 5 | @callback count_payment(map, map) :: integer 6 | 7 | defdelegate list_payment(fields, opts), to: @balance_service 8 | defdelegate count_payment(fields, opts), to: @balance_service 9 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/external/catalogue_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.CatalogueService do 2 | alias BlueJet.Catalogue.{Product, Price} 3 | 4 | @catalogue_service Application.get_env(:blue_jet, :storefront)[:catalogue_service] 5 | 6 | @callback list_product(map, map) :: list 7 | @callback get_product(map, map) :: Product.t | nil 8 | @callback get_price(map, map) :: Price.t | nil 9 | 10 | defdelegate list_product(fields, opts), to: @catalogue_service 11 | defdelegate get_product(fields, opts), to: @catalogue_service 12 | defdelegate get_price(fields, opts), to: @catalogue_service 13 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/external/crm_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.CRMService do 2 | alias BlueJet.CRM.{Customer, PointAccount, PointTransaction} 3 | 4 | @crm_service Application.get_env(:blue_jet, :storefront)[:crm_service] 5 | 6 | @callback get_customer(map, map) :: Customer.t | nil 7 | @callback get_point_account(map, map) :: PointAccount.t | nil 8 | @callback create_point_transaction(map, map) :: {:ok, PointTransaction.t} | {:error, any} 9 | @callback update_point_transaction(String.t, map, map) :: {:ok, PointTransaction.t} | {:error, any} 10 | @callback get_point_transaction(map, map) :: PointTransaction.t | nil 11 | 12 | defdelegate get_customer(fields, opts), to: @crm_service 13 | defdelegate get_point_account(fields, opts), to: @crm_service 14 | defdelegate create_point_transaction(fields, opts), to: @crm_service 15 | defdelegate update_point_transaction(id, fields, opts), to: @crm_service 16 | defdelegate get_point_transaction(fields, opts), to: @crm_service 17 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/external/fulfillment_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.FulfillmentService do 2 | alias BlueJet.Fulfillment.{FulfillmentPackage, FulfillmentItem} 3 | 4 | @fulfillment_service Application.get_env(:blue_jet, :storefront)[:fulfillment_service] 5 | 6 | @callback create_fulfillment_package(map, map) :: {:ok, FulfillmentPackage.t} | {:error, any} 7 | @callback create_fulfillment_item(map, map) :: {:ok, FulfillmentItem.t} | {:error, any} 8 | @callback list_fulfillment_item(map, map) :: list 9 | 10 | defdelegate create_fulfillment_package(fields, opts), to: @fulfillment_service 11 | defdelegate create_fulfillment_item(fields, opts), to: @fulfillment_service 12 | defdelegate list_fulfillment_item(filter, opts), to: @fulfillment_service 13 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/external/goods_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.GoodsService do 2 | alias BlueJet.Goods.{Stockable, Unlockable, Depositable} 3 | 4 | @goods_service Application.get_env(:blue_jet, :storefront)[:goods_service] 5 | 6 | @callback get_stockable(map, map) :: Stockable.t | nil 7 | @callback get_unlockable(map, map) :: Unlockable.t | nil 8 | @callback get_depositable(map, map) :: Depositable.t | nil 9 | 10 | defdelegate get_stockable(fields, opts), to: @goods_service 11 | defdelegate get_unlockable(fields, opts), to: @goods_service 12 | defdelegate get_depositable(fields, opts), to: @goods_service 13 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/external/identity_service.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.IdentityService do 2 | @identity_service Application.get_env(:blue_jet, :storefront)[:identity_service] 3 | 4 | @callback put_vas_data(map) :: map 5 | @callback get_account(String.t | map) :: map 6 | 7 | defdelegate get_account(id_or_struct), to: @identity_service 8 | defdelegate put_vas_data(request), to: @identity_service 9 | end -------------------------------------------------------------------------------- /lib/blue_jet/app/storefront/order/query.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Storefront.Order.Query do 2 | use BlueJet, :query 3 | use BlueJet.Query.Search, for: [ 4 | :name, 5 | :email, 6 | :phone_number, 7 | :code, 8 | :id 9 | ] 10 | use BlueJet.Query.Filter, for: [ 11 | :id, 12 | :status, 13 | :customer_id, 14 | :payment_status, 15 | :fulfillment_status 16 | ] 17 | 18 | alias BlueJet.Storefront.{Order, OrderLineItem} 19 | 20 | def default() do 21 | from o in Order 22 | end 23 | 24 | def preloads({:root_line_items, root_line_item_preloads}, options) do 25 | query = 26 | OrderLineItem.Query.root() 27 | |> order_by([desc: :sort_index, asc: :inserted_at]) 28 | 29 | [root_line_items: {query, OrderLineItem.Query.preloads(root_line_item_preloads, options)}] 30 | end 31 | 32 | def preloads(_, _) do 33 | [] 34 | end 35 | end -------------------------------------------------------------------------------- /lib/blue_jet/application.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Application do 2 | use Application 3 | 4 | # See http://elixir-lang.org/docs/stable/elixir/Application.html 5 | # for more information on OTP Applications 6 | def start(_type, _args) do 7 | import Supervisor.Spec 8 | 9 | # Define workers and child supervisors to be supervised 10 | children = [ 11 | # Start the Ecto repository 12 | supervisor(BlueJet.Repo, []), 13 | # Start the endpoint when the application starts 14 | supervisor(BlueJetWeb.Endpoint, []), 15 | # Start your own worker by calling: BlueJet.Worker.start_link(arg1, arg2, arg3) 16 | # worker(BlueJet.Worker, [arg1, arg2, arg3]), 17 | ] 18 | 19 | # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html 20 | # for other strategies and supported options 21 | opts = [strategy: :one_for_one, name: BlueJet.Supervisor] 22 | 23 | :ok = :error_logger.add_report_handler(Sentry.Logger) 24 | 25 | Supervisor.start_link(children, opts) 26 | end 27 | 28 | # Tell Phoenix to update the endpoint configuration 29 | # whenever the application is updated. 30 | def config_change(changed, _new, removed) do 31 | BlueJetWeb.Endpoint.config_change(changed, removed) 32 | :ok 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/blue_jet/core/context_response.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.ContextResponse do 2 | defstruct meta: %{}, data: %{}, errors: [] 3 | 4 | def put_meta(response, key, value) do 5 | new_meta = Map.put(response.meta, key, value) 6 | Map.put(response, :meta, new_meta) 7 | end 8 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/control_flow.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.ControlFlow do 2 | def tt({:ok, any}), do: {:ok, any} 3 | def tt({:error, error}), do: {:ok, error} 4 | def tt(nil), do: {:error, nil} 5 | def tt(:error), do: {:error, :error} 6 | def tt(any), do: {:ok, any} 7 | 8 | def map({:ok, value}, fun) when is_function(fun, 1) do 9 | case fun.(value) do 10 | nil -> {:error, nil} 11 | other -> {:ok, other} 12 | end 13 | end 14 | 15 | def map({:error, reason}, _), do: {:error, reason} 16 | 17 | def flat_map({:ok, value}, fun) when is_function(fun, 1), do: fun.(value) 18 | def flat_map({:error, reason}, _), do: {:error, reason} 19 | 20 | defmacro lhs ~> {call, line, args} do 21 | value = quote do: value 22 | args = [value | args || []] 23 | 24 | quote do 25 | BlueJet.ControlFlow.map(unquote(lhs), fn unquote(value) -> unquote({call, line, args}) end) 26 | end 27 | end 28 | 29 | defmacro lhs ~>> {call, line, args} do 30 | value = quote do: value 31 | args = [value | args || []] 32 | 33 | quote do 34 | BlueJet.ControlFlow.flat_map(unquote(lhs), fn unquote(value) -> unquote({call, line, args}) end) 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/data.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Data do 2 | @moduledoc """ 3 | Defines the functions that a data module should implement in order to be 4 | used with default service functions. 5 | 6 | Callbacks defined in this behaviour are used in `BlueJet.Service.default_*` 7 | functions. If you wish to use those default functions for your specific data 8 | module, then that data module must implement this behaviour, otherwise you 9 | are not required to use this behaviour. 10 | """ 11 | alias Ecto.Changeset 12 | 13 | @callback changeset(data :: struct, action :: :insert, fields :: map) :: Changeset.t() 14 | 15 | @callback changeset(data :: struct, action :: :update, fields :: map, locale :: String.t() | nil) :: Changeset.t() 16 | 17 | @callback changeset(data :: struct, action :: :delete) :: Changeset.t() 18 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/event/event_emitter.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.EventEmitter do 2 | def event_emitter(namespace) do 3 | quote do 4 | def emit_event(name, data) do 5 | listeners = Map.get(Application.get_env(:blue_jet, unquote(namespace), %{}), :listeners, []) 6 | 7 | Enum.reduce_while(listeners, {:ok, []}, fn(listener, acc) -> 8 | with {:ok, result} <- listener.handle_event(name, data) do 9 | {:ok, acc_result} = acc 10 | {:cont, {:ok, acc_result ++ [{listener, result}]}} 11 | else 12 | {:error, errors} -> {:halt, {:error, errors}} 13 | other -> {:halt, other} 14 | end 15 | end) 16 | end 17 | end 18 | end 19 | 20 | defmacro __using__(namespace: namespace) do 21 | event_emitter(namespace) 22 | end 23 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/event/event_handler.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.EventHandler do 2 | @callback handle_event(String.t, any) :: {:ok, any} 3 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/event_bus.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.EventBus do 2 | @subscribers Application.get_env(:blue_jet, :event_bus, %{}) 3 | 4 | def dispatch(name, data, opts \\ []) 5 | 6 | def dispatch(_, _, skip: true), do: {:ok, :skipped} 7 | 8 | def dispatch(name, data, _) do 9 | subscribers = (@subscribers[name] || []) ++ (@subscribers["*"] || []) 10 | 11 | Enum.reduce_while(subscribers, {:ok, []}, fn(subscriber, acc) -> 12 | with {:ok, result} <- subscriber.handle_event(name, data) do 13 | {:ok, acc_result} = acc 14 | {:cont, {:ok, acc_result ++ [{subscriber, result}]}} 15 | else 16 | {:error, errors} -> {:halt, {:error, errors}} 17 | other -> {:halt, other} 18 | end 19 | end) 20 | end 21 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/policy/common.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Policy.Common do 2 | def common do 3 | quote do 4 | def authorize(request = %{ role: nil }, endpoint) do 5 | identity_service = 6 | Atom.to_string(__MODULE__) 7 | |> String.split(".") 8 | |> Enum.drop(-1) 9 | |> Enum.join(".") 10 | |> Module.concat(IdentityService) 11 | 12 | request 13 | |> identity_service.put_vas_data() 14 | |> authorize(endpoint) 15 | end 16 | end 17 | end 18 | 19 | defmacro __using__(_) do 20 | common() 21 | end 22 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/proxy/common.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Proxy.Common do 2 | def common do 3 | quote do 4 | def get_account(data) do 5 | identity_service = 6 | Atom.to_string(__MODULE__) 7 | |> String.split(".") 8 | |> Enum.drop(-2) 9 | |> Enum.join(".") 10 | |> Module.concat(IdentityService) 11 | 12 | Map.get(data, :account) || identity_service.get_account(data) 13 | end 14 | 15 | def put_account(nil), do: nil 16 | 17 | def put_account(data) do 18 | %{ data | account: get_account(data) } 19 | end 20 | 21 | def put(nil, _, _), do: nil 22 | 23 | def put(struct_or_structs, targets, options) when is_list(targets) and length(targets) == 0 do 24 | struct_or_structs 25 | end 26 | 27 | def put(structs, targets, options) when is_list(structs) do 28 | Enum.map(structs, fn(struct) -> 29 | put(struct, targets, options) 30 | end) 31 | end 32 | 33 | def put(struct, targets, options) when is_list(targets) do 34 | [target | rest] = targets 35 | 36 | struct 37 | |> put(target, options) 38 | |> put(rest, options) 39 | end 40 | 41 | def put(struct, target, options) when is_atom(target) do 42 | put(struct, {target, nil}, options) 43 | end 44 | end 45 | end 46 | 47 | defmacro __using__(_) do 48 | common() 49 | end 50 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/proxy/option.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Proxy.Option do 2 | def get_sopts(%{ account_id: account_id, account: nil }) do 3 | %{ account_id: account_id } 4 | end 5 | 6 | def get_sopts(%{ account: account }) do 7 | %{ account: account } 8 | end 9 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/query/filter.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Query.Filter do 2 | def filter(filterable_fields) do 3 | quote do 4 | def filter_by(query, filter) do 5 | BlueJet.Query.filter_by(query, filter, unquote(filterable_fields)) 6 | end 7 | end 8 | end 9 | 10 | defmacro __using__(opts) do 11 | filter(opts[:for]) 12 | end 13 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/query/helper.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Query.Helper do 2 | def get_preload_filter(opts, key) do 3 | filters = opts[:filters] || %{} 4 | filters[key] || %{} 5 | end 6 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/query/preloads.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Query.Preloads do 2 | def preloads do 3 | quote do 4 | def preloads(targets) when is_list(targets) and length(targets) == 0 do 5 | [] 6 | end 7 | 8 | def preloads(targets) when is_list(targets) do 9 | [target | rest] = targets 10 | preloads(target) ++ preloads(rest) 11 | end 12 | 13 | def preloads(targets, _) when is_list(targets) and length(targets) == 0 do 14 | [] 15 | end 16 | 17 | def preloads(targets, options) when is_list(targets) do 18 | [target | rest] = targets 19 | preloads(target, options) ++ preloads(rest, options) 20 | end 21 | 22 | def preloads(target, options) when is_atom(target) do 23 | preloads({target, nil}, options) 24 | end 25 | 26 | def preloads({nil, nil}, _) do 27 | [] 28 | end 29 | end 30 | end 31 | 32 | defmacro __using__(_) do 33 | preloads() 34 | end 35 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/query/search.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Query.Search do 2 | def search(searchable_fields) do 3 | quote do 4 | def search(query, keyword) do 5 | BlueJet.Query.search(query, unquote(searchable_fields), keyword) 6 | end 7 | 8 | def search(query, keyword, locale, default_locale) do 9 | translatable_fields = 10 | Atom.to_string(__MODULE__) 11 | |> String.split(".") 12 | |> Enum.drop(-1) 13 | |> Enum.join(".") 14 | |> String.to_existing_atom() 15 | |> apply(:translatable_fields, []) 16 | 17 | if translatable_fields do 18 | BlueJet.Query.search(query, unquote(searchable_fields), keyword, locale, default_locale, translatable_fields) 19 | else 20 | BlueJet.Query.search(query, unquote(searchable_fields), keyword) 21 | end 22 | end 23 | end 24 | end 25 | 26 | defmacro __using__(opts) do 27 | search(opts[:for]) 28 | end 29 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/remote_csv.ex: -------------------------------------------------------------------------------- 1 | defmodule RemoteCSV do 2 | def stream(path) do 3 | Stream.resource(fn -> start_stream(path) end, 4 | &continue_stream/1, 5 | fn(_) -> :ok end) 6 | end 7 | 8 | defp start_stream(path) do 9 | {:ok, _status, _headers, ref} = :hackney.get(path, [], "") 10 | 11 | {ref, ""} 12 | end 13 | 14 | defp continue_stream(:halt), do: {:halt, []} 15 | defp continue_stream({ref, partial_row}) do 16 | case :hackney.stream_body(ref) do 17 | {:ok, data} -> 18 | data = partial_row <> data 19 | 20 | if ends_with_line_break?(data) do 21 | rows = split(data) 22 | 23 | {rows, {ref, ""}} 24 | else 25 | {rows, partial_row} = extract_partial_row(data) 26 | 27 | {rows, {ref, partial_row}} 28 | end 29 | 30 | :done -> 31 | if partial_row == "" do 32 | {:halt, []} 33 | else 34 | {[partial_row], :halt} 35 | end 36 | 37 | {:error, reason} -> 38 | raise reason 39 | end 40 | end 41 | 42 | defp extract_partial_row(data) do 43 | data = split(data) 44 | rows = Enum.drop(data, -1) 45 | partial = List.last(data) 46 | 47 | {rows, partial} 48 | end 49 | 50 | defp split(data), do: String.split(data, ~r/(\r?\n|\r)/, trim: true) 51 | 52 | defp ends_with_line_break?(data), do: String.match?(data, ~r/(\r?\n|\r)$/) 53 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/service/helper.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Service.Helper do 2 | def extract_filter(fields) do 3 | fields[:filter] || %{} 4 | end 5 | 6 | def extract_nil_filter(map) do 7 | Enum.reduce(map, %{}, fn({k, v}, acc) -> 8 | if is_nil(v) do 9 | Map.put(acc, k, v) 10 | else 11 | acc 12 | end 13 | end) 14 | end 15 | 16 | def extract_clauses(map) do 17 | Enum.reduce(map, %{}, fn({k, v}, acc) -> 18 | if is_nil(v) do 19 | acc 20 | else 21 | Map.put(acc, k, v) 22 | end 23 | end) 24 | end 25 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/service/option.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Service.Option do 2 | def extract_account(opts, fallback_function \\ fn(_) -> nil end) do 3 | opts[:account] || fallback_function.(opts) 4 | end 5 | 6 | def extract_account_id(opts, fallback_function \\ fn(_) -> nil end) do 7 | opts[:account_id] || extract_account(opts, fallback_function).id 8 | end 9 | 10 | def extract_pagination(opts) do 11 | Map.merge(%{size: 20, number: 1}, opts[:pagination] || %{}) 12 | end 13 | 14 | def extract_preloads(opts, account \\ nil) do 15 | account = account || extract_account(opts) 16 | preload = opts[:preloads] || opts[:preload] || %{} # TODO: remove opts[:preloads] 17 | path = preload[:path] || preload[:paths] || [] # TODO: remove preloads[:path] 18 | 19 | opts = preload[:opts] || %{} 20 | opts = Map.put(opts, :account, account) 21 | 22 | %{ path: path, opts: opts } 23 | end 24 | 25 | def extract_preload(opts) do 26 | account = extract_account(opts) 27 | preload = opts[:preload] || %{} 28 | opts = Map.put(preload[:opts] || %{}, :account, account) 29 | 30 | %{paths: preload[:paths] || [], opts: opts} 31 | end 32 | end -------------------------------------------------------------------------------- /lib/blue_jet/core/service/preload.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Service.Preload do 2 | alias BlueJet.Repo 3 | 4 | def preload([], _, _), do: [] 5 | 6 | def preload(nil, _, _), do: nil 7 | 8 | def preload(struct_or_structs, path, opts) do 9 | struct_module = if is_list(struct_or_structs) do 10 | Enum.at(struct_or_structs, 0).__struct__ 11 | else 12 | struct_or_structs.__struct__ 13 | end 14 | query_module = Module.concat(struct_module, Query) 15 | proxy_module = Module.concat(struct_module, Proxy) 16 | preload_query = query_module.preloads(path, opts) 17 | 18 | struct_or_structs 19 | |> Repo.preload(preload_query) 20 | |> proxy_module.put(path, opts) 21 | end 22 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/aws/cloudfront_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Cloudfront.Client do 2 | def get_url(key) do 3 | cdn_root_url = System.get_env("CDN_ROOT_URL") 4 | "#{cdn_root_url}/#{key}" 5 | end 6 | 7 | def get_policy(url, expiry_time) do 8 | Poison.encode!(%{ 9 | "Statement" => [ 10 | %{ 11 | "Resource" => url, 12 | "Condition" => %{ 13 | "DateLessThan" => %{ 14 | "AWS:EpochTime" => expiry_time 15 | } 16 | } 17 | } 18 | ] 19 | }) 20 | end 21 | 22 | def get_signature(policy) do 23 | pem = System.get_env("CLOUDFRONT_PRIVATE_KEY") 24 | key = get_cloudfront_private_key(pem) 25 | 26 | policy 27 | |> :public_key.sign(:sha, key) 28 | |> Base.encode64() 29 | |> String.replace("+", "-") 30 | |> String.replace("=", "_") 31 | |> String.replace("/", "~") 32 | end 33 | 34 | def get_policy_signature(url, expiry_time) do 35 | get_policy(url, expiry_time) 36 | |> get_signature() 37 | end 38 | 39 | def get_cloudfront_private_key(pem) do 40 | [private_entry] = :public_key.pem_decode(pem) 41 | :public_key.pem_entry_decode(private_entry) 42 | end 43 | 44 | def get_presigned_url(key) do 45 | url = get_url(key) 46 | expires = :os.system_time(:seconds) + 3600 47 | signature = get_policy_signature(url, expires) 48 | key_pair_id = System.get_env("CLOUDFRONT_ACCESS_KEY_ID") 49 | 50 | "#{url}?Expires=#{expires}&Signature=#{signature}&Key-Pair-Id=#{key_pair_id}" 51 | end 52 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/aws/s3_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.S3.Client do 2 | def get_presigned_url(key, method) do 3 | config = ExAws.Config.new(:s3) 4 | {:ok, url} = ExAws.S3.presigned_url(config, method, System.get_env("AWS_S3_BUCKET_NAME"), key) 5 | 6 | url 7 | end 8 | 9 | def delete_object(keys) when is_list(keys) do 10 | ExAws.S3.delete_all_objects(System.get_env("AWS_S3_BUCKET_NAME"), keys) 11 | |> ExAws.request() 12 | 13 | :ok 14 | end 15 | 16 | def delete_object(key) do 17 | ExAws.S3.delete_object(System.get_env("AWS_S3_BUCKET_NAME"), key) 18 | |> ExAws.request() 19 | 20 | :ok 21 | end 22 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/mailer/account_mailer.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.AccountMailer do 2 | use Bamboo.Mailer, otp_app: :blue_jet 3 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/mailer/global_mailer.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.GlobalMailer do 2 | use Bamboo.Mailer, otp_app: :blue_jet 3 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/oauth/client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.OauthClient do 2 | def post(path, body) do 3 | {:ok, response} = OauthHttpClient.post(path, body) 4 | unwrap_response(response) 5 | end 6 | 7 | def get(path) do 8 | {:ok, response} = OauthHttpClient.get(path) 9 | unwrap_response(response) 10 | end 11 | 12 | def unwrap_response(response = %{ status_code: 200 }) do 13 | {:ok, response.body} 14 | end 15 | def unwrap_response(response = %{ status_code: 201 }) do 16 | {:ok, response.body} 17 | end 18 | def unwrap_response(response) do 19 | {:error, response.body} 20 | end 21 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/oauth/http_client.ex: -------------------------------------------------------------------------------- 1 | defmodule OauthHttpClient do 2 | use HTTPoison.Base 3 | 4 | defp process_request_headers(headers) do 5 | headers = headers ++ [ 6 | {"Content-Type", "application/x-www-form-urlencoded"} 7 | ] 8 | 9 | headers 10 | end 11 | 12 | defp process_request_body(body = %{}) do 13 | body |> UriQuery.params |> URI.encode_query 14 | end 15 | defp process_request_body(body), do: body 16 | 17 | defp process_response_body(body) do 18 | body 19 | |> Poison.decode! 20 | end 21 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/repo.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo do 2 | use Ecto.Repo, otp_app: :blue_jet 3 | 4 | @doc """ 5 | Dynamically loads the repository url from the 6 | DATABASE_URL environment variable. 7 | """ 8 | def init(_, opts) do 9 | {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))} 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/blue_jet/vendors/stripe/client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Stripe.Client do 2 | alias BlueJet.Stripe.HttpClient 3 | 4 | def post(path, body, options \\ []) do 5 | key = stripe_secret_key(options) 6 | {:ok, response} = HttpClient.post(path, body, [{"Authorization", "Bearer #{key}"}]) 7 | unwrap_response(response) 8 | end 9 | 10 | def delete(path, options \\ []) do 11 | key = stripe_secret_key(options) 12 | {:ok, response} = HttpClient.delete(path, [{"Authorization", "Bearer #{key}"}]) 13 | unwrap_response(response) 14 | end 15 | 16 | def get(path, options \\ []) do 17 | key = stripe_secret_key(options) 18 | {:ok, response} = HttpClient.get(path, [{"Authorization", "Bearer #{key}"}]) 19 | unwrap_response(response) 20 | end 21 | 22 | def unwrap_response(response = %{ status_code: 200 }) do 23 | {:ok, response.body} 24 | end 25 | def unwrap_response(response = %{ status_code: 201 }) do 26 | {:ok, response.body} 27 | end 28 | def unwrap_response(response) do 29 | {:error, response.body} 30 | end 31 | 32 | defp stripe_secret_key(options) do 33 | if options[:mode] == "test" do 34 | System.get_env("STRIPE_TEST_SECRET_KEY") 35 | else 36 | System.get_env("STRIPE_LIVE_SECRET_KEY") 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /lib/blue_jet/vendors/stripe/http_client.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Stripe.HttpClient do 2 | use HTTPoison.Base 3 | 4 | defp process_url(url) do 5 | "https://api.stripe.com/v1" <> url 6 | end 7 | 8 | defp process_request_headers(headers) do 9 | headers = headers ++ [ 10 | {"Content-Type", "application/x-www-form-urlencoded"} 11 | ] 12 | 13 | headers 14 | end 15 | 16 | defp process_request_body(body = %{}) do 17 | body |> UriQuery.params(add_indices_to_lists: false) |> URI.encode_query 18 | end 19 | defp process_request_body(body), do: body 20 | 21 | defp process_response_body(body) do 22 | body 23 | |> Poison.decode! 24 | end 25 | end -------------------------------------------------------------------------------- /lib/blue_jet_web/channels/user_socket.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.UserSocket do 2 | use Phoenix.Socket 3 | 4 | ## Channels 5 | # channel "room:*", BlueJet.RoomChannel 6 | 7 | ## Transports 8 | transport :websocket, Phoenix.Transports.WebSocket, 9 | timeout: 45_000 10 | # transport :longpoll, Phoenix.Transports.LongPoll 11 | 12 | # Socket params are passed from the client and can 13 | # be used to verify and authenticate a user. After 14 | # verification, you can put default assigns into 15 | # the socket that will be set for all channels, ie 16 | # 17 | # {:ok, assign(socket, :user_id, verified_user_id)} 18 | # 19 | # To deny connection, return `:error`. 20 | # 21 | # See `Phoenix.Token` documentation for examples in 22 | # performing token verification on connect. 23 | def connect(_params, socket) do 24 | {:ok, socket} 25 | end 26 | 27 | # Socket id's are topics that allow you to identify all sockets for a given user: 28 | # 29 | # def id(socket), do: "users_socket:#{socket.assigns.user_id}" 30 | # 31 | # Would allow you to broadcast a "disconnect" event and terminate 32 | # all active sockets and channels for a given user: 33 | # 34 | # BlueJet.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{}) 35 | # 36 | # Returning `nil` makes this socket anonymous. 37 | def id(_socket), do: nil 38 | end 39 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/balance_settings_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.BalanceSettingsController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Balance 5 | alias JaSerializer.Params 6 | 7 | action_fallback BlueJetWeb.FallbackController 8 | 9 | plug :scrub_params, "data" when action in [:create, :update] 10 | 11 | def show(conn = %{ assigns: assigns }, _) do 12 | request = %ContextRequest{ 13 | vas: assigns[:vas], 14 | preloads: assigns[:preloads], 15 | locale: assigns[:locale] 16 | } 17 | 18 | {:ok, %{ data: balance_settings }} = Balance.get_settings(request) 19 | 20 | render(conn, "show.json-api", data: balance_settings, opts: [include: conn.query_params["include"]]) 21 | end 22 | 23 | def update(conn = %{ assigns: assigns }, %{ "data" => data = %{ "type" => "BalanceSettings" } }) do 24 | request = %ContextRequest{ 25 | vas: assigns[:vas], 26 | fields: Params.to_attributes(data), 27 | preloads: assigns[:preloads], 28 | locale: assigns[:locale] 29 | } 30 | 31 | case Balance.update_settings(request) do 32 | {:ok, %{ data: balance_settings }} -> 33 | render(conn, "show.json-api", data: balance_settings, opts: [include: conn.query_params["include"]]) 34 | 35 | {:error, %{ errors: errors }} -> 36 | conn 37 | |> put_status(:unprocessable_entity) 38 | |> render(:errors, data: extract_errors(errors)) 39 | 40 | other -> other 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/catalogue/price_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PriceController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Catalogue 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(%{assigns: assigns} = conn, params) do 11 | filter = Map.put(assigns[:filter], :product_id, params["product_id"]) 12 | 13 | conn 14 | |> assign(:filter, filter) 15 | |> default(:index, &Catalogue.list_price/1) 16 | end 17 | 18 | def create(conn, %{"data" => %{"type" => "Price"}}), 19 | do: default(conn, :create, &Catalogue.create_price/1, fields: ["product_id"]) 20 | 21 | def show(conn, %{"id" => _}), 22 | do: default(conn, :show, &Catalogue.get_price/1) 23 | 24 | def update(conn, %{"id" => _, "data" => %{"type" => "Price"}}), 25 | do: default(conn, :update, &Catalogue.update_price/1) 26 | 27 | def delete(conn, %{"id" => _}), 28 | do: default(conn, :delete, &Catalogue.delete_price/1) 29 | end 30 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/catalogue/product_collection_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ProductCollectionController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Catalogue 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Catalogue.list_product_collection/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "ProductCollection"}}), 14 | do: default(conn, :create, &Catalogue.create_product_collection/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Catalogue.get_product_collection/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "ProductCollection"}}), 20 | do: default(conn, :update, &Catalogue.update_product_collection/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Catalogue.delete_product_collection/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/catalogue/product_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ProductController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Catalogue 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Catalogue.list_product/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "Product"}}), 14 | do: default(conn, :create, &Catalogue.create_product/1, normalize: ["kind", "name_sync"]) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Catalogue.get_product/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "Product"}}), 20 | do: default(conn, :update, &Catalogue.update_product/1, normalize: ["kind", "name_sync"]) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Catalogue.delete_product/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/crm/customer_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.CustomerController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.CRM 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &CRM.list_customer/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "Customer"}}), 14 | do: default(conn, :create, &CRM.create_customer/1) 15 | 16 | @valid_identifiers [:id, :code, :phone_number, :email, :name] 17 | def show(conn, _), 18 | do: default(conn, :show, &CRM.get_customer/1, identifiers: @valid_identifiers) 19 | 20 | def update(conn, %{"id" => _, "data" => %{"type" => "Customer"}}), 21 | do: default(conn, :update, &CRM.update_customer/1) 22 | 23 | def delete(conn, %{"id" => _}), 24 | do: default(conn, :delete, &CRM.delete_customer/1) 25 | end 26 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/crm/point_transaction_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PointTransactionController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.CRM 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(%{assigns: assigns} = conn, params) do 11 | filter = 12 | (assigns[:filter] || %{}) 13 | |> Map.put("point_account_id", params["point_account_id"]) 14 | 15 | conn 16 | |> assign(:filter, filter) 17 | |> default(:index, &CRM.list_point_transaction/1) 18 | end 19 | 20 | def create(conn, %{"data" => %{"type" => "PointTransaction"}}), 21 | do: default(conn, :create, &CRM.create_point_transaction/1, fields: ["point_account_id"]) 22 | 23 | def show(conn, %{"id" => _}), 24 | do: default(conn, :show, &CRM.get_point_transaction/1) 25 | 26 | def update(conn, %{"id" => _, "data" => %{"type" => "PointTransaction"}}), 27 | do: default(conn, :update, &CRM.update_point_transaction/1) 28 | 29 | def delete(conn, %{"id" => _}), 30 | do: default(conn, :delete, &CRM.delete_point_transaction/1) 31 | end 32 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/data_import_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.DataImportController do 2 | use BlueJetWeb, :controller 3 | 4 | alias JaSerializer.Params 5 | alias BlueJet.DataTrading 6 | 7 | plug :scrub_params, "data" when action in [:create, :update] 8 | 9 | def create(conn = %{ assigns: assigns }, %{ "data" => data = %{ "type" => "DataImport" } }) do 10 | request = %ContextRequest{ 11 | vas: assigns[:vas], 12 | fields: Params.to_attributes(data), 13 | preloads: assigns[:preloads] 14 | } 15 | 16 | case DataTrading.create_data_import(request) do 17 | {:ok, %ContextResponse{ data: data_import }} -> 18 | conn 19 | |> put_status(:created) 20 | |> render("show.json-api", data: data_import, opts: [include: conn.query_params["include"]]) 21 | {:error, %ContextResponse{ errors: errors }} -> 22 | conn 23 | |> put_status(:unprocessable_entity) 24 | |> render(:errors, data: extract_errors(errors)) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/file_storage/file_collection_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FileCollectionController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.FileStorage 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &FileStorage.list_file_collection/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "FileCollection"}}), 14 | do: default(conn, :create, &FileStorage.create_file_collection/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &FileStorage.get_file_collection/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "FileCollection"}}), 20 | do: default(conn, :update, &FileStorage.update_file_collection/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &FileStorage.delete_file_collection/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/file_storage/file_collection_membership_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FileCollectionMembershipController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.FileStorage 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(%{assigns: assigns} = conn, params) do 11 | filter = Map.merge(assigns[:filter], %{collection_id: params["file_collection_id"]}) 12 | 13 | conn 14 | |> assign(:filter, filter) 15 | |> default(:index, &FileStorage.list_file_collection_membership/1) 16 | end 17 | 18 | def create(conn, %{"data" => %{"type" => "FileCollectionMembership"}}), 19 | do: default(conn, :create, &FileStorage.create_file_collection_membership/1) 20 | 21 | def show(conn, %{"id" => _}), 22 | do: default(conn, :show, &FileStorage.get_file_collection_membership/1) 23 | 24 | def update(conn, %{"id" => _, "data" => %{"type" => "FileCollectionMembership"}}), 25 | do: default(conn, :update, &FileStorage.update_file_collection_membership/1) 26 | 27 | def delete(conn, %{"id" => _}), 28 | do: default(conn, :delete, &FileStorage.delete_file_collection_membership/1) 29 | end 30 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/file_storage/file_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FileController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.FileStorage 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &FileStorage.list_file/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "File"}}), 14 | do: default(conn, :create, &FileStorage.create_file/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &FileStorage.get_file/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "File"}}), 20 | do: default(conn, :update, &FileStorage.update_file/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &FileStorage.delete_file/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/goods/depositable_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.DepositableController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Goods 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Goods.list_depositable/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "Depositable"}}), 14 | do: default(conn, :create, &Goods.create_depositable/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Goods.get_depositable/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "Depositable"}}), 20 | do: default(conn, :update, &Goods.update_depositable/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Goods.delete_depositable/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/goods/stockable_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.StockableController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Goods 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Goods.list_stockable/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "Stockable"}}), 14 | do: default(conn, :create, &Goods.create_stockable/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Goods.get_stockable/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "Stockable"}}), 20 | do: default(conn, :update, &Goods.update_stockable/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Goods.delete_stockable/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/goods/unlockable_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.UnlockableController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Goods 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Goods.list_unlockable/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "Unlockable"}}), 14 | do: default(conn, :create, &Goods.create_unlockable/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Goods.get_unlockable/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "Unlockable"}}), 20 | do: default(conn, :update, &Goods.update_unlockable/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Goods.delete_unlockable/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/account_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.AccountController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def show(conn, _), 11 | do: default(conn, :show, &Identity.get_account/1) 12 | 13 | def update(conn, %{"data" => %{"type" => "Account"}}), 14 | do: default(conn, :update, &Identity.update_account/1) 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/account_membership_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.AccountMembershipController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _) do 11 | conn 12 | |> assign(:include, "user,account") 13 | |> default(:index, &Identity.list_account_membership/1, normalize: ["role"], params: ["target"]) 14 | end 15 | 16 | def update(conn, %{"id" => _, "data" => %{"type" => "AccountMembership"}}) do 17 | conn 18 | |> assign(:include, "user,account") 19 | |> default(:update, &Identity.update_account_membership/1, normalize: ["role"]) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/account_reset_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.AccountResetController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create] 9 | 10 | def create(conn, %{"data" => %{"type" => "AccountReset"}}), 11 | do: default(conn, :create, &Identity.reset_account/1, status: :no_content) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/email_verification_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailVerificationController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def create(conn, %{"data" => %{"type" => "EmailVerification"}}), 11 | do: default(conn, :create, &Identity.create_email_verification/1, status: :no_content) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/email_verification_token_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailVerificationTokenController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def create(conn, %{"data" => %{"type" => "EmailVerificationToken"}}), 11 | do: default(conn, :create, &Identity.create_email_verification_token/1, status: :no_content) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/password_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PasswordController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def update(conn, %{"data" => %{"type" => "Password"}}), 11 | do: default(conn, :update, &Identity.update_password/1, status: :no_content, identifiers: ["id", "reset_token"]) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/password_reset_token_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PasswordResetTokenController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def create(conn, %{"data" => %{"type" => "PasswordResetToken"}}), 11 | do: default(conn, :create, &Identity.create_password_reset_token/1, status: :no_content) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/phone_verification_code_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PhoneVerificationCodeController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def create(conn, %{"data" => %{"type" => "PhoneVerificationCode"}}), 11 | do: default(conn, :create, &Identity.create_phone_verification_code/1, status: :no_content) 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/refresh_token_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.RefreshTokenController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | plug :scrub_params, "data" when action in [:create, :update] 7 | 8 | def show(conn, _), 9 | do: default(conn, :show, &Identity.get_refresh_token/1) 10 | end 11 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/token_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.TokenController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | def create(conn, params) do 7 | otp = Enum.at(get_req_header(conn, "x-freshcom-otp"), 0) 8 | params = Map.put(params, "otp", otp) 9 | 10 | with {:ok, %{data: token}} <- Identity.create_access_token(%ContextRequest{fields: params}) do 11 | conn 12 | |> put_status(:ok) 13 | |> json(token) 14 | else 15 | {:error, %{errors: errors}} -> 16 | conn = if errors.error == :invalid_otp do 17 | put_resp_header(conn, "X-Freshcom-OTP", "required; auth_method=tfa_sms") 18 | else 19 | conn 20 | end 21 | 22 | errors = if errors.error == :invalid_otp do 23 | %{ errors | error: :invalid_request, error_description: "OTP is invalid, please set OTP using the X-Freshcom-OTP header." } 24 | else 25 | errors 26 | end 27 | 28 | conn 29 | |> put_status(:bad_request) 30 | |> json(errors) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/identity/user_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.UserController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Identity 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def create(conn, %{"data" => %{"type" => "User"}}) do 11 | request = build_context_request(conn, :create, normalize: ["role"]) 12 | 13 | case Identity.create_user(request) do 14 | {:ok, %{data: %{account_id: nil}}} -> 15 | send_resp(conn, :no_content, "") 16 | 17 | {:ok, %{data: user, meta: meta}} -> 18 | conn 19 | |> put_status(:created) 20 | |> render("show.json-api", data: user, opts: [meta: camelize_map(meta), include: conn.query_params["include"]]) 21 | 22 | {:error, %ContextResponse{ errors: errors }} -> 23 | conn 24 | |> put_status(:unprocessable_entity) 25 | |> render(:errors, data: extract_errors(errors)) 26 | 27 | other -> 28 | other 29 | end 30 | end 31 | 32 | def show(conn, _), 33 | do: default(conn, :show, &Identity.get_user/1) 34 | 35 | def update(conn, %{"data" => %{"type" => "User"}}), 36 | do: default(conn, :update, &Identity.update_user/1, normalize: ["role"]) 37 | 38 | def delete(conn, %{"id" => _}), 39 | do: default(conn, :delete, &Identity.delete_user/1) 40 | end 41 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/notification/email_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Notification 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | def index(conn, _), 9 | do: default(conn, :index, &Notification.list_email/1) 10 | 11 | def show(conn, %{"id" => _}), 12 | do: default(conn, :show, &Notification.get_email/1) 13 | end 14 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/notification/email_template_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailTemplateController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Notification 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Notification.list_email_template/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "EmailTemplate"}}), 14 | do: default(conn, :create, &Notification.create_email_template/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Notification.get_email_template/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "EmailTemplate"}}), 20 | do: default(conn, :update, &Notification.update_email_template/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Notification.delete_email_template/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/notification/notification_trigger_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.NotificationTriggerController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Notification 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Notification.list_trigger/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "NotificationTrigger"}}), 14 | do: default(conn, :create, &Notification.create_trigger/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Notification.get_trigger/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "NotificationTrigger"}}), 20 | do: default(conn, :update, &Notification.update_trigger/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Notification.delete_trigger/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/notification/sms_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.SMSController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Notification 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Notification.list_sms/1) 12 | 13 | def show(conn, %{"id" => _}), 14 | do: default(conn, :show, &Notification.get_sms/1) 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/notification/sms_template_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.SMSTemplateController do 2 | use BlueJetWeb, :controller 3 | 4 | alias BlueJet.Notification 5 | 6 | action_fallback BlueJetWeb.FallbackController 7 | 8 | plug :scrub_params, "data" when action in [:create, :update] 9 | 10 | def index(conn, _), 11 | do: default(conn, :index, &Notification.list_sms_template/1) 12 | 13 | def create(conn, %{"data" => %{"type" => "SMSTemplate"}}), 14 | do: default(conn, :create, &Notification.create_sms_template/1) 15 | 16 | def show(conn, %{"id" => _}), 17 | do: default(conn, :show, &Notification.get_sms_template/1) 18 | 19 | def update(conn, %{"id" => _, "data" => %{"type" => "SMSTemplate"}}), 20 | do: default(conn, :update, &Notification.update_sms_template/1) 21 | 22 | def delete(conn, %{"id" => _}), 23 | do: default(conn, :delete, &Notification.delete_sms_template/1) 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/return_item_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ReturnItemController do 2 | use BlueJetWeb, :controller 3 | 4 | alias JaSerializer.Params 5 | alias BlueJet.Fulfillment 6 | 7 | action_fallback BlueJetWeb.FallbackController 8 | 9 | plug :scrub_params, "data" when action in [:create, :update] 10 | 11 | def create(conn = %{ assigns: assigns }, %{ "data" => data = %{ "type" => "ReturnItem" } }) do 12 | request = %ContextRequest{ 13 | vas: assigns[:vas], 14 | fields: Params.to_attributes(data), 15 | preloads: assigns[:preloads] 16 | } 17 | 18 | case Fulfillment.create_return_item(request) do 19 | {:ok, %{ data: return_item, meta: meta }} -> 20 | conn 21 | |> put_status(:created) 22 | |> render("show.json-api", data: return_item, opts: [meta: camelize_map(meta), include: conn.query_params["include"]]) 23 | 24 | {:error, %{ errors: errors }} -> 25 | conn 26 | |> put_status(:unprocessable_entity) 27 | |> render(:errors, data: extract_errors(errors)) 28 | 29 | other -> other 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/return_package_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ReturnPackageController do 2 | use BlueJetWeb, :controller 3 | 4 | alias JaSerializer.Params 5 | alias BlueJet.Fulfillment 6 | 7 | action_fallback BlueJetWeb.FallbackController 8 | 9 | plug :scrub_params, "data" when action in [:create, :update] 10 | 11 | def index(conn = %{ assigns: assigns }, _) do 12 | request = %ContextRequest{ 13 | vas: assigns[:vas], 14 | filter: assigns[:filter], 15 | pagination: %{ size: assigns[:page_size], number: assigns[:page_number] }, 16 | preloads: assigns[:preloads], 17 | locale: assigns[:locale] 18 | } 19 | 20 | case Fulfillment.list_return_package(request) do 21 | {:ok, %{ data: return_packages, meta: meta }} -> 22 | render(conn, "index.json-api", data: return_packages, opts: [meta: camelize_map(meta), include: conn.query_params["include"]]) 23 | 24 | other -> other 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/blue_jet_web/controllers/welcome_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.WelcomeController do 2 | use BlueJetWeb, :controller 3 | 4 | def index(conn, _params) do 5 | text conn, "Welcome" 6 | end 7 | 8 | def options(conn, _params) do 9 | text conn, "" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/blue_jet_web/gettext.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.Gettext do 2 | @moduledoc """ 3 | A module providing Internationalization with a gettext-based API. 4 | 5 | By using [Gettext](https://hexdocs.pm/gettext), 6 | your module gains a set of macros for translations, for example: 7 | 8 | import BlueJet.Gettext 9 | 10 | # Simple translation 11 | gettext "Here is the string to translate" 12 | 13 | # Plural translation 14 | ngettext "Here is the string to translate", 15 | "Here are the strings to translate", 16 | 3 17 | 18 | # Domain-based translation 19 | dgettext "errors", "Here is the error message to translate" 20 | 21 | See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. 22 | """ 23 | use Gettext, otp_app: :blue_jet 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/plugs/fields.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Plugs.Fields do 2 | import Plug.Conn 3 | 4 | def init(default), do: default 5 | 6 | def call(%Plug.Conn{query_params: %{"fields" => fields}} = conn, _default) do 7 | fields = Enum.reduce(fields, %{}, fn({k, v}, acc) -> 8 | Map.put(acc, k, Inflex.underscore(v)) 9 | end) 10 | 11 | assign(conn, :fields, fields) 12 | end 13 | def call(conn, default), do: assign(conn, :fields, default) 14 | end 15 | -------------------------------------------------------------------------------- /lib/blue_jet_web/plugs/filter.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Plugs.Filter do 2 | import Plug.Conn 3 | 4 | def init(opts), do: opts 5 | 6 | def call(%Plug.Conn{query_params: %{"filter" => filter}} = conn, _default) do 7 | filter = Enum.reduce(filter, %{}, fn({k, v}, acc) -> 8 | Map.put(acc, Inflex.underscore(k), v) 9 | end) 10 | 11 | assign(conn, :filter, filter) 12 | end 13 | 14 | def call(conn, opts), do: assign(conn, :filter, opts[:default]) 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet_web/plugs/include.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Plugs.Include do 2 | import Plug.Conn 3 | 4 | def init(opts), do: opts 5 | 6 | def call(%Plug.Conn{query_params: %{"include" => include}} = conn, _) do 7 | normalized_include = Inflex.underscore(include) 8 | query_params = Map.put(conn.query_params, "include", normalized_include) 9 | conn = %{conn | query_params: query_params} 10 | 11 | assign(conn, :include, normalized_include) 12 | end 13 | 14 | def call(conn, opts), do: assign(conn, :include, opts[:default]) 15 | end 16 | -------------------------------------------------------------------------------- /lib/blue_jet_web/plugs/locale.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Plugs.Locale do 2 | import Plug.Conn 3 | 4 | def init(default), do: default 5 | 6 | def call(conn = %Plug.Conn{ query_params: %{ "locale" => locale } }, _default) do 7 | Gettext.put_locale(BlueJet.Gettext, "en") 8 | assign(conn, :locale, locale) 9 | end 10 | def call(conn, default), do: assign(conn, :locale, default) 11 | end 12 | -------------------------------------------------------------------------------- /lib/blue_jet_web/plugs/pagination.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Plugs.Pagination do 2 | import Plug.Conn 3 | 4 | def defaults do 5 | [ 6 | number: 1, 7 | size: 50 8 | ] 9 | end 10 | 11 | def init(options) do 12 | Keyword.merge(defaults(), options) 13 | end 14 | 15 | def call(conn = %Plug.Conn{ query_params: query_params }, options) do 16 | query_params = query_params["page"] || %{} 17 | page_number = query_params |> Map.get("number", options[:number]) |> to_int 18 | page_size = query_params |> Map.get("size", options[:size]) |> to_int 19 | 20 | conn 21 | |> assign(:page_number, page_number) 22 | |> assign(:page_size, page_size) 23 | end 24 | 25 | defp to_int(i) when is_integer(i), do: i 26 | defp to_int(s) when is_binary(s) do 27 | case Integer.parse(s) do 28 | {i, _} -> i 29 | :error -> :error 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/account_membership_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.AccountMembershipView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :account_name, 7 | :user_name, 8 | :user_username, 9 | :user_email, 10 | :role, 11 | :user_kind, 12 | :inserted_at, 13 | :updated_at 14 | ] 15 | 16 | has_one :user, serializer: BlueJetWeb.IdentifierView, identifiers: :always 17 | 18 | def type do 19 | "AccountMembership" 20 | end 21 | 22 | def role(membership) do 23 | Inflex.camelize(membership.role, :lower) 24 | end 25 | 26 | def user_kind(membership) do 27 | if membership.user.account_id, do: "managed", else: "standard" 28 | end 29 | 30 | def account_name(membership) do 31 | membership.account.name 32 | end 33 | 34 | def user_name(membership) do 35 | membership.user.name 36 | end 37 | 38 | def user_username(membership) do 39 | membership.user.username 40 | end 41 | 42 | def user_email(membership) do 43 | membership.user.email 44 | end 45 | 46 | def user(%{ user_id: user_id }, _), do: %{ id: user_id, type: "User" } 47 | end 48 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/account_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.AccountView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :name, 7 | :mode, 8 | :company_name, 9 | :website_url, 10 | :support_email, 11 | :tech_email, 12 | :is_ready_for_live_transaction, 13 | :default_auth_method, 14 | :default_locale, 15 | :test_account_id, 16 | :caption, 17 | :description, 18 | :custom_data 19 | ] 20 | 21 | def type do 22 | "Account" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/billing_settings_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.BalanceSettingsView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :stripe_user_id, 7 | :stripe_livemode, 8 | :stripe_access_token, 9 | :stripe_refresh_token, 10 | :stripe_publishable_key, 11 | :stripe_scope, 12 | :locale, 13 | :inserted_at, 14 | :updated_at 15 | ] 16 | 17 | def type(_, _) do 18 | "BalanceSettings" 19 | end 20 | 21 | def locale(_, %{ assigns: %{ locale: locale } }), do: locale 22 | end 23 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/card_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.CardView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :last_four_digit, 12 | :exp_month, 13 | :exp_year, 14 | :fingerprint, 15 | :cardholder_name, 16 | :brand, 17 | :country, 18 | :stripe_card_id, 19 | :primary, 20 | 21 | :caption, 22 | :description, 23 | :custom_data, 24 | 25 | :inserted_at, 26 | :updated_at 27 | ] 28 | 29 | has_one :owner, serializer: BlueJetWeb.IdentifierView, identifiers: :always 30 | 31 | def type do 32 | "Card" 33 | end 34 | 35 | def owner(struct, _) do 36 | %{ 37 | id: struct.owner_id, 38 | type: struct.owner_type 39 | } 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/changeset_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ChangesetView do 2 | use BlueJetWeb, :view 3 | 4 | @doc """ 5 | Traverses and translates changeset errors. 6 | 7 | See `Ecto.Changeset.traverse_errors/2` and 8 | `BlueJet.ErrorHelpers.translate_error/1` for more details. 9 | """ 10 | def translate_errors(changeset) do 11 | Ecto.Changeset.traverse_errors(changeset, &translate_error/1) 12 | end 13 | 14 | def render("error.json", %{changeset: changeset}) do 15 | # When encoded, the changeset returns its errors 16 | # as a JSON object. So we just pass it forward. 17 | %{errors: translate_errors(changeset)} 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/data_import_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.DataImportView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :data_url, 8 | :data_type, 9 | :inserted_at, 10 | :updated_at 11 | ] 12 | 13 | def type(_, _) do 14 | "DataImport" 15 | end 16 | 17 | def locale(_, %{ assigns: %{ locale: locale } }), do: locale 18 | end 19 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/depositable_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.DepositableView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :code, 7 | :status, 8 | :name, 9 | :label, 10 | 11 | :print_name, 12 | :amount, 13 | :gateway, 14 | 15 | :caption, 16 | :description, 17 | :custom_data, 18 | 19 | :inserted_at, 20 | :updated_at 21 | ] 22 | 23 | has_one :avatar, serializer: BlueJetWeb.FileView, identifiers: :always 24 | has_many :file_collections, serializer: BlueJetWeb.FileCollectionView, identifiers: :when_included 25 | 26 | def type do 27 | "Depositable" 28 | end 29 | 30 | def avatar(%{ avatar_id: nil }, _), do: nil 31 | def avatar(%{ avatar_id: avatar_id, avatar: nil }, _), do: %{ id: avatar_id, type: "File" } 32 | def avatar(%{ avatar: avatar }, _), do: avatar 33 | end 34 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/email_template_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailTemplateView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :system_label, 7 | 8 | :name, 9 | :subject, 10 | :reply_to, 11 | :to, 12 | :body_html, 13 | :body_text, 14 | :description, 15 | 16 | :inserted_at, 17 | :updated_at 18 | ] 19 | 20 | has_many :emails, serializer: BlueJetWeb.EmailView, identifiers: :when_included 21 | 22 | def type do 23 | "EmailTemplate" 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/email_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | 8 | :subject, 9 | :to, 10 | :from, 11 | :reply_to, 12 | :body_html, 13 | :body_text, 14 | :locale, 15 | 16 | :inserted_at, 17 | :updated_at 18 | ] 19 | 20 | has_one :trigger, serializer: BlueJetWeb.NotificationTriggerView, identifiers: :always 21 | has_one :template, serializer: BlueJetWeb.EmailTemplateView, identifiers: :always 22 | 23 | def type do 24 | "Email" 25 | end 26 | 27 | def trigger(%{ trigger_id: nil }, _), do: nil 28 | def trigger(%{ trigger_id: trigger_id, trigger: %Ecto.Association.NotLoaded{} }, _), do: %{ id: trigger_id, type: "NotificationTrigger" } 29 | def trigger(%{ trigger: trigger }, _), do: trigger 30 | 31 | def template(%{ template_id: nil }, _), do: nil 32 | def template(%{ template_id: template_id, template: %Ecto.Association.NotLoaded{} }, _), do: %{ id: template_id, type: "EmailTemplate" } 33 | def template(%{ template: template }, _), do: template 34 | end 35 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/error_helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ErrorHelpers do 2 | @moduledoc """ 3 | Conveniences for translating and building error messages. 4 | """ 5 | 6 | @doc """ 7 | Translates an error message using gettext. 8 | """ 9 | def translate_error({msg, opts}) do 10 | # Because error messages were defined within Ecto, we must 11 | # call the Gettext module passing our Gettext backend. We 12 | # also use the "errors" domain as translations are placed 13 | # in the errors.po file. 14 | # Ecto will pass the :count keyword if the error message is 15 | # meant to be pluralized. 16 | # On your own code and templates, depending on whether you 17 | # need the message to be pluralized or not, this could be 18 | # written simply as: 19 | # 20 | # dngettext "errors", "1 file", "%{count} files", count 21 | # dgettext "errors", "is invalid" 22 | # 23 | if count = opts[:count] do 24 | Gettext.dngettext(BlueJet.Gettext, "errors", msg, msg, count, opts) 25 | else 26 | Gettext.dgettext(BlueJet.Gettext, "errors", msg, opts) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/file_collection_membership_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FileCollectionMembershipView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :sort_index, 7 | :inserted_at, 8 | :updated_at 9 | ] 10 | 11 | has_one :collection, serializer: BlueJetWeb.FileCollectionView, identifiers: :always 12 | has_one :file, serializer: BlueJetWeb.FileView, identifiers: :always 13 | 14 | def type(_, _conn) do 15 | "FileCollectionMembership" 16 | end 17 | 18 | def collection(membership = %{ collection: %Ecto.Association.NotLoaded{} }, _) do 19 | case membership.collection_id do 20 | nil -> nil 21 | _ -> %{ type: "ProductCollection", id: membership.collection_id } 22 | end 23 | end 24 | def collection(membership, _), do: Map.get(membership, :collection) 25 | 26 | def file(membership = %{ file: %Ecto.Association.NotLoaded{} }, _) do 27 | case membership.file_id do 28 | nil -> nil 29 | _ -> %{ type: "Product", id: membership.file_id } 30 | end 31 | end 32 | def file(membership, _), do: Map.get(membership, :file) 33 | end 34 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/file_collection_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FileCollectionView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :file_count, 12 | 13 | :caption, 14 | :description, 15 | :custom_data, 16 | 17 | :inserted_at, 18 | :updated_at 19 | ] 20 | 21 | has_many :files, serializer: BlueJetWeb.FileView, identifiers: :when_included 22 | has_many :memberships, serializer: BlueJetWeb.FileCollectionMembershipView, identifiers: :when_included 23 | has_one :owner, serializer: BlueJetWeb.IdentifierView, identifiers: :always 24 | 25 | def type do 26 | "FileCollection" 27 | end 28 | 29 | def owner(struct, _) do 30 | if struct.owner_id do 31 | %{ 32 | id: struct.owner_id, 33 | type: struct.owner_type 34 | } 35 | else 36 | nil 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/file_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FileView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :content_type, 12 | :size_bytes, 13 | :public_readable, 14 | :url, 15 | 16 | :caption, 17 | :description, 18 | :custom_data, 19 | 20 | :inserted_at, 21 | :updated_at 22 | ] 23 | 24 | def type do 25 | "File" 26 | end 27 | 28 | # Avoid conflicts with path helper url/1 29 | def url(file, _) do 30 | file.url 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/fulfillment_package_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.FulfillmentPackageView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | :system_label, 11 | 12 | :caption, 13 | :description, 14 | :custom_data, 15 | 16 | :inserted_at, 17 | :updated_at 18 | ] 19 | 20 | has_one :source, serializer: BlueJetWeb.IdentifierView, identifiers: :always 21 | has_one :order, serializer: BlueJetWeb.OrderView, identifiers: :always 22 | has_one :customer, serializer: BlueJetWeb.CustomerView, identifiers: :always 23 | 24 | has_many :items, serializer: BlueJetWeb.FulfillmentItemView, identifiers: :when_included 25 | has_many :file_collections, serializer: BlueJetWeb.FileCollectionView, identifiers: :when_included 26 | 27 | def type do 28 | "FulfillmentPackage" 29 | end 30 | 31 | def order(%{ order_id: nil }, _), do: nil 32 | def order(%{ order_id: order_id, order: nil }, _), do: %{ id: order_id, type: "Order" } 33 | def order(%{ order: order }, _), do: order 34 | 35 | def customer(%{ customer_id: nil }, _), do: nil 36 | def customer(%{ customer_id: customer_id, customer: nil }, _), do: %{ id: customer_id, type: "Order" } 37 | def customer(%{ customer: customer }, _), do: customer 38 | end 39 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/identifier_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.IdentifierView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | def type(struct, _) do 6 | Map.get(struct, :type) || Enum.at(Module.split(struct.__struct__), 0) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/notification_trigger_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.NotificationTriggerView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :name, 7 | :status, 8 | :event, 9 | :action_type, 10 | :action_target, 11 | :description, 12 | :updated_at, 13 | :inserted_at 14 | ] 15 | 16 | def type do 17 | "NotificationTrigger" 18 | end 19 | 20 | def action_type(struct, _) do 21 | Inflex.camelize(struct.action_type, :lower) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/password_reset_token_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PasswordResetTokenView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :email 7 | ] 8 | 9 | def type do 10 | "PasswordResetToken" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/password_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PasswordView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | def type do 6 | "Password" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/phone_verification_code_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PhoneVerificationCodeView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :phone_number 7 | ] 8 | 9 | def type do 10 | "PhoneVerificationCode" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/point_account_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PointAccountView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :balance 8 | ] 9 | 10 | has_one :customer, serializer: BlueJetWeb.CustomerView, identifiers: :always 11 | has_many :transactions, serializer: BlueJetWeb.PointTransactionView, identifiers: :when_included 12 | 13 | def type(_, _) do 14 | "PointAccount" 15 | end 16 | 17 | def locale(_, %{ assigns: %{ locale: locale } }), do: locale 18 | 19 | def customer(point_account = %{ customer: %Ecto.Association.NotLoaded{} }, _) do 20 | case point_account.customer_id do 21 | nil -> nil 22 | _ -> %{ type: "Customer", id: point_account.customer_id } 23 | end 24 | end 25 | def customer(point_account, _), do: point_account.customer 26 | end 27 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/point_transaction_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PointTransactionView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :reason_label, 12 | :amount, 13 | :balance_after_commit, 14 | 15 | :caption, 16 | :description, 17 | :custom_data, 18 | 19 | :committed_at, 20 | :inserted_at 21 | ] 22 | 23 | has_one :point_account, serializer: BlueJetWeb.PointAccountView, identifiers: :always 24 | has_one :source, serializer: BlueJetWeb.IdentifierView, identifiers: :always 25 | 26 | def type do 27 | "PointTransaction" 28 | end 29 | 30 | def source(%{ source_id: nil }, _), do: nil 31 | def source(%{ source_id: source_id, source_type: source_type, source: nil }, _), do: %{ id: source_id, type: source_type } 32 | def source(%{ source: source }, _), do: source 33 | 34 | def point_account(%{ point_account_id: nil }, _), do: nil 35 | 36 | def point_account(%{ point_account_id: point_account_id, point_account: %Ecto.Association.NotLoaded{} }, _) do 37 | %{ id: point_account_id, type: "PointAccount" } 38 | end 39 | 40 | def point_account(%{ point_account: point_account }, _), do: point_account 41 | end 42 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/price_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PriceView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :currency_code, 12 | :charge_amount_cents, 13 | :charge_unit, 14 | :order_unit, 15 | 16 | :estimate_average_percentage, 17 | :estimate_maximum_percentage, 18 | :minimum_order_quantity, 19 | :estimate_by_default, 20 | 21 | :tax_one_percentage, 22 | :tax_two_percentage, 23 | :tax_three_percentage, 24 | 25 | :start_time, 26 | :end_time, 27 | 28 | :caption, 29 | :description, 30 | :custom_data, 31 | 32 | :inserted_at, 33 | :updated_at 34 | ] 35 | 36 | has_one :product, serializer: BlueJetWeb.ProductView, identifiers: :always 37 | has_many :children, serializer: BlueJetWeb.PriceView, identifiers: :when_included 38 | 39 | def type do 40 | "Price" 41 | end 42 | 43 | def product(%{ product_id: nil }, _), do: nil 44 | def product(%{ product_id: product_id, product: %Ecto.Association.NotLoaded{} }, _), do: %{ id: product_id, type: "Customer" } 45 | def product(%{ product: product }, _), do: product 46 | end 47 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/product_collection_membership_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ProductCollectionMembershipView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :sort_index, 7 | :inserted_at, 8 | :updated_at 9 | ] 10 | 11 | has_one :collection, serializer: BlueJetWeb.ProductCollectionView, identifiers: :always 12 | has_one :product, serializer: BlueJetWeb.ProductView, identifiers: :always 13 | 14 | def type(_, _conn) do 15 | "ProductCollectionMembership" 16 | end 17 | 18 | def collection(membership = %{ collection: %Ecto.Association.NotLoaded{} }, _) do 19 | case membership.collection_id do 20 | nil -> nil 21 | _ -> %{ type: "ProductCollection", id: membership.collection_id } 22 | end 23 | end 24 | def collection(membership, _), do: Map.get(membership, :collection) 25 | 26 | def product(membership = %{ product: %Ecto.Association.NotLoaded{} }, _) do 27 | case membership.product_id do 28 | nil -> nil 29 | _ -> %{ type: "Product", id: membership.product_id } 30 | end 31 | end 32 | def product(membership, _), do: Map.get(membership, :product) 33 | end 34 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/product_collection_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ProductCollectionView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | :sort_index, 11 | :product_count, 12 | 13 | :caption, 14 | :description, 15 | :custom_data, 16 | 17 | :inserted_at, 18 | :updated_at 19 | ] 20 | 21 | has_one :avatar, serializer: BlueJetWeb.FileView, identifiers: :always 22 | 23 | has_many :memberships, serializer: BlueJetWeb.ProductCollectionMembershipView, identifiers: :when_included 24 | has_many :products, serializer: BlueJetWeb.ProductView, include: false, identifiers: :when_included 25 | 26 | def type do 27 | "ProductCollection" 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/refresh_token_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.RefreshTokenView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [:prefixed_id] 6 | 7 | def type do 8 | "RefreshToken" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/refund_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.RefundView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :code, 7 | :label, 8 | :gateway, 9 | :amount_cents, 10 | :processor_fee_cents, 11 | :freshcom_fee_cents, 12 | :caption, 13 | :descrption, 14 | :custom_data, 15 | :inserted_at 16 | ] 17 | 18 | has_one :payment, serializer: BlueJetWeb.PaymentView, identifiers: :always 19 | has_one :target, serializer: BlueJetWeb.IdentifierView, identifiers: :always 20 | has_one :owner, serializer: BlueJetWeb.IdentifierView, identifiers: :always 21 | 22 | def type do 23 | "Refund" 24 | end 25 | 26 | def payment(%{ payment_id: nil }, _), do: nil 27 | def payment(%{ payment_id: payment_id, payment: %Ecto.Association.NotLoaded{} }, _), do: %{ id: payment_id, type: "Product" } 28 | def payment(%{ payment: payment }, _), do: payment 29 | 30 | def owner(%{ owner_id: nil }, _), do: nil 31 | def owner(%{ owner_id: owner_id, owner_type: owner_type, owner: nil }, _), do: %{ id: owner_id, type: owner_type } 32 | def owner(%{ owner: owner }, _), do: owner 33 | 34 | def target(%{ target_id: nil }, _), do: nil 35 | def target(%{ target_id: target_id, target_type: target_type, target: nil }, _), do: %{ id: target_id, type: target_type } 36 | def target(%{ target: target }, _), do: target 37 | end 38 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/return_package_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ReturnPackageView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | :system_label, 11 | 12 | :print_name, 13 | 14 | :caption, 15 | :description, 16 | :custom_data, 17 | 18 | :inserted_at, 19 | :updated_at 20 | ] 21 | 22 | has_one :source, serializer: BlueJetWeb.IdentifierView, identifiers: :always 23 | 24 | has_many :items, serializer: BlueJetWeb.ReturnItemView, identifiers: :when_included 25 | has_many :file_collections, serializer: BlueJetWeb.FileCollectionView, identifiers: :when_included 26 | 27 | def type do 28 | "ReturnPackage" 29 | end 30 | 31 | def order(%{ order_id: nil }, _), do: nil 32 | def order(%{ order_id: order_id, order: nil }, _), do: %{ id: order_id, type: "Order" } 33 | def order(%{ order: order }, _), do: order 34 | end 35 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/sms_template_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.SMSTemplateView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :system_label, 7 | 8 | :name, 9 | :to, 10 | :body, 11 | :description, 12 | 13 | :inserted_at, 14 | :updated_at 15 | ] 16 | 17 | has_many :smses, serializer: BlueJetWeb.SMSView, identifiers: :when_included 18 | 19 | def type do 20 | "SMSTemplate" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/sms_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.SMSView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | 8 | :to, 9 | :body, 10 | :locale, 11 | 12 | :inserted_at, 13 | :updated_at 14 | ] 15 | 16 | has_one :trigger, serializer: BlueJetWeb.NotificationTriggerView, identifiers: :always 17 | has_one :template, serializer: BlueJetWeb.SMSTemplateView, identifiers: :always 18 | 19 | def type do 20 | "SMS" 21 | end 22 | 23 | def trigger(%{ trigger_id: nil }, _), do: nil 24 | def trigger(%{ trigger_id: trigger_id, trigger: %Ecto.Association.NotLoaded{} }, _), do: %{ id: trigger_id, type: "NotificationTrigger" } 25 | def trigger(%{ trigger: trigger }, _), do: trigger 26 | 27 | def template(%{ template_id: nil }, _), do: nil 28 | def template(%{ template_id: template_id, template: %Ecto.Association.NotLoaded{} }, _), do: %{ id: template_id, type: "SMSTemplate" } 29 | def template(%{ template: template }, _), do: template 30 | end 31 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/stockable_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.StockableView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :print_name, 12 | :unit_of_measure, 13 | :variable_weight, 14 | 15 | :storage_type, 16 | :storage_size, 17 | :stackable, 18 | 19 | :specification, 20 | :storage_description, 21 | 22 | :caption, 23 | :description, 24 | :custom_data, 25 | 26 | :inserted_at, 27 | :updated_at 28 | ] 29 | 30 | has_one :avatar, serializer: BlueJetWeb.FileView, identifiers: :always 31 | has_many :file_collections, serializer: BlueJetWeb.FileCollectionView, identifiers: :when_included 32 | 33 | def type do 34 | "Stockable" 35 | end 36 | 37 | def avatar(%{ avatar_id: nil }, _), do: nil 38 | def avatar(%{ avatar_id: avatar_id, avatar: nil }, _), do: %{ id: avatar_id, type: "File" } 39 | def avatar(%{ avatar: avatar }, _), do: avatar 40 | end 41 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/unlock_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.UnlockView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [:inserted_at, :updated_at] 6 | 7 | has_one :unlockable, serializer: BlueJetWeb.UnlockableView, identifiers: :always 8 | has_one :customer, serializer: BlueJetWeb.CustomerView, identifiers: :always 9 | 10 | def type do 11 | "Unlock" 12 | end 13 | 14 | def unlockable(%{ unlockable_id: nil }, _), do: nil 15 | def unlockable(%{ unlockable_id: unlockable_id, unlockable: nil }, _), do: %{ id: unlockable_id, type: "Unlockable" } 16 | def unlockable(%{ unlockable: unlockable }, _), do: unlockable 17 | 18 | def customer(%{ customer_id: nil }, _), do: nil 19 | def customer(%{ customer_id: customer_id, customer: nil }, _), do: %{ id: customer_id, type: "Customer" } 20 | def customer(%{ customer: customer }, _), do: customer 21 | end 22 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/unlockable_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.UnlockableView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :code, 8 | :name, 9 | :label, 10 | 11 | :print_name, 12 | 13 | :caption, 14 | :description, 15 | :custom_data, 16 | 17 | :inserted_at, 18 | :updated_at 19 | ] 20 | 21 | has_one :avatar, serializer: BlueJetWeb.FileView, identifiers: :always 22 | has_one :file, serializer: BlueJetWeb.FileView, identifiers: :always 23 | has_many :file_collections, serializer: BlueJetWeb.FileCollectionView, identifiers: :when_included 24 | 25 | def type do 26 | "Unlockable" 27 | end 28 | 29 | def avatar(%{ avatar_id: nil }, _), do: nil 30 | def avatar(%{ avatar_id: avatar_id, avatar: nil }, _), do: %{ id: avatar_id, type: "File" } 31 | def avatar(%{ avatar: avatar }, _), do: avatar 32 | 33 | def file(%{ file_id: nil }, _), do: nil 34 | def file(%{ file_id: file_id, file: nil }, _), do: %{ id: file_id, type: "File" } 35 | def file(%{ file: file }, _), do: file 36 | end 37 | -------------------------------------------------------------------------------- /lib/blue_jet_web/views/user_view.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.UserView do 2 | use BlueJetWeb, :view 3 | use JaSerializer.PhoenixView 4 | 5 | attributes [ 6 | :status, 7 | :email, 8 | :username, 9 | :first_name, 10 | :last_name, 11 | :name, 12 | :role, 13 | :auth_method, 14 | :phone_number, 15 | :email_verified, 16 | :email_verified_at, 17 | :inserted_at, 18 | :updated_at 19 | ] 20 | 21 | has_one :account, serializer: BlueJetWeb.AccountView, identifiers: :always 22 | 23 | def type do 24 | "User" 25 | end 26 | 27 | def role(user) do 28 | Inflex.camelize(user.role, :lower) 29 | end 30 | 31 | def account(%{ account_id: nil }, _) do 32 | nil 33 | end 34 | 35 | def account(%{ account_id: account_id, account: %Ecto.Association.NotLoaded{} }, _) do 36 | %{ id: account_id, type: "Account" } 37 | end 38 | 39 | def account(%{ account: account }, _), do: account 40 | end 41 | -------------------------------------------------------------------------------- /old/test/blue_jet_web/controllers/email_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailControllerTest do 2 | use BlueJetWeb.ConnCase 3 | 4 | import BlueJet.Identity.TestHelper 5 | 6 | setup do 7 | conn = build_conn() 8 | |> put_req_header("accept", "application/vnd.api+json") 9 | |> put_req_header("content-type", "application/vnd.api+json") 10 | 11 | %{conn: conn} 12 | end 13 | 14 | describe "GET /v1/emails" do 15 | test "with no access token", %{conn: conn} do 16 | conn = get(conn, "/v1/emails") 17 | 18 | assert conn.status == 401 19 | end 20 | 21 | test "with a valid request", %{conn: conn} do 22 | %{ user: user } = create_global_identity("administrator") 23 | uat = create_access_token(user.username, "test1234") 24 | conn = put_req_header(conn, "authorization", "Bearer #{uat}") 25 | 26 | conn = get(conn, "/v1/emails") 27 | 28 | assert conn.status == 200 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /old/test/blue_jet_web/controllers/helpers_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.HelpersTest do 2 | use BlueJet.DataCase 3 | 4 | alias BlueJetWeb.Controller.Helpers 5 | 6 | describe "pointer_for/1" do 7 | test "with :fields as field" do 8 | assert Helpers.pointer_for(:fields) == "/data" 9 | end 10 | 11 | test "with :attributes as field" do 12 | assert Helpers.pointer_for(:attributes) == "/data/attributes" 13 | end 14 | 15 | test "with :relationships as field" do 16 | assert Helpers.pointer_for(:relationships) == "/data/relationships" 17 | end 18 | 19 | test "with :attr_name as field" do 20 | assert Helpers.pointer_for(:attr_name) == "/data/attributes/attrName" 21 | end 22 | 23 | test "with :one_example_id as field" do 24 | assert Helpers.pointer_for(:one_example_id) == "/data/relationships/oneExample" 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /priv/repo/migrations/001_create_account.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateAccount do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:accounts, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :name, :string 8 | add :company_name, :string 9 | 10 | add :default_locale, :string, null: false 11 | add :live_account_id, references(:accounts, type: :binary_id, on_delete: :delete_all) 12 | add :mode, :string, null: false, default: "live" 13 | add :default_auth_method, :string, null: false, default: "simple" 14 | add :website_url, :string 15 | add :support_email, :string 16 | add :tech_email, :string 17 | 18 | add :caption, :string 19 | add :description, :text 20 | add :custom_data, :map, null: false 21 | add :translations, :map, null: false 22 | 23 | timestamps() 24 | end 25 | 26 | create index(:accounts, :live_account_id) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /priv/repo/migrations/003_create_account_memberships.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateAccountMemberships do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:account_memberships, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false 9 | add :role, :string, null: false 10 | 11 | timestamps() 12 | end 13 | 14 | create index(:account_memberships, [:account_id, :user_id]) 15 | create index(:account_memberships, :user_id) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/005_create_refresh_token.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateRefreshToken do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:refresh_tokens, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :user_id, references(:users, type: :binary_id, on_delete: :delete_all) 9 | add :scope, :text 10 | 11 | timestamps() 12 | end 13 | 14 | create index(:refresh_tokens, [:account_id, :user_id]) 15 | create index(:refresh_tokens, :user_id) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /priv/repo/migrations/007_create_stockable.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateStockable do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:stockables, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :avatar_id, references(:files, type: :binary_id, on_delete: :nilify_all) 9 | 10 | add :status, :string, null: false 11 | add :code, :string 12 | add :name, :string, null: false 13 | add :label, :string 14 | 15 | add :print_name, :string 16 | add :unit_of_measure, :string, null: false 17 | add :variable_weight, :boolean, null: false, default: false 18 | 19 | add :storage_type, :string 20 | add :storage_size, :integer, null: false, default: 0 21 | add :stackable, :boolean, null: false, default: false 22 | 23 | add :specification, :text 24 | add :storage_description, :text 25 | 26 | add :caption, :string 27 | add :description, :text 28 | add :custom_data, :map, null: false, default: "{}" 29 | add :translations, :map, null: false, default: "{}" 30 | 31 | timestamps() 32 | end 33 | 34 | create unique_index(:stockables, [:account_id, :code], where: "code IS NOT NULL") 35 | create index(:stockables, [:account_id, :status]) 36 | create index(:stockables, [:account_id, :name]) 37 | create index(:stockables, [:account_id, :label], where: "label IS NOT NULL") 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /priv/repo/migrations/008_create_unlockable.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateUnlockable do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:unlockables, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :avatar_id, references(:files, type: :binary_id, on_delete: :nilify_all) 9 | add :file_id, references(:files, type: :binary_id, on_delete: :nilify_all) 10 | 11 | add :status, :string, null: false 12 | add :code, :string 13 | add :name, :string, null: false 14 | add :label, :string 15 | 16 | add :print_name, :string 17 | 18 | add :caption, :string 19 | add :description, :text 20 | add :custom_data, :map, null: false, default: "{}" 21 | add :translations, :map, null: false, default: "{}" 22 | 23 | timestamps() 24 | end 25 | 26 | create unique_index(:unlockables, [:account_id, :code], where: "code IS NOT NULL") 27 | create index(:unlockables, [:account_id, :status]) 28 | create index(:unlockables, [:account_id, :name]) 29 | create index(:unlockables, [:account_id, :label], where: "label IS NOT NULL") 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /priv/repo/migrations/011_create_file_collection.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateFileCollection do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:file_collections, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all) 8 | add :owner_id, :binary_id 9 | add :owner_type, :string 10 | 11 | add :status, :string, null: false 12 | add :code, :string 13 | add :name, :string, null: false 14 | add :label, :string 15 | 16 | add :content_type, :string 17 | 18 | add :caption, :string 19 | add :description, :text 20 | add :custom_data, :map, null: false, default: "{}" 21 | add :translations, :map, null: false, default: "{}" 22 | 23 | timestamps() 24 | end 25 | 26 | create unique_index(:file_collections, [:account_id, :code], where: "code IS NOT NULL") 27 | create index(:file_collections, [:account_id, :status]) 28 | create index(:file_collections, [:account_id, :name]) 29 | create index(:file_collections, [:account_id, :label], where: "label IS NOT NULL") 30 | create index(:file_collections, [:account_id, :owner_id, :owner_type], where: "owner_id IS NOT NULL") 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /priv/repo/migrations/012_create_file_collection_membership.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateFileCollectionMembership do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:file_collection_memberships, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :collection_id, references(:file_collections, on_delete: :delete_all, type: :binary_id) 10 | add :file_id, references(:files, on_delete: :delete_all, type: :binary_id) 11 | 12 | add :sort_index, :integer, null: false 13 | 14 | timestamps() 15 | end 16 | create index(:file_collection_memberships, [:account_id, :collection_id]) 17 | create index(:file_collection_memberships, [:account_id, :file_id]) 18 | create index(:file_collection_memberships, [:account_id, :sort_index]) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /priv/repo/migrations/017_create_unlock.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateUnlock do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:unlocks, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :unlockable_id, references(:unlockables, type: :binary_id, on_delete: :delete_all), null: false 10 | add :customer_id, references(:customers, type: :binary_id, on_delete: :delete_all), null: false 11 | 12 | add :source_id, :binary_id 13 | add :source_type, :string 14 | 15 | add :sort_index, :integer, null: false 16 | 17 | add :custom_data, :map, null: false, default: "{}" 18 | add :translations, :map, null: false, default: "{}" 19 | 20 | timestamps() 21 | end 22 | 23 | create unique_index(:unlocks, [:customer_id, :unlockable_id]) 24 | create index(:unlocks, [:account_id, :customer_id]) 25 | create index(:unlocks, [:account_id, :unlockable_id]) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /priv/repo/migrations/019_create_card.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateCard do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:cards, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :stripe_card_id, :string 10 | add :stripe_customer_id, :string 11 | 12 | add :owner_id, :binary_id 13 | add :owner_type, :string 14 | 15 | add :status, :string, null: false 16 | add :code, :string 17 | add :name, :string 18 | add :label, :string 19 | 20 | add :last_four_digit, :string 21 | add :exp_month, :integer 22 | add :exp_year, :integer 23 | add :fingerprint, :string 24 | add :cardholder_name, :string 25 | add :brand, :string 26 | add :country, :string 27 | add :primary, :boolean, null: false, default: false 28 | 29 | add :caption, :string 30 | add :description, :text 31 | add :custom_data, :map, null: false, default: "{}" 32 | add :translations, :map, null: false, default: "{}" 33 | 34 | timestamps() 35 | end 36 | 37 | create unique_index(:cards, [:account_id, :code], where: "code IS NOT NULL") 38 | create unique_index(:cards, [:account_id, :owner_id, :owner_type, :fingerprint]) 39 | create index(:cards, [:account_id, :status]) 40 | create index(:cards, [:account_id, :label], where: "label IS NOT NULL") 41 | end 42 | end -------------------------------------------------------------------------------- /priv/repo/migrations/020_create_balance_settings.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateBalanceSettings do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:balance_settings, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :stripe_user_id, :string 10 | add :stripe_livemode, :boolean 11 | add :stripe_access_token, :string 12 | add :stripe_refresh_token, :string 13 | add :stripe_publishable_key, :string 14 | add :stripe_scope, :string 15 | add :stripe_variable_fee_percentage, :decimal, null: false 16 | add :stripe_fixed_fee_cents, :integer, null: false 17 | 18 | add :freshcom_transaction_fee_percentage, :decimal, null: false 19 | 20 | add :country, :string 21 | add :default_currency, :string 22 | 23 | timestamps() 24 | end 25 | 26 | create unique_index(:balance_settings, :account_id) 27 | end 28 | end -------------------------------------------------------------------------------- /priv/repo/migrations/021_create_data_import.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateDataImport do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:data_imports, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :file_id, references(:files, type: :binary_id, on_delete: :nilify_all) 9 | 10 | add :status, :string 11 | add :data_url, :text 12 | add :data_type, :string 13 | 14 | add :data_count, :integer 15 | add :time_spent_seconds, :integer 16 | 17 | timestamps() 18 | end 19 | 20 | create index(:data_imports, [:account_id, :status]) 21 | end 22 | end -------------------------------------------------------------------------------- /priv/repo/migrations/022_create_point_account.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreatePointAccount do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:point_accounts, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :customer_id, references(:customers, type: :binary_id, on_delete: :delete_all), null: false 9 | 10 | add :status, :string 11 | add :balance, :integer, null: false, default: 0 12 | 13 | add :custom_data, :map, null: false, default: "{}" 14 | add :translations, :map, null: false, default: "{}" 15 | 16 | timestamps() 17 | end 18 | 19 | create index(:point_accounts, [:account_id, :customer_id]) 20 | end 21 | end -------------------------------------------------------------------------------- /priv/repo/migrations/024_create_depositable.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateDepositable do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:depositables, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :avatar_id, references(:files, type: :binary_id, on_delete: :nilify_all) 9 | 10 | add :status, :string, null: false 11 | add :code, :string 12 | add :name, :string, null: false 13 | add :label, :string 14 | 15 | add :print_name, :string 16 | add :amount, :integer, null: false 17 | add :gateway, :string, null: false 18 | 19 | add :caption, :string 20 | add :description, :string 21 | add :custom_data, :map, null: false, default: "{}" 22 | add :translations, :map, null: false, default: "{}" 23 | 24 | timestamps() 25 | end 26 | 27 | create unique_index(:depositables, [:account_id, :code], where: "code IS NOT NULL") 28 | create index(:depositables, [:account_id, :status]) 29 | create index(:depositables, [:account_id, :name]) 30 | create index(:depositables, [:account_id, :label], where: "label IS NOT NULL") 31 | end 32 | end -------------------------------------------------------------------------------- /priv/repo/migrations/025_create_product_collections.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateProductCollection do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:product_collections, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :avatar_id, references(:files, type: :binary_id, on_delete: :nilify_all) 9 | 10 | add :status, :string, null: false 11 | add :code, :string 12 | add :name, :string, null: false 13 | add :label, :string 14 | 15 | add :sort_index, :integer, null: false 16 | 17 | add :caption, :string 18 | add :description, :text 19 | add :custom_data, :map, null: false, default: "{}" 20 | add :translations, :map, null: false, default: "{}" 21 | 22 | timestamps() 23 | end 24 | 25 | create unique_index(:product_collections, [:account_id, :code], where: "code IS NOT NULL") 26 | create index(:product_collections, [:account_id, :status]) 27 | create index(:product_collections, [:account_id, :name]) 28 | create index(:product_collections, [:account_id, :label], where: "label IS NOT NULL") 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /priv/repo/migrations/026_create_product_collection_membership.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateProductCollectionMembership do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:product_collection_memberships, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :collection_id, references(:product_collections, on_delete: :delete_all, type: :binary_id) 10 | add :product_id, references(:products, on_delete: :delete_all, type: :binary_id) 11 | 12 | add :sort_index, :integer, null: false 13 | 14 | timestamps() 15 | end 16 | 17 | create unique_index(:product_collection_memberships, [:product_id, :collection_id]) 18 | create index(:product_collection_memberships, [:account_id, :collection_id]) 19 | create index(:product_collection_memberships, [:account_id, :product_id]) 20 | create index(:product_collection_memberships, [:account_id, :sort_index]) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /priv/repo/migrations/027_create_fulfillment_package.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateFulfillmentPackage do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:fulfillment_packages, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :customer_id, references(:customers, type: :binary_id, on_delete: :nilify_all) 10 | add :order_id, references(:orders, type: :binary_id, on_delete: :nilify_all) 11 | 12 | add :system_label, :string 13 | 14 | add :status, :string, null: false 15 | add :code, :string 16 | add :name, :string 17 | add :label, :string 18 | 19 | add :caption, :string 20 | add :description, :text 21 | add :custom_data, :map, null: false, default: "{}" 22 | add :translations, :map, null: false, default: "{}" 23 | 24 | timestamps() 25 | end 26 | 27 | create unique_index(:fulfillment_packages, [:account_id, :code], where: "code IS NOT NULL") 28 | create index(:fulfillment_packages, [:account_id, :system_label]) 29 | create index(:fulfillment_packages, [:account_id, :order_id]) 30 | create index(:fulfillment_packages, [:account_id, :name]) 31 | create index(:fulfillment_packages, [:account_id, :label], where: "label IS NOT NULL") 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /priv/repo/migrations/029_create_email_template.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateEmailTemplate do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:email_templates, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :system_label, :string 9 | add :name, :string, null: false 10 | 11 | add :subject, :string, null: false 12 | add :to, :string 13 | add :reply_to, :string 14 | add :from, :string 15 | add :body_html, :text 16 | add :body_text, :text 17 | add :description, :text 18 | 19 | add :translations, :map, null: false, default: "{}" 20 | 21 | timestamps() 22 | end 23 | 24 | create index(:email_templates, [:account_id, :name]) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /priv/repo/migrations/030_create_notification_trigger.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateNotificationTrigger do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:notification_triggers, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :status, :string, null: false 9 | add :name, :string 10 | add :system_label, :string 11 | 12 | add :event, :string, null: false 13 | add :description, :text 14 | 15 | add :action_target, :string, null: false 16 | add :action_type, :string, null: false 17 | 18 | timestamps() 19 | end 20 | 21 | create index(:notification_triggers, [:account_id, :name]) 22 | create index(:notification_triggers, [:account_id, :status]) 23 | create index(:notification_triggers, [:account_id, :event]) 24 | create index(:notification_triggers, [:account_id, :action_type, :action_target], where: "action_type IS NOT NULL") 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /priv/repo/migrations/031_create_email.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateEmail do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:emails, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :recipient_id, references(:users, type: :binary_id, on_delete: :nilify_all) 10 | add :trigger_id, references(:notification_triggers, type: :binary_id, on_delete: :nilify_all) 11 | add :template_id, references(:email_templates, type: :binary_id, on_delete: :nilify_all) 12 | 13 | add :status, :string, null: false 14 | 15 | add :subject, :text 16 | add :to, :string 17 | add :from, :string 18 | add :reply_to, :string 19 | add :body_html, :text 20 | add :body_text, :text 21 | add :locale, :string 22 | 23 | timestamps() 24 | end 25 | 26 | create index(:emails, [:account_id, :status]) 27 | create index(:emails, [:account_id, :to]) 28 | create index(:emails, [:account_id, :from]) 29 | create index(:emails, [:account_id, :reply_to]) 30 | create index(:emails, [:account_id, :subject]) 31 | create index(:emails, [:account_id, :trigger_id]) 32 | create index(:emails, [:account_id, :template_id]) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /priv/repo/migrations/032_create_return_package.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateReturnPackage do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:return_packages, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :customer_id, references(:customers, type: :binary_id, on_delete: :nilify_all) 10 | add :order_id, references(:orders, type: :binary_id, on_delete: :nilify_all) 11 | 12 | add :system_label, :string 13 | 14 | add :status, :string, null: false 15 | add :code, :string 16 | add :name, :string 17 | add :label, :string 18 | 19 | add :caption, :string 20 | add :description, :text 21 | add :custom_data, :map, null: false, default: "{}" 22 | add :translations, :map, null: false, default: "{}" 23 | 24 | timestamps() 25 | end 26 | 27 | create unique_index(:return_packages, [:account_id, :code], where: "code IS NOT NULL") 28 | create index(:return_packages, [:account_id, :system_label]) 29 | create index(:return_packages, [:account_id, :order_id]) 30 | create index(:return_packages, [:account_id, :name]) 31 | create index(:return_packages, [:account_id, :label], where: "label IS NOT NULL") 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /priv/repo/migrations/034_create_sms_template.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateSMSTemplate do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:sms_templates, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | add :system_label, :string 9 | add :name, :string, null: false 10 | 11 | add :to, :string 12 | add :body, :text 13 | add :description, :text 14 | 15 | add :translations, :map, null: false, default: "{}" 16 | 17 | timestamps() 18 | end 19 | 20 | create index(:sms_templates, [:account_id, :name]) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /priv/repo/migrations/035_create_sms.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreateSMS do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:sms, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :recipient_id, references(:users, type: :binary_id, on_delete: :nilify_all) 10 | add :trigger_id, references(:notification_triggers, type: :binary_id, on_delete: :nilify_all) 11 | add :template_id, references(:sms_templates, type: :binary_id, on_delete: :nilify_all) 12 | 13 | add :status, :string, null: false 14 | 15 | add :to, :string 16 | add :body, :text 17 | add :locale, :string 18 | 19 | timestamps() 20 | end 21 | 22 | create index(:sms, [:account_id, :status]) 23 | create index(:sms, [:account_id, :to]) 24 | create index(:sms, [:account_id, :trigger_id]) 25 | create index(:sms, [:account_id, :template_id]) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /priv/repo/migrations/036_create_phone_verification_code.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.CreatePhoneVerificationCode do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:phone_verification_codes, primary_key: false) do 6 | add :id, :binary_id, primary_key: true 7 | add :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 8 | 9 | add :phone_number, :string, null: false 10 | add :value, :string, null: false 11 | add :expires_at, :utc_datetime, null: false 12 | 13 | timestamps() 14 | end 15 | 16 | create unique_index(:phone_verification_codes, [:account_id, :value, :expires_at]) 17 | create index(:phone_verification_codes, [:account_id, :phone_number]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /priv/repo/migrations/037_add_is_ready_for_live_transaction_to_account.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.AddIsReadyForLiveTransactionToAccount do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table("accounts") do 6 | add :is_ready_for_live_transaction, :boolean, null: false, default: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/038_update_account_id_nullable_for_customer.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.UpdateAccountIdNullableForCustomer do 2 | use Ecto.Migration 3 | 4 | def change do 5 | drop constraint("customers", "customers_account_id_fkey") 6 | 7 | alter table("customers") do 8 | modify :account_id, references(:accounts, type: :binary_id, on_delete: :delete_all), null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/039_add_ppi_to_product.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.AddPpiToProduct do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table("products") do 6 | add :price_proportion_index, :integer, null: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/040_add_is_owner_to_account_membership.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Repo.Migrations.AddIsOwnerToAccountMembership do 2 | use Ecto.Migration 3 | 4 | def change do 5 | alter table("account_memberships") do 6 | add :is_owner, :boolean, null: false, default: false 7 | end 8 | 9 | flush() 10 | BlueJet.Repo.update_all(BlueJet.Identity.AccountMembership, set: [is_owner: true]) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /priv/repo/seeds.exs: -------------------------------------------------------------------------------- 1 | # Script for populating the database. You can run it as: 2 | # 3 | # mix run priv/repo/seeds.exs 4 | # 5 | # Inside the script, you can read and write to any of your 6 | # repositories directly: 7 | # 8 | # BlueJet.Repo.insert!(%BlueJet.SomeModel{}) 9 | # 10 | # We recommend using the bang functions (`insert!`, `update!` 11 | # and so on) as they will fail if something goes wrong. 12 | -------------------------------------------------------------------------------- /test/blue_jet/data_trading/data_trading_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.DataTradingTest do 2 | use BlueJet.ContextCase 3 | 4 | alias BlueJet.Identity.{Account, User} 5 | alias BlueJet.DataTrading 6 | alias BlueJet.DataTrading.ServiceMock 7 | alias BlueJet.DataTrading.DataImport 8 | 9 | describe "create_data_import/1" do 10 | test "when role is not authorized" do 11 | request = %ContextRequest{ 12 | account: %Account{}, 13 | user: %User{}, 14 | role: "customer" 15 | } 16 | 17 | {:error, :access_denied} = DataTrading.create_data_import(request) 18 | end 19 | 20 | test "when request is valid" do 21 | account = %Account{} 22 | request = %ContextRequest{ 23 | account: account, 24 | user: %User{}, 25 | role: "administrator", 26 | fields: %{ "data_url" => Faker.Internet.url() } 27 | } 28 | 29 | ServiceMock 30 | |> expect(:create_data_import, fn(fields, opts) -> 31 | assert fields == request.fields 32 | assert opts[:account] == account 33 | 34 | {:ok, %DataImport{}} 35 | end) 36 | 37 | {:ok, _} = DataTrading.create_data_import(request) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/blue_jet/file_storage/file_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.FileTest do 2 | use BlueJet.DataCase 3 | 4 | alias BlueJet.FileStorage.File 5 | 6 | test "writable_fields/0" do 7 | assert File.writable_fields() == [ 8 | :status, 9 | :code, 10 | :name, 11 | :label, 12 | :content_type, 13 | :size_bytes, 14 | :public_readable, 15 | :version_name, 16 | :version_label, 17 | :caption, 18 | :description, 19 | :custom_data 20 | ] 21 | end 22 | 23 | describe "validate/1" do 24 | test "when missing required fields" do 25 | changeset = 26 | change(%File{}, %{}) 27 | |> File.validate() 28 | 29 | refute changeset.valid? 30 | assert Keyword.keys(changeset.errors) == [:name, :content_type, :size_bytes] 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/blue_jet/identity/jwt_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Identity.JwtTest do 2 | use BlueJet.DataCase 3 | 4 | alias BlueJet.Identity.Jwt 5 | 6 | test "sign_token/1" do 7 | assert Jwt.sign_token(%{"data" => "value"}) 8 | end 9 | 10 | describe "verify_token/1" do 11 | test "when given invalid token" do 12 | {valid, _} = Jwt.verify_token("test") 13 | 14 | refute valid 15 | end 16 | 17 | test "when given valid token" do 18 | signed = Jwt.sign_token(%{"data" => "value"}) 19 | {valid, claims} = Jwt.verify_token(signed) 20 | 21 | assert valid 22 | assert claims["data"] == "value" 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /test/blue_jet/notification/trigger_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.Notification.TriggerTest do 2 | use BlueJet.DataCase 3 | 4 | alias BlueJet.Identity.Account 5 | alias BlueJet.Notification.Trigger 6 | 7 | describe "schema" do 8 | test "when account is deleted trigger should be automatically deleted" do 9 | account = Repo.insert!(%Account{}) 10 | trigger = Repo.insert!(%Trigger{ 11 | account_id: account.id, 12 | name: Faker.Lorem.sentence(5), 13 | event: "test", 14 | action_type: "webhook", 15 | action_target: Faker.Internet.url() 16 | }) 17 | Repo.delete!(account) 18 | 19 | refute Repo.get(Trigger, trigger.id) 20 | end 21 | end 22 | 23 | test "writable_fields/0" do 24 | assert Trigger.writable_fields() == [ 25 | :status, 26 | :name, 27 | :event, 28 | :description, 29 | :action_target, 30 | :action_type 31 | ] 32 | end 33 | 34 | describe "validate/1" do 35 | test "when missing required fields" do 36 | changeset = 37 | change(%Trigger{ }, %{}) 38 | |> Trigger.validate() 39 | 40 | refute changeset.valid? 41 | assert Keyword.keys(changeset.errors) == [:name, :event, :action_type, :action_target] 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/blue_jet/support/channel_case.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ChannelCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | channel tests. 5 | 6 | Such tests rely on `Phoenix.ChannelTest` and also 7 | import other functionality to make it easier 8 | to build common datastructures and query the data layer. 9 | 10 | Finally, if the test case interacts with the database, 11 | it cannot be async. For this reason, every test runs 12 | inside a transaction which is reset at the beginning 13 | of the test unless the test case is marked as async. 14 | """ 15 | 16 | use ExUnit.CaseTemplate 17 | 18 | using do 19 | quote do 20 | # Import conveniences for testing with channels 21 | use Phoenix.ChannelTest 22 | 23 | # The default endpoint for testing 24 | @endpoint BlueJetWeb.Endpoint 25 | end 26 | end 27 | 28 | 29 | setup tags do 30 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(BlueJet.Repo) 31 | unless tags[:async] do 32 | Ecto.Adapters.SQL.Sandbox.mode(BlueJet.Repo, {:shared, self()}) 33 | end 34 | :ok 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /test/blue_jet/support/event_handler_case.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.EventHandlerCase do 2 | use ExUnit.CaseTemplate 3 | 4 | using do 5 | quote do 6 | alias BlueJet.Repo 7 | 8 | import Mox 9 | import BlueJet.EventHandlerCase 10 | 11 | setup :verify_on_exit! 12 | end 13 | end 14 | 15 | setup tags do 16 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(BlueJet.Repo) 17 | 18 | unless tags[:async] do 19 | Ecto.Adapters.SQL.Sandbox.mode(BlueJet.Repo, {:shared, self()}) 20 | end 21 | 22 | :ok 23 | end 24 | 25 | @doc """ 26 | A helper that transform changeset errors to a map of messages. 27 | 28 | assert {:error, changeset} = Accounts.create_user(%{password: "short"}) 29 | assert "password is too short" in errors_on(changeset).password 30 | assert %{password: ["password is too short"]} = errors_on(changeset) 31 | 32 | """ 33 | def errors_on(changeset) do 34 | Ecto.Changeset.traverse_errors(changeset, fn {message, opts} -> 35 | Enum.reduce(opts, message, fn {key, value}, acc -> 36 | String.replace(acc, "%{#{key}}", to_string(value)) 37 | end) 38 | end) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/blue_jet/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | 3 | Ecto.Adapters.SQL.Sandbox.mode(BlueJet.Repo, :manual) 4 | 5 | -------------------------------------------------------------------------------- /test/blue_jet_web/controllers/identity/account_reset_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.AccountResetControllerTest do 2 | use BlueJetWeb.ConnCase 3 | 4 | setup do 5 | conn = 6 | build_conn() 7 | |> put_req_header("accept", "application/vnd.api+json") 8 | |> put_req_header("content-type", "application/vnd.api+json") 9 | 10 | %{conn: conn} 11 | end 12 | 13 | # Create an account reset (internal) 14 | describe "POST /v1/account_resets" do 15 | test "without UAT", %{conn: conn} do 16 | conn = post(conn, "/v1/account_resets", %{ 17 | "data" => %{ 18 | "type" => "AccountReset" 19 | } 20 | }) 21 | 22 | assert conn.status == 401 23 | end 24 | 25 | # TODO: This test causes error due to account reset uses 26 | # seperate process for deletion, find a better way to 27 | # test this 28 | # test "with UAT", %{conn: conn} do 29 | # standard_user = create_standard_user() 30 | # uat = get_uat(standard_user, mode: :test) 31 | 32 | # conn = put_req_header(conn, "authorization", "Bearer #{uat}") 33 | # conn = post(conn, "/v1/account_resets", %{ 34 | # "data" => %{ 35 | # "type" => "AccountReset" 36 | # } 37 | # }) 38 | 39 | # assert conn.status == 202 40 | # end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /test/blue_jet_web/controllers/identity/email_verification_token_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.EmailVerificationTokenControllerTest do 2 | use BlueJetWeb.ConnCase 3 | 4 | import BlueJet.Identity.TestHelper 5 | 6 | setup do 7 | conn = 8 | build_conn() 9 | |> put_req_header("accept", "application/vnd.api+json") 10 | |> put_req_header("content-type", "application/vnd.api+json") 11 | 12 | %{conn: conn} 13 | end 14 | 15 | # Create a email verification token 16 | describe "POST /v1/email_verification_tokens" do 17 | test "without UAT", %{conn: conn} do 18 | conn = post(conn, "/v1/email_verification_tokens", %{ 19 | "data" => %{ 20 | "type" => "EmailVerificationToken" 21 | } 22 | }) 23 | 24 | assert conn.status == 401 25 | end 26 | 27 | test "with UAT", %{conn: conn} do 28 | user = standard_user_fixture() 29 | uat = get_uat(user.default_account, user) 30 | 31 | conn = put_req_header(conn, "authorization", "Bearer #{uat}") 32 | conn = post(conn, "/v1/email_verification_tokens", %{ 33 | "data" => %{ 34 | "type" => "EmailVerificationToken", 35 | "relationships" => %{ 36 | "user" => %{ 37 | "data" => %{ 38 | "id" => user.id, 39 | "type" => "User" 40 | } 41 | } 42 | } 43 | } 44 | }) 45 | 46 | assert conn.status == 204 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/blue_jet_web/controllers/identity/phone_verification_code_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.PhoneVerificationCodeControllerTest do 2 | use BlueJetWeb.ConnCase 3 | 4 | import BlueJet.Identity.TestHelper 5 | 6 | setup do 7 | conn = 8 | build_conn() 9 | |> put_req_header("accept", "application/vnd.api+json") 10 | |> put_req_header("content-type", "application/vnd.api+json") 11 | 12 | %{conn: conn} 13 | end 14 | 15 | # Create a phone verification code 16 | describe "POST /v1/phone_verification_codes" do 17 | test "without access token", %{conn: conn} do 18 | conn = post(conn, "/v1/phone_verification_codes", %{ 19 | "data" => %{ 20 | "type" => "PhoneVerificationCode" 21 | } 22 | }) 23 | 24 | assert conn.status == 401 25 | end 26 | 27 | test "with PAT", %{conn: conn} do 28 | user = standard_user_fixture() 29 | pat = get_pat(user.default_account) 30 | 31 | conn = put_req_header(conn, "authorization", "Bearer #{pat}") 32 | conn = post(conn, "/v1/phone_verification_codes", %{ 33 | "data" => %{ 34 | "type" => "PhoneVerificationCode", 35 | "attributes" => %{ 36 | "phoneNumber" => "+11234567890" 37 | } 38 | } 39 | }) 40 | 41 | assert conn.status == 204 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/blue_jet_web/controllers/identity/refresh_token_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.RefreshTokenControllerTest do 2 | use BlueJetWeb.ConnCase 3 | 4 | import BlueJet.Identity.TestHelper 5 | 6 | setup do 7 | conn = 8 | build_conn() 9 | |> put_req_header("accept", "application/vnd.api+json") 10 | |> put_req_header("content-type", "application/vnd.api+json") 11 | 12 | %{conn: conn} 13 | end 14 | 15 | # Retrieve the publishable refresh token (internal) 16 | describe "GET /v1/refresh_token" do 17 | test "without UAT", %{conn: conn} do 18 | conn = get(conn, "/v1/refresh_token") 19 | 20 | assert conn.status == 401 21 | end 22 | 23 | test "with UAT", %{conn: conn} do 24 | user = standard_user_fixture() 25 | uat = get_uat(user.default_account, user) 26 | 27 | conn = put_req_header(conn, "authorization", "Bearer #{uat}") 28 | conn = get(conn, "/v1/refresh_token") 29 | 30 | response = json_response(conn, 200) 31 | assert response["data"]["attributes"]["prefixedId"] 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/blue_jet_web/support/conn_case.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJetWeb.ConnCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | tests that require setting up a connection. 5 | 6 | Such tests rely on `Phoenix.ConnTest` and also 7 | import other functionality to make it easier 8 | to build and query models. 9 | 10 | Finally, if the test case interacts with the database, 11 | it cannot be async. For this reason, every test runs 12 | inside a transaction which is reset at the beginning 13 | of the test unless the test case is marked as async. 14 | """ 15 | 16 | use ExUnit.CaseTemplate 17 | 18 | using do 19 | quote do 20 | # Import conveniences for testing with connections 21 | use Phoenix.ConnTest 22 | import BlueJetWeb.Router.Helpers 23 | 24 | # The default endpoint for testing 25 | @endpoint BlueJetWeb.Endpoint 26 | ###### 27 | 28 | import Ecto 29 | import Ecto.Changeset 30 | import Ecto.Query 31 | 32 | alias Ecto.UUID 33 | alias BlueJet.Repo 34 | alias BlueJet.ContextRequest 35 | alias BlueJet.CreateRequest 36 | alias BlueJet.Identity 37 | end 38 | end 39 | 40 | setup tags do 41 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(BlueJet.Repo) 42 | unless tags[:async] do 43 | Ecto.Adapters.SQL.Sandbox.mode(BlueJet.Repo, {:shared, self()}) 44 | end 45 | {:ok, conn: Phoenix.ConnTest.build_conn()} 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/blue_jet_web/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | 3 | Ecto.Adapters.SQL.Sandbox.mode(BlueJet.Repo, :manual) 4 | 5 | -------------------------------------------------------------------------------- /test/support/crm_helper.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.CRM.TestHelper do 2 | alias BlueJet.CRM.Service 3 | import BlueJet.Utils, only: [stringify_keys: 1] 4 | 5 | def customer_fixture(account, fields \\ %{}) do 6 | default_fields = %{ 7 | name: Faker.Name.name(), 8 | email: Faker.Internet.safe_email(), 9 | password: "test1234" 10 | } 11 | fields = 12 | default_fields 13 | |> Map.merge(fields) 14 | |> stringify_keys() 15 | 16 | {:ok, customer} = Service.create_customer(fields, %{account: account, skip_dispatch: true}) 17 | 18 | customer 19 | end 20 | 21 | def point_transaction_fixture(account, point_account, fields \\ %{}) do 22 | default_fields = %{ 23 | amount: System.unique_integer([:positive]), 24 | point_account_id: point_account.id 25 | } 26 | fields = 27 | default_fields 28 | |> Map.merge(fields) 29 | |> stringify_keys() 30 | 31 | {:ok, transaction} = Service.create_point_transaction(fields, %{account: account}) 32 | 33 | transaction 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/support/file_storage_helper.ex: -------------------------------------------------------------------------------- 1 | defmodule BlueJet.FileStorage.TestHelper do 2 | alias BlueJet.FileStorage.Service 3 | 4 | def file_fixture(account, fields \\ %{}) do 5 | default_fields = %{ 6 | name: Faker.File.file_name(), 7 | content_type: Faker.File.mime_type(), 8 | size_bytes: System.unique_integer([:positive]) 9 | } 10 | fields = Map.merge(default_fields, fields) 11 | 12 | {:ok, file} = Service.create_file(fields, %{account: account}) 13 | 14 | file 15 | end 16 | 17 | def file_collection_fixture(account, fields \\ %{}) do 18 | default_fields = %{name: Faker.Commerce.product_name()} 19 | fields = Map.merge(default_fields, fields) 20 | 21 | {:ok, collection} = Service.create_file_collection(fields, %{account: account}) 22 | 23 | collection 24 | end 25 | 26 | def file_collection_membership_fixture(account, collection, file, fields \\ %{}) do 27 | default_fields = %{ 28 | collection_id: collection.id, 29 | file_id: file.id 30 | } 31 | fields = Map.merge(default_fields, fields) 32 | 33 | {:ok, membership} = Service.create_file_collection_membership(fields, %{account: account}) 34 | 35 | membership 36 | end 37 | end 38 | --------------------------------------------------------------------------------