Merge pull request #2 from exstake/feature/user-registration-api

Feature/user registration api
This commit is contained in:
João Geonizeli
2021-08-04 23:08:50 -03:00
committed by GitHub
30 changed files with 669 additions and 19 deletions

View File

@@ -10,3 +10,9 @@ AllCops:
Exclude: Exclude:
- db/schema.rb - db/schema.rb
- bin/**/* - bin/**/*
RSpec/ExampleLength:
Enabled: false
RSpec/MultipleExpectations:
Enabled: false

18
Gemfile
View File

@@ -5,7 +5,6 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "2.7.4" ruby "2.7.4"
gem "jbuilder", "~> 2.7"
gem "pg", "~> 1.1" gem "pg", "~> 1.1"
gem "puma", "~> 5.0" gem "puma", "~> 5.0"
gem "rails", "~> 6.1.4" gem "rails", "~> 6.1.4"
@@ -13,14 +12,19 @@ gem "sass-rails", ">= 6"
gem "turbolinks", "~> 5" gem "turbolinks", "~> 5"
gem "webpacker", "~> 5.0" gem "webpacker", "~> 5.0"
gem "bootsnap", ">= 1.4.4", require: false gem "bootsnap", ">= 1.4.4", require: false
gem "image_processing", "~> 1.12"
gem "devise" gem "devise"
gem "devise-bootstrap-views"
gem "devise-i18n" gem "devise-i18n"
gem "administrate" gem "devise-bootstrap-views"
gem "graphql"
gem "administrate-field-active_storage"
gem "tailwindcss-rails" gem "tailwindcss-rails"
gem "administrate"
gem "enumerize"
gem "httparty" gem "httparty"
gem "graphql"
gem "pundit"
group :development, :test do group :development, :test do
gem "dotenv-rails" gem "dotenv-rails"
@@ -34,9 +38,15 @@ group :development, :test do
end end
group :development do group :development do
gem "annotate"
gem "graphql_playground-rails" gem "graphql_playground-rails"
gem "web-console", ">= 4.1.0" gem "web-console", ">= 4.1.0"
gem "listen", "~> 3.3" gem "listen", "~> 3.3"
gem "spring" gem "spring"
end end
group :test do
gem "shoulda-matchers", "~> 5.0"
gem "rspec-graphql_matchers", "~> 1.3"
end

View File

