├── .gitignore ├── .rspec ├── .ruby-version ├── .tool-versions ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── admin │ │ └── v1 │ │ │ ├── api_controller.rb │ │ │ ├── categories_controller.rb │ │ │ ├── coupons_controller.rb │ │ │ ├── dashboard │ │ │ ├── dashboard_controller.rb │ │ │ ├── sales_ranges_controller.rb │ │ │ ├── summaries_controller.rb │ │ │ └── top_five_products_controller.rb │ │ │ ├── home_controller.rb │ │ │ ├── licenses_controller.rb │ │ │ ├── orders_controller.rb │ │ │ ├── products_controller.rb │ │ │ ├── system_requirements_controller.rb │ │ │ └── users_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ ├── .keep │ │ ├── authenticatable.rb │ │ ├── simple_error_renderable.rb │ │ └── static_token_authenticatable.rb │ ├── juno │ │ └── v1 │ │ │ └── payment_confirmations_controller.rb │ └── storefront │ │ └── v1 │ │ ├── api_controller.rb │ │ ├── categories_controller.rb │ │ ├── checkouts_controller.rb │ │ ├── coupon_validations_controller.rb │ │ ├── games_controller.rb │ │ ├── home_controller.rb │ │ ├── orders_controller.rb │ │ ├── products_controller.rb │ │ └── wish_items_controller.rb ├── jobs │ ├── admin │ │ ├── alocate_license_job.rb │ │ └── finish_delivered_orders_job.rb │ ├── application_job.rb │ └── juno │ │ └── charge_creation_job.rb ├── mailers │ ├── application_mailer.rb │ ├── checkout_mailer.rb │ └── license_mailer.rb ├── models │ ├── address.rb │ ├── application_record.rb │ ├── category.rb │ ├── concerns │ │ ├── .keep │ │ ├── like_searchable.rb │ │ └── paginatable.rb │ ├── coupon.rb │ ├── game.rb │ ├── juno.rb │ ├── juno │ │ ├── charge.rb │ │ └── credit_card_payment.rb │ ├── license.rb │ ├── line_item.rb │ ├── order.rb │ ├── product.rb │ ├── product_category.rb │ ├── system_requirement.rb │ ├── user.rb │ └── wish_item.rb ├── services │ ├── admin │ │ ├── alocate_licenses_service.rb │ │ ├── dashboard │ │ │ ├── sales_range_service.rb │ │ │ ├── summary_service.rb │ │ │ └── top_five_products_service.rb │ │ ├── finish_delivered_orders_service.rb │ │ ├── model_loading_service.rb │ │ └── product_saving_service.rb │ ├── juno │ │ └── charge_creation_service.rb │ └── storefront │ │ ├── checkout_processor_service.rb │ │ ├── home_loader_service.rb │ │ └── products_filter_service.rb ├── validators │ ├── cpf_cnpj_validator.rb │ └── future_date_validator.rb └── views │ ├── admin │ └── v1 │ │ ├── categories │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ │ ├── coupons │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ │ ├── dashboard │ │ ├── sales_ranges │ │ │ └── index.json.jbuilder │ │ ├── summaries │ │ │ └── index.json.jbuilder │ │ └── top_five_products │ │ │ └── index.json.jbuilder │ │ ├── games │ │ └── _game.json.jbuilder │ │ ├── licenses │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ │ ├── orders │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ │ ├── products │ │ ├── _product.json.jbuilder │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ │ ├── system_requirements │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ │ └── users │ │ ├── index.json.jbuilder │ │ └── show.json.jbuilder │ ├── checkout_mailer │ ├── generic_error.html.erb │ ├── payment_error.html.erb │ └── success.html.erb │ ├── devise │ └── mailer │ │ └── reset_password_instructions.html.erb │ ├── layouts │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── license_mailer │ └── send_license.html.erb │ ├── shared │ ├── _pagination.json.jbuilder │ └── _simple_error.json.jbuilder │ └── storefront │ └── v1 │ ├── categories │ └── index.json.jbuilder │ ├── checkouts │ └── show.json.jbuilder │ ├── coupon_validations │ └── show.json.jbuilder │ ├── games │ ├── _game.json.jbuilder │ └── index.json.jbuilder │ ├── home │ └── index.json.jbuilder │ ├── orders │ ├── index.json.jbuilder │ └── show.json.jbuilder │ ├── products │ ├── _product.json.jbuilder │ ├── index.json.jbuilder │ └── show.json.jbuilder │ └── wish_items │ ├── _wish_item.json.jbuilder │ ├── index.json.jbuilder │ └── show.json.jbuilder ├── bin ├── bundle ├── rails ├── rake ├── setup └── spring ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── credentials │ ├── development.yml.enc │ └── test.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── backtrace_silencers.rb │ ├── cors.rb │ ├── devise.rb │ ├── devise_token_auth.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ └── wrap_parameters.rb ├── locales │ └── pt-BR │ │ ├── active_record.yml │ │ ├── controllers │ │ └── coupon_validations.yml │ │ ├── custom_validations.yml │ │ ├── devise.yml │ │ ├── mailers │ │ ├── checkout_mailer.yml │ │ └── license_mailer.yml │ │ ├── models │ │ └── wish_item.yml │ │ └── services │ │ └── checkout_processor_service.yml ├── puma.rb ├── routes.rb ├── sidekiq.yml ├── spring.rb └── storage.yml ├── db ├── migrate │ ├── 20200920200639_devise_token_auth_create_users.rb │ ├── 20200927152708_create_categories.rb │ ├── 20200927161952_create_products.rb │ ├── 20200927162631_create_product_categories.rb │ ├── 20200927171715_create_system_requirements.rb │ ├── 20200927173409_create_games.rb │ ├── 20200927192509_create_coupons.rb │ ├── 20201018131945_create_active_storage_tables.active_storage.rb │ ├── 20201122234724_add_status_to_products.rb │ ├── 20210107010752_create_licenses.rb │ ├── 20210110175644_create_wish_items.rb │ ├── 20210114010037_add_featured_to_products.rb │ ├── 20210207154531_create_orders.rb │ ├── 20210209120441_create_line_items.rb │ ├── 20210306190339_create_juno_charges.rb │ ├── 20210306200906_create_juno_credit_card_payments.rb │ ├── 20210314134141_add_line_item_reference_to_licenses.rb │ └── 20210315110855_add_status_to_line_items.rb ├── schema.rb └── seeds.rb ├── lib ├── juno_api │ ├── auth.rb │ ├── charge.rb │ ├── credit_card_payment.rb │ └── request_error.rb ├── middlewares │ └── static_token_auth.rb └── tasks │ ├── .keep │ └── dev │ └── prime.rake ├── log └── .keep ├── public └── robots.txt ├── snippets ├── 1_iniciando_o_projeto.md └── 2_configurando_autenticacao.md ├── spec ├── factories │ ├── addresses.rb │ ├── categories.rb │ ├── coupons.rb │ ├── games.rb │ ├── juno │ │ ├── charges.rb │ │ └── credit_card_payments.rb │ ├── licenses.rb │ ├── line_items.rb │ ├── orders.rb │ ├── product_categories.rb │ ├── products.rb │ ├── system_requirements.rb │ ├── users.rb │ └── wish_items.rb ├── libs │ ├── juno_api │ │ ├── auth_spec.rb │ │ ├── charge_spec.rb │ │ └── credit_card_payment_spec.rb │ └── middlewares │ │ └── static_token_auth_spec.rb ├── models │ ├── address_spec.rb │ ├── category_spec.rb │ ├── coupon_spec.rb │ ├── game_spec.rb │ ├── juno │ │ ├── charge_spec.rb │ │ └── credit_card_payment_spec.rb │ ├── license_spec.rb │ ├── line_item_spec.rb │ ├── order_spec.rb │ ├── product_category_spec.rb │ ├── product_spec.rb │ ├── system_requirement_spec.rb │ ├── user_spec.rb │ └── wish_item_spec.rb ├── rails_helper.rb ├── requests │ ├── admin │ │ └── v1 │ │ │ ├── categories │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unautheticated_requests_spec.rb │ │ │ ├── coupons │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unautheticated_requests_spec.rb │ │ │ ├── dashboard │ │ │ ├── sales_ranges │ │ │ │ ├── admin_requests_spec.rb │ │ │ │ ├── client_requests_spec.rb │ │ │ │ └── unautheticated_requests_spec.rb │ │ │ ├── summaries │ │ │ │ ├── admin_requests_spec.rb │ │ │ │ ├── client_requests_spec.rb │ │ │ │ └── unautheticated_requests_spec.rb │ │ │ └── top_five_products │ │ │ │ ├── admin_requests_spec.rb │ │ │ │ ├── client_requests_spec.rb │ │ │ │ └── unautheticated_requests_spec.rb │ │ │ ├── home_spec.rb │ │ │ ├── licenses │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unauthenticated_requests_spec.rb │ │ │ ├── orders │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unautheticated_requests_spec.rb │ │ │ ├── products │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unautheticated_requests_spec.rb │ │ │ ├── system_requirements │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unautheticated_requests_spec.rb │ │ │ └── users │ │ │ ├── admin_requests_spec.rb │ │ │ ├── client_requests_spec.rb │ │ │ └── unautheticated_requests_spec.rb │ ├── auth │ │ └── v1 │ │ │ ├── sign_in_spec.rb │ │ │ ├── sign_out_spec.rb │ │ │ └── sign_up_spec.rb │ ├── juno │ │ └── v1 │ │ │ └── payment_confirmations │ │ │ ├── authenticated_requests_spec.rb │ │ │ └── unauthenticated_requests_spec.rb │ └── storefront │ │ └── v1 │ │ ├── categories │ │ └── unathenticated_requests_spec.rb │ │ ├── checkouts │ │ ├── authenticated_request_spec.rb │ │ └── unautheticated_requests_spec.rb │ │ ├── coupon_validations │ │ ├── authenticated_requests_spec.rb │ │ └── unautheticated_requests_spec.rb │ │ ├── games │ │ ├── authenticated_requests_spec.rb │ │ └── unautheticated_requests_spec.rb │ │ ├── home │ │ └── unauthenticated_requests_spec.rb │ │ ├── orders │ │ ├── authenticated_requests_spec.rb │ │ └── unautheticated_requests_spec.rb │ │ ├── products │ │ └── unauthenticated_requests_spec.rb │ │ └── wish_items │ │ ├── authenticated_requests_spec.rb │ │ └── unautheticated_requests_spec.rb ├── services │ ├── admin │ │ ├── alocate_licenses_service_spec.rb │ │ ├── dashboard │ │ │ ├── sales_range_service_spec.rb │ │ │ ├── summary_service_spec.rb │ │ │ └── top_five_products_service_spec.rb │ │ ├── finish_delivered_orders_service_spec.rb │ │ ├── model_loading_service_spec.rb │ │ └── product_saving_service_spec.rb │ ├── juno │ │ └── charge_creation_service_spec.rb │ └── storefront │ │ ├── checkout_processor_service_spec.rb │ │ ├── home_loader_service_spec.rb │ │ └── products_filter_service_spec.rb ├── shared_examples │ ├── forbidden_access_example.rb │ ├── like_searchable_concern_example.rb │ ├── paginatable_concern_examples.rb │ ├── pagination_meta_attributes_examples.rb │ ├── sign_in_examples.rb │ └── unauthenticated_access_example.rb ├── spec_helper.rb ├── support │ ├── factory_bot.rb │ ├── images │ │ └── product_image.png │ ├── request_api.rb │ ├── shoulda_matchers.rb │ └── time_helpers.rb └── validators │ ├── cpf_cnpj_validator_spec.rb │ └── future_date_validator_spec.rb ├── storage └── .keep ├── tmp ├── .keep └── pids │ └── .keep └── vendor └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore pidfiles, but keep the directory. 17 | /tmp/pids/* 18 | !/tmp/pids/ 19 | !/tmp/pids/.keep 20 | 21 | # Ignore uploaded files in development. 22 | /storage/* 23 | !/storage/.keep 24 | .byebug_history 25 | 26 | # Ignore master key for decrypting credentials and more. 27 | /config/master.key 28 | 29 | /config/credentials/development.key 30 | 31 | /config/credentials/test.key 32 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.7.1 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 2.7.1 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby '2.7.1' 5 | 6 | gem 'rails', '~> 6.0.3', '>= 6.0.3.3' 7 | 8 | # Basic 9 | gem 'bootsnap', '>= 1.4.2', require: false 10 | gem 'pg', '>= 0.18', '< 2.0' 11 | gem 'puma', '~> 4.1' 12 | 13 | # Auth 14 | gem 'devise_token_auth', '~> 1.1.4' 15 | 16 | # CORS 17 | gem 'rack-cors', '~> 1.1.1' 18 | 19 | # Validation 20 | gem 'cpf_cnpj', '~> 0.5.0' 21 | 22 | # Delayed Jobs 23 | gem "sidekiq", '~> 6.1.3' 24 | gem "sidekiq-scheduler", '~> 3.0.1' 25 | 26 | # Rendering 27 | gem 'jbuilder', '~> 2.10.1' 28 | 29 | # HTTP Request 30 | gem 'httparty', '~> 0.18.1' 31 | 32 | group :development, :test do 33 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 34 | gem 'factory_bot_rails' 35 | gem 'faker' 36 | gem 'rspec-rails', '~> 4.0.1' 37 | gem 'shoulda-matchers', '~> 4.0' 38 | end 39 | 40 | group :development do 41 | gem 'listen', '~> 3.2' 42 | gem 'spring' 43 | gem 'spring-watcher-listen', '~> 2.0.0' 44 | end 45 | 46 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 47 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/api_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class ApiController < ApplicationController 3 | class ForbiddenAccess < StandardError; end 4 | 5 | include Authenticatable 6 | 7 | include SimpleErrorRenderable 8 | self.simple_error_partial = "shared/simple_error" 9 | 10 | rescue_from ForbiddenAccess do 11 | render_error(message: "Forbidden access", status: :forbidden) 12 | end 13 | 14 | before_action :restrict_access_for_admin! 15 | 16 | private 17 | 18 | def restrict_access_for_admin! 19 | raise ForbiddenAccess unless current_user.admin? 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/categories_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class CategoriesController < ApiController 3 | before_action :load_category, only: [:show, :update, :destroy] 4 | 5 | def index 6 | @loading_service = Admin::ModelLoadingService.new(Category.all, searchable_params) 7 | @loading_service.call 8 | end 9 | 10 | def create 11 | @category = Category.new 12 | @category.attributes = category_params 13 | save_category! 14 | end 15 | 16 | def show; end 17 | 18 | def update 19 | @category.attributes = category_params 20 | save_category! 21 | end 22 | 23 | def destroy 24 | @category.destroy! 25 | rescue 26 | render_error(fields: @category.errors.messages) 27 | end 28 | 29 | private 30 | 31 | def load_category 32 | @category = Category.find(params[:id]) 33 | end 34 | 35 | def searchable_params 36 | params.permit({ search: :name }, { order: {} }, :page, :length) 37 | end 38 | 39 | def category_params 40 | return {} unless params.has_key?(:category) 41 | params.require(:category).permit(:id, :name) 42 | end 43 | 44 | def save_category! 45 | @category.save! 46 | render :show 47 | rescue 48 | render_error(fields: @category.errors.messages) 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/coupons_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class CouponsController < ApiController 3 | before_action :load_coupon, only: [:show, :update, :destroy] 4 | 5 | def index 6 | permitted = params.permit({ search: :name }, { order: {} }, :page, :length) 7 | @loading_service = Admin::ModelLoadingService.new(Coupon.all, permitted) 8 | @loading_service.call 9 | end 10 | 11 | def create 12 | @coupon = Coupon.new 13 | @coupon.attributes = coupon_params 14 | save_coupon! 15 | end 16 | 17 | 18 | def show; end 19 | 20 | def update 21 | @coupon.attributes = coupon_params 22 | save_coupon! 23 | end 24 | 25 | def destroy 26 | @coupon.destroy! 27 | rescue 28 | render_error(fields: @coupon.errors.messages) 29 | end 30 | 31 | private 32 | 33 | def load_coupon 34 | @coupon = Coupon.find(params[:id]) 35 | end 36 | 37 | def coupon_params 38 | return {} unless params.has_key?(:coupon) 39 | params.require(:coupon).permit(:id, :name, :code, :status, :discount_value, :max_use, :due_date) 40 | end 41 | 42 | def save_coupon! 43 | @coupon.save! 44 | render :show 45 | rescue 46 | render_error(fields: @coupon.errors.messages) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/dashboard/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1::Dashboard 2 | class DashboardController < Admin::V1::ApiController 3 | def get_date(date) 4 | Time.parse(params[date], "%Y-%m-%d") 5 | rescue 6 | nil 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/controllers/admin/v1/dashboard/sales_ranges_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1::Dashboard 2 | class SalesRangesController < DashboardController 3 | def index 4 | @service = Admin::Dashboard::SalesRangeService.new(min: get_date(:min_date), max: get_date(:max_date)) 5 | @service.call 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/controllers/admin/v1/dashboard/summaries_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1::Dashboard 2 | class SummariesController < DashboardController 3 | def index 4 | @service = Admin::Dashboard::SummaryService.new(min: get_date(:min_date), max: get_date(:max_date)) 5 | @service.call 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/controllers/admin/v1/dashboard/top_five_products_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1::Dashboard 2 | class TopFiveProductsController < DashboardController 3 | def index 4 | @service = Admin::Dashboard::TopFiveProductsService.new(min: get_date(:min_date), max: get_date(:max_date)) 5 | @service.call 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/controllers/admin/v1/home_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class HomeController < ApiController 3 | def index 4 | render json: { message: "Uhul!" } 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /app/controllers/admin/v1/licenses_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class LicensesController < ApiController 3 | before_action :load_license, only: [:show, :update, :destroy] 4 | 5 | def index 6 | game_licenses = License.where(game_id: params[:game_id]) 7 | @loading_service = Admin::ModelLoadingService.new(game_licenses, searchable_params) 8 | @loading_service.call 9 | end 10 | 11 | def create 12 | @license = License.new(game_id: params[:game_id]) 13 | @license.attributes = license_params 14 | save_license! 15 | end 16 | 17 | def show; end 18 | 19 | def update 20 | @license.attributes = license_params 21 | save_license! 22 | end 23 | 24 | def destroy 25 | @license.destroy! 26 | rescue 27 | render_error(fields: @license.errors.messages) 28 | end 29 | 30 | private 31 | 32 | def load_license 33 | @license = License.find(params[:id]) 34 | end 35 | 36 | def searchable_params 37 | params.permit({ search: :key }, { order: {} }, :page, :length) 38 | end 39 | 40 | def license_params 41 | return {} unless params.has_key?(:license) 42 | params.require(:license).permit(:id, :key, :platform, :status) 43 | end 44 | 45 | def save_license! 46 | @license.save! 47 | render :show 48 | rescue 49 | render_error(fields: @license.errors.messages) 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/orders_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class OrdersController < ApiController 3 | def index 4 | @loading_service = Admin::ModelLoadingService.new(Order.all, searchable_params) 5 | @loading_service.call 6 | end 7 | 8 | def show 9 | @order = Order.find(params[:id]) 10 | end 11 | 12 | private 13 | 14 | def searchable_params 15 | params.permit({ order: {} }, :page, :length) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/products_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class ProductsController < ApiController 3 | before_action :load_product, only: %i(show update destroy) 4 | 5 | def index 6 | @loading_service = Admin::ModelLoadingService.new(Product.all, searchable_params) 7 | @loading_service.call 8 | end 9 | 10 | def create 11 | run_service 12 | rescue Admin::ProductSavingService::NotSavedProductError 13 | render_error(fields: @saving_service.errors) 14 | end 15 | 16 | def show; end 17 | 18 | def update 19 | run_service 20 | rescue Admin::ProductSavingService::NotSavedProductError 21 | render_error(fields: @saving_service.errors) 22 | end 23 | 24 | def destroy 25 | @product.productable.destroy! 26 | @product.destroy! 27 | rescue ActiveRecord::RecordNotDestroyed 28 | render_error(fields: @product.errors.messages.merge(@product.productable.errors.messages)) 29 | end 30 | 31 | private 32 | 33 | def load_product 34 | @product = Product.find(params[:id]) 35 | end 36 | 37 | def searchable_params 38 | params.permit({ search: :name }, { order: {} }, :page, :length) 39 | end 40 | 41 | def run_service 42 | @saving_service = Admin::ProductSavingService.new(product_params.to_h, @product) 43 | @saving_service.call 44 | @product = @saving_service.product 45 | render :show 46 | end 47 | 48 | def product_params 49 | return {} unless params.has_key?(:product) 50 | permitted_params = params.require(:product).permit(:id, :name, :description, :image, :price, :productable, 51 | :status, :featured, category_ids: []) 52 | permitted_params.merge(productable_params) 53 | end 54 | 55 | def productable_params 56 | productable_type = params[:product][:productable] || @product&.productable_type&.underscore 57 | return unless productable_type.present? 58 | productable_attributes = send("#{productable_type}_params") 59 | { productable_attributes: productable_attributes } 60 | end 61 | 62 | def game_params 63 | params.require(:product).permit(:mode, :release_date, :developer, :system_requirement_id) 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/system_requirements_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class SystemRequirementsController < ApiController 3 | before_action :load_system_requirement, only: [:show, :update, :destroy] 4 | 5 | def index 6 | permitted = params.permit({ search: :name }, { order: {} }, :page, :length) 7 | @loading_service = Admin::ModelLoadingService.new(SystemRequirement.all, permitted) 8 | @loading_service.call 9 | end 10 | 11 | def create 12 | @system_requirement = SystemRequirement.new 13 | @system_requirement.attributes = system_requirement_params 14 | save_system_requirement! 15 | end 16 | 17 | def show; end 18 | 19 | def update 20 | @system_requirement.attributes = system_requirement_params 21 | save_system_requirement! 22 | end 23 | 24 | def destroy 25 | @system_requirement.destroy! 26 | rescue 27 | render_error(fields: @system_requirement.errors.messages) 28 | end 29 | 30 | private 31 | 32 | def load_system_requirement 33 | @system_requirement = SystemRequirement.find(params[:id]) 34 | end 35 | 36 | def system_requirement_params 37 | return {} unless params.has_key?(:system_requirement) 38 | params.require(:system_requirement).permit(:id, :name, :operational_system, :storage, 39 | :processor, :memory, :video_board) 40 | end 41 | 42 | def save_system_requirement! 43 | @system_requirement.save! 44 | render :show 45 | rescue 46 | render_error(fields: @system_requirement.errors.messages) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/controllers/admin/v1/users_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin::V1 2 | class UsersController < ApiController 3 | before_action :load_user, only: [:show, :update, :destroy] 4 | 5 | def index 6 | scope_without_current_user = User.where.not(id: @current_user.id) 7 | permitted = params.permit({ search: :name }, { order: {} }, :page, :length) 8 | @loading_service = Admin::ModelLoadingService.new(scope_without_current_user, permitted) 9 | @loading_service.call 10 | end 11 | 12 | def create 13 | @user = User.new 14 | @user.attributes = user_params 15 | save_user! 16 | end 17 | 18 | def update 19 | @user.attributes = user_params 20 | save_user! 21 | end 22 | 23 | def show; end 24 | 25 | def destroy 26 | @user.destroy! 27 | rescue 28 | render_error(fields: @user.errors.messages) 29 | end 30 | 31 | private 32 | 33 | def load_user 34 | @user = User.find(params[:id]) 35 | end 36 | 37 | def user_params 38 | return {} unless params.has_key?(:user) 39 | params.require(:user).permit(:id, :name, :email, :password, :password_confirmation, :profile) 40 | end 41 | 42 | def save_user! 43 | @user.save! 44 | render :show 45 | rescue 46 | render_error(fields: @user.errors.messages) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | before_action :configure_permitted_parameters, if: :devise_controller? 3 | 4 | protected 5 | 6 | def configure_permitted_parameters 7 | devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :email, :password, :password_confirmation]) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/concerns/authenticatable.rb: -------------------------------------------------------------------------------- 1 | module Authenticatable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | include DeviseTokenAuth::Concerns::SetUserByToken 6 | before_action :authenticate_user! 7 | end 8 | end -------------------------------------------------------------------------------- /app/controllers/concerns/simple_error_renderable.rb: -------------------------------------------------------------------------------- 1 | module SimpleErrorRenderable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | class_attribute :simple_error_partial 6 | 7 | def render_error(message: nil, fields: nil, status: :unprocessable_entity) 8 | render partial: self.class.simple_error_partial, locals: { message: message, fields: fields }, 9 | status: status 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /app/controllers/concerns/static_token_authenticatable.rb: -------------------------------------------------------------------------------- 1 | module StaticTokenAuthenticatable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | STATIC_TOKEN_AUTH = ENV['STATIC_TOKEN_AUTH'] || Rails.application.credentials.token[:auth] 6 | 7 | before_action :authenticate! 8 | 9 | def authenticate! 10 | unless params[:token] == STATIC_TOKEN_AUTH 11 | head :unauthorized 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /app/controllers/juno/v1/payment_confirmations_controller.rb: -------------------------------------------------------------------------------- 1 | module Juno::V1 2 | class PaymentConfirmationsController < ApplicationController 3 | include StaticTokenAuthenticatable 4 | 5 | def create 6 | if params.has_key?(:chargeCode) 7 | Juno::Charge.find_by(code: params[:chargeCode])&.order&.update(status: :payment_accepted) 8 | end 9 | head :ok 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/api_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class ApiController < ApplicationController 3 | include Authenticatable 4 | 5 | include SimpleErrorRenderable 6 | self.simple_error_partial = "shared/simple_error" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/storefront/v1/categories_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class CategoriesController < ApplicationController 3 | 4 | def index 5 | @categories = Category.order(:name) 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/checkouts_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class CheckoutsController < ApiController 3 | def create 4 | run_service 5 | rescue Storefront::CheckoutProcessorService::InvalidParamsError 6 | render_error(fields: @service.errors) 7 | end 8 | 9 | private 10 | 11 | def run_service 12 | @service = Storefront::CheckoutProcessorService.new(checkout_params) 13 | @service.call 14 | render :show 15 | end 16 | 17 | def checkout_params 18 | params.require(:checkout).permit(:payment_type, :installments, :coupon_id, :card_hash, 19 | :document, items: [:quantity, :product_id], 20 | address: [:street, :number, :city, :state, :post_code]) 21 | .reverse_merge(user_id: current_user.id, installments: 1) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/storefront/v1/coupon_validations_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class CouponValidationsController < ApiController 3 | def create 4 | @coupon = Coupon.find_by(code: params[:coupon_code]) 5 | @coupon.validate_use! 6 | render :show 7 | rescue Coupon::InvalidUse, NoMethodError 8 | render_error(message: I18n.t('storefront/v1/coupon_validations.create.failure')) 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/games_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class GamesController < ApiController 3 | def index 4 | @games = Game.includes(product: { line_items: [:order, :licenses] }) 5 | .where(orders: { user_id: current_user }) 6 | .select("products.*, games.*, licenses.key") 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/home_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class HomeController < ApplicationController 3 | 4 | def index 5 | @loader_service = Storefront::HomeLoaderService.new 6 | @loader_service.call 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/orders_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class OrdersController < ApiController 3 | def index 4 | @orders = current_user.orders 5 | end 6 | 7 | def show 8 | @order = current_user.orders.includes(:line_items).find(params[:id]) 9 | rescue ActiveRecord::RecordNotFound 10 | render_error(message: "Forbidden access", status: :forbidden) 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/products_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class ProductsController < ApplicationController 3 | 4 | def index 5 | @service = Storefront::ProductsFilterService.new(search_params) 6 | @service.call 7 | end 8 | 9 | def show 10 | @product = Product.find(params[:id]) 11 | end 12 | 13 | private 14 | 15 | def search_params 16 | params.permit(:search, :productable, :page, :length, order: {}, category_ids: [], price: [:min, :max], 17 | release_date: [:min, :max]).merge(productable: :game) 18 | 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /app/controllers/storefront/v1/wish_items_controller.rb: -------------------------------------------------------------------------------- 1 | module Storefront::V1 2 | class WishItemsController < ApiController 3 | 4 | def index 5 | @wish_items = current_user.wish_items.joins(:product) 6 | .includes(:product) 7 | .order("products.name ASC") 8 | end 9 | 10 | def create 11 | @wish_item = current_user.wish_items.build(wish_item_params) 12 | @wish_item.save! 13 | render :show 14 | rescue ActiveRecord::RecordInvalid 15 | render_error(fields: @wish_item.errors.messages) 16 | end 17 | 18 | def destroy 19 | @wish_item = current_user.wish_items.find(params[:id]) 20 | @wish_item.destroy! 21 | rescue ActiveRecord::RecordNotFound 22 | head :not_found 23 | end 24 | 25 | private 26 | 27 | def wish_item_params 28 | params.require(:wish_item).permit(:product_id) 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /app/jobs/admin/alocate_license_job.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class AlocateLicenseJob < ApplicationJob 3 | queue_as :default 4 | 5 | def perform(line_item) 6 | AlocateLicensesService.new(line_item).call 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/jobs/admin/finish_delivered_orders_job.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class FinishDeliveredOrdersJob < ApplicationJob 3 | queue_as :default 4 | 5 | def perform 6 | FinishDeliveredOrdersService.call 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/juno/charge_creation_job.rb: -------------------------------------------------------------------------------- 1 | class Juno::ChargeCreationJob < ApplicationJob 2 | queue_as :default 3 | 4 | def perform(order, order_params) 5 | order.attributes = order_params.slice(:document, :card_hash) 6 | order.address = Address.new(order_params[:address]) 7 | Juno::ChargeCreationService.new(order).call 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/checkout_mailer.rb: -------------------------------------------------------------------------------- 1 | class CheckoutMailer < ApplicationMailer 2 | def success 3 | mail(to: params[:order].user.email, subject: default_i18n_subject(order_number: params[:order].id)) 4 | end 5 | 6 | def generic_error 7 | mail(to: params[:order].user.email, subject: default_i18n_subject(order_number: params[:order].id)) 8 | end 9 | 10 | def payment_error(message) 11 | @message = message 12 | mail(to: params[:order].user.email, subject: default_i18n_subject(order_number: params[:order].id)) 13 | end 14 | end -------------------------------------------------------------------------------- /app/mailers/license_mailer.rb: -------------------------------------------------------------------------------- 1 | class LicenseMailer < ApplicationMailer 2 | def send_license 3 | mail(to: params[:license].line_item.order.user.email) 4 | end 5 | end -------------------------------------------------------------------------------- /app/models/address.rb: -------------------------------------------------------------------------------- 1 | class Address 2 | include ActiveModel::Model 3 | include ActiveModel::Attributes 4 | 5 | attribute :street 6 | attribute :number 7 | attribute :city 8 | attribute :state 9 | attribute :post_code 10 | 11 | validates :street, presence: true 12 | validates :number, presence: true 13 | validates :city, presence: true 14 | validates :state, presence: true 15 | validates :post_code, presence: true 16 | end -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/category.rb: -------------------------------------------------------------------------------- 1 | class Category < ApplicationRecord 2 | include LikeSearchable 3 | include Paginatable 4 | 5 | has_many :product_categories, dependent: :destroy 6 | has_many :products, through: :product_categories 7 | 8 | validates :name, presence: true, uniqueness: { case_sensitive: false } 9 | end 10 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/concerns/like_searchable.rb: -------------------------------------------------------------------------------- 1 | module LikeSearchable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | scope :like, -> (key, value) do 6 | self.where(self.arel_table[key].matches("%#{value}%")) 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/models/concerns/paginatable.rb: -------------------------------------------------------------------------------- 1 | module Paginatable 2 | extend ActiveSupport::Concern 3 | 4 | MAX_PER_PAGE = 10 5 | DEFAULT_PAGE = 1 6 | 7 | included do 8 | scope :paginate, -> (page, length) do 9 | page = page.present? && page > 0 ? page : DEFAULT_PAGE 10 | length = length.present? && length > 0 ? length : MAX_PER_PAGE 11 | starts_at = (page - 1) * length 12 | limit(length).offset(starts_at) 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /app/models/coupon.rb: -------------------------------------------------------------------------------- 1 | class Coupon < ApplicationRecord 2 | class InvalidUse < StandardError; end 3 | 4 | include LikeSearchable 5 | include Paginatable 6 | 7 | validates :name, presence: true 8 | validates :code, presence: true, uniqueness: { case_sensitive: false } 9 | validates :status, presence: true 10 | validates :discount_value, presence: true, numericality: { greater_than: 0 } 11 | validates :due_date, presence: true, future_date: true 12 | 13 | enum status: { active:1, inactive: 2 } 14 | 15 | def validate_use! 16 | raise InvalidUse unless self.active? && self.due_date >= Time.now 17 | true 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/game.rb: -------------------------------------------------------------------------------- 1 | class Game < ApplicationRecord 2 | include LikeSearchable 3 | 4 | belongs_to :system_requirement 5 | has_one :product, as: :productable 6 | has_many :licenses 7 | 8 | validates :mode, presence: true 9 | validates :release_date, presence: true 10 | validates :developer, presence: true 11 | 12 | enum mode: { pvp: 1, pve: 2, both: 3 } 13 | 14 | def ship!(line_item) 15 | Admin::AlocateLicenseJob.perform_later(line_item) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/juno.rb: -------------------------------------------------------------------------------- 1 | module Juno 2 | def self.table_name_prefix 3 | 'juno_' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/models/juno/charge.rb: -------------------------------------------------------------------------------- 1 | class Juno::Charge < ApplicationRecord 2 | belongs_to :order 3 | has_many :credit_card_payments, class_name: "Juno::CreditCardPayment" 4 | 5 | validates :key, presence: true 6 | validates :code, presence: true 7 | validates :number, presence: true, 8 | uniqueness: { scope: :order_id }, 9 | numericality: { only_integer: true, greater_than: 0 } 10 | validates :amount, presence: true, numericality: { greater_than: 0 } 11 | validates :status, presence: true 12 | end 13 | -------------------------------------------------------------------------------- /app/models/juno/credit_card_payment.rb: -------------------------------------------------------------------------------- 1 | class Juno::CreditCardPayment < ApplicationRecord 2 | belongs_to :charge 3 | 4 | validates :key, presence: true 5 | validates :release_date, presence: true 6 | validates :status, presence: true 7 | end 8 | -------------------------------------------------------------------------------- /app/models/license.rb: -------------------------------------------------------------------------------- 1 | class License < ApplicationRecord 2 | include Paginatable 3 | include LikeSearchable 4 | 5 | belongs_to :game 6 | belongs_to :line_item, optional: true 7 | 8 | validates :key, presence: true, uniqueness: { case_sensitive: false, scope: :platform } 9 | validates :platform, presence: true 10 | validates :status, presence: true 11 | validates :line_item, presence: true, if: -> { self.status == 'in_use' } 12 | 13 | enum platform: { steam: 1, battle_net: 2, origin: 3 } 14 | enum status: { available: 1, in_use: 2, inactive: 3 } 15 | end 16 | -------------------------------------------------------------------------------- /app/models/line_item.rb: -------------------------------------------------------------------------------- 1 | class LineItem < ApplicationRecord 2 | belongs_to :order 3 | belongs_to :product 4 | has_many :licenses 5 | 6 | validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 } 7 | validates :payed_price, presence: true, numericality: { greater_than: 0 } 8 | validates :status, presence: true, on: :update 9 | 10 | enum status: { waiting_order: 1, preparing: 2, en_route: 3, delivered: 4 } 11 | 12 | before_validation :set_default_status, on: :create 13 | 14 | def total 15 | self.payed_price * self.quantity 16 | end 17 | 18 | def ship! 19 | self.product.productable.ship!(self) 20 | self.update!(status: :preparing) 21 | end 22 | 23 | private 24 | 25 | def set_default_status 26 | self.status = :waiting_order 27 | end 28 | end -------------------------------------------------------------------------------- /app/models/order.rb: -------------------------------------------------------------------------------- 1 | class Order < ApplicationRecord 2 | include Paginatable 3 | 4 | DAYS_TO_DUE = 7 5 | 6 | attribute :address 7 | attribute :card_hash 8 | attribute :document 9 | 10 | belongs_to :user 11 | belongs_to :coupon, optional: true 12 | has_many :line_items 13 | has_many :juno_charges, class_name: 'Juno::Charge' 14 | 15 | validates :status, presence: true, on: :update 16 | validates :subtotal, presence: true, numericality: { greater_than: 0 } 17 | validates :total_amount, presence: true, numericality: { greater_than: 0 } 18 | validates :payment_type, presence: true 19 | validates :installments, presence: true, numericality: { only_integer: true, greater_than: 0 } 20 | validates :document, presence: true, cpf_cnpj: true, on: :create 21 | 22 | with_options if: ->{ credit_card? }, on: :create do 23 | validates :card_hash, presence: true 24 | validates :address, presence: true 25 | validates_associated :address 26 | end 27 | 28 | enum status: { processing_order: 1, processing_error: 2, waiting_payment: 3, 29 | payment_accepted: 4, payment_denied: 5, finished: 6 } 30 | 31 | enum payment_type: { credit_card: 1, billet: 2 } 32 | 33 | before_validation :set_default_status, on: :create 34 | after_commit :enqueue_juno_charge_creation, on: :create 35 | around_update :ship_order, if: -> { self.status_changed?(to: 'payment_accepted') } 36 | 37 | 38 | def due_date 39 | self.created_at + DAYS_TO_DUE.days 40 | end 41 | 42 | private 43 | 44 | def set_default_status 45 | self.status = :processing_order 46 | end 47 | 48 | def enqueue_juno_charge_creation 49 | order_attrs = { document: self.document, card_hash: self.card_hash, address: self.address.attributes } 50 | Juno::ChargeCreationJob.perform_later(self, order_attrs) 51 | end 52 | 53 | def ship_order 54 | yield 55 | self.line_items.each { |line_item| line_item.ship! } 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/models/product.rb: -------------------------------------------------------------------------------- 1 | class Product < ApplicationRecord 2 | include LikeSearchable 3 | include Paginatable 4 | 5 | belongs_to :productable, polymorphic: true 6 | has_many :product_categories, dependent: :destroy 7 | has_many :categories, through: :product_categories 8 | has_many :wish_items 9 | has_many :line_items 10 | 11 | has_one_attached :image 12 | 13 | validates :name, presence: true, uniqueness: { case_sensitive: false } 14 | validates :description, presence: true 15 | validates :price, presence: true, numericality: { greater_than: 0 } 16 | validates :image, presence: true 17 | validates :status, presence: true 18 | validates :featured, presence: true, if: -> { featured.nil? } 19 | 20 | enum status: { available: 1, unavailable: 2 } 21 | 22 | def sells_count 23 | LineItem.joins(:order).where(orders: { status: :finished }, product: self).sum(:quantity) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/product_category.rb: -------------------------------------------------------------------------------- 1 | class ProductCategory < ApplicationRecord 2 | belongs_to :product 3 | belongs_to :category 4 | end 5 | -------------------------------------------------------------------------------- /app/models/system_requirement.rb: -------------------------------------------------------------------------------- 1 | class SystemRequirement < ApplicationRecord 2 | include LikeSearchable 3 | include Paginatable 4 | 5 | has_many :games, dependent: :restrict_with_error 6 | 7 | validates :name, presence: true, uniqueness: { case_sensitive: false } 8 | validates :operational_system, presence: true 9 | validates :storage, presence: true 10 | validates :processor, presence: true 11 | validates :memory, presence: true 12 | validates :video_board, presence: true 13 | end 14 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class User < ActiveRecord::Base 4 | include LikeSearchable 5 | include Paginatable 6 | 7 | devise :database_authenticatable, :registerable, 8 | :recoverable, :rememberable, :validatable 9 | 10 | include DeviseTokenAuth::Concerns::User 11 | 12 | has_many :wish_items 13 | has_many :orders 14 | 15 | validates :name, presence: true 16 | validates :profile, presence: true 17 | 18 | enum profile: { admin: 0, client: 1 } 19 | end 20 | -------------------------------------------------------------------------------- /app/models/wish_item.rb: -------------------------------------------------------------------------------- 1 | class WishItem < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :product 4 | 5 | validates :product_id, uniqueness: { scope: :user_id } 6 | end 7 | -------------------------------------------------------------------------------- /app/services/admin/alocate_licenses_service.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class AlocateLicensesService 3 | def initialize(line_item) 4 | @line_item = line_item 5 | end 6 | 7 | def call 8 | licenses = @line_item.product.productable.licenses.where(status: :available).take(@line_item.quantity) 9 | License.transaction { update_licenses(licenses) } 10 | send_licenses 11 | @line_item.update!(status: :delivered) 12 | end 13 | 14 | private 15 | 16 | def update_licenses(licenses) 17 | licenses.map { |license| license.attributes = { status: :in_use, line_item: @line_item } } 18 | licenses.each { |license| license.save! } 19 | end 20 | 21 | def send_licenses 22 | @line_item.licenses.each do |license| 23 | LicenseMailer.with(license: license).send_license.deliver_later 24 | end 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /app/services/admin/dashboard/sales_range_service.rb: -------------------------------------------------------------------------------- 1 | module Admin::Dashboard 2 | class SalesRangeService 3 | attr_reader :records 4 | 5 | def initialize(min: nil, max: nil) 6 | @min_date = min.present? ? min.beginning_of_day : nil 7 | @max_date = max.present? ? max.end_of_day : nil 8 | @records = {} 9 | end 10 | 11 | def call 12 | if @max_date.present? && @min_date.present? && @max_date - @min_date < 1.month 13 | @records = group_sales_by_day 14 | else 15 | @records = group_sales_by_month 16 | end 17 | end 18 | 19 | private 20 | 21 | def group_sales_by_day 22 | order_filter 23 | .group("year, month, day") 24 | .order("year, month, day") 25 | .pluck(order_arel[:year], order_arel[:month], order_arel[:day], line_item_arel[:total_sold]) 26 | .map { |record| { date: format_date(*record[0..2]), total_sold: record[3].to_f } } 27 | end 28 | 29 | def group_sales_by_month 30 | order_filter 31 | .group("year, month") 32 | .order("year, month") 33 | .pluck(order_arel[:year], order_arel[:month], line_item_arel[:total_sold]) 34 | .map { |record| { date: format_date(*record[0..1]), total_sold: record[2].to_f } } 35 | end 36 | 37 | def order_filter 38 | Order.joins(:line_items) 39 | .where(status: :finished, created_at: @min_date..@max_date) 40 | end 41 | 42 | def format_date(year, month, day = nil) 43 | year = "%04d" % year 44 | month = ("-" + "%02d" % month) 45 | day = ("-" + "%02d" % day) if day.present? 46 | year + month + day.to_s 47 | end 48 | 49 | def line_item_arel 50 | @line_item_arel if @line_item_arel.present? 51 | arel = LineItem.arel_table 52 | total_sold = (arel[:payed_price] * arel[:quantity]).sum.as('total_sold') 53 | @line_item_arel = { total_sold: total_sold } 54 | end 55 | 56 | def order_arel 57 | @order_arel if @order_arel.present? 58 | field = Order.arel_table[:created_at] 59 | @order_arel = { month: field.extract('month').as('month'), year: field.extract('year').as('year'), 60 | day: field.extract('day').as('day') } 61 | end 62 | end 63 | end -------------------------------------------------------------------------------- /app/services/admin/dashboard/summary_service.rb: -------------------------------------------------------------------------------- 1 | module Admin::Dashboard 2 | class SummaryService 3 | attr_reader :records 4 | 5 | def initialize(min: nil, max: nil) 6 | @min_date = min.present? ? min.beginning_of_day : nil 7 | @max_date = max.present? ? max.end_of_day : nil 8 | @records = {} 9 | end 10 | 11 | def call 12 | @records[:users] = User.where(created_at: @min_date..@max_date).count 13 | @records[:products] = Product.where(created_at: @min_date..@max_date).count 14 | calculate_orders 15 | end 16 | 17 | private 18 | 19 | def calculate_orders 20 | arel = Order.arel_table 21 | calc = Order.where(status: :finished, created_at: @min_date..@max_date) 22 | .pluck(arel[:id].count, arel[:total_amount].sum).flatten 23 | @records[:orders] = calc.first 24 | @records[:profit] = calc.second 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /app/services/admin/dashboard/top_five_products_service.rb: -------------------------------------------------------------------------------- 1 | module Admin::Dashboard 2 | class TopFiveProductsService 3 | NUMBER_OF_RECORDS = 5 4 | 5 | attr_reader :records 6 | 7 | def initialize(min: nil, max: nil) 8 | @min_date = min.present? ? min.beginning_of_day : nil 9 | @max_date = max.present? ? max.end_of_day : nil 10 | @records = [] 11 | end 12 | 13 | def call 14 | @records = search_top_five.map do |product| 15 | build_product_hash(product) 16 | end 17 | @records 18 | end 19 | 20 | private 21 | 22 | def search_top_five 23 | range_date_orders = Order.where(status: :finished, created_at: @min_date..@max_date) 24 | Product.joins(line_items: :order).merge(range_date_orders).group(:id) 25 | .order('total_sold DESC, total_qty DESC') 26 | .limit(NUMBER_OF_RECORDS) 27 | .select(:id, :name, line_item_arel[:sold].as('total_sold'), line_item_arel[:quantity].as('total_qty')) 28 | end 29 | 30 | def build_product_hash(product) 31 | { 32 | product: product.name, 33 | image: Rails.application.routes.url_helpers.rails_blob_url(product.image), 34 | total_sold: product.total_sold, 35 | quantity: product.total_qty 36 | } 37 | end 38 | 39 | def line_item_arel 40 | return @line_item_arel if @line_item_arel 41 | arel = LineItem.arel_table 42 | total_sold = (arel[:payed_price] * arel[:quantity]).sum 43 | quantity_sum = arel[:quantity].sum 44 | @line_item_arel = { sold: total_sold, quantity: quantity_sum } 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/services/admin/finish_delivered_orders_service.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class FinishDeliveredOrdersService 3 | def self.call 4 | delivered_orders = Order.includes(:line_items).where(status: :payment_accepted).select do |order| 5 | order.line_items.all?(&:delivered?) 6 | end 7 | Order.where(id: delivered_orders.map(&:id)).update_all(status: :finished) 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /app/services/admin/model_loading_service.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class ModelLoadingService 3 | attr_reader :records, :pagination 4 | 5 | def initialize(searchable_model, params = {}) 6 | @searchable_model = searchable_model 7 | @params = params || {} 8 | @records = [] 9 | @pagination = {} 10 | end 11 | 12 | def call 13 | set_pagination_values 14 | searched = search_records(@searchable_model) 15 | @records = searched.order(@params[:order].to_h) 16 | .paginate(@params[:page], @params[:length]) 17 | set_pagination_attributes(searched.count) 18 | end 19 | 20 | private 21 | 22 | def set_pagination_values 23 | @params[:page] = @params[:page].to_i 24 | @params[:length] = @params[:length].to_i 25 | @params[:page] = @searchable_model.model::DEFAULT_PAGE if @params[:page] <= 0 26 | @params[:length] = @searchable_model.model::MAX_PER_PAGE if @params[:length] <= 0 27 | end 28 | 29 | def search_records(searched) 30 | return searched unless @params.has_key?(:search) 31 | @params[:search].each do |key, value| 32 | searched = searched.like(key, value) 33 | end 34 | searched 35 | end 36 | 37 | def set_pagination_attributes(total_filtered) 38 | total_pages = (total_filtered / @params[:length].to_f).ceil 39 | @pagination.merge!(page: @params[:page], length: @records.count, 40 | total: total_filtered, total_pages: total_pages) 41 | end 42 | end 43 | end -------------------------------------------------------------------------------- /app/services/admin/product_saving_service.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class ProductSavingService 3 | class NotSavedProductError < StandardError; end 4 | 5 | attr_reader :product, :errors 6 | 7 | def initialize(params, product = nil) 8 | params = params.deep_symbolize_keys 9 | @product_params = params.reject { |key| key == :productable_attributes } 10 | @productable_params = params[:productable_attributes] || {} 11 | @errors = {} 12 | @product = product || Product.new 13 | end 14 | 15 | def call 16 | Product.transaction do 17 | @product.attributes = @product_params.reject { |key| key == :productable } 18 | build_productable 19 | ensure 20 | save! 21 | end 22 | end 23 | 24 | def build_productable 25 | @product.productable ||= @product_params[:productable].camelcase.safe_constantize.new 26 | @product.productable.attributes = @productable_params 27 | end 28 | 29 | def save! 30 | save_record!(@product.productable) if @product.productable.present? 31 | save_record!(@product) 32 | raise NotSavedProductError if @errors.present? 33 | rescue => e 34 | raise NotSavedProductError 35 | end 36 | 37 | def save_record!(record) 38 | record.save! 39 | rescue ActiveRecord::RecordInvalid 40 | @errors.merge!(record.errors.messages) 41 | end 42 | end 43 | end -------------------------------------------------------------------------------- /app/services/juno/charge_creation_service.rb: -------------------------------------------------------------------------------- 1 | require_relative "../../../lib/juno_api/charge" 2 | require_relative "../../../lib/juno_api/credit_card_payment" 3 | 4 | module Juno 5 | class ChargeCreationService 6 | PAYMENT_ERROR_CODES = %W[289999 509999] 7 | 8 | def initialize(order) 9 | @order = order 10 | end 11 | 12 | def call 13 | create_charges 14 | create_credit_card_payment if @order.credit_card? 15 | CheckoutMailer.with(order: @order).success.deliver_later 16 | rescue JunoApi::RequestError => e 17 | set_order_error(e.error) 18 | end 19 | 20 | private 21 | 22 | def create_charges 23 | charges = JunoApi::Charge.new.create!(@order) 24 | Juno::Charge.transaction do 25 | charges.each.with_index { |charge, index| create_charge(charge, index + 1) } 26 | end 27 | @order.update!(status: :waiting_payment) 28 | end 29 | 30 | def create_credit_card_payment 31 | credit_card_payments = JunoApi::CreditCardPayment.new.create!(@order) 32 | Juno::CreditCardPayment.transaction do 33 | credit_card_payments.each { |payment| create_charge_payment(payment) } 34 | end 35 | @order.update!(status: :payment_accepted) 36 | end 37 | 38 | def set_order_error(error) 39 | if error.present? && PAYMENT_ERROR_CODES.include?(error.first['error_code']) 40 | set_payment_denied_error(error.first['message']) 41 | else 42 | set_generic_error 43 | end 44 | end 45 | 46 | def create_charge(charge, position) 47 | charge.merge!(number: position) 48 | charge[:billet_url] = charge.delete(:installment_link) 49 | charge[:key] = charge.delete(:id) 50 | @order.juno_charges.create!(charge) 51 | end 52 | 53 | def create_charge_payment(payment) 54 | charge = Juno::Charge.find_by(key: payment[:charge]) 55 | payment.merge!(charge: charge) 56 | Juno::CreditCardPayment.create!(payment) 57 | end 58 | 59 | def set_payment_denied_error(message) 60 | @order.update!(status: :payment_denied) 61 | CheckoutMailer.with(order: @order).payment_error(message).deliver_later 62 | end 63 | 64 | def set_generic_error 65 | @order.update!(status: :processing_error) 66 | CheckoutMailer.with(order: @order).generic_error.deliver_later 67 | end 68 | end 69 | end -------------------------------------------------------------------------------- /app/services/storefront/checkout_processor_service.rb: -------------------------------------------------------------------------------- 1 | module Storefront 2 | class CheckoutProcessorService 3 | class InvalidParamsError < StandardError; end 4 | 5 | attr_reader :errors, :order 6 | 7 | def initialize(params) 8 | @params = params 9 | @order = nil 10 | @errors = {} 11 | end 12 | 13 | def call 14 | check_presence_of_items_param 15 | check_emptyness_of_items_param 16 | validate_coupon 17 | do_checkout 18 | raise InvalidParamsError if @errors.present? 19 | end 20 | 21 | private 22 | 23 | def check_presence_of_items_param 24 | unless @params.has_key?(:items) 25 | @errors[:items] = I18n.t('storefront/checkout_processor_service.errors.items.presence') 26 | end 27 | end 28 | 29 | def check_emptyness_of_items_param 30 | if @params[:items].blank? 31 | @errors[:items] = I18n.t('storefront/checkout_processor_service.errors.items.empty') 32 | end 33 | end 34 | 35 | def validate_coupon 36 | return unless @params.has_key?(:coupon_id) 37 | @coupon = Coupon.find(@params[:coupon_id]) 38 | @coupon.validate_use! 39 | rescue Coupon::InvalidUse, ActiveRecord::RecordNotFound 40 | @errors[:coupon] = I18n.t('storefront/checkout_processor_service.errors.coupon.invalid') 41 | end 42 | 43 | def do_checkout 44 | create_order 45 | rescue ActiveRecord::RecordInvalid => e 46 | @errors.merge! e.record.errors.messages 47 | @errors.merge!(address: e.record.address.errors.messages) if e.record.errors.has_key?(:address) 48 | end 49 | 50 | def create_order 51 | Order.transaction do 52 | @order = instantiate_order 53 | line_items = @params[:items].map { |line_item_params| instantiate_line_items(line_item_params) } 54 | save!(line_items) 55 | end 56 | rescue ArgumentError => e 57 | @errors[:base] = e.message 58 | end 59 | 60 | def instantiate_order 61 | order_params = @params.slice(:document, :payment_type, :installments, :card_hash, :coupon_id, :user_id) 62 | order = Order.new(order_params) 63 | order.address = Address.new(@params[:address]) 64 | order 65 | end 66 | 67 | def instantiate_line_items(line_item_params) 68 | line_item = @order.line_items.build(line_item_params) 69 | line_item.payed_price = line_item.product.price if line_item.product.present? 70 | line_item.validate! 71 | line_item 72 | end 73 | 74 | def save!(line_items) 75 | @order.subtotal = line_items.sum(&:total).floor(2) 76 | @order.total_amount = (@order.subtotal * (1 - @coupon.discount_value / 100)).floor(2) if @coupon.present? 77 | @order.total_amount ||= @order.subtotal 78 | @order.save! 79 | line_items.each(&:save!) 80 | end 81 | end 82 | end -------------------------------------------------------------------------------- /app/services/storefront/home_loader_service.rb: -------------------------------------------------------------------------------- 1 | module Storefront 2 | class HomeLoaderService 3 | QUANTITY_OF_RECORDS_PER_GROUP = 4 4 | MIN_RELEASE_DAYS = 7 5 | 6 | attr_reader :featured, :last_releases, :cheapest 7 | 8 | def initialize 9 | @featured = [] 10 | @recently_releases = [] 11 | @cheapest = [] 12 | end 13 | 14 | def call 15 | games = Product.joins("JOIN games ON productable_type = 'Game' AND productable_id = games.id") 16 | .includes(productable: [:game]).where(status: :available) 17 | @featured = load_featured_games(games) 18 | @last_releases = load_last_released_games(games) 19 | @cheapest = load_cheapest_games(games) 20 | end 21 | 22 | private 23 | 24 | def load_featured_games(games) 25 | games.where(featured: true).sample(QUANTITY_OF_RECORDS_PER_GROUP) 26 | end 27 | 28 | def load_last_released_games(games) 29 | games.where(games: { release_date: MIN_RELEASE_DAYS.days.ago.beginning_of_day..Time.now.end_of_day }) 30 | .sample(QUANTITY_OF_RECORDS_PER_GROUP) 31 | end 32 | 33 | def load_cheapest_games(games) 34 | games.order(price: :asc).take(QUANTITY_OF_RECORDS_PER_GROUP) 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /app/services/storefront/products_filter_service.rb: -------------------------------------------------------------------------------- 1 | module Storefront 2 | class ProductsFilterService 3 | attr_reader :records, :pagination 4 | 5 | def initialize(params = {}) 6 | @records = Product.all 7 | @params = params || {} 8 | @pagination = {} 9 | end 10 | 11 | def call 12 | set_pagination_values 13 | get_available_products 14 | searched = filter_records.select("products.*, games.mode, games.developer, games.release_date").distinct 15 | @records = searched.order(@params[:order].to_h).paginate(@params[:page], @params[:length]) 16 | set_pagination_attributes(searched.size) 17 | end 18 | 19 | private 20 | 21 | def set_pagination_values 22 | @params[:page] = @params[:page].to_i 23 | @params[:length] = @params[:length].to_i 24 | @params[:page] = Product::DEFAULT_PAGE if @params[:page] <= 0 25 | @params[:length] = Product::MAX_PER_PAGE if @params[:length] <= 0 26 | end 27 | 28 | def get_available_products 29 | @records = @records.joins("JOIN games ON productable_type = 'Game' AND productable_id = games.id") 30 | .left_joins(:categories) 31 | .includes(productable: [:game], categories: {}) 32 | .where(status: :available) 33 | end 34 | 35 | def filter_records 36 | searched = @records.merge filter_by_search 37 | searched.merge! filter_by_categories 38 | searched.merge! filter_by_price 39 | searched.merge! filter_by_release_date 40 | end 41 | 42 | def filter_by_search 43 | return @records.all unless @params.has_key?(:search) 44 | filtered_records = @records.like(:name, @params[:search]) 45 | filtered_records = filtered_records.or(@records.like(:description, @params[:search])) 46 | filtered_records.or @records.merge(Game.like(:developer, @params[:search])) 47 | end 48 | 49 | def filter_by_categories 50 | return @records.all unless @params.has_key?(:category_ids) 51 | @records.where(categories: { id: @params[:category_ids] }) 52 | end 53 | 54 | def filter_by_price 55 | min_price = @params.dig(:price, :min) 56 | max_price = @params.dig(:price, :max) 57 | return @records.all if min_price.blank? && max_price.blank? 58 | @records.where(price: min_price..max_price) 59 | end 60 | 61 | def filter_by_release_date 62 | min_date = Time.parse(@params.dig(:release_date, :min)).beginning_of_day rescue nil 63 | max_date = Time.parse(@params.dig(:release_date, :max)).end_of_day rescue nil 64 | return @records.all if min_date.blank? && max_date.blank? 65 | Game.where(release_date: min_date..max_date) 66 | end 67 | 68 | def set_pagination_attributes(total_filtered) 69 | total_pages = (total_filtered / @params[:length].to_f).ceil 70 | @pagination.merge!(page: @params[:page], length: @records.size, 71 | total: total_filtered, total_pages: total_pages) 72 | end 73 | end 74 | end -------------------------------------------------------------------------------- /app/validators/cpf_cnpj_validator.rb: -------------------------------------------------------------------------------- 1 | require "cpf_cnpj" 2 | 3 | class CpfCnpjValidator < ActiveModel::EachValidator 4 | def validate_each(record, attribute, value) 5 | return unless value.present? 6 | unless CPF.valid?(value) || CNPJ.valid?(value) 7 | record.errors.add(attribute, :invalid_cpf_cnpj) 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /app/validators/future_date_validator.rb: -------------------------------------------------------------------------------- 1 | class FutureDateValidator < ActiveModel::EachValidator 2 | def validate_each(record, attribute, value) 3 | if value.present? && value <= Time.zone.now 4 | message = options[:message] || :future_date 5 | record.errors.add(attribute, message) 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /app/views/admin/v1/categories/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.categories do 2 | json.array! @loading_service.records, :id, :name 3 | end 4 | 5 | json.meta do 6 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 7 | end -------------------------------------------------------------------------------- /app/views/admin/v1/categories/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.category do 2 | json.(@category, :id, :name) 3 | end -------------------------------------------------------------------------------- /app/views/admin/v1/coupons/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.coupons do 2 | json.array! @loading_service.records, :id, :name, :code, :status, :discount_value, :due_date 3 | end 4 | 5 | json.meta do 6 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 7 | end -------------------------------------------------------------------------------- /app/views/admin/v1/coupons/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.coupon do 2 | json.(@coupon, :id, :name, :code, :status, :discount_value, :due_date) 3 | end -------------------------------------------------------------------------------- /app/views/admin/v1/dashboard/sales_ranges/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.sales_ranges @service.records -------------------------------------------------------------------------------- /app/views/admin/v1/dashboard/summaries/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.summary do 2 | json.users @service.records[:users] 3 | json.products @service.records[:products] 4 | json.orders @service.records[:orders] 5 | json.profit @service.records[:profit] 6 | end -------------------------------------------------------------------------------- /app/views/admin/v1/dashboard/top_five_products/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.top_five_products do 2 | json.array! @service.records do |record| 3 | json.product record[:product] 4 | json.image record[:image] 5 | json.quantity record[:quantity] 6 | json.total_sold record[:total_sold].to_f 7 | end 8 | end -------------------------------------------------------------------------------- /app/views/admin/v1/games/_game.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(game, :mode, :release_date, :developer) 2 | json.system_requirement game.system_requirement -------------------------------------------------------------------------------- /app/views/admin/v1/licenses/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.licenses do 2 | json.array! @loading_service.records, :id, :key, :platform, :status, :game_id 3 | end 4 | 5 | json.meta do 6 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 7 | end -------------------------------------------------------------------------------- /app/views/admin/v1/licenses/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.license do 2 | json.(@license, :id, :key, :platform, :status, :game_id) 3 | end -------------------------------------------------------------------------------- /app/views/admin/v1/orders/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.orders do 2 | json.array! @loading_service.records do |order| 3 | json.(order, :id, :status, :total_amount, :payment_type) 4 | end 5 | end 6 | 7 | json.meta do 8 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 9 | end -------------------------------------------------------------------------------- /app/views/admin/v1/orders/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.order do 2 | json.(@order, :id, :status, :total_amount, :subtotal, :payment_type) 3 | json.discount @order.coupon&.discount_value&.to_f 4 | json.line_items @order.line_items do |line_item| 5 | json.(line_item, :quantity, :payed_price) 6 | json.image_url rails_blob_url(line_item.product.image) 7 | json.product line_item.product.name 8 | end 9 | end -------------------------------------------------------------------------------- /app/views/admin/v1/products/_product.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(product, :id, :name, :description, :price, :status, :featured) 2 | json.image_url rails_blob_url(product.image) 3 | json.productable product.productable_type.underscore 4 | json.productable_id product.productable_id 5 | json.categories product.categories -------------------------------------------------------------------------------- /app/views/admin/v1/products/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.products do 2 | json.array! @loading_service.records do |product| 3 | json.partial! product 4 | json.partial! product.productable 5 | end 6 | end 7 | 8 | json.meta do 9 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 10 | end -------------------------------------------------------------------------------- /app/views/admin/v1/products/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.product do 2 | json.partial! @product 3 | json.partial! @product.productable 4 | end -------------------------------------------------------------------------------- /app/views/admin/v1/system_requirements/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.system_requirements do 2 | json.array! @loading_service.records, 3 | :id, :name, :operational_system, :storage, :processor, :memory, :video_board 4 | end 5 | 6 | json.meta do 7 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 8 | end -------------------------------------------------------------------------------- /app/views/admin/v1/system_requirements/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.system_requirement do 2 | json.(@system_requirement, :id, :name, :operational_system, :storage, :processor, :memory, :video_board) 3 | end -------------------------------------------------------------------------------- /app/views/admin/v1/users/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.users do 2 | json.array! @loading_service.records, :id, :name, :email, :profile 3 | end 4 | 5 | json.meta do 6 | json.partial! 'shared/pagination', pagination: @loading_service.pagination 7 | end -------------------------------------------------------------------------------- /app/views/admin/v1/users/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.user do 2 | json.(@user, :id, :name, :email, :profile) 3 | end -------------------------------------------------------------------------------- /app/views/checkout_mailer/generic_error.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.title', order_number: params[:order].id) %>

2 | 3 |

<%= t('.body.html') %>

-------------------------------------------------------------------------------- /app/views/checkout_mailer/payment_error.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.title', order_number: params[:order].id) %>

2 | 3 |

<%= t('.body.html', message: @message) %>

-------------------------------------------------------------------------------- /app/views/checkout_mailer/success.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.title', order_number: params[:order].id) %>

2 | 3 |

<%= t('.body') %>

4 | 5 | <% if params[:order].billet? %> 6 |

<%= t('.billet_payment.html', billet_link: params[:order].juno_charges.first.billet_url) %>

7 | <% elsif params[:order].credit_card? %> 8 |

<%= t('.credit_card_payment') %>

9 | <% end %> -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password. You can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', "#{message['redirect-url']}?reset_password_token=#{@token}" %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/license_mailer/send_license.html.erb: -------------------------------------------------------------------------------- 1 |

<%= t('.title') %>

2 | 3 |

<%= t('.body', game_name: params[:license].game.product.name) %>

4 | 5 |

<%= params[:license].key %>

-------------------------------------------------------------------------------- /app/views/shared/_pagination.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.page pagination[:page] 2 | json.length pagination[:length] 3 | json.total pagination[:total] 4 | json.total_pages pagination[:total_pages] -------------------------------------------------------------------------------- /app/views/shared/_simple_error.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.errors do 2 | json.fields fields if defined?(fields) && fields.present? 3 | json.message message if defined?(message) && message.present? 4 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/categories/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.categories do 2 | json.array! @categories, :id, :name 3 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/checkouts/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.order do 2 | json.(@service.order, :id, :payment_type, :installments) 3 | json.subtotal @service.order.subtotal.to_f 4 | json.total_amount @service.order.total_amount.to_f 5 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/coupon_validations/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.coupon do 2 | json.(@coupon, :id, :code, :discount_value) 3 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/games/_game.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(game, :mode, :release_date, :developer) 2 | json.system_requirement game.system_requirement -------------------------------------------------------------------------------- /app/views/storefront/v1/games/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.games do 2 | json.array! @games do |game| 3 | json.(game.product, :id, :name, :description) 4 | json.image_url rails_blob_url(game.product.image) 5 | json.partial! game 6 | json.licenses game.product.line_items.map(&:licenses).flatten.map(&:key) 7 | end 8 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/home/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.featured do 2 | json.array! @loader_service.featured do |product| 3 | json.(product, :id, :name, :description) 4 | json.price product.price.to_f 5 | json.image_url rails_blob_url(product.image) 6 | end 7 | end 8 | 9 | json.last_releases do 10 | json.array! @loader_service.last_releases do |product| 11 | json.(product, :id, :name, :description) 12 | json.price product.price.to_f 13 | json.image_url rails_blob_url(product.image) 14 | end 15 | end 16 | 17 | json.cheapest do 18 | json.array! @loader_service.cheapest do |product| 19 | json.(product, :id, :name, :description) 20 | json.price product.price.to_f 21 | json.image_url rails_blob_url(product.image) 22 | end 23 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/orders/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.orders do 2 | json.array! @orders do |order| 3 | json.(order, :id, :status, :total_amount, :payment_type) 4 | end 5 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/orders/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.order do 2 | json.(@order, :id, :status, :total_amount, :subtotal, :payment_type) 3 | json.discount @order.coupon&.discount_value&.to_f 4 | json.line_items @order.line_items do |line_item| 5 | json.(line_item, :quantity, :payed_price) 6 | json.product line_item.product.name 7 | end 8 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/products/_product.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.(product, :id, :name, :description, :status, :featured) 2 | json.price product.price.to_f 3 | json.image_url rails_blob_url(product.image) 4 | json.productable product.productable_type.underscore 5 | json.productable_id product.productable_id 6 | json.categories product.categories 7 | json.favorited_count product.wish_items.count 8 | json.sells_count product.sells_count -------------------------------------------------------------------------------- /app/views/storefront/v1/products/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.products do 2 | json.array! @service.records do |product| 3 | json.(product, :id, :name, :description) 4 | json.price product.price.to_f 5 | json.image_url rails_blob_url(product.image) 6 | json.categories product.categories.pluck(:name) 7 | end 8 | end 9 | 10 | json.meta do 11 | json.partial! 'shared/pagination', pagination: @service.pagination 12 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/products/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.product do 2 | json.partial! @product 3 | json.partial! @product.productable 4 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/wish_items/_wish_item.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.id wish_item.id 2 | json.(wish_item.product, :name, :description) 3 | json.price wish_item.product.price.to_f 4 | json.image_url rails_blob_url(wish_item.product.image) 5 | json.categories wish_item.product.categories.pluck(:name) -------------------------------------------------------------------------------- /app/views/storefront/v1/wish_items/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.wish_items do 2 | json.array! @wish_items do |wish_item| 3 | json.partial! wish_item 4 | end 5 | end -------------------------------------------------------------------------------- /app/views/storefront/v1/wish_items/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.wish_item do 2 | json.partial! @wish_item 3 | end -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path('..', __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to setup or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?('config/database.yml') 22 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! 'bin/rails db:prepare' 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! 'bin/rails log:clear tmp:clear' 30 | 31 | puts "\n== Restarting application server ==" 32 | system! 'bin/rails restart' 33 | end 34 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads Spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "active_storage/engine" 9 | require "action_controller/railtie" 10 | require "action_mailer/railtie" 11 | require "action_mailbox/engine" 12 | require "action_text/engine" 13 | require "action_view/railtie" 14 | require "action_cable/engine" 15 | # require "sprockets/railtie" 16 | # require "rails/test_unit/railtie" 17 | 18 | # Require the gems listed in Gemfile, including any gems 19 | # you've limited to :test, :development, or :production. 20 | Bundler.require(*Rails.groups) 21 | 22 | module EcommerceApi 23 | class Application < Rails::Application 24 | # Initialize configuration defaults for originally generated Rails version. 25 | config.load_defaults 6.0 26 | 27 | # I18n config 28 | config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}')] 29 | config.i18n.default_locale = :'pt-BR' 30 | 31 | # Settings in config/environments/* take precedence over those specified here. 32 | # Application configuration can go into files in config/initializers 33 | # -- all .rb files in that directory are automatically loaded after loading 34 | # the framework and any gems in your application. 35 | 36 | # Only loads a smaller set of middleware suitable for API only apps. 37 | # Middleware like session, flash, cookies can be added back manually. 38 | # Skip views, helpers and assets when generating a new resource. 39 | config.api_only = true 40 | 41 | config.autoload_paths += %W["#{config.root}/app/validators/"] 42 | 43 | config.active_job.queue_adapter = :sidekiq 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: ecommerce_api_production 11 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | WzAGMBoPUlSjTQ32Jc7G/lcIwkXg+9W3Fm04AhSDXcU1L63Js6rbVow5NsUsb9YQiukrpfoEjFZCZ+xvjDbu0/LhfYdmmOwczwf8uKOmbO8XNXZSToPbId2bpmqAGltlNQJ4d72+MqYoOM5Luk0q8AtWQPdWK9UQhON0UlFaioRMbH2382vmiYSlaZkcwA0gD+I6yrz20P2No3NzwAUpsv4ckXw+0aqu7ypFlS0JefPJJay1olitPbAuCdyeFETENZ31mi8Fb49nq6AwOXXTkbAW9/yvv8HTR5agDL0xg5eiHrU7cLD3UX7KT6eouwW9YNRZeNfQ9xU/gS/lqUeiV+1aClcZWMYVQ0X3PKfiKcsLxvSmAw22aZBZl50Kd27WcZsLDRPI4ZV4GspR+uSOdkahfomDk3U2doi2--6FHNx05E7+NRSSPw--RJjo63YgM5Gz+OhacCmKPQ== -------------------------------------------------------------------------------- /config/credentials/development.yml.enc: -------------------------------------------------------------------------------- 1 | b1R8FuKKBWi1Kd3jkP1OLAzBwHLvn+5IeCqav9As+gOneB4pthgiHh/ce192GjKX1dODWUmN0bYcZor/H0+hRaCf9cKqEZbgOVrw667OFyYYh5jZRorOCY1sv9c9lptdyTDH0+wz/iiCzaS+8abbPJsCjHEm9uo8h4gzkvjkHdN+AATXfd8oAm6M6XMAaVLd5w4S49lSrhAxTIHj4ou0kIOJtigrNCdGhEB3l7XKyb1O32B8STJZM2T/Rm2Hsgs820Z9o49HOTvgy0cgcRwrFnkPRVMU9tw77tAGQtUS8Z7lh4R6u5TPy6X7Ihii4e994tdUatS3tO2QcFAoHZrr1X7CgFJ2FGlFooDh+rwLqMYeeUSfn78kvHJ4HrgN2QYxVv/Aj3zxq+AsrdmpWbE5wZBlHV7+3OYXrrPTeNO04cGz7LMn588O1qDoHPE9mK4nXIdCHX5+1658Iph3sAXkmtk2MA1crzySFSw9GaaaTRJ+w0UUjXD8lI7WLC1Gng0QkFGiy5DRA36CLuRGOnlVmduoFAso+3k/uZPkkroQaaAeUuH8U1WVWRtkx1WqAKQFzuSdlHid+WIJmZLsqX/32HD1jjW8S9d6GZKM2dR3UVRkyn3wxZFfP54zitMGhKOW371E6IXrXDx+e+lb/8SW5itb78B41UIOHBG/96Rmu7/hfdUdBxD2n9NOgef3vQGaTYhWeb+s5H/BkH4JGkYw1JPBVlamz7CkIH7YygZI/3RHs0rEGxIEwyAc+Xt0/zsEh6uiEiPrex9lOIqaNcpYJmVozjyWJlFZj0g2--jgAOxzc7bexKitw+--s1SYYKxjOkeuZyLPSfIKiw== -------------------------------------------------------------------------------- /config/credentials/test.yml.enc: -------------------------------------------------------------------------------- 1 | gZdzZ50vkGxBm14AIwFsouRKEMT9NjVmk9tK2I/LCOdEwR2RFJEhVdlq89ps54DgB/IZgSCGDxiCF8CQyfRx8LwWK2h8q3qH2JZ1uPCSK23INcsphjkwUpFsvScAuET6Ij2apt/ZBqUCtLSiUMKSBFHl15loMbyN0XB8XSRyfftMpgqfa2/VmjIYM8YGV0ioEG/wlxNX+xBJDZ9tsJwbrjpobhl3EZYhN4RIOsxX6EiAc+BCixeSTt9iSuAawymaYtmtbGBX00K0BhLkHPy1gwNhFvJd2d/qT0zb9xVb1As9uChAZeogI61pRbGNbqTBzCK7ikC0KsNjQTUUdklF5DruiJBmB4FkSHAhkqKA+7FsA9a118P9VXrUO6sXr1vpc8ZMSxSDWArx87g5c0cjXqDuOrbhPDY7FRoAVT+EIgs6SNWwFkCwOQ6iSlORRrXBrkR3NK7o/lcKd2PMzkuX2hbrSCB8pXL1Jj2eGZWf28wWP9mm3oV9dkXa1B4vk7x+VnymAqtgRx7O49nXPkTW1z2egHTKOq5gSjOl8rdLPgTAazx0uCpkYwT7cHsU0X++M3Ysiqd8LQwkVhNhS0CTq/Jun3Xh+BTUFF5GUihH2A74A53YGf/XKO1UhMfQMHAwewIxrgGnkJ7iUPzom3+O2ygciZ01TGDYGmfHH3F9K5vMKcA8RfYoOj2gRXfovO3RgY4WmlmXn372NpJjvBO+DCb7fL6Dxrz1k04Kg28SQu6N3mawV/XFb294woawrcswMJhPA78XV2/pGXuzracsd1WT2hwA7cxyOwyB--lwUWtaJkbKlS8q83--udp1VZ3lHFp0SAUs7PRhVg== -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | user: postgres 5 | password: postgres 6 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 7 | 8 | development: 9 | <<: *default 10 | database: ecommerce_api_development 11 | 12 | test: 13 | <<: *default 14 | database: ecommerce_api_test 15 | 16 | production: 17 | <<: *default 18 | database: ecommerce_api_production 19 | username: ecommerce_api 20 | password: <%= ENV['ECOMMERCE_API_DATABASE_PASSWORD'] %> 21 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Configure hosts 5 | config.hosts << /[\w|\d]*\.ngrok.io/ 6 | 7 | # In the development environment your application's code is reloaded on 8 | # every request. This slows down response time but is perfect for development 9 | # since you don't have to restart the web server when you make code changes. 10 | config.cache_classes = false 11 | 12 | # Do not eager load code on boot. 13 | config.eager_load = false 14 | 15 | # Show full error reports. 16 | config.consider_all_requests_local = true 17 | 18 | # Enable/disable caching. By default caching is disabled. 19 | # Run rails dev:cache to toggle caching. 20 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 21 | config.cache_store = :memory_store 22 | config.public_file_server.headers = { 23 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 24 | } 25 | else 26 | config.action_controller.perform_caching = false 27 | 28 | config.cache_store = :null_store 29 | end 30 | 31 | # Store uploaded files on the local file system (see config/storage.yml for options). 32 | config.active_storage.service = :local 33 | 34 | # Don't care if the mailer can't send. 35 | config.action_mailer.raise_delivery_errors = false 36 | 37 | config.action_mailer.perform_caching = false 38 | 39 | config.action_mailer.default_url_options = { host: "http://localhost:3000" } 40 | Rails.application.routes.default_url_options[:host] = "http://localhost:3000" 41 | 42 | config.action_mailer.delivery_method = :smtp 43 | config.action_mailer.smtp_settings = { 44 | address: 'localhost', 45 | port: 1025, 46 | } 47 | 48 | # Print deprecation notices to the Rails logger. 49 | config.active_support.deprecation = :log 50 | default_url_options[:host] 51 | # Raise an error on page load if there are pending migrations. 52 | config.active_record.migration_error = :page_load 53 | 54 | # Highlight code that triggered database queries in logs. 55 | config.active_record.verbose_query_logs = true 56 | 57 | 58 | # Raises error for missing translations. 59 | # config.action_view.raise_on_missing_translations = true 60 | 61 | # Use an evented file watcher to asynchronously detect changes in source code, 62 | # routes, locales, etc. This feature depends on the listen gem. 63 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 64 | 65 | JUNO_AUTH_URL = "https://sandbox.boletobancario.com" 66 | JUNO_RESOURCE_URL = "https://sandbox.boletobancario.com/api-integration" 67 | end 68 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # The test environment is used exclusively to run your application's 2 | # test suite. You never need to work with it otherwise. Remember that 3 | # your test database is "scratch space" for the test suite and is wiped 4 | # and recreated between test runs. Don't rely on the data there! 5 | 6 | Rails.application.configure do 7 | # Settings specified here will take precedence over those in config/application.rb. 8 | 9 | config.cache_classes = false 10 | config.action_view.cache_template_loading = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | config.cache_store = :null_store 27 | 28 | # Raise exceptions instead of rendering exception templates. 29 | config.action_dispatch.show_exceptions = false 30 | 31 | # Disable request forgery protection in test environment. 32 | config.action_controller.allow_forgery_protection = false 33 | 34 | # Store uploaded files on the local file system in a temporary directory. 35 | config.active_storage.service = :test 36 | 37 | config.action_mailer.perform_caching = false 38 | 39 | # Tell Action Mailer not to deliver emails to the real world. 40 | # The :test delivery method accumulates sent emails in the 41 | # ActionMailer::Base.deliveries array. 42 | config.action_mailer.delivery_method = :test 43 | 44 | config.action_mailer.default_url_options = { host: "http://localhost:3000" } 45 | Rails.application.routes.default_url_options[:host] = "http://localhost:3000" 46 | 47 | # Print deprecation notices to the stderr. 48 | config.active_support.deprecation = :stderr 49 | 50 | # Raises error for missing translations. 51 | # config.action_view.raise_on_missing_translations = true 52 | 53 | config.active_job.queue_adapter = :test 54 | 55 | JUNO_AUTH_URL = "https://sandbox.boletobancario.com" 56 | JUNO_RESOURCE_URL = "https://sandbox.boletobancario.com/api-integration" 57 | end 58 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 2 | allow do 3 | origins '*' 4 | resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete], 5 | expose: ['access-token', 'client', 'expiry', 'token-type', 'uid'] 6 | end 7 | end -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/pt-BR/controllers/coupon_validations.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | storefront/v1/coupon_validations: 3 | create: 4 | failure: Cupom não pode ser utilizado -------------------------------------------------------------------------------- /config/locales/pt-BR/custom_validations.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | activerecord: 3 | errors: 4 | messages: 5 | future_date: "deve ser uma data futura" 6 | invalid_cpf_cnpj: "deve ser um CPF ou CNPJ válido" -------------------------------------------------------------------------------- /config/locales/pt-BR/mailers/checkout_mailer.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | checkout_mailer: 3 | success: 4 | subject: "Pedido #%{order_number} concluído com sucesso!" 5 | title: "Seu pedido #%{order_number} foi um sucesso!" 6 | body: | 7 | Obrigado por ter comprando com a gente! 8 | billet_payment: 9 | html: O seu boleto está neste link =D 10 | credit_card_payment: | 11 | O pagamento já foi um sucesso! Em breve vamos liberar o seu pedido =) 12 | 13 | generic_error: 14 | subject: "Ocorreu um erro com o seu pedido #%{order_number}" 15 | title: "Encontramos um erro ao processar o seu pedido #%{order_number}" 16 | body: 17 | html: | 18 | Infelizmente não conseguimos processar o seu pedido.
19 | Tente novamente daqui alguns minutos. Se o erro persistir o nosso suporte estará à sua disposição =) 20 | 21 | payment_error: 22 | subject: "Ocorreu um erro com o seu pedido #%{order_number}" 23 | title: "Encontramos um erro no pagamento do seu pedido #%{order_number}" 24 | body: 25 | html: | 26 | Infelizmente não conseguimos processar o seu pagamento.
27 | Ao tentarmos processar o seu pagamento, tivemos o seguinte status sobre o seu cartão: %{message} -------------------------------------------------------------------------------- /config/locales/pt-BR/mailers/license_mailer.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | license_mailer: 3 | send_license: 4 | subject: Licença liberada! \o/ 5 | title: A sua licença foi liberada 6 | body: | 7 | A licença para o jogo %{game_name} que você comprou está abaixo. Baixa você copiar e adicionar 8 | na sua plataforma jogos -------------------------------------------------------------------------------- /config/locales/pt-BR/models/wish_item.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | activerecord: 3 | errors: 4 | models: 5 | wish_item: 6 | attributes: 7 | product_id: 8 | taken: "já está na lista do usuário" -------------------------------------------------------------------------------- /config/locales/pt-BR/services/checkout_processor_service.yml: -------------------------------------------------------------------------------- 1 | pt-BR: 2 | storefront/checkout_processor_service: 3 | errors: 4 | items: 5 | presence: deve estar presente 6 | empty: não pode estar vazio 7 | coupon: 8 | invalid: não é valido -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 12 | # 13 | port ENV.fetch("PORT") { 3000 } 14 | 15 | # Specifies the `environment` that Puma will run in. 16 | # 17 | environment ENV.fetch("RAILS_ENV") { "development" } 18 | 19 | # Specifies the `pidfile` that Puma will use. 20 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 21 | 22 | # Specifies the number of `workers` to boot in clustered mode. 23 | # Workers are forked web server processes. If using threads and workers together 24 | # the concurrency of the application would be max `threads` * `workers`. 25 | # Workers do not work on JRuby or Windows (both of which do not support 26 | # processes). 27 | # 28 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 29 | 30 | # Use the `preload_app!` method when specifying a `workers` number. 31 | # This directive tells Puma to first boot the application and load code 32 | # before forking the application. This takes advantage of Copy On Write 33 | # process behavior so workers use less memory. 34 | # 35 | # preload_app! 36 | 37 | # Allow puma to be restarted by `rails restart` command. 38 | plugin :tmp_restart 39 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | require 'sidekiq/web' 2 | require 'sidekiq-scheduler/web' 3 | require_relative '../lib/middlewares/static_token_auth' 4 | 5 | Rails.application.routes.draw do 6 | Sidekiq::Web.use StaticTokenAuth 7 | mount Sidekiq::Web => '/sidekiq/:token' 8 | 9 | mount_devise_token_auth_for 'User', at: 'auth/v1/user' 10 | 11 | namespace :admin, defaults: { format: :json } do 12 | namespace :v1 do 13 | get "home" => "home#index" 14 | resources :categories 15 | resources :coupons 16 | resources :games, only: [], shallow: true do 17 | resources :licenses 18 | end 19 | resources :orders, only: [:index, :show] 20 | resources :products 21 | resources :system_requirements 22 | resources :users 23 | 24 | namespace :dashboard do 25 | resources :sales_ranges, only: :index 26 | resources :summaries, only: :index 27 | resources :top_five_products, only: :index 28 | end 29 | end 30 | end 31 | 32 | namespace :storefront, defaults: { format: :json } do 33 | namespace :v1 do 34 | get "home" => "home#index" 35 | resources :products, only: [:index, :show] 36 | resources :categories, only: :index 37 | resources :checkouts, only: :create 38 | post "/coupons/:coupon_code/validations", to: "coupon_validations#create" 39 | resources :games, only: :index 40 | resources :orders, only: [:index, :show] 41 | resources :wish_items, only: [:index, :create, :destroy] 42 | end 43 | end 44 | 45 | namespace :juno do 46 | namespace :v1 do 47 | resources :payment_confirmations, only: :create 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | :schedule: 2 | Admin::FinishDeliveredOrdersJob: 3 | every: ['1h', first_in: '2m'] -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /db/migrate/20200920200639_devise_token_auth_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | 4 | create_table(:users) do |t| 5 | ## Required 6 | t.string :provider, :null => false, :default => "email" 7 | t.string :uid, :null => false, :default => "" 8 | 9 | ## Database authenticatable 10 | t.string :encrypted_password, :null => false, :default => "" 11 | 12 | ## Recoverable 13 | t.string :reset_password_token 14 | t.datetime :reset_password_sent_at 15 | t.boolean :allow_password_change, :default => false 16 | 17 | ## Rememberable 18 | t.datetime :remember_created_at 19 | 20 | ## Confirmable 21 | t.string :confirmation_token 22 | t.datetime :confirmed_at 23 | t.datetime :confirmation_sent_at 24 | t.string :unconfirmed_email # Only if using reconfirmable 25 | 26 | ## Lockable 27 | # t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts 28 | # t.string :unlock_token # Only if unlock strategy is :email or :both 29 | # t.datetime :locked_at 30 | 31 | ## User Info 32 | t.string :name 33 | t.string :email 34 | t.integer :profile, default: 1 35 | 36 | ## Tokens 37 | t.json :tokens 38 | 39 | t.timestamps 40 | end 41 | 42 | add_index :users, :email, unique: true 43 | add_index :users, [:uid, :provider], unique: true 44 | add_index :users, :reset_password_token, unique: true 45 | add_index :users, :confirmation_token, unique: true 46 | # add_index :users, :unlock_token, unique: true 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /db/migrate/20200927152708_create_categories.rb: -------------------------------------------------------------------------------- 1 | class CreateCategories < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :categories do |t| 4 | t.string :name 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20200927161952_create_products.rb: -------------------------------------------------------------------------------- 1 | class CreateProducts < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :products do |t| 4 | t.string :name 5 | t.text :description 6 | t.decimal :price, precision: 10, scale: 2 7 | t.references :productable, polymorphic: true, null: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200927162631_create_product_categories.rb: -------------------------------------------------------------------------------- 1 | class CreateProductCategories < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :product_categories do |t| 4 | t.references :product, null: false, foreign_key: true 5 | t.references :category, null: false, foreign_key: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20200927171715_create_system_requirements.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemRequirements < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :system_requirements do |t| 4 | t.string :name 5 | t.string :operational_system 6 | t.string :storage 7 | t.string :processor 8 | t.string :memory 9 | t.string :video_board 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20200927173409_create_games.rb: -------------------------------------------------------------------------------- 1 | class CreateGames < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :games do |t| 4 | t.integer :mode 5 | t.datetime :release_date 6 | t.string :developer 7 | t.references :system_requirement, null: false, foreign_key: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200927192509_create_coupons.rb: -------------------------------------------------------------------------------- 1 | class CreateCoupons < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :coupons do |t| 4 | t.string :name 5 | t.string :code 6 | t.integer :status 7 | t.decimal :discount_value, precision: 5, scale: 2 8 | t.datetime :due_date 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20201018131945_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2] 3 | def change 4 | create_table :active_storage_blobs do |t| 5 | t.string :key, null: false 6 | t.string :filename, null: false 7 | t.string :content_type 8 | t.text :metadata 9 | t.bigint :byte_size, null: false 10 | t.string :checksum, null: false 11 | t.datetime :created_at, null: false 12 | 13 | t.index [ :key ], unique: true 14 | end 15 | 16 | create_table :active_storage_attachments do |t| 17 | t.string :name, null: false 18 | t.references :record, null: false, polymorphic: true, index: false 19 | t.references :blob, null: false 20 | 21 | t.datetime :created_at, null: false 22 | 23 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true 24 | t.foreign_key :active_storage_blobs, column: :blob_id 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /db/migrate/20201122234724_add_status_to_products.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToProducts < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :products, :status, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210107010752_create_licenses.rb: -------------------------------------------------------------------------------- 1 | class CreateLicenses < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :licenses do |t| 4 | t.string :key 5 | t.integer :platform 6 | t.integer :status 7 | t.references :game, null: false, foreign_key: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20210110175644_create_wish_items.rb: -------------------------------------------------------------------------------- 1 | class CreateWishItems < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :wish_items do |t| 4 | t.references :user, null: false, foreign_key: true 5 | t.references :product, null: false, foreign_key: true 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20210114010037_add_featured_to_products.rb: -------------------------------------------------------------------------------- 1 | class AddFeaturedToProducts < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :products, :featured, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210207154531_create_orders.rb: -------------------------------------------------------------------------------- 1 | class CreateOrders < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :orders do |t| 4 | t.integer :status 5 | t.decimal :subtotal, precision: 10, scale: 2 6 | t.decimal :total_amount, precision: 10, scale: 2 7 | t.integer :payment_type 8 | t.integer :installments 9 | t.references :user, null: false, foreign_key: true 10 | t.references :coupon, null: true, foreign_key: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20210209120441_create_line_items.rb: -------------------------------------------------------------------------------- 1 | class CreateLineItems < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :line_items do |t| 4 | t.integer :quantity 5 | t.decimal :payed_price, precision: 10, scale: 2 6 | t.references :order, null: false, foreign_key: true 7 | t.references :product, null: false, foreign_key: true 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20210306190339_create_juno_charges.rb: -------------------------------------------------------------------------------- 1 | class CreateJunoCharges < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :juno_charges do |t| 4 | t.string :key 5 | t.string :code 6 | t.string :number 7 | t.decimal :amount, precision: 10, scale: 2 8 | t.string :status 9 | t.string :billet_url 10 | t.references :order, null: false, foreign_key: true 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20210306200906_create_juno_credit_card_payments.rb: -------------------------------------------------------------------------------- 1 | class CreateJunoCreditCardPayments < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :juno_credit_card_payments do |t| 4 | t.string :key 5 | t.datetime :release_date 6 | t.string :status 7 | t.string :reason 8 | t.references :charge, null: false, foreign_key: { to_table: :juno_charges } 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20210314134141_add_line_item_reference_to_licenses.rb: -------------------------------------------------------------------------------- 1 | class AddLineItemReferenceToLicenses < ActiveRecord::Migration[6.0] 2 | def change 3 | add_reference :licenses, :line_item, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210315110855_add_status_to_line_items.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToLineItems < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :line_items, :status, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /lib/juno_api/auth.rb: -------------------------------------------------------------------------------- 1 | module JunoApi 2 | class Auth 3 | include HTTParty 4 | 5 | PATH = "/authorization-server/oauth/token" 6 | SECONDS_TO_WAIT_PROCESSING = 0.5 7 | LIMIT_RATE_TO_RENEW = 90 8 | 9 | base_uri "#{JUNO_AUTH_URL}" 10 | 11 | attr_reader :access_token, :expires_in, :request_time 12 | private_class_method :new 13 | 14 | def self.singleton 15 | wait_until_process_is_done 16 | check_instance 17 | @instance 18 | end 19 | 20 | private 21 | 22 | def self.wait_until_process_is_done 23 | while @processing 24 | sleep SECONDS_TO_WAIT_PROCESSING 25 | end 26 | end 27 | 28 | def self.check_instance 29 | if @instance.blank? || is_about_to_expire?(@instance) 30 | @processing = true 31 | @instance = new 32 | @processing = false 33 | end 34 | end 35 | 36 | def self.is_about_to_expire?(instance) 37 | expiration_rate = LIMIT_RATE_TO_RENEW / 100.0 38 | instance.request_time + instance.expires_in * expiration_rate < Time.zone.now 39 | end 40 | 41 | def initialize 42 | auth = process_auth! 43 | @access_token = auth['access_token'] 44 | @expires_in = auth['expires_in'] 45 | @request_time = Time.zone.now 46 | end 47 | 48 | def process_auth! 49 | body = { grant_type: 'client_credentials' } 50 | response = self.class.post(PATH, headers: { 'Authorization' => 'Basic ' + auth_token }, body: body ) 51 | raise Error.new("Bad request") if response.code != 200 52 | response.parsed_response 53 | end 54 | 55 | def auth_token 56 | auth_data = Rails.application.credentials.juno.slice(:client, :secret) 57 | Base64.strict_encode64(auth_data[:client] + ":" + auth_data[:secret]) 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /lib/juno_api/charge.rb: -------------------------------------------------------------------------------- 1 | require_relative "./auth" 2 | require_relative "./request_error" 3 | 4 | module JunoApi 5 | class Charge 6 | include HTTParty 7 | 8 | PAYMENT_TYPE = { 'billet' => "BOLETO", 'credit_card' =>"CREDIT_CARD" } 9 | CHARGE_KEYS_TO_KEEP = %i[id code installment_link amount status] 10 | 11 | base_uri "#{JUNO_RESOURCE_URL}/charges" 12 | 13 | headers 'Content-Type' => 'application/json' 14 | headers 'X-Api-Version' => '2' 15 | headers 'X-Resource-Token' => Rails.application.credentials.juno[:private_token] 16 | 17 | def initialize 18 | @auth = Auth.singleton 19 | end 20 | 21 | def create!(order) 22 | auth_header = { 'Authorization' => "Bearer #{auth.access_token}" } 23 | body = prepare_create_body(order) 24 | response = self.class.post("/", headers: auth_header, body: body.to_json) 25 | raise_error(response) if response.code != 200 26 | organize_response(response) 27 | end 28 | 29 | private 30 | 31 | attr_reader :auth, :auth_header 32 | 33 | def prepare_create_body(order) 34 | { 35 | charge: build_charge(order), 36 | billing: { name: order.user.name, document: order.document, email: order.user.email } 37 | } 38 | end 39 | 40 | def raise_error(response) 41 | details = response.parsed_response['details'].map { |detail| detail.transform_keys(&:underscore) } 42 | raise RequestError.new("Invalid request sent to Juno", details) 43 | rescue NoMethodError => e 44 | raise RequestError.new("Invalid request sent to Juno") 45 | end 46 | 47 | def organize_response(response) 48 | response.parsed_response['_embedded']['charges'].map do |charge| 49 | charge.deep_transform_keys! { |key| key.underscore.to_sym } 50 | charge.keep_if { |key, _| CHARGE_KEYS_TO_KEEP.include?(key) } 51 | end 52 | end 53 | 54 | def build_charge(order) 55 | { 56 | description: "Order ##{order.id}", amount: (order.total_amount / order.installments).floor(2), 57 | dueDate: order.due_date.strftime("%Y-%m-%d"), installments: order.installments, 58 | discountAmount: (order.coupon&.discount_value).to_f, paymentTypes: [PAYMENT_TYPE[order.payment_type]] 59 | } 60 | end 61 | end 62 | end -------------------------------------------------------------------------------- /lib/juno_api/credit_card_payment.rb: -------------------------------------------------------------------------------- 1 | require_relative "./auth" 2 | require_relative "./request_error" 3 | 4 | module JunoApi 5 | class CreditCardPayment 6 | include HTTParty 7 | 8 | base_uri "#{JUNO_RESOURCE_URL}/payments" 9 | 10 | headers 'Content-Type' => 'application/json' 11 | headers 'X-Api-Version' => '2' 12 | headers 'X-Resource-Token' => Rails.application.credentials.juno[:private_token] 13 | 14 | def initialize 15 | @auth = Auth.singleton 16 | end 17 | 18 | def create!(order) 19 | auth_header = { 'Authorization' => "Bearer #{auth.access_token}" } 20 | body = prepare_create_body(order, order.juno_charges.first.key) 21 | response = self.class.post("/", headers: auth_header, body: body.to_json) 22 | raise_error(response) if response.code != 200 23 | organize_response(response) 24 | end 25 | 26 | private 27 | 28 | attr_reader :auth 29 | 30 | def prepare_create_body(order, charge_key) 31 | { 32 | chargeId: charge_key, 33 | creditCardDetails: { creditCardHash: order.card_hash }, 34 | billing: { email: order.user.email, address: build_address_attributes(order.address) } 35 | } 36 | end 37 | 38 | def raise_error(response) 39 | details = response.parsed_response['details'].map { |detail| detail.transform_keys(&:underscore) } 40 | raise RequestError.new("Invalid request sent to Juno", details) 41 | rescue NoMethodError => e 42 | raise RequestError.new("Invalid request sent to Juno") 43 | end 44 | 45 | def organize_response(response) 46 | response.parsed_response['payments'].map do |payment| 47 | { 48 | key: payment['id'], charge: payment['chargeId'], release_date: payment['releaseDate'], 49 | status: payment['status'], reason: payment['failReason'] 50 | } 51 | end 52 | end 53 | 54 | def build_address_attributes(address) 55 | address.attributes.transform_keys { |key| key.camelize(:lower) } 56 | end 57 | end 58 | end -------------------------------------------------------------------------------- /lib/juno_api/request_error.rb: -------------------------------------------------------------------------------- 1 | module JunoApi 2 | class RequestError < StandardError 3 | attr_reader :error 4 | 5 | def initialize(message, error = nil) 6 | @error = error 7 | super(message) 8 | end 9 | end 10 | end -------------------------------------------------------------------------------- /lib/middlewares/static_token_auth.rb: -------------------------------------------------------------------------------- 1 | class StaticTokenAuth 2 | TOKEN_TO_VERIFY = Rails.application.credentials.token[:sidekiq] 3 | 4 | def initialize(app) 5 | @app = app 6 | end 7 | 8 | def call(env) 9 | token = env.dig('action_dispatch.request.path_parameters', :token) 10 | if token == TOKEN_TO_VERIFY 11 | return @app.call(env) 12 | end 13 | [401, {}, ['Invalid Token']] 14 | end 15 | end -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/dev/prime.rake: -------------------------------------------------------------------------------- 1 | if Rails.env.development? || Rails.env.test? 2 | require 'factory_bot' 3 | 4 | namespace :dev do 5 | desc 'Sample data for local development environment' 6 | task prime: 'db:setup' do 7 | include FactoryBot::Syntax::Methods 8 | 9 | 15.times do 10 | profile = [:admin, :client].sample 11 | create(:user, profile: profile) 12 | end 13 | 14 | system_requirements = [] 15 | ['Basic', 'Intermediate', 'Advanced'].each do |sr_name| 16 | system_requirements << create(:system_requirement, name: sr_name) 17 | end 18 | 19 | 15.times do 20 | coupon_status = [:active, :inactive].sample 21 | create(:coupon, status: coupon_status) 22 | end 23 | 24 | categories = [] 25 | 25.times do 26 | categories << create(:category, name: Faker::Game.unique.genre) 27 | end 28 | 29 | 30.times do 30 | game_name = Faker::Game.unique.title 31 | availability = [:available, :unavailable].sample 32 | categories_count = rand(0..3) 33 | featured = [true, false].sample 34 | price = Faker::Commerce.price(range: 5.0..30.0) 35 | release_date = (0..15).to_a.sample.days.ago 36 | game_categories_ids = [] 37 | categories_count.times { game_categories_ids << Category.all.sample.id } 38 | game = create(:game, system_requirement: system_requirements.sample, release_date: release_date) 39 | create(:product, name: game_name, status: availability, featured: featured, price: price, 40 | category_ids: game_categories_ids, productable: game) 41 | end 42 | 43 | 50.times do 44 | game = Game.all[0...5].sample 45 | status = [:available, :inactive].sample 46 | platform = [:steam, :battle_net, :origin].sample 47 | create(:license, status: status, platform: platform, game: game) 48 | end 49 | 50 | 10.times do 51 | product = Product.all.sample 52 | (1..10).to_a.sample.times do 53 | product.wish_items.create(user: User.all.sample) 54 | end 55 | end 56 | end 57 | end 58 | end -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/log/.keep -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /spec/factories/addresses.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :address do 3 | street { Faker::Address.street_name } 4 | number { Faker::Address.building_number } 5 | city { Faker::Address.city } 6 | state { Faker::Address.state_abbr } 7 | post_code { Faker::Address.postcode } 8 | 9 | skip_create 10 | initialize_with { new(**attributes) } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/factories/categories.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :category do 3 | sequence(:name) { |n| "Category #{n}" } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/coupons.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :coupon do 3 | sequence(:name) { |n| "My Coupon #{n}" } 4 | code { Faker::Commerce.unique.promotion_code(digits: 4) } 5 | status { :active } 6 | discount_value { 25 } 7 | due_date { 3.days.from_now } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/games.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :game do 3 | mode { %i(pvp pve both).sample } 4 | release_date { '2020-06-01' } 5 | developer { Faker::Company.name } 6 | system_requirement 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/juno/charges.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :juno_charge, class: 'Juno::Charge' do 3 | key { "chr_#{Faker::Lorem.characters(number: 20) }" } 4 | code { Faker::Number.number(digits: 20) } 5 | sequence(:number) { |n| n } 6 | amount { Faker::Commerce.price(range: 40..100) } 7 | status { "ACTIVE" } 8 | billet_url { Faker::Internet.url(host: 'pay.juno.com') } 9 | order 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/factories/juno/credit_card_payments.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :juno_credit_card_payment, class: 'Juno::CreditCardPayment' do 3 | key { "pay_#{Faker::Lorem.characters(number: 20) }" } 4 | release_date { 1.month.from_now } 5 | status { "CONFIRMED" } 6 | reason { nil } 7 | charge { association :juno_charge } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/licenses.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :license do 3 | key { Faker::Lorem.characters(number: 15) } 4 | platform { :steam } 5 | status { :available } 6 | game 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/line_items.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :line_item do 3 | quantity { 1 } 4 | payed_price { Faker::Commerce.price(range: 100.00..200.00) } 5 | status { :waiting_order } 6 | order 7 | product 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/orders.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :order do 3 | status { :processing_order } 4 | subtotal { Faker::Commerce.price(range: 200.00..400.00) } 5 | total_amount { subtotal } 6 | payment_type { :credit_card } 7 | card_hash { Faker::Lorem.characters } 8 | installments { 5 } 9 | address { build(:address) } 10 | document { "03.000.050/0001-67" } 11 | user 12 | 13 | trait :with_items do 14 | after :build do |order| 15 | items = create_list(:line_items, 5, order: order) 16 | order.subtotal = items.sum(:payed_price) 17 | order.total_amount = order.subtotal 18 | end 19 | end 20 | 21 | trait :with_coupon do 22 | after :build do |order| 23 | coupon = create(:coupon, discount_value: 10) 24 | order.total_amout = order.subtotal * (1 - coupon.discount_value / 100) 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/factories/product_categories.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :product_category do 3 | product 4 | category 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/products.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :product do 3 | sequence(:name) { |n| "Product #{n}" } 4 | description { Faker::Lorem.paragraph } 5 | price { Faker::Commerce.price(range: 100.0..400.0) } 6 | image { Rack::Test::UploadedFile.new(Rails.root.join("spec/support/images/product_image.png")) } 7 | status { :available } 8 | featured { true } 9 | 10 | after :build do |product| 11 | product.productable ||= create(:game) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/factories/system_requirements.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :system_requirement do 3 | sequence(:name) { |n| "Basic #{n}" } 4 | operational_system { Faker::Computer.os } 5 | storage { "5GB" } 6 | processor { "AMD Ryzen 7" } 7 | memory { "2GB" } 8 | video_board { "N/A" } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | name { Faker::Name.name } 4 | email { Faker::Internet.email } 5 | password { "123456" } 6 | password_confirmation { "123456" } 7 | profile { :admin } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/wish_items.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :wish_item do 3 | user 4 | product 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/libs/juno_api/auth_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | require_relative "../../../lib/juno_api/auth" 3 | 4 | describe JunoApi::Auth do 5 | let(:auth_class) { JunoApi::Auth.clone } 6 | 7 | context "when call #singleton" do 8 | let(:response) do 9 | double( 10 | parsed_response: { 'access_token' => SecureRandom.hex, 'expires_in' => 1.day.from_now.to_i }, 11 | code: 200 12 | ) 13 | end 14 | 15 | it "returns only one instance" do 16 | allow(auth_class).to receive(:post).and_return(response) 17 | object_ids = 0.upto(4).collect do 18 | auth_class.singleton 19 | end 20 | object_ids.uniq! 21 | expect(object_ids.size).to eq 1 22 | end 23 | 24 | it "call Juno API only once" do 25 | allow(auth_class).to receive(:post).and_return(response).once 26 | object_ids = 0.upto(4).collect do 27 | auth_class.singleton 28 | end 29 | end 30 | end 31 | 32 | context "when call #access_token" do 33 | let(:first_response) do 34 | double( 35 | parsed_response: { 'access_token' => SecureRandom.hex, 'expires_in' => 1.day }, 36 | code: 200 37 | ) 38 | end 39 | 40 | let(:second_response) do 41 | double( 42 | parsed_response: { 'access_token' => SecureRandom.hex, 'expires_in' => 1.day }, 43 | code: 200 44 | ) 45 | end 46 | 47 | before(:each) do 48 | allow(auth_class).to receive(:post).and_return(first_response, second_response) 49 | end 50 | 51 | it "returns same access token before expiration" do 52 | first_auth = auth_class.singleton 53 | second_auth = auth_class.singleton 54 | expect(first_auth.access_token).to eq second_auth.access_token 55 | end 56 | 57 | it "returns another access token when it is expired" do 58 | first_auth = auth_class.singleton 59 | travel 3.days do 60 | second_auth = auth_class.singleton 61 | expect(first_auth.access_token).to_not eq second_auth.access_token 62 | end 63 | end 64 | 65 | it "returns another access token it reaches expiration rate" do 66 | first_auth = auth_class.singleton 67 | seconds_to_travel = first_auth.expires_in * 0.91 68 | travel seconds_to_travel.seconds do 69 | second_auth = auth_class.singleton 70 | expect(first_auth.access_token).to_not eq second_auth.access_token 71 | end 72 | end 73 | end 74 | end -------------------------------------------------------------------------------- /spec/libs/juno_api/charge_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | require_relative "../../../lib/juno_api/charge" 3 | 4 | describe JunoApi::Charge do 5 | let!(:order) { create(:order) } 6 | 7 | describe "#create" do 8 | before(:each) do 9 | singleton = double(access_token: SecureRandom.hex) 10 | allow(JunoApi::Auth).to receive(:singleton).and_return(singleton) 11 | end 12 | 13 | context "with invalid params" do 14 | it "should raise an error" do 15 | error = { details: [{ message: "Some error", errorCode: "10000" }] }.to_json 16 | error_response = double(code: 400, body: error, parsed_response: JSON.parse(error)) 17 | allow(JunoApi::Charge).to receive(:post).and_return(error_response) 18 | expect do 19 | described_class.new.create!(order) 20 | end.to raise_error(JunoApi::RequestError) 21 | end 22 | end 23 | 24 | context "with valid params" do 25 | let(:return_from_api) do 26 | installment_to_pay = (order.total_amount / order.installments).floor(2) 27 | charges = 0.upto(order.installments - 1).map do |num| 28 | { 29 | id: "000#{num}", code: num, dueDate: (order.due_date + num.months).strftime("%Y-%m-%d"), 30 | reference: "", amount: installment_to_pay, checkoutUrl: Faker::Internet.url(host: 'checkout.juno.com'), 31 | status: "ACTIVE", _links: { self: { href: Faker::Internet.url(host: 'checkout.juno.com') } } 32 | } 33 | end 34 | { _embedded: { charges: charges } }.to_json 35 | end 36 | 37 | before(:each) do 38 | api_response = double(code: 200, body: return_from_api, parsed_response: JSON.parse(return_from_api)) 39 | allow(JunoApi::Charge).to receive(:post).and_return(api_response) 40 | end 41 | 42 | it "returns same quantity of charges as installments" do 43 | charges = described_class.new.create!(order) 44 | expect(charges.count).to eq order.installments 45 | end 46 | 47 | it "return all charges with same installment amout" do 48 | charges = described_class.new.create!(order) 49 | installment_amount = charges.map { |charge| charge[:amount] }.uniq 50 | expect(installment_amount.size).to eq 1 51 | end 52 | 53 | 54 | it "return right amount on each installment" do 55 | installment_for_payment = (order.total_amount / order.installments.to_f).floor(2).to_f 56 | charges = described_class.new.create!(order) 57 | charges.each do |charge| 58 | expect(charge[:amount].to_f).to eq installment_for_payment 59 | end 60 | end 61 | end 62 | end 63 | end -------------------------------------------------------------------------------- /spec/libs/juno_api/credit_card_payment_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | require_relative "../../../lib/juno_api/credit_card_payment" 3 | 4 | describe JunoApi::CreditCardPayment do 5 | let!(:order) { create(:order) } 6 | 7 | describe "#create" do 8 | let!(:order) { create(:order) } 9 | let!(:charges) { create_list(:juno_charge, 5, order: order) } 10 | 11 | before(:each) do 12 | singleton = double(access_token: SecureRandom.hex) 13 | allow(JunoApi::Auth).to receive(:singleton).and_return(singleton) 14 | end 15 | 16 | context "with invalid params" do 17 | it "should raise an error" do 18 | error = { details: [{ message: "Some error", errorCode: "10000" }] }.to_json 19 | error_response = double(code: 400, body: error, parsed_response: JSON.parse(error)) 20 | allow(JunoApi::CreditCardPayment).to receive(:post).and_return(error_response) 21 | expect do 22 | described_class.new.create!(order) 23 | end.to raise_error(JunoApi::RequestError) 24 | end 25 | end 26 | 27 | context "with valid params" do 28 | let(:return_from_api) do 29 | payments = charges.map.with_index do |charge, index| 30 | release_date = (Time.zone.now + index.months).strftime("%Y-%m-%d") 31 | { 32 | id: "pay_000#{index}", chargeId: charge.key, date: Time.zone.now.strftime("%Y-%m-%d"), 33 | releaseDate: release_date, amount: charge.amount.to_f, fee: 2, type: "INSTALLMENT_CREDIT_CARD", 34 | status: "CONFIRMED", failReason: nil 35 | } 36 | end 37 | { transactionId: SecureRandom.hex, installments: charges.count, payments: payments }.to_json 38 | end 39 | 40 | before(:each) do 41 | api_response = double(code: 200, body: return_from_api, parsed_response: JSON.parse(return_from_api)) 42 | allow(JunoApi::CreditCardPayment).to receive(:post).and_return(api_response) 43 | end 44 | 45 | it "returns same quantity of charges" do 46 | payments = described_class.new.create!(order) 47 | expect(payments.count).to eq charges.count 48 | end 49 | 50 | it "return expected payments hash" do 51 | expected_payments = charges.map.with_index do |charge, index| 52 | release_date = (Time.zone.now + index.months).strftime("%Y-%m-%d") 53 | { key: "pay_000#{index}", charge: charge.key, release_date: release_date, status: "CONFIRMED", reason: nil } 54 | end 55 | payments = described_class.new.create!(order) 56 | expect(payments).to eq expected_payments 57 | end 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /spec/libs/middlewares/static_token_auth_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe StaticTokenAuth do 4 | let(:app) { ->(env){ [200, env, "my middleware spec"] } } 5 | let(:token) { Rails.application.credentials.token[:sidekiq] } 6 | 7 | it "returns 401 when it does not have any token" do 8 | env_mock = Rack::MockRequest.env_for("my.testing.com") 9 | middleware = described_class.new(app) 10 | response = middleware.call(env_mock) 11 | expect(response.first).to eq 401 12 | end 13 | 14 | it "returns 401 when token is invalid" do 15 | env_mock = Rack::MockRequest.env_for("my.testing.com") 16 | env_mock['action_dispatch.request.path_parameters'] = { token: 'some_random_token' } 17 | middleware = described_class.new(app) 18 | response = middleware.call(env_mock) 19 | expect(response.first).to eq 401 20 | end 21 | 22 | it "returns 200 when token is valid" do 23 | env_mock = Rack::MockRequest.env_for("my.testing.com") 24 | env_mock['action_dispatch.request.path_parameters'] = { token: token } 25 | middleware = described_class.new(app) 26 | response = middleware.call(env_mock) 27 | expect(response.first).to eq 200 28 | end 29 | end -------------------------------------------------------------------------------- /spec/models/address_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Address, type: :model do 4 | it { is_expected.to validate_presence_of(:street) } 5 | it { is_expected.to validate_presence_of(:number) } 6 | it { is_expected.to validate_presence_of(:city) } 7 | it { is_expected.to validate_presence_of(:state) } 8 | it { is_expected.to validate_presence_of(:post_code) } 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/category_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Category, type: :model do 4 | it { is_expected.to validate_presence_of(:name) } 5 | it { is_expected.to validate_uniqueness_of(:name).case_insensitive } 6 | 7 | it { is_expected.to have_many(:product_categories).dependent(:destroy) } 8 | it { is_expected.to have_many(:products).through(:product_categories) } 9 | 10 | it_has_behavior_of "like searchable concern", :category, :name 11 | it_behaves_like "paginatable concern", :category 12 | end 13 | -------------------------------------------------------------------------------- /spec/models/coupon_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Coupon, type: :model do 4 | it { is_expected.to validate_presence_of :name } 5 | it { is_expected.to validate_presence_of :code } 6 | it { is_expected.to validate_uniqueness_of(:code).case_insensitive } 7 | it { is_expected.to validate_presence_of :status } 8 | it { is_expected.to define_enum_for(:status).with_values({ active: 1, inactive: 2 }) } 9 | it { is_expected.to validate_presence_of :discount_value } 10 | it { is_expected.to validate_numericality_of(:discount_value).is_greater_than(0) } 11 | it { is_expected.to validate_presence_of :due_date } 12 | 13 | it "can't have past due_date" do 14 | subject.due_date = 1.day.ago 15 | subject.valid? 16 | expect(subject.errors.keys).to include :due_date 17 | end 18 | 19 | it "is invalid with current due_date" do 20 | subject.due_date = Time.zone.now 21 | subject.valid? 22 | expect(subject.errors.keys).to include :due_date 23 | end 24 | 25 | it "is valid with future date" do 26 | subject.due_date = Time.zone.now + 1.hour 27 | subject.valid? 28 | expect(subject.errors.keys).to_not include :due_date 29 | end 30 | 31 | context "on #validate_use!" do 32 | subject { build(:coupon) } 33 | 34 | it "raise InvalidUse when it's overdue" do 35 | subject.due_date = 2.days.ago 36 | expect do 37 | subject.validate_use! 38 | end.to raise_error(Coupon::InvalidUse) 39 | end 40 | 41 | it "raise InvalidUse when it's inactive" do 42 | subject.status = :inactive 43 | expect do 44 | subject.validate_use! 45 | end.to raise_error(Coupon::InvalidUse) 46 | end 47 | 48 | it "returns true when it's on date and active" do 49 | expect(subject.validate_use!).to eq true 50 | end 51 | end 52 | 53 | it_has_behavior_of "like searchable concern", :coupon, :name 54 | it_behaves_like "paginatable concern", :coupon 55 | end 56 | -------------------------------------------------------------------------------- /spec/models/game_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Game, type: :model do 4 | it { is_expected.to validate_presence_of(:mode) } 5 | it { is_expected.to define_enum_for(:mode).with_values({ pvp: 1, pve: 2, both: 3 }) } 6 | it { is_expected.to validate_presence_of(:release_date) } 7 | it { is_expected.to validate_presence_of(:developer) } 8 | 9 | it { is_expected.to belong_to :system_requirement } 10 | it { is_expected.to have_one :product } 11 | it { is_expected.to have_many :licenses } 12 | 13 | it_has_behavior_of "like searchable concern", :game, :developer 14 | 15 | it "#ship! must schedule job AlocateLicenseJob sending Line Item" do 16 | subject.product = create(:product) 17 | line_item = create(:line_item, product: subject.product) 18 | expect do 19 | subject.ship!(line_item) 20 | end.to have_enqueued_job(Admin::AlocateLicenseJob).with(line_item) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/models/juno/charge_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Juno::Charge, type: :model do 4 | subject { build(:juno_charge) } 5 | 6 | it { is_expected.to belong_to :order } 7 | it { is_expected.to have_many :credit_card_payments } 8 | 9 | it { is_expected.to validate_presence_of :key } 10 | it { is_expected.to validate_presence_of :code } 11 | it { is_expected.to validate_presence_of(:number) } 12 | it { is_expected.to validate_uniqueness_of(:number).scoped_to(:order_id).case_insensitive } 13 | it { is_expected.to validate_numericality_of(:number).is_greater_than(0).only_integer } 14 | it { is_expected.to validate_presence_of(:amount) } 15 | it { is_expected.to validate_numericality_of(:amount).is_greater_than(0) } 16 | it { is_expected.to validate_presence_of :status } 17 | end 18 | -------------------------------------------------------------------------------- /spec/models/juno/credit_card_payment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Juno::CreditCardPayment, type: :model do 4 | it { is_expected.to belong_to :charge } 5 | 6 | it { is_expected.to validate_presence_of :key } 7 | it { is_expected.to validate_presence_of :release_date } 8 | it { is_expected.to validate_presence_of :status } 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/license_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe License, type: :model do 4 | subject { build(:license) } 5 | 6 | it { is_expected.to belong_to :game } 7 | it { is_expected.to belong_to(:line_item).optional } 8 | 9 | it { is_expected.to validate_presence_of(:key) } 10 | it { is_expected.to validate_uniqueness_of(:key).case_insensitive.scoped_to(:platform) } 11 | it { is_expected.to validate_presence_of(:platform) } 12 | it { is_expected.to define_enum_for(:platform).with_values({ steam: 1, battle_net: 2, origin: 3 }) } 13 | it { is_expected.to validate_presence_of(:status) } 14 | it { is_expected.to define_enum_for(:status).with_values({ available: 1, in_use: 2, inactive: 3 }) } 15 | 16 | it_behaves_like "paginatable concern", :license 17 | it_has_behavior_of "like searchable concern", :license, :key 18 | 19 | it "must have a :line_item if it's :in_use" do 20 | subject.status = 'in_use' 21 | is_expected.to validate_presence_of(:line_item) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/models/line_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe LineItem, type: :model do 4 | it { is_expected.to validate_presence_of :quantity } 5 | it { is_expected.to validate_numericality_of(:quantity).only_integer.is_greater_than(0) } 6 | it { is_expected.to validate_presence_of :payed_price } 7 | it { is_expected.to validate_numericality_of(:payed_price).is_greater_than(0) } 8 | it { is_expected.to validate_presence_of(:status).on(:update) } 9 | it { is_expected.to define_enum_for(:status).with_values(waiting_order: 1, preparing: 2, en_route: 3, delivered: 4) } 10 | 11 | it { is_expected.to belong_to :order } 12 | it { is_expected.to belong_to :product } 13 | it { is_expected.to have_many :licenses } 14 | 15 | it "receives :waiting_order status as default on creation" do 16 | subject = create(:line_item, status: nil) 17 | expect(subject.status).to eq 'waiting_order' 18 | end 19 | 20 | it "#total must be :payed_price multiplied by :quantity" do 21 | payed_price = 153.32 22 | quantity = 2 23 | subject = build(:line_item, payed_price: payed_price, quantity: quantity) 24 | expected_value = payed_price * quantity 25 | expect(subject.total).to eq expected_value 26 | end 27 | 28 | context "when #ship!" do 29 | it "sets line item with :processing status" do 30 | subject = create(:line_item) 31 | subject.ship! 32 | subject.reload 33 | expect(subject.status).to eq 'preparing' 34 | end 35 | 36 | it "#forwards to :productable #ship! method" do 37 | line_item = create(:line_item) 38 | productable = line_item.product.productable 39 | expect(productable).to receive(:ship!).with(line_item) 40 | line_item.ship! 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/models/product_category_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe ProductCategory, type: :model do 4 | it { is_expected.to belong_to :product } 5 | it { is_expected.to belong_to :category } 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/product_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Product, type: :model do 4 | subject { build(:product) } 5 | 6 | it { is_expected.to validate_presence_of(:name) } 7 | it { is_expected.to validate_uniqueness_of(:name).case_insensitive } 8 | it { is_expected.to validate_presence_of(:description) } 9 | it { is_expected.to validate_presence_of(:price) } 10 | it { is_expected.to validate_numericality_of(:price).is_greater_than(0) } 11 | it { is_expected.to validate_presence_of(:image) } 12 | it { is_expected.to validate_presence_of(:status) } 13 | it { is_expected.to define_enum_for(:status).with_values({ available: 1, unavailable: 2 }) } 14 | it { is_expected.to validate_presence_of(:featured) } 15 | 16 | it { is_expected.to belong_to :productable } 17 | it { is_expected.to have_many(:product_categories).dependent(:destroy) } 18 | it { is_expected.to have_many(:categories).through(:product_categories) } 19 | it { is_expected.to have_many(:wish_items) } 20 | it { is_expected.to have_many(:line_items) } 21 | 22 | it_has_behavior_of "like searchable concern", :product, :name 23 | it_behaves_like "paginatable concern", :product 24 | 25 | it "creates as unfeatured by default" do 26 | subject.featured = nil 27 | subject.save(validate: false) 28 | expect(subject.featured).to be_falsey 29 | end 30 | 31 | it "#sells_count returns quantity product was sold" do 32 | order = create(:order) 33 | order.update(status: :finished) 34 | product = create(:product) 35 | create_list(:line_item, 2, quantity: 3, product: product, order: order) 36 | expect(product.sells_count).to eq 6 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/models/system_requirement_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe SystemRequirement, type: :model do 4 | it { is_expected.to validate_presence_of(:name) } 5 | it { is_expected.to validate_uniqueness_of(:name).case_insensitive } 6 | it { is_expected.to validate_presence_of(:operational_system) } 7 | it { is_expected.to validate_presence_of(:storage) } 8 | it { is_expected.to validate_presence_of(:processor) } 9 | it { is_expected.to validate_presence_of(:memory) } 10 | it { is_expected.to validate_presence_of(:video_board) } 11 | 12 | it { is_expected.to have_many(:games).dependent(:restrict_with_error) } 13 | 14 | it_has_behavior_of "like searchable concern", :system_requirement, :name 15 | it_behaves_like "paginatable concern", :system_requirement 16 | end 17 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe User, type: :model do 4 | it { is_expected.to have_many :wish_items } 5 | it { is_expected.to have_many :orders } 6 | 7 | it { is_expected.to validate_presence_of(:name) } 8 | it { is_expected.to validate_presence_of(:profile) } 9 | it { is_expected.to define_enum_for(:profile).with_values({ admin: 0, client: 1 }) } 10 | 11 | it_has_behavior_of "like searchable concern", :user, :name 12 | it_behaves_like "paginatable concern", :user 13 | end 14 | -------------------------------------------------------------------------------- /spec/models/wish_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe WishItem, type: :model do 4 | subject { build(:wish_item) } 5 | 6 | it { is_expected.to belong_to :user } 7 | it { is_expected.to belong_to :product } 8 | 9 | it { is_expected.to validate_uniqueness_of(:product_id).scoped_to(:user_id) } 10 | end 11 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | require 'spec_helper' 3 | ENV['RAILS_ENV'] ||= 'test' 4 | require File.expand_path('../config/environment', __dir__) 5 | # Prevent database truncation if the environment is production 6 | abort("The Rails environment is running in production mode!") if Rails.env.production? 7 | require 'rspec/rails' 8 | 9 | Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } 10 | Dir[Rails.root.join('spec', 'shared_examples', '**', '*.rb')].each { |f| require f } 11 | 12 | begin 13 | ActiveRecord::Migration.maintain_test_schema! 14 | rescue ActiveRecord::PendingMigrationError => e 15 | puts e.to_s.strip 16 | exit 1 17 | end 18 | 19 | RSpec.configure do |config| 20 | config.use_transactional_fixtures = true 21 | config.infer_spec_type_from_file_location! 22 | config.filter_rails_from_backtrace! 23 | config.alias_it_behaves_like_to :it_has_behavior_of, 'has behavior of' 24 | end 25 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/categories/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Categories as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /categories" do 7 | let(:url) { "/admin/v1/categories" } 8 | let!(:categories) { create_list(:category, 5) } 9 | 10 | before(:each) { get url, headers: auth_header(user) } 11 | 12 | include_examples "forbidden access" 13 | end 14 | 15 | context "POST /categories" do 16 | let(:url) { "/admin/v1/categories" } 17 | 18 | before(:each) { post url, headers: auth_header(user) } 19 | 20 | include_examples "forbidden access" 21 | end 22 | 23 | context "GET /categories/:id" do 24 | let(:category) { create(:category) } 25 | let(:url) { "/admin/v1/categories/#{category.id}" } 26 | 27 | before(:each) { get url, headers: auth_header(user) } 28 | 29 | include_examples "forbidden access" 30 | end 31 | 32 | context "PATCH /categories/:id" do 33 | let(:category) { create(:category) } 34 | let(:url) { "/admin/v1/categories/#{category.id}" } 35 | 36 | before(:each) { patch url, headers: auth_header(user) } 37 | 38 | include_examples "forbidden access" 39 | end 40 | 41 | context "DELETE /categories/:id" do 42 | let!(:category) { create(:category) } 43 | let(:url) { "/admin/v1/categories/#{category.id}" } 44 | 45 | before(:each) { delete url, headers: auth_header(user) } 46 | 47 | include_examples "forbidden access" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/categories/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Categories without authentication", type: :request do 4 | 5 | context "GET /categories" do 6 | let(:url) { "/admin/v1/categories" } 7 | let!(:categories) { create_list(:category, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "POST /categories" do 15 | let(:url) { "/admin/v1/categories" } 16 | 17 | before(:each) { post url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | 22 | context "GET /categories/:id" do 23 | let(:category) { create(:category) } 24 | let(:url) { "/admin/v1/categories/#{category.id}" } 25 | 26 | before(:each) { get url } 27 | 28 | include_examples "unauthenticated access" 29 | end 30 | 31 | context "PATCH /categories/:id" do 32 | let(:category) { create(:category) } 33 | let(:url) { "/admin/v1/categories/#{category.id}" } 34 | 35 | before(:each) { patch url } 36 | 37 | include_examples "unauthenticated access" 38 | end 39 | 40 | context "DELETE /categories/:id" do 41 | let!(:category) { create(:category) } 42 | let(:url) { "/admin/v1/categories/#{category.id}" } 43 | 44 | before(:each) { delete url } 45 | 46 | include_examples "unauthenticated access" 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/coupons/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Coupons as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /coupons" do 7 | let(:url) { "/admin/v1/coupons" } 8 | let!(:coupons) { create_list(:coupon, 5) } 9 | 10 | before(:each) { get url, headers: auth_header(user) } 11 | 12 | include_examples "forbidden access" 13 | end 14 | 15 | context "POST /coupons" do 16 | let(:url) { "/admin/v1/coupons" } 17 | 18 | before(:each) { post url, headers: auth_header(user) } 19 | 20 | include_examples "forbidden access" 21 | end 22 | 23 | context "GET /coupons/:id" do 24 | let(:coupon) { create(:coupon) } 25 | let(:url) { "/admin/v1/coupons/#{coupon.id}" } 26 | 27 | before(:each) { get url, headers: auth_header(user) } 28 | 29 | include_examples "forbidden access" 30 | end 31 | 32 | context "PATCH /coupons/:id" do 33 | let(:coupon) { create(:coupon) } 34 | let(:url) { "/admin/v1/coupons/#{coupon.id}" } 35 | 36 | before(:each) { patch url, headers: auth_header(user) } 37 | 38 | include_examples "forbidden access" 39 | end 40 | 41 | context "DELETE /coupons/:id" do 42 | let!(:coupon) { create(:coupon) } 43 | let(:url) { "/admin/v1/coupons/#{coupon.id}" } 44 | 45 | before(:each) { delete url, headers: auth_header(user) } 46 | 47 | include_examples "forbidden access" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/coupons/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Coupons without authentication", type: :request do 4 | 5 | context "GET /coupons" do 6 | let(:url) { "/admin/v1/coupons" } 7 | let!(:coupons) { create_list(:coupon, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "POST /coupons" do 15 | let(:url) { "/admin/v1/coupons" } 16 | 17 | before(:each) { post url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | 22 | context "GET /coupons/:id" do 23 | let(:coupon) { create(:coupon) } 24 | let(:url) { "/admin/v1/coupons/#{coupon.id}" } 25 | 26 | before(:each) { get url } 27 | 28 | include_examples "unauthenticated access" 29 | end 30 | 31 | context "PATCH /coupons/:id" do 32 | let(:coupon) { create(:coupon) } 33 | let(:url) { "/admin/v1/coupons/#{coupon.id}" } 34 | 35 | before(:each) { patch url } 36 | 37 | include_examples "unauthenticated access" 38 | end 39 | 40 | context "DELETE /coupons/:id" do 41 | let!(:coupon) { create(:coupon) } 42 | let(:url) { "/admin/v1/coupons/#{coupon.id}" } 43 | 44 | before(:each) { delete url } 45 | 46 | include_examples "unauthenticated access" 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/sales_ranges/admin_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Sales Ranges as :admin", type: :request do 4 | let(:user) { create(:user) } 5 | 6 | context "GET /dashboard/sales_ranges" do 7 | let(:url) { "/admin/v1/dashboard/sales_ranges" } 8 | 9 | context "when date range is less than one 1 month" do 10 | let(:params) { { min_date: 20.days.ago.strftime("%Y-%m-%d"), max_date: Date.current.strftime("%Y-%m-%d") } } 11 | 12 | let(:product) { create(:product) } 13 | 14 | let!(:sales_line_items) do 15 | 20.downto(1).map do |num| 16 | order = create(:order, created_at: num.days.ago) 17 | order.update_column(:status, :finished) 18 | create(:line_item, order: order, product: product, payed_price: 200, quantity: num) 19 | end 20 | end 21 | 22 | it "returns products in a daily basis" do 23 | get url, headers: auth_header(user), params: params 24 | expected_result = sales_line_items.map do |line_item| 25 | day = line_item.order.created_at.strftime("%Y-%m-%d") 26 | total_sold = line_item.payed_price * line_item.quantity 27 | { "date" => day, "total_sold" => total_sold.to_f } 28 | end 29 | expect(body_json['sales_ranges']).to eq expected_result 30 | end 31 | 32 | it "returns :ok status" do 33 | get url, headers: auth_header(user), params: params 34 | expect(response).to have_http_status(:ok) 35 | end 36 | end 37 | 38 | context "when date range is more than one 1 month" do 39 | let(:params) { { min_date: 5.months.ago.strftime("%Y-%m-%d"), max_date: Date.current.strftime("%Y-%m-%d") } } 40 | 41 | let(:product) { create(:product) } 42 | 43 | let!(:sales_line_items) do 44 | 5.downto(1).map do |num| 45 | order = create(:order, created_at: num.months.ago) 46 | order.update_column(:status, :finished) 47 | create(:line_item, order: order, product: product, payed_price: 200, quantity: num) 48 | end 49 | end 50 | 51 | it "returns products in a monthly basis" do 52 | get url, headers: auth_header(user), params: params 53 | expected_result = sales_line_items.map do |line_item| 54 | month = line_item.order.created_at.strftime("%Y-%m") 55 | total_sold = line_item.payed_price * line_item.quantity 56 | { "date" => month, "total_sold" => total_sold.to_f } 57 | end 58 | expect(body_json['sales_ranges']).to eq expected_result 59 | end 60 | 61 | it "returns :ok status" do 62 | get url, headers: auth_header(user), params: params 63 | expect(response).to have_http_status(:ok) 64 | end 65 | end 66 | end 67 | end -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/sales_ranges/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Sales Ranges as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /dashboard/sales_ranges" do 7 | let(:url) { "/admin/v1/dashboard/sales_ranges" } 8 | 9 | before(:each) { get url, headers: auth_header(user) } 10 | 11 | include_examples "forbidden access" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/sales_ranges/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Sales Ranges without authentication", type: :request do 4 | 5 | context "GET /dashboard/sales_ranges" do 6 | let(:url) { "/admin/v1/dashboard/sales_ranges" } 7 | 8 | before(:each) { get url } 9 | 10 | include_examples "unauthenticated access" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/summaries/admin_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Summaries as :admin", type: :request do 4 | let(:user) { create(:user) } 5 | 6 | context "GET /dashboard/summaries" do 7 | let(:url) { "/admin/v1/dashboard/summaries" } 8 | let(:params) { { min_date: 3.days.ago.strftime("%Y-%m-%d"), max_date: Date.current.strftime("%Y-%m-%d") } } 9 | let!(:users) { create_list(:user, 5, created_at: 2.days.ago) } 10 | let!(:users_out_of_range) { create_list(:user, 5, created_at: 4.days.ago) } 11 | let!(:products) { create_list(:product, 5, created_at: 2.days.ago) } 12 | let!(:products_out_of_range) { create_list(:product, 5, created_at: 4.days.ago) } 13 | let!(:orders) do 14 | create_list(:order, 5, user: user, created_at: 2.days.ago, total_amount: 54.15).map do |order| 15 | order.update_column(:status, :finished) 16 | order 17 | end 18 | end 19 | let!(:orders_out_of_range) do 20 | create_list(:order, 5, user: user, created_at: 4.days.ago, total_amount: 54.15).map do |order| 21 | order.update_column(:status, :finished) 22 | order 23 | end 24 | end 25 | 26 | it "returns right summary of data" do 27 | get url, headers: auth_header(user), params: params 28 | expected_profit = orders.sum(&:total_amount) 29 | expected_result = { 30 | users: users.size + 1, products: products.size, orders: orders.size, profit: expected_profit 31 | }.as_json 32 | expect(body_json['summary']).to eq expected_result 33 | end 34 | 35 | it "returns :ok status" do 36 | get url, headers: auth_header(user), params: params 37 | expect(response).to have_http_status(:ok) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/summaries/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Summaries as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /dashboard/summaries" do 7 | let(:url) { "/admin/v1/dashboard/summaries" } 8 | 9 | before(:each) { get url, headers: auth_header(user) } 10 | 11 | include_examples "forbidden access" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/summaries/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Summaries without authentication", type: :request do 4 | 5 | context "GET /dashboard/summaries" do 6 | let(:url) { "/admin/v1/dashboard/summaries" } 7 | 8 | before(:each) { get url } 9 | 10 | include_examples "unauthenticated access" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/top_five_products/admin_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Top Five Products as :admin", type: :request do 4 | let(:user) { create(:user) } 5 | 6 | context "GET /dashboard/top_five_products" do 7 | let(:url) { "/admin/v1/dashboard/top_five_products" } 8 | let(:params) { { min_date: 5.days.ago.strftime("%Y-%m-%d"), max_date: Date.current.strftime("%Y-%m-%d") } } 9 | 10 | let(:top_five_products) { create_list(:product, 5) } 11 | let(:less_sold_products) { create_list(:product, 5) } 12 | let(:order) do 13 | order = create(:order, created_at: 4.days.ago) 14 | order.update_column(:status, :finished) 15 | order 16 | end 17 | let!(:top_five_line_itens) do 18 | top_five_products.map.with_index do |product, index| 19 | create(:line_item, payed_price: 200, quantity: (index + 1), order: order, product: product) 20 | end 21 | end 22 | let(:out_of_date_order) do 23 | order = create(:order, created_at: 8.days.ago) 24 | order.update_column(:status, :finished) 25 | order 26 | end 27 | let!(:out_of_date_line_items) do 28 | less_sold_products.map.with_index do |product, index| 29 | create(:line_item, payed_price: 2000, quantity: (index + 1), order: out_of_date_order, product: product) 30 | end 31 | end 32 | 33 | 34 | it "returns right top five products" do 35 | get url, headers: auth_header(user), params: params 36 | expected_result = top_five_line_itens.reverse.map do |line_item| 37 | total_sold = line_item.quantity * line_item.payed_price 38 | product = line_item.product 39 | { 40 | 'product' => product.name, 'image' => rails_blob_url(product.image, host: "localhost", port: 3000), 41 | 'quantity' => line_item.quantity, 'total_sold' => total_sold.to_f 42 | } 43 | end 44 | expect(body_json['top_five_products']).to eq expected_result 45 | end 46 | 47 | it "returns :ok status" do 48 | get url, headers: auth_header(user), params: params 49 | expect(response).to have_http_status(:ok) 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/top_five_products/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Top Five Products as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /dashboard/top_five_products" do 7 | let(:url) { "/admin/v1/dashboard/top_five_products" } 8 | 9 | before(:each) { get url, headers: auth_header(user) } 10 | 11 | include_examples "forbidden access" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/dashboard/top_five_products/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Dashboard Top Five Products without authentication", type: :request do 4 | 5 | context "GET /dashboard/top_five_products" do 6 | let(:url) { "/admin/v1/dashboard/top_five_products" } 7 | 8 | before(:each) { get url } 9 | 10 | include_examples "unauthenticated access" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/home_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe "Home", type: :request do 4 | let(:user) { create(:user) } 5 | 6 | it "tests home" do 7 | get '/admin/v1/home', headers: auth_header(user) 8 | expect(body_json).to eq({ 'message' => 'Uhul!' }) 9 | end 10 | 11 | it "tests home" do 12 | get '/admin/v1/home', headers: auth_header(user) 13 | expect(response).to have_http_status(:ok) 14 | end 15 | end -------------------------------------------------------------------------------- /spec/requests/admin/v1/licenses/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Licenses as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | let(:game) { create(:game) } 6 | 7 | context "GET /games/:game_id/licenses" do 8 | let(:url) { "/admin/v1/games/#{game.id}/licenses" } 9 | let!(:licenses) { create_list(:license, 5, game: game) } 10 | 11 | before(:each) { get url, headers: auth_header(user) } 12 | 13 | include_examples "forbidden access" 14 | end 15 | 16 | context "POST /games/:game_id/licenses" do 17 | let(:url) { "/admin/v1/games/#{game.id}/licenses" } 18 | 19 | before(:each) { post url, headers: auth_header(user) } 20 | 21 | include_examples "forbidden access" 22 | end 23 | 24 | context "GET /licenses/:id" do 25 | let(:license) { create(:license) } 26 | let(:url) { "/admin/v1/licenses/#{license.id}" } 27 | 28 | before(:each) { get url, headers: auth_header(user) } 29 | 30 | include_examples "forbidden access" 31 | end 32 | 33 | context "PATCH /licenses/:id" do 34 | let(:license) { create(:license) } 35 | let(:url) { "/admin/v1/licenses/#{license.id}" } 36 | 37 | before(:each) { patch url, headers: auth_header(user) } 38 | 39 | include_examples "forbidden access" 40 | end 41 | 42 | context "DELETE /licenses/:id" do 43 | let!(:license) { create(:license) } 44 | let(:url) { "/admin/v1/licenses/#{license.id}" } 45 | 46 | before(:each) { delete url, headers: auth_header(user) } 47 | 48 | include_examples "forbidden access" 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/licenses/unauthenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Licenses without authentication", type: :request do 4 | let(:game) { create(:game) } 5 | 6 | context "GET /games/:game_id/licenses" do 7 | let(:url) { "/admin/v1/games/#{game.id}/licenses" } 8 | let!(:licenses) { create_list(:license, 5) } 9 | 10 | before(:each) { get url } 11 | 12 | include_examples "unauthenticated access" 13 | end 14 | 15 | context "POST /games/:game_id/licenses" do 16 | let(:url) { "/admin/v1/games/#{game.id}/licenses" } 17 | 18 | before(:each) { post url } 19 | 20 | include_examples "unauthenticated access" 21 | end 22 | 23 | context "GET /licenses/:id" do 24 | let(:license) { create(:license) } 25 | let(:url) { "/admin/v1/licenses/#{license.id}" } 26 | 27 | before(:each) { get url } 28 | 29 | include_examples "unauthenticated access" 30 | end 31 | 32 | context "PATCH /licenses/:id" do 33 | let(:license) { create(:license) } 34 | let(:url) { "/admin/v1/licenses/#{license.id}" } 35 | 36 | before(:each) { patch url } 37 | 38 | include_examples "unauthenticated access" 39 | end 40 | 41 | context "DELETE /licenses/:id" do 42 | let!(:license) { create(:license) } 43 | let(:url) { "/admin/v1/licenses/#{license.id}" } 44 | 45 | before(:each) { delete url } 46 | 47 | include_examples "unauthenticated access" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/orders/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Orders as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /orders" do 7 | let(:url) { "/admin/v1/orders" } 8 | let!(:orders) { create_list(:order, 5) } 9 | 10 | before(:each) { get url, headers: auth_header(user) } 11 | 12 | include_examples "forbidden access" 13 | end 14 | 15 | context "GET /orders/:id" do 16 | let(:order) { create(:order) } 17 | let(:url) { "/admin/v1/orders/#{order.id}" } 18 | 19 | before(:each) { get url, headers: auth_header(user) } 20 | 21 | include_examples "forbidden access" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/orders/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Orders without authentication", type: :request do 4 | context "GET /orders" do 5 | let(:url) { "/admin/v1/orders" } 6 | let!(:orders) { create_list(:order, 5) } 7 | 8 | before(:each) { get url } 9 | 10 | include_examples "unauthenticated access" 11 | end 12 | 13 | context "GET /orders/:id" do 14 | let(:order) { create(:order) } 15 | let(:url) { "/admin/v1/orders/#{order.id}" } 16 | 17 | before(:each) { get url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/products/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Products as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /products" do 7 | let(:url) { "/admin/v1/products" } 8 | let!(:products) { create_list(:product, 5) } 9 | 10 | before(:each) { get url, headers: auth_header(user) } 11 | 12 | include_examples "forbidden access" 13 | end 14 | 15 | context "POST /products" do 16 | let(:url) { "/admin/v1/products" } 17 | 18 | before(:each) { post url, headers: auth_header(user) } 19 | 20 | include_examples "forbidden access" 21 | end 22 | 23 | context "GET /products/:id" do 24 | let(:product) { create(:product) } 25 | let(:url) { "/admin/v1/products/#{product.id}" } 26 | 27 | before(:each) { get url, headers: auth_header(user) } 28 | 29 | include_examples "forbidden access" 30 | end 31 | 32 | context "PATCH /products/:id" do 33 | let(:product) { create(:product) } 34 | let(:url) { "/admin/v1/products/#{product.id}" } 35 | 36 | before(:each) { patch url, headers: auth_header(user) } 37 | 38 | include_examples "forbidden access" 39 | end 40 | 41 | context "DELETE /products/:id" do 42 | let!(:product) { create(:product) } 43 | let(:url) { "/admin/v1/products/#{product.id}" } 44 | 45 | before(:each) { delete url, headers: auth_header(user) } 46 | 47 | include_examples "forbidden access" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/products/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Product without authentication", type: :request do 4 | 5 | context "GET /products" do 6 | let(:url) { "/admin/v1/products" } 7 | let!(:products) { create_list(:product, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "POST /products" do 15 | let(:url) { "/admin/v1/products" } 16 | 17 | before(:each) { post url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | 22 | context "GET /products/:id" do 23 | let(:product) { create(:product) } 24 | let(:url) { "/admin/v1/products/#{product.id}" } 25 | 26 | before(:each) { get url } 27 | 28 | include_examples "unauthenticated access" 29 | end 30 | 31 | context "PATCH /products/:id" do 32 | let(:product) { create(:product) } 33 | let(:url) { "/admin/v1/products/#{product.id}" } 34 | 35 | before(:each) { patch url } 36 | 37 | include_examples "unauthenticated access" 38 | end 39 | 40 | context "DELETE /products/:id" do 41 | let!(:product) { create(:product) } 42 | let(:url) { "/admin/v1/products/#{product.id}" } 43 | 44 | before(:each) { delete url } 45 | 46 | include_examples "unauthenticated access" 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/system_requirements/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 System Requirements as :client", type: :request do 4 | let(:user) { create(:user, profile: :client) } 5 | 6 | context "GET /system_requirements" do 7 | let(:url) { "/admin/v1/system_requirements" } 8 | let!(:system_requirements) { create_list(:system_requirement, 5) } 9 | 10 | before(:each) { get url, headers: auth_header(user) } 11 | 12 | include_examples "forbidden access" 13 | end 14 | 15 | context "POST /system_requirements" do 16 | let(:url) { "/admin/v1/system_requirements" } 17 | 18 | before(:each) { post url, headers: auth_header(user) } 19 | 20 | include_examples "forbidden access" 21 | end 22 | 23 | context "GET /system_requirements/:id" do 24 | let(:system_requirement) { create(:system_requirement) } 25 | let(:url) { "/admin/v1/system_requirements/#{system_requirement.id}" } 26 | 27 | before(:each) { get url, headers: auth_header(user) } 28 | 29 | include_examples "forbidden access" 30 | end 31 | 32 | context "PATCH /system_requirements/:id" do 33 | let(:system_requirement) { create(:system_requirement) } 34 | let(:url) { "/admin/v1/system_requirements/#{system_requirement.id}" } 35 | 36 | before(:each) { patch url, headers: auth_header(user) } 37 | 38 | include_examples "forbidden access" 39 | end 40 | 41 | context "DELETE /system_requirements/:id" do 42 | let!(:system_requirement) { create(:system_requirement) } 43 | let(:url) { "/admin/v1/system_requirements/#{system_requirement.id}" } 44 | 45 | before(:each) { delete url, headers: auth_header(user) } 46 | 47 | include_examples "forbidden access" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/system_requirements/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 System Requirements without authentication", type: :request do 4 | 5 | context "GET /system_requirements" do 6 | let(:url) { "/admin/v1/system_requirements" } 7 | let!(:system_requirements) { create_list(:system_requirement, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "POST /system_requirements" do 15 | let(:url) { "/admin/v1/system_requirements" } 16 | 17 | before(:each) { post url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | 22 | context "GET /system_requirements/:id" do 23 | let(:system_requirement) { create(:system_requirement) } 24 | let(:url) { "/admin/v1/system_requirements/#{system_requirement.id}" } 25 | 26 | before(:each) { get url } 27 | 28 | include_examples "unauthenticated access" 29 | end 30 | 31 | context "PATCH /system_requirements/:id" do 32 | let(:system_requirement) { create(:system_requirement) } 33 | let(:url) { "/admin/v1/system_requirements/#{system_requirement.id}" } 34 | 35 | before(:each) { patch url } 36 | 37 | include_examples "unauthenticated access" 38 | end 39 | 40 | context "DELETE /system_requirements/:id" do 41 | let!(:system_requirement) { create(:system_requirement) } 42 | let(:url) { "/admin/v1/system_requirements/#{system_requirement.id}" } 43 | 44 | before(:each) { delete url } 45 | 46 | include_examples "unauthenticated access" 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/users/client_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Users as :client", type: :request do 4 | let(:login_user) { create(:user, profile: :client) } 5 | 6 | context "GET /users" do 7 | let(:url) { "/admin/v1/users" } 8 | let!(:users) { create_list(:user, 5) } 9 | 10 | before(:each) { get url, headers: auth_header(login_user) } 11 | 12 | include_examples "forbidden access" 13 | end 14 | 15 | context "POST /users" do 16 | let(:url) { "/admin/v1/users" } 17 | 18 | before(:each) { post url, headers: auth_header(login_user) } 19 | 20 | include_examples "forbidden access" 21 | end 22 | 23 | context "GET /users/:id" do 24 | let(:user) { create(:user) } 25 | let(:url) { "/admin/v1/users/#{user.id}" } 26 | 27 | before(:each) { get url, headers: auth_header(login_user) } 28 | 29 | include_examples "forbidden access" 30 | end 31 | 32 | context "PATCH /users/:id" do 33 | let(:user) { create(:user) } 34 | let(:url) { "/admin/v1/users/#{user.id}" } 35 | 36 | before(:each) { patch url, headers: auth_header(login_user) } 37 | 38 | include_examples "forbidden access" 39 | end 40 | 41 | context "DELETE /users/:id" do 42 | let!(:user) { create(:user) } 43 | let(:url) { "/admin/v1/users/#{user.id}" } 44 | 45 | before(:each) { delete url, headers: auth_header(login_user) } 46 | 47 | include_examples "forbidden access" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/requests/admin/v1/users/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Admin V1 Users without authentication", type: :request do 4 | 5 | context "GET /users" do 6 | let(:url) { "/admin/v1/users" } 7 | let!(:users) { create_list(:user, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "POST /users" do 15 | let(:url) { "/admin/v1/users" } 16 | 17 | before(:each) { post url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | 22 | context "GET /users/:id" do 23 | let(:user) { create(:user) } 24 | let(:url) { "/admin/v1/users/#{user.id}" } 25 | 26 | before(:each) { get url } 27 | 28 | include_examples "unauthenticated access" 29 | end 30 | 31 | context "PATCH /users/:id" do 32 | let(:user) { create(:user) } 33 | let(:url) { "/admin/v1/users/#{user.id}" } 34 | 35 | before(:each) { patch url } 36 | 37 | include_examples "unauthenticated access" 38 | end 39 | 40 | context "DELETE /users/:id" do 41 | let!(:user) { create(:user) } 42 | let(:url) { "/admin/v1/users/#{user.id}" } 43 | 44 | before(:each) { delete url } 45 | 46 | include_examples "unauthenticated access" 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/requests/auth/v1/sign_in_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Auth V1 Sign in", type: :request do 4 | context "as :admin" do 5 | let!(:user) { create(:user, email: "admin@test.com", password: "123456") } 6 | 7 | include_examples 'sign in', 'admin@test.com', '123456' 8 | end 9 | 10 | context "as :client" do 11 | let!(:user) { create(:user, profile: :client, email: "client@test.com", password: "123456") } 12 | 13 | include_examples 'sign in', 'client@test.com', '123456' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/requests/auth/v1/sign_out_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Auth V1 Sign out", type: :request do 4 | let(:url) { '/auth/v1/user/sign_out' } 5 | let!(:user) { create(:user) } 6 | 7 | it "removes a token from User" do 8 | user_headers = auth_header(user) 9 | user_token = user_headers['client'] 10 | delete url, headers: user_headers 11 | user.reload 12 | expect(user.tokens.keys).to_not include(user_token) 13 | end 14 | 15 | it "returns :ok status" do 16 | delete url, headers: auth_header(user) 17 | expect(response).to have_http_status(:ok) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/requests/auth/v1/sign_up_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Auth V1 Sign up", type: :request do 4 | let(:url) { '/auth/v1/user' } 5 | 6 | context "with valid params" do 7 | let(:user_params) { attributes_for(:user, profile: nil) } 8 | 9 | it "adds new User" do 10 | expect { 11 | post url, params: user_params 12 | }.to change(User, :count).by(1) 13 | end 14 | 15 | it "add User as :client" do 16 | post url, params: user_params 17 | expect(User.last.profile).to eq 'client' 18 | end 19 | 20 | it "returns :ok status" do 21 | post url, params: user_params 22 | expect(response).to have_http_status(:ok) 23 | end 24 | end 25 | 26 | context "with invalid params" do 27 | let(:user_invalid_params) { attributes_for(:user, email: nil) } 28 | 29 | it "does not add a new User" do 30 | expect { 31 | post url, params: user_invalid_params 32 | }.to_not change(User, :count) 33 | end 34 | 35 | it "return :unprocessable_entity status" do 36 | post url, params: user_invalid_params 37 | expect(response).to have_http_status(:unprocessable_entity) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/requests/juno/v1/payment_confirmations/authenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe "Juno V1 Payment Confirmations without authentication", type: :request do 4 | context "POST /payment_confirmations" do 5 | let(:token) { Rails.application.credentials.token[:auth] } 6 | let(:url) { "/juno/v1/payment_confirmations?token=#{token}" } 7 | let!(:order) { create(:order, status: :waiting_payment, payment_type: :billet) } 8 | let!(:juno_charges) { create(:juno_charge, order: order) } 9 | 10 | context "when 'chargeCode' param is present" do 11 | let(:params) do 12 | { 'paymentToken' => SecureRandom.hex, 'chargeReference' => '', 'chargeCode' => juno_charges.code } 13 | end 14 | 15 | it "sets order with :payment_accepted status" do 16 | post url, headers: unauthenticated_header, params: params.to_json 17 | order.reload 18 | expect(order.status).to eq 'payment_accepted' 19 | end 20 | 21 | it "returns :ok status" do 22 | post url, headers: unauthenticated_header, params: params.to_json 23 | expect(response).to have_http_status(:ok) 24 | end 25 | end 26 | 27 | context "when 'chargeCode' param does not exist" do 28 | let(:params) do 29 | { 'paymentToken' => SecureRandom.hex, 'chargeReference' => '', 'chargeCode' => 'some_random_code' } 30 | end 31 | 32 | it "keep order with same status" do 33 | post url, headers: unauthenticated_header, params: params.to_json 34 | old_status = order.status 35 | order.reload 36 | expect(order.status).to eq old_status 37 | end 38 | 39 | it "returns :ok status" do 40 | post url, headers: unauthenticated_header, params: params.to_json 41 | expect(response).to have_http_status(:ok) 42 | end 43 | end 44 | 45 | context "when 'chargeCode' param is not present" do 46 | let(:params) { { 'someOtherParam' => 'some_param_value' } } 47 | 48 | it "keep order with same status" do 49 | post url, headers: unauthenticated_header, params: params.to_json 50 | old_status = order.status 51 | order.reload 52 | expect(order.status).to eq old_status 53 | end 54 | 55 | it "returns :ok status" do 56 | post url, headers: unauthenticated_header, params: params.to_json 57 | expect(response).to have_http_status(:ok) 58 | end 59 | end 60 | end 61 | end -------------------------------------------------------------------------------- /spec/requests/juno/v1/payment_confirmations/unauthenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe "Juno V1 Payment Confirmations", type: :request do 4 | context "POST /payment_confirmations" do 5 | let(:url) { "/juno/v1/payment_confirmations" } 6 | let!(:order) { create(:order, status: :waiting_payment, payment_type: :billet) } 7 | let!(:juno_charges) { create(:juno_charge, order: order) } 8 | 9 | before(:each) { post url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | end -------------------------------------------------------------------------------- /spec/requests/storefront/v1/categories/unathenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe "Storefront V1 Home", type: :request do 4 | context "GET /categories" do 5 | let(:url) { "/storefront/v1/categories" } 6 | let!(:categories) { create_list(:category, 15) } 7 | 8 | it "returns all Categories" do 9 | get url, headers: unauthenticated_header 10 | expect(body_json['categories'].count).to eq 15 11 | end 12 | 13 | it "returns Categories ordered by name" do 14 | get url, headers: unauthenticated_header 15 | expected_categories = categories.sort { |a, b| a[:name] <=> b[:name] }.as_json(only: %i(id name)) 16 | expect(body_json['categories']).to contain_exactly *expected_categories 17 | end 18 | 19 | it "returns success status" do 20 | get url, headers: unauthenticated_header 21 | expect(response).to have_http_status(:ok) 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /spec/requests/storefront/v1/checkouts/authenticated_request_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "Storefront V1 Checkout as authenticated user", type: :request do 2 | let(:user) { create(:user, [:admin, :client].sample) } 3 | 4 | context "POST /checkouts" do 5 | let(:url) { "/storefront/v1/checkouts" } 6 | let!(:products) { create_list(:product, 3) } 7 | 8 | context "with valid params" do 9 | let!(:coupon) { create(:coupon) } 10 | let(:params) do 11 | { 12 | checkout: { 13 | payment_type: :credit_card, installments: 2, 14 | document: '03.000.050/0001-67', card_hash: "123456", address: attributes_for(:address), 15 | items: [ 16 | { quantity: 2, product_id: products.first.id }, 17 | { quantity: 3, product_id: products.second.id } 18 | ] 19 | } 20 | }.to_json 21 | end 22 | 23 | it 'creates a new Order' do 24 | expect do 25 | post url, headers: auth_header, params: params 26 | end.to change(Order, :count).by(1) 27 | end 28 | 29 | it 'creates associated Line Items' do 30 | expect do 31 | post url, headers: auth_header, params: params 32 | end.to change(LineItem, :count).by(2) 33 | end 34 | 35 | it 'returns created Order with associated Line Itens and Coupon' do 36 | post url, headers: auth_header, params: params 37 | order = Order.last 38 | expected_order = order.as_json(only: %i(id payment_type installments)) 39 | expected_order.merge!('subtotal' => order.subtotal.to_f, 'total_amount' => order.total_amount.to_f) 40 | expect(body_json['order']).to eq expected_order 41 | end 42 | 43 | it 'returns success status' do 44 | post url, headers: auth_header, params: params 45 | expect(response).to have_http_status(:ok) 46 | end 47 | end 48 | 49 | context "with invalid params" do 50 | let(:invalid_params) do 51 | { 52 | checkout: { 53 | installments: 2, document: '03.000.050/0001-67', 54 | items: [ 55 | { quantity: 2, product_id: products.first.id }, 56 | { quantity: 3, product_id: products.second.id } 57 | ] 58 | } 59 | }.to_json 60 | end 61 | 62 | it 'does not create a Order' do 63 | expect do 64 | post url, headers: auth_header, params: invalid_params 65 | end.to_not change(Order, :count) 66 | end 67 | 68 | it 'does not create any Line Items' do 69 | expect do 70 | post url, headers: auth_header, params: invalid_params 71 | end.to_not change(LineItem, :count) 72 | end 73 | 74 | it 'returns error message' do 75 | post url, headers: auth_header, params: invalid_params 76 | expect(body_json['errors']['fields']).to have_key('payment_type') 77 | end 78 | 79 | it 'returns unprocessable_entity status' do 80 | post url, headers: auth_header, params: invalid_params 81 | expect(response).to have_http_status(:unprocessable_entity) 82 | end 83 | end 84 | end 85 | end -------------------------------------------------------------------------------- /spec/requests/storefront/v1/checkouts/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Checkouts without authentication", type: :request do 4 | 5 | context "POST /checkouts" do 6 | let(:url) { "/storefront/v1/checkouts" } 7 | 8 | before(:each) { post url } 9 | 10 | include_examples "unauthenticated access" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/coupon_validations/authenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Coupon Validation as authenticated user", type: :request do 4 | let(:user) { create(:user, [:admin, :client].sample) } 5 | 6 | context "POST /coupons/:coupon_code/validations" do 7 | context "with valid coupon" do 8 | let(:coupon) { create(:coupon) } 9 | let(:url) { "/storefront/v1/coupons/#{coupon.code}/validations" } 10 | 11 | it 'returns success status' do 12 | post url, headers: auth_header(user) 13 | expect(response).to have_http_status(:ok) 14 | end 15 | 16 | it 'returns valid Coupon' do 17 | post url, headers: auth_header(user) 18 | expected_coupon = coupon.as_json(only: %i(id code discount_value)) 19 | expect(body_json['coupon']).to eq expected_coupon 20 | end 21 | end 22 | 23 | context "with invalid coupon" do 24 | let(:coupon) { create(:coupon, status: :inactive) } 25 | let(:url) { "/storefront/v1/coupons/#{coupon.code}/validations" } 26 | 27 | it 'returns unprocessable_entity status' do 28 | post url, headers: auth_header(user) 29 | expect(response).to have_http_status(:unprocessable_entity) 30 | end 31 | 32 | it 'returns valid Coupon' do 33 | post url, headers: auth_header(user) 34 | failure_message = I18n.t('storefront/v1/coupon_validations.create.failure') 35 | expect(body_json['errors']['message']).to eq failure_message 36 | end 37 | end 38 | 39 | context "when coupon does not exist" do 40 | let(:url) { "/storefront/v1/coupons/aaa/validations" } 41 | 42 | it 'returns unprocessable_entity status' do 43 | post url, headers: auth_header(user) 44 | expect(response).to have_http_status(:unprocessable_entity) 45 | end 46 | 47 | it 'returns valid Coupon' do 48 | post url, headers: auth_header(user) 49 | failure_message = I18n.t('storefront/v1/coupon_validations.create.failure') 50 | expect(body_json['errors']['message']).to eq failure_message 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/coupon_validations/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Coupon Validation without authentication", type: :request do 4 | 5 | context "POST /coupons/:coupon_code/validations" do 6 | let(:coupon) { create(:coupon) } 7 | let(:url) { "/storefront/v1/coupons/#{coupon.code}/validations" } 8 | 9 | before(:each) { post url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/games/authenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Games as authenticated user", type: :request do 4 | let(:user) { create(:user) } 5 | 6 | context "GET /games" do 7 | let(:url) { "/storefront/v1/games" } 8 | let!(:user_games) { create_list(:product, 3) } 9 | let!(:order) { create(:order, user: user) } 10 | let!(:line_items) do 11 | 0.upto(2).map { |index| create(:line_item, order: order, product: user_games[index], quantity: 1) } 12 | end 13 | let!(:licenses) do 14 | line_items.map { |line_item| create_list(:license, line_item.quantity, line_item: line_item) } 15 | end 16 | let!(:non_user_order) { create(:order) } 17 | let!(:non_user_line_items) { create_list(:line_item, 4, order: non_user_order, product: user_games.first) } 18 | let!(:non_user_licenses) do 19 | non_user_line_items.map { |line_item| create_list(:license, line_item.quantity, line_item: line_item) } 20 | end 21 | 22 | it "returns all user games" do 23 | get url, headers: auth_header(user) 24 | expected_games = build_game_structure(user_games, line_items) 25 | expect(body_json['games']).to contain_exactly *expected_games 26 | end 27 | 28 | it "does not return any non-user licenses" do 29 | get url, headers: auth_header(user) 30 | game = body_json['games'].select { |game| game['id'] == user_games.first.id }.first 31 | unexpected_licenses = non_user_line_items.map { |line_item| line_item.licenses.map(&:key) }.flatten 32 | expect(game['licenses']).to_not include *unexpected_licenses 33 | end 34 | 35 | it "returns success status" do 36 | get url, headers: auth_header(user) 37 | expect(response).to have_http_status(:ok) 38 | end 39 | end 40 | 41 | def build_game_structure(products, line_items) 42 | products.map do |product| 43 | json = product.as_json(only: %i[id name description]) 44 | json['image_url'] = rails_blob_url(product.image) 45 | json.merge! product.productable.as_json(only: %i[mode developer release_date]) 46 | json['system_requirement'] = product.productable.system_requirement.as_json 47 | game_licenses = line_items.select { |line_item| line_item.product_id == product.id }.map(&:licenses).flatten 48 | json.merge!({ 'licenses' => game_licenses.map(&:key) }) 49 | json 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/games/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Games without authentication", type: :request do 4 | 5 | context "GET /games" do 6 | let(:url) { "/storefront/v1/games" } 7 | let!(:games) { create_list(:product, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/orders/authenticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Orders as authenticated user", type: :request do 4 | let(:user) { create(:user) } 5 | 6 | context "GET /orders" do 7 | let(:url) { "/storefront/v1/orders" } 8 | let!(:user_orders) { create_list(:order, 10, user: user) } 9 | let!(:non_user_orders) { create_list(:order, 10) } 10 | 11 | it "returns all user orders" do 12 | get url, headers: auth_header(user) 13 | expected_orders = user_orders.as_json(only: %i[id status total_amount payment_type]) 14 | expect(body_json['orders']).to contain_exactly *expected_orders 15 | end 16 | 17 | it "does not return any non-user orders" do 18 | get url, headers: auth_header(user) 19 | unexpected_orders = non_user_orders.as_json(only: %i[id status total_amount payment_type]) 20 | expect(body_json['orders']).to_not include *unexpected_orders 21 | end 22 | 23 | it "returns success status" do 24 | get url, headers: auth_header(user) 25 | expect(response).to have_http_status(:ok) 26 | end 27 | end 28 | 29 | context "GET /order/:id" do 30 | context "when user tries to access its own order" do 31 | let!(:coupon) { create(:coupon) } 32 | let!(:order) { create(:order, user: user, coupon: coupon) } 33 | let!(:line_items) { create_list(:line_item, 5, order: order) } 34 | let(:url) { "/storefront/v1/orders/#{order.id}" } 35 | 36 | it "returns requested Order" do 37 | get url, headers: auth_header(user) 38 | expected_line_items = line_items.map do |line_item| 39 | formatted = line_item.as_json(only: %i[quantity payed_price]) 40 | formatted['product'] = line_item.product.name 41 | formatted 42 | end 43 | expected_order = order.as_json(only: %i[id status total_amount subtotal payment_type]) 44 | .merge({ 'discount' => coupon.discount_value.to_f, 'line_items' => expected_line_items }) 45 | expect(body_json['order']).to eq expected_order 46 | end 47 | 48 | it "returns success status" do 49 | get url, headers: auth_header(user) 50 | expect(response).to have_http_status(:ok) 51 | end 52 | end 53 | 54 | context "when user tries to access another user order" do 55 | let!(:another_user_order) { create(:order) } 56 | let(:url) { "/storefront/v1/orders/#{another_user_order.id}" } 57 | 58 | it "returns success status" do 59 | get url, headers: auth_header(user) 60 | expect(response).to have_http_status(:forbidden) 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/orders/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Orders without authentication", type: :request do 4 | 5 | context "GET /orders" do 6 | let(:url) { "/storefront/v1/orders" } 7 | let!(:orders) { create_list(:order, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "GET /orders/:id" do 15 | let(:order) { create(:order) } 16 | let(:url) { "/storefront/v1/orders/#{order.id}" } 17 | 18 | before(:each) { get url } 19 | 20 | include_examples "unauthenticated access" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/requests/storefront/v1/wish_items/unautheticated_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Storefront V1 Wish Items without authentication", type: :request do 4 | 5 | context "GET /wish_items" do 6 | let(:url) { "/storefront/v1/wish_items" } 7 | let!(:wish_items) { create_list(:wish_item, 5) } 8 | 9 | before(:each) { get url } 10 | 11 | include_examples "unauthenticated access" 12 | end 13 | 14 | context "POST /wish_items" do 15 | let(:url) { "/storefront/v1/wish_items" } 16 | 17 | before(:each) { post url } 18 | 19 | include_examples "unauthenticated access" 20 | end 21 | 22 | context "DELETE /wish_items/:id" do 23 | let!(:wish_item) { create(:wish_item) } 24 | let(:url) { "/storefront/v1/wish_items/#{wish_item.id}" } 25 | 26 | before(:each) { delete url } 27 | 28 | include_examples "unauthenticated access" 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/services/admin/alocate_licenses_service_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Admin::AlocateLicensesService do 4 | context "when #call" do 5 | let!(:order) { create(:order) } 6 | let!(:line_item) { create(:line_item, order: order) } 7 | let!(:licenses) { create_list(:license, line_item.quantity, game: line_item.product.productable) } 8 | 9 | it "allocates same number of licenses as line item quantity" do 10 | expect do 11 | described_class.new(line_item).call 12 | end.to change(line_item.licenses, :count).by(line_item.quantity) 13 | end 14 | 15 | it "licenses receives :in_use status" do 16 | described_class.new(line_item).call 17 | licenses_status = line_item.licenses.pluck(:status).uniq 18 | expect(licenses_status).to eq ['in_use'] 19 | end 20 | 21 | it "line item receives :delivered status" do 22 | described_class.new(line_item).call 23 | line_item.reload 24 | expect(line_item.status).to eq 'delivered' 25 | end 26 | 27 | it "send an email for each allocated license" do 28 | described_class.new(line_item).call 29 | line_item.licenses.each do |license| 30 | expect(ActionMailer::MailDeliveryJob).to have_been_enqueued.with( 31 | 'LicenseMailer', 'send_license', 'deliver_now', { params: { license: license }, args: [] } 32 | ) 33 | end 34 | end 35 | end 36 | end -------------------------------------------------------------------------------- /spec/services/admin/dashboard/sales_range_service_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Admin::Dashboard::SalesRangeService do 4 | context "#call" do 5 | context "when range is less than 30 days" do 6 | let(:min_date) { 20.days.ago } 7 | let(:max_date) { Date.current } 8 | 9 | let(:product) { create(:product) } 10 | 11 | let!(:sales_line_items) do 12 | 20.downto(1).map do |num| 13 | order = create(:order, created_at: num.days.ago) 14 | order.update_column(:status, :finished) 15 | create(:line_item, order: order, product: product, payed_price: 200, quantity: num) 16 | end 17 | end 18 | 19 | it "returns data grouped by day" do 20 | expected_return = sales_line_items.map do |line_item| 21 | day = line_item.order.created_at.strftime("%Y-%m-%d") 22 | total_sold = line_item.payed_price * line_item.quantity 23 | { date: day, total_sold: total_sold.to_f } 24 | end 25 | service = described_class.new(min: min_date, max: max_date) 26 | service.call 27 | expect(service.records).to eq expected_return 28 | end 29 | end 30 | 31 | context "when range is greater than 30 days" do 32 | let(:min_date) { 5.months.ago } 33 | let(:max_date) { Date.current } 34 | 35 | let(:product) { create(:product) } 36 | 37 | let!(:sales_line_items) do 38 | 5.downto(1).map do |num| 39 | order = create(:order, created_at: num.months.ago) 40 | order.update_column(:status, :finished) 41 | create(:line_item, order: order, product: product, payed_price: 200, quantity: num) 42 | end 43 | end 44 | 45 | it "returns data grouped by month" do 46 | expected_return = sales_line_items.map do |line_item| 47 | month = line_item.order.created_at.strftime("%Y-%m") 48 | total_sold = line_item.payed_price * line_item.quantity 49 | { date: month, total_sold: total_sold.to_f } 50 | end 51 | service = described_class.new(min: min_date, max: max_date) 52 | service.call 53 | expect(service.records).to eq expected_return 54 | end 55 | end 56 | end 57 | 58 | def build_product(line_item) 59 | total_sold = line_item.quantity * line_item.payed_price 60 | product = line_item.product 61 | product_image = Rails.application.routes.url_helpers.rails_blob_path(product.image, only_path: false) 62 | { product: product.name, image: product_image, quantity: line_item.quantity, total_sold: total_sold } 63 | end 64 | end -------------------------------------------------------------------------------- /spec/services/admin/dashboard/summary_service_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Admin::Dashboard::SummaryService do 4 | let(:min_date) { 7.days.ago } 5 | let(:max_date) { Date.current } 6 | 7 | context "#call" do 8 | it "returns users count follwing range date" do 9 | users_to_count = create_list(:user, 10, created_at: 2.days.ago) 10 | users_out_of_range = create_list(:user, 5, created_at: 8.days.ago) 11 | service = described_class.new(min: min_date, max: max_date) 12 | service.call 13 | expect(service.records[:users]).to eq users_to_count.size 14 | end 15 | 16 | it "returns products count following range date" do 17 | products_to_count = create_list(:product, 10, created_at: 2.days.ago) 18 | products_out_of_range = create_list(:product, 5, created_at: 8.days.ago) 19 | service = described_class.new(min: min_date, max: max_date) 20 | service.call 21 | expect(service.records[:products]).to eq products_to_count.size 22 | end 23 | 24 | it "returns orders count following range date and order :finished status" do 25 | orders_to_count = create_list(:order, 10, created_at: 2.days.ago) 26 | orders_to_count.each { |order| order.update_column(:status, :finished) } 27 | unfinished_orders = create_list(:order, 7, created_at: 2.days.ago) 28 | orders_out_of_range = create_list(:order, 5, created_at: 8.days.ago) 29 | service = described_class.new(min: min_date, max: max_date) 30 | service.call 31 | expect(service.records[:orders]).to eq orders_to_count.size 32 | end 33 | 34 | it "returns profit following range date and order :finished status" do 35 | orders_to_sum = create_list(:order, 10, created_at: 2.days.ago, total_amount: 65.43) 36 | orders_to_sum.each { |order| order.update_column(:status, :finished) } 37 | unfinished_orders = create_list(:order, 7, created_at: 2.days.ago, total_amount: 76.12) 38 | orders_out_of_range = create_list(:order, 5, created_at: 8.days.ago, total_amount: 43.12) 39 | service = described_class.new(min: min_date, max: max_date) 40 | service.call 41 | expected_amount = orders_to_sum.sum(&:total_amount) 42 | expect(service.records[:profit]).to eq expected_amount 43 | end 44 | end 45 | end -------------------------------------------------------------------------------- /spec/services/admin/dashboard/top_five_products_service_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Admin::Dashboard::TopFiveProductsService do 4 | let(:min_date) { 7.days.ago } 5 | let(:max_date) { Date.current } 6 | 7 | context "#call" do 8 | let(:top_five_products) { create_list(:product, 5) } 9 | let(:less_sold_products) { create_list(:product, 5) } 10 | let(:order) do 11 | order = create(:order, created_at: 4.days.ago) 12 | order.update_column(:status, :finished) 13 | order 14 | end 15 | let!(:top_five_line_itens) do 16 | top_five_products.map.with_index do |product, index| 17 | create(:line_item, payed_price: 200, quantity: (index + 1), order: order, product: product) 18 | end 19 | end 20 | let(:out_of_date_order) do 21 | order = create(:order, created_at: 8.days.ago) 22 | order.update_column(:status, :finished) 23 | order 24 | end 25 | let!(:out_of_date_line_items) do 26 | less_sold_products.map.with_index do |product, index| 27 | create(:line_item, payed_price: 2000, quantity: (index + 1), order: out_of_date_order, product: product) 28 | end 29 | end 30 | 31 | it "returns right sold products follwing range date" do 32 | expected_return = top_five_line_itens.reverse.map do |line_item| 33 | build_product(line_item) 34 | end 35 | service = described_class.new(min: min_date, max: max_date) 36 | service.call 37 | expect(service.records).to eq expected_return 38 | end 39 | 40 | it "does not return any product out of range date" do 41 | unexpected_return = out_of_date_line_items.reverse.map do |line_item| 42 | build_product(line_item) 43 | end 44 | service = described_class.new(min: min_date, max: max_date) 45 | service.call 46 | expect(service.records).to_not include *unexpected_return 47 | end 48 | end 49 | 50 | def build_product(line_item) 51 | total_sold = line_item.quantity * line_item.payed_price 52 | product = line_item.product 53 | product_image = Rails.application.routes.url_helpers.rails_blob_url(product.image, host: "localhost", port: 3000) 54 | { product: product.name, image: product_image, quantity: line_item.quantity, total_sold: total_sold } 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/services/admin/finish_delivered_orders_service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Admin::FinishDeliveredOrdersService do 4 | context "#call" do 5 | let!(:order) { create(:order) } 6 | let!(:delivered_line_items) { create_list(:line_item, 2, order: order) } 7 | 8 | before(:each) do 9 | order.update!(status: :payment_accepted) 10 | delivered_line_items.each { |line_item| line_item.update!(status: :delivered) } 11 | end 12 | 13 | it "set order as :finished when all line items are :delived" do 14 | described_class.call 15 | order.reload 16 | expect(order.status).to eq 'finished' 17 | end 18 | 19 | it "does not set order as :finished when at least one line item is not delived yet" do 20 | create(:line_item, order: order, status: :preparing) 21 | described_class.call 22 | order.reload 23 | expect(order.status).to_not eq 'finished' 24 | end 25 | 26 | it "does not set order as :finished until it is on :payment_accepted status" do 27 | order.update!(status: :processing_order) 28 | described_class.call 29 | order.reload 30 | expect(order.status).to eq 'processing_order' 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/shared_examples/forbidden_access_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples "forbidden access" do 2 | it "returns error message" do 3 | expect(body_json['errors']['message']).to eq "Forbidden access" 4 | end 5 | 6 | it "returns forbidden status" do 7 | expect(response).to have_http_status(:forbidden) 8 | end 9 | end -------------------------------------------------------------------------------- /spec/shared_examples/like_searchable_concern_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples "like searchable concern" do |factory_name, field| 2 | let!(:search_param) { "Example" } 3 | let!(:records_to_find) do 4 | (0..3).to_a.map { |index| create(factory_name, field => "Example #{index}") } 5 | end 6 | let!(:records_to_ignore) { create_list(factory_name, 3) } 7 | 8 | it "found records with expression in :#{field}" do 9 | found_records = described_class.like(field, search_param) 10 | expect(found_records.to_a).to contain_exactly(*records_to_find) 11 | end 12 | 13 | it "ignores records without expression in :#{field}" do 14 | found_records = described_class.like(field, search_param) 15 | expect(found_records.to_a).to_not include(*records_to_ignore) 16 | end 17 | end -------------------------------------------------------------------------------- /spec/shared_examples/pagination_meta_attributes_examples.rb: -------------------------------------------------------------------------------- 1 | shared_examples "pagination meta attributes" do |pagination_attr| 2 | it "returns :meta attribute with right pagination data" do 3 | pagination_attr.stringify_keys! 4 | expect(body_json['meta']).to include(pagination_attr) 5 | end 6 | end -------------------------------------------------------------------------------- /spec/shared_examples/sign_in_examples.rb: -------------------------------------------------------------------------------- 1 | shared_examples "sign in" do |email, password| 2 | let(:url) { '/auth/v1/user/sign_in' } 3 | 4 | context "when :email and :password are right" do 5 | it "retuns user tokens on header" do 6 | post url, params: { email: email, password: password } 7 | sign_in_headers = %W(access-token token-type client expiry uid) 8 | expect(response.headers).to include(*sign_in_headers) 9 | end 10 | 11 | it "returns :ok status" do 12 | post url, params: { email: email, password: password } 13 | expect(response).to have_http_status(:ok) 14 | end 15 | end 16 | 17 | context "with invalid credentials" do 18 | it "does not return token on header" do 19 | post url, params: { email: "", password: password } 20 | sign_in_headers = %W(access-token token-type client expiry uid) 21 | expect(response.headers).to_not include(*sign_in_headers) 22 | end 23 | 24 | it "returns :unauthenticated status" do 25 | post url, params: { email: "", password: password } 26 | expect(response).to have_http_status(:unauthorized) 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /spec/shared_examples/unauthenticated_access_example.rb: -------------------------------------------------------------------------------- 1 | shared_examples "unauthenticated access" do 2 | it "returns unauthorized status" do 3 | expect(response).to have_http_status(:unauthorized) 4 | end 5 | end -------------------------------------------------------------------------------- /spec/support/factory_bot.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryBot::Syntax::Methods 3 | end -------------------------------------------------------------------------------- /spec/support/images/product_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/spec/support/images/product_image.png -------------------------------------------------------------------------------- /spec/support/request_api.rb: -------------------------------------------------------------------------------- 1 | module RequestAPI 2 | def body_json(symbolize_keys: false) 3 | json = JSON.parse(response.body) 4 | symbolize_keys ? json.deep_symbolize_keys : json 5 | rescue 6 | return {} 7 | end 8 | 9 | def auth_header(user = nil, merge_with: {}) 10 | user ||= create(:user) 11 | auth = user.create_new_auth_token 12 | header = auth.merge({ 'Content-Type' => 'application/json', 'Accept' => 'application/json' }) 13 | header.merge merge_with 14 | end 15 | 16 | def unauthenticated_header(merge_with: {}) 17 | default_header = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' } 18 | default_header.merge merge_with 19 | end 20 | end 21 | 22 | RSpec.configure do |config| 23 | config.include RequestAPI, type: :request 24 | end -------------------------------------------------------------------------------- /spec/support/shoulda_matchers.rb: -------------------------------------------------------------------------------- 1 | Shoulda::Matchers.configure do |config| 2 | config.integrate do |with| 3 | with.test_framework :rspec 4 | with.library :rails 5 | end 6 | end -------------------------------------------------------------------------------- /spec/support/time_helpers.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include ActiveSupport::Testing::TimeHelpers 3 | end -------------------------------------------------------------------------------- /spec/validators/cpf_cnpj_validator_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | class Validatable 4 | include ActiveModel::Validations 5 | attr_accessor :document 6 | validates :document, cpf_cnpj: true 7 | end 8 | 9 | describe CpfCnpjValidator do 10 | subject { Validatable.new } 11 | 12 | context "when it is an invalid CPF" do 13 | before { subject.document = "431.321.551-12" } 14 | 15 | it "should be invalid" do 16 | expect(subject.valid?).to be_falsey 17 | end 18 | 19 | it "adds an error on model" do 20 | subject.valid? 21 | expect(subject.errors.keys).to include(:document) 22 | end 23 | end 24 | 25 | context "when it is an invalid CNPJ" do 26 | before { subject.document = "27.021.724/0001-53" } 27 | 28 | it "should be invalid" do 29 | expect(subject.valid?).to be_falsey 30 | end 31 | 32 | it "adds an error on model" do 33 | subject.valid? 34 | expect(subject.errors.keys).to include(:document) 35 | end 36 | end 37 | 38 | context "when it's does not have CPF or CNPJ format" do 39 | before { subject.document = "431.321" } 40 | 41 | it "should be invalid" do 42 | expect(subject.valid?).to be_falsey 43 | end 44 | 45 | it "adds an error on model" do 46 | subject.valid? 47 | expect(subject.errors.keys).to include(:document) 48 | end 49 | end 50 | 51 | it "is valid when it is a CPF" do 52 | subject.document = "865.922.358-61" 53 | expect(subject.valid?).to be_truthy 54 | end 55 | 56 | it "is valid when it is a CNPJ" do 57 | subject.document = "27.021.724/0001-78" 58 | expect(subject.valid?).to be_truthy 59 | end 60 | end -------------------------------------------------------------------------------- /spec/validators/future_date_validator_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | class Validatable 4 | include ActiveModel::Validations 5 | attr_accessor :date 6 | validates :date, future_date: true 7 | end 8 | 9 | describe FutureDateValidator do 10 | subject { Validatable.new } 11 | 12 | context "when date is before current date" do 13 | before { subject.date = 1.day.ago } 14 | 15 | it "should be invalid" do 16 | expect(subject.valid?).to be_falsey 17 | end 18 | 19 | it "adds an error on model" do 20 | subject.valid? 21 | expect(subject.errors.keys).to include(:date) 22 | end 23 | end 24 | 25 | context "when date is equal current date" do 26 | before { subject.date = Time.zone.now } 27 | 28 | it "should be invalid" do 29 | expect(subject.valid?).to be_falsey 30 | end 31 | 32 | it "adds an error on model" do 33 | subject.valid? 34 | expect(subject.errors.keys).to include(:date) 35 | end 36 | end 37 | 38 | context "when date is greater than current date" do 39 | before { subject.date = Time.zone.now + 1.day } 40 | 41 | it "should be valid" do 42 | expect(subject.valid?).to be_truthy 43 | end 44 | end 45 | end -------------------------------------------------------------------------------- /storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/storage/.keep -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/tmp/.keep -------------------------------------------------------------------------------- /tmp/pids/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/tmp/pids/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneBitCodeBlog/e-commerce-Api-Dev/cf15a94a7f2f7ac43089ab4cdb7e7176afbc8bee/vendor/.keep --------------------------------------------------------------------------------