@@ -72,6 +72,12 @@ GEM
momentjs-rails (~> 2.8) momentjs-rails (~> 2.8)
sassc-rails (~> 2.1) sassc-rails (~> 2.1)
selectize-rails (~> 0.6) selectize-rails (~> 0.6)
administrate-field-active_storage (0.3.7)
administrate (>= 0.2.2)
rails (>= 6.0)
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
rake (>= 10.4, < 14.0)
ast (2.4.2) ast (2.4.2)
bcrypt (3.1.16) bcrypt (3.1.16)
bindex (0.8.1) bindex (0.8.1)
@@ -106,6 +112,8 @@ GEM
dotenv-rails (2.7.6) dotenv-rails (2.7.6)
dotenv (= 2.7.6) dotenv (= 2.7.6)
railties (>= 3.2) railties (>= 3.2)
enumerize (2.4.0)
activesupport (>= 3.2)
erubi (1.10.0) erubi (1.10.0)
ffi (1.15.3) ffi (1.15.3)
globalid (0.5.2) globalid (0.5.2)
@@ -118,8 +126,9 @@ GEM
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
i18n (1.8.10) i18n (1.8.10)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jbuilder (2.11.2) image_processing (1.12.1)
activesupport (>= 5.0.0) mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
jquery-rails (4.4.0) jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
@@ -149,6 +158,7 @@ GEM
mime-types (3.3.1) mime-types (3.3.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2021.0704) mime-types-data (3.2021.0704)
mini_magick (4.11.0)
mini_mime (1.1.0) mini_mime (1.1.0)
mini_portile2 (2.6.1) mini_portile2 (2.6.1)
minitest (5.14.4) minitest (5.14.4)
@@ -174,6 +184,8 @@ GEM
public_suffix (4.0.6) public_suffix (4.0.6)
puma (5.4.0) puma (5.4.0)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
racc (1.5.2) racc (1.5.2)
rack (2.2.3) rack (2.2.3)
rack-proxy (0.7.0) rack-proxy (0.7.0)
@@ -221,6 +233,8 @@ GEM
rspec-expectations (3.10.1) rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0) rspec-support (~> 3.10.0)
rspec-graphql_matchers (1.3.0)
graphql (>= 1.8, < 2.0)
rspec-mocks (3.10.2) rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.10.0) rspec-support (~> 3.10.0)
@@ -254,6 +268,8 @@ GEM
rubocop-shopify (2.2.0) rubocop-shopify (2.2.0)
rubocop (~> 1.18) rubocop (~> 1.18)
ruby-progressbar (1.11.0) ruby-progressbar (1.11.0)
ruby-vips (2.1.2)
ffi (~> 1.12)
sass-rails (6.0.0) sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1) sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0) sassc (2.4.0)
@@ -266,6 +282,8 @@ GEM
tilt tilt
selectize-rails (0.12.6) selectize-rails (0.12.6)
semantic_range (3.0.0) semantic_range (3.0.0)
shoulda-matchers (5.0.0)
activesupport (>= 5.2.0)
spring (2.1.1) spring (2.1.1)
sprockets (4.0.2) sprockets (4.0.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
@@ -308,26 +326,32 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
administrate administrate
administrate-field-active_storage
annotate
bootsnap (>= 1.4.4) bootsnap (>= 1.4.4)
capybara capybara
devise devise
devise-bootstrap-views devise-bootstrap-views
devise-i18n devise-i18n
dotenv-rails dotenv-rails
enumerize
graphql graphql
graphql_playground-rails graphql_playground-rails
httparty httparty
jbuilder (~> 2.7) image_processing (~> 1.12)
listen (~> 3.3) listen (~> 3.3)
pg (~> 1.1) pg (~> 1.1)
pry-byebug pry-byebug
puma (~> 5.0) puma (~> 5.0)
pundit
rails (~> 6.1.4) rails (~> 6.1.4)
rspec-graphql_matchers (~> 1.3)
rspec-rails rspec-rails
rubocop-rails rubocop-rails
rubocop-rspec rubocop-rspec
rubocop-shopify rubocop-shopify
sass-rails (>= 6) sass-rails (>= 6)
shoulda-matchers (~> 5.0)
spring spring
tailwindcss-rails tailwindcss-rails
turbolinks (~> 5) turbolinks (~> 5)

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Admin
class UserDocumentsController < Admin::ApplicationController
def valid_action?(name, resource = resource_class)
["destroy"].exclude?(name.to_s) && super
end
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Admin
class UsersController < Admin::ApplicationController
def valid_action?(name, resource = resource_class)
["new", "destroy"].exclude?(name.to_s) && super
end
end
end

View File

@@ -1,3 +1,4 @@
# frozen_string_literal: true # frozen_string_literal: true
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
include Pundit
end end

View File

@@ -9,8 +9,8 @@ class GraphqlController < ApplicationController
query = params[:query] query = params[:query]
operation_name = params[:operationName] operation_name = params[:operationName]
context = { context = {
current_user: current_admin_user, # || current_auth.current_user,
current_auth: current_auth, current_auth: current_auth,
current_user: current_admin_user, # || current_auth.current_user,
} }
result = XStakeSchema.execute(query, variables: variables, context: context, operation_name: operation_name) result = XStakeSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
render(json: result) render(json: result)

View File

@@ -0,0 +1,55 @@
# frozen_string_literal: true
require "administrate/base_dashboard"
class UserDashboard < Administrate::BaseDashboard
# ATTRIBUTE_TYPES
# a hash that describes the type of each of the model's fields.
#
# Each different type represents an Administrate::Field object,
# which determines how the attribute is displayed
# on pages throughout the dashboard.
ATTRIBUTE_TYPES = {
id: Field::Number,
full_name: Field::String,
first_name: Field::String,
last_name: Field::String,
email: Field::String,
created_at: Field::DateTime,
updated_at: Field::DateTime,
}.freeze
# COLLECTION_ATTRIBUTES
# an array of attributes that will be displayed on the model's index page.
#
# By default, it's limited to four items to reduce clutter on index pages.
# Feel free to add, remove, or rearrange items.
COLLECTION_ATTRIBUTES = [:full_name, :email].freeze
# SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page.
SHOW_PAGE_ATTRIBUTES = [:id, :first_name, :last_name, :email, :created_at, :updated_at].freeze
# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = [:first_name, :last_name].freeze
# COLLECTION_FILTERS
# a hash that defines filters that can be used while searching via the search
# field of the dashboard.
#
# For example to add an option to search for open resources by typing "open:"
# in the search field:
#
# COLLECTION_FILTERS = {
# open: ->(resources) { resources.where(open: true) }
# }.freeze
COLLECTION_FILTERS = {}.freeze
# Overwrite this method to customize how users are displayed
# across all pages of the admin dashboard.
#
# def display_resource(user)
# "User ##{user.id}"
# end
end

View File

@@ -0,0 +1,54 @@
# frozen_string_literal: true
require "administrate/base_dashboard"
class UserDocumentDashboard < Administrate::BaseDashboard
# ATTRIBUTE_TYPES
# a hash that describes the type of each of the model's fields.
#
# Each different type represents an Administrate::Field object,
# which determines how the attribute is displayed
# on pages throughout the dashboard.
ATTRIBUTE_TYPES = {
id: Field::Number,
user: Field::BelongsTo,
image: Field::ActiveStorage,
status: Field::Select.with_options(collection: UserDocument.status.values),
created_at: Field::DateTime,
updated_at: Field::DateTime,
}.freeze
# COLLECTION_ATTRIBUTES
# an array of attributes that will be displayed on the model's index page.
#
# By default, it's limited to four items to reduce clutter on index pages.
# Feel free to add, remove, or rearrange items.
COLLECTION_ATTRIBUTES = [:user, :status].freeze
# SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page.
SHOW_PAGE_ATTRIBUTES = [:id, :user, :status, :image, :created_at, :updated_at].freeze
# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = [:status].freeze
# COLLECTION_FILTERS
# a hash that defines filters that can be used while searching via the search
# field of the dashboard.
#
# For example to add an option to search for open resources by typing "open:"
# in the search field:
#
# COLLECTION_FILTERS = {
# open: ->(resources) { resources.where(open: true) }
# }.freeze
COLLECTION_FILTERS = {}.freeze
# Overwrite this method to customize how user documents are displayed
# across all pages of the admin dashboard.
#
# def display_resource(user_document)
# "UserDocument ##{user_document.id}"
# end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
module Inputs
class UserAttributesInput < Types::BaseInputObject
graphql_name "UserAttributesInput"
argument :first_name, String, required: true
argument :last_name, String, required: true
end
end

View File

@@ -5,5 +5,9 @@ module Mutations
field_class Types::BaseField field_class Types::BaseField
input_object_class Types::BaseInputObject input_object_class Types::BaseInputObject
object_class Types::BaseObject object_class Types::BaseObject
field :errors, [String],
null: true,
description: "Errors encountered during execution of the mutation."
end end
end end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
module Mutations
class CreateUser < BaseMutation
field :success, Boolean, null: false
argument :user, Inputs::UserAttributesInput, required: true
def resolve(user:)
User.create!({ **user, email: context[:current_auth].email })
{ success: true }
rescue ActiveRecord::RecordInvalid => e
{ success: false, errors: [e.message] }
end
end
end

View File

@@ -1,11 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
module Types module Types
class MutationType < Types::BaseObject class MutationType < Types::BaseObject
# TODO: remove me field :create_user, mutation: Mutations::CreateUser
field :test_field, String, null: false,
description: "An example field added by the generator"
def test_field
"Hello World"
end
end end
end end

View File

@@ -1,4 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
# == Schema Information
#
# Table name: admin_users
#
# id :bigint not null, primary key
# email :string default(""), not null
# encrypted_password :string default(""), not null
# remember_created_at :datetime
# reset_password_sent_at :datetime
# reset_password_token :string
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_admin_users_on_email (email) UNIQUE
# index_admin_users_on_reset_password_token (reset_password_token) UNIQUE
#
class AdminUser < ApplicationRecord class AdminUser < ApplicationRecord
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable

27
app/models/user.rb Normal file
View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: users
#
# id :bigint not null, primary key
# email :string not null
# first_name :string not null
# last_name :string not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_users_on_email (email) UNIQUE
#
class User < ApplicationRecord
has_many :documents, class_name: "UserDocument", dependent: :destroy
validates :first_name, :last_name, :email, presence: true
validates :email, uniqueness: true
def full_name
"#{first_name} #{last_name}"
end
end

View File

@@ -0,0 +1,30 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: user_documents
#
# id :bigint not null, primary key
# status :string not null
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint not null
#
# Indexes
#
# index_user_documents_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
class UserDocument < ApplicationRecord
extend Enumerize
belongs_to :user
has_one_attached :image
enumerize :status,
in: [:pending_review, :approved, :refused],
default: :pending_review
end

View File

@@ -0,0 +1,50 @@
# frozen_string_literal: true
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end

View File

@@ -8,6 +8,8 @@ module Auth
end end
def profile def profile
return nil if jwt_token.blank?
Auth0Client.find_profile(jwt_token) Auth0Client.find_profile(jwt_token)
end end
end end

View File

@@ -3,14 +3,16 @@ Rails.application.routes.draw do
devise_for :admin_users devise_for :admin_users
namespace :admin do namespace :admin do
resources :users
resources :user_documents
resources :admin_users resources :admin_users
root to: "admin_users#index" root to: "users#index"
end end
root to: "home#index" root to: "home#index"
get "*all" => "home#index", constraints: lambda { |req| get "*all" => "home#index", constraints: lambda { |req|
Rails.env.development? ? req.path.exclude?("playground") : req req.path.exclude?("playground") && req.path.exclude?("rails")
} }
post "/graphql", to: "graphql#execute" post "/graphql", to: "graphql#execute"

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table(:users) do |t|
t.string(:first_name, null: false)
t.string(:last_name, null: false)
t.string(:email, null: false)
t.timestamps
end
add_index(:users, :email, unique: true)
end
end

View File

@@ -0,0 +1,43 @@
# frozen_string_literal: true
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table(:active_storage_blobs) do |t|
t.string(:key, null: false)
t.string(:filename, null: false)
t.string(:content_type)
t.text(:metadata)
t.string(:service_name, null: false)
t.bigint(:byte_size, null: false)
t.string(:checksum, null: false)
t.datetime(:created_at, null: false)
t.index([:key], unique: true)
end
create_table(:active_storage_attachments) do |t|
t.string(:name, null: false)
t.references(:record, null: false, polymorphic: true, index: false)
t.references(:blob, null: false)
t.datetime(:created_at, null: false)
t.index(
[:record_type, :record_id, :name, :blob_id],
name: "index_active_storage_attachments_uniqueness",
unique: true
)
t.foreign_key(:active_storage_blobs, column: :blob_id)
end
# rubocop:disable Rails/CreateTableWithTimestamps
create_table(:active_storage_variant_records) do |t|
t.belongs_to(:blob, null: false, index: false)
t.string(:variation_digest, null: false)
t.index([:blob_id, :variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true)
t.foreign_key(:active_storage_blobs, column: :blob_id)
end
# rubocop:enable Rails/CreateTableWithTimestamps
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class CreateUserDocuments < ActiveRecord::Migration[6.1]
def change
create_table(:user_documents) do |t|
t.string(:status, null: false)
t.references(:user, null: false, foreign_key: true)
t.timestamps
end
end
end

48
db/schema.rb generated
View File

@@ -10,11 +10,39 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_08_03_222524) do ActiveRecord::Schema.define(version: 2021_08_05_000225) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.bigint "record_id", null: false
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.string "service_name", null: false
t.bigint "byte_size", null: false
t.string "checksum", null: false
t.datetime "created_at", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", force: :cascade do |t|
t.bigint "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "admin_users", force: :cascade do |t| create_table "admin_users", force: :cascade do |t|
t.string "email", default: "", null: false t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false t.string "encrypted_password", default: "", null: false
@@ -27,10 +55,24 @@ ActiveRecord::Schema.define(version: 2021_08_03_222524) do
t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
end end
create_table "customers", force: :cascade do |t| create_table "user_documents", force: :cascade do |t|
t.string "name" t.string "status", null: false
t.bigint "user_id", null: false
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_user_documents_on_user_id"
end end
create_table "users", force: :cascade do |t|
t.string "first_name", null: false
t.string "last_name", null: false
t.string "email", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email", unique: true
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "user_documents", "users"
end end

View File

@@ -0,0 +1,60 @@
# frozen_string_literal: true
# NOTE: only doing this in development as some production environments (Heroku)
# NOTE: are sensitive to local FS writes, and besides -- it's just not proper
# NOTE: to have a dev-mode tool do its thing in production.
if Rails.env.development?
require "annotate"
task set_annotation_options: :environment do
# You can override any of these by setting an environment variable of the
# same name.
Annotate.set_defaults(
"active_admin" => "false",
"additional_file_patterns" => [],
"routes" => "false",
"models" => "true",
"position_in_routes" => "before",
"position_in_class" => "before",
"position_in_test" => "before",
"position_in_fixture" => "before",
"position_in_factory" => "before",
"position_in_serializer" => "before",
"show_foreign_keys" => "true",
"show_complete_foreign_keys" => "false",
"show_indexes" => "true",
"simple_indexes" => "false",
"model_dir" => "app/models",
"root_dir" => "",
"include_version" => "false",
"require" => "",
"exclude_tests" => "false",
"exclude_fixtures" => "false",
"exclude_factories" => "false",
"exclude_serializers" => "false",
"exclude_scaffolds" => "true",
"exclude_controllers" => "true",
"exclude_helpers" => "true",
"exclude_sti_subclasses" => "false",
"ignore_model_sub_dir" => "false",
"ignore_columns" => nil,
"ignore_routes" => nil,
"ignore_unknown_models" => "false",
"hide_limit_column_types" => "integer,bigint,boolean",
"hide_default_column_types" => "json,jsonb,hstore",
"skip_on_db_migrate" => "false",
"format_bare" => "true",
"format_rdoc" => "false",
"format_yard" => "false",
"format_markdown" => "false",
"sort" => "false",
"force" => "false",
"frozen" => "false",
"classified_sort" => "true",
"trace" => "false",
"wrapper_open" => nil,
"wrapper_close" => nil,
"with_comment" => "true"
)
end
Annotate.load_tasks
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe(Inputs::UserAttributesInput) do
subject { described_class }
describe "arguments" do
it { is_expected.to(accept_argument(:first_name).of_type("String!")) }
it { is_expected.to(accept_argument(:last_name).of_type("String!")) }
end
end

View File

@@ -0,0 +1,66 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe(Mutations::CreateUser) do
describe "#resolve" do
let(:mutation_string) do
<<~GQL
mutation($input: CreateUserInput!) {
createUser(input: $input) {
success
errors
}
}
GQL
end
let(:context) do
{
current_auth: Auth::Profile.new({
id: "_",
email: "user@example.com",
}),
}
end
let(:variables) do
{
input: { user: {
firstName: "First Name",
lastName: "Last Name",
} },
}
end
context "when current_auth is not being used by any user" do
it "create a user to auth" do
result = XStakeSchema.execute(
mutation_string,
variables: variables,
context: context
).to_h
expect(result["data"]["createUser"]["success"]).to(eq(true))
end
end
context "when auth is being used by no users" do
it "returns error" do
User.create(
first_name: "First Name",
last_name: "Last Name",
email: "user@example.com"
)
result = XStakeSchema.execute(
mutation_string,
variables: variables,
context: context
).to_h
expect(result["data"]["createUser"]["success"]).to(eq(false))
expect(result["data"]["createUser"]["errors"]).to(eq(["Validation failed: Email has already been taken"]))
end
end
end
end

View File

@@ -1,4 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
# == Schema Information
#
# Table name: admin_users
#
# id :bigint not null, primary key
# email :string default(""), not null
# encrypted_password :string default(""), not null
# remember_created_at :datetime
# reset_password_sent_at :datetime
# reset_password_token :string
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_admin_users_on_email (email) UNIQUE
# index_admin_users_on_reset_password_token (reset_password_token) UNIQUE
#
require "rails_helper" require "rails_helper"
RSpec.describe(AdminUser, type: :model) do RSpec.describe(AdminUser, type: :model) do

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: user_documents
#
# id :bigint not null, primary key
# status :string not null
# created_at :datetime not null
# updated_at :datetime not null
# user_id :bigint not null
#
# Indexes
#
# index_user_documents_on_user_id (user_id)
#
# Foreign Keys
#
# fk_rails_... (user_id => users.id)
#
require "rails_helper"
RSpec.describe(UserDocument, type: :model) do
describe "associations" do
it { is_expected.to(belong_to(:user)) }
end
end

30
spec/models/user_spec.rb Normal file
View File

@@ -0,0 +1,30 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: users
#
# id :bigint not null, primary key
# email :string not null
# first_name :string not null
# last_name :string not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_users_on_email (email) UNIQUE
#
require "rails_helper"
RSpec.describe(User, type: :model) do
describe "validations" do
it { is_expected.to(validate_presence_of(:first_name)) }
it { is_expected.to(validate_presence_of(:last_name)) }
it { is_expected.to(validate_presence_of(:email)) }
end
describe "associations" do
it { is_expected.to(have_many(:documents)) }
end
end

View File

@@ -67,3 +67,10 @@ RSpec.configure do |config|
# arbitrary gems may also be filtered via: # arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name") # config.filter_gems_from_backtrace("gem name")
end end
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework(:rspec)
with.library(:rails)
end
end