From 279093ad98d96106c60de9bfea3192c9088f0aec Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 1 Dec 2024 14:03:06 +0100 Subject: [PATCH 1/4] Configure registration endpoint to create a wedding as well --- app/controllers/application_controller.rb | 4 ++++ .../users/registrations_controller.rb | 20 +++++++++++++++++++ app/models/wedding.rb | 2 +- .../mailer/confirmation_instructions.html.erb | 5 +++++ app/views/users/mailer/email_changed.html.erb | 7 +++++++ .../users/mailer/password_change.html.erb | 3 +++ .../reset_password_instructions.html.erb | 8 ++++++++ .../users/mailer/unlock_instructions.html.erb | 7 +++++++ config/initializers/devise.rb | 2 +- config/routes.rb | 2 +- spec/requests/users/registrations_spec.rb | 7 +++++++ 11 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 app/views/users/mailer/confirmation_instructions.html.erb create mode 100644 app/views/users/mailer/email_changed.html.erb create mode 100644 app/views/users/mailer/password_change.html.erb create mode 100644 app/views/users/mailer/reset_password_instructions.html.erb create mode 100644 app/views/users/mailer/unlock_instructions.html.erb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 13400e3..f163c3e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -30,6 +30,10 @@ class ApplicationController < ActionController::Base private + def default_url_options(options = {}) + options.merge(path_params: { slug: ActsAsTenant.current_tenant&.slug }) + end + def set_tenant ActsAsTenant.current_tenant = Wedding.find_by(slug: params[:slug]) end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 09ffe33..24d8eca 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -3,4 +3,24 @@ class Users::RegistrationsController < Devise::RegistrationsController clear_respond_to respond_to :json + + def create + wedding = Wedding.create(wedding_params) + unless wedding.persisted? + render json: { errors: wedding.errors.full_messages }, status: :unprocessable_entity + return + end + + ActsAsTenant.with_tenant(wedding) do + super do |user| + wedding.destroy unless user.persisted? + end + end + end + + private + + def wedding_params + { slug: params[:slug], **params.expect(wedding: :date) } + end end \ No newline at end of file diff --git a/app/models/wedding.rb b/app/models/wedding.rb index fa6c6e4..abc0999 100644 --- a/app/models/wedding.rb +++ b/app/models/wedding.rb @@ -16,5 +16,5 @@ # class Wedding < ApplicationRecord validates :date, presence: true - validates :slug, presence: true, uniqueness: true + validates :slug, presence: true, uniqueness: true, format: { with: /\A[a-z]+\z/ } end diff --git a/app/views/users/mailer/confirmation_instructions.html.erb b/app/views/users/mailer/confirmation_instructions.html.erb new file mode 100644 index 0000000..7b71e44 --- /dev/null +++ b/app/views/users/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(slug: ActsAsTenant.current_tenant&.slug, confirmation_token: @token) %>

diff --git a/app/views/users/mailer/email_changed.html.erb b/app/views/users/mailer/email_changed.html.erb new file mode 100644 index 0000000..32f4ba8 --- /dev/null +++ b/app/views/users/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/app/views/users/mailer/password_change.html.erb b/app/views/users/mailer/password_change.html.erb new file mode 100644 index 0000000..b41daf4 --- /dev/null +++ b/app/views/users/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/users/mailer/reset_password_instructions.html.erb b/app/views/users/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..010a079 --- /dev/null +++ b/app/views/users/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

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

+ +

<%= link_to 'Change my password', edit_password_url(slug: ActsAsTenant.current_tenant&.slug, reset_password_token: @token) %>

+ +

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

+

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

diff --git a/app/views/users/mailer/unlock_instructions.html.erb b/app/views/users/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..313fe8e --- /dev/null +++ b/app/views/users/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(slug: ActsAsTenant.current_tenant&.slug, unlock_token: @token) %>

diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 77d542f..5f631ef 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -246,7 +246,7 @@ Devise.setup do |config| # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you # are using only default views. - # config.scoped_views = false + config.scoped_views = true # Configure the default scope given to Warden. By default it's the first # devise role declared in your routes (usually :user). diff --git a/config/routes.rb b/config/routes.rb index b407e01..9659d05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,7 +2,7 @@ Rails.application.routes.draw do mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? - scope ":slug", constraints: {slug: /[a-z]+/} do + scope ":slug", constraints: { slug: /[a-z]+/ } do devise_for :users, skip: [:registration, :session, :confirmation] devise_scope :user do post 'users', to: 'users/registrations#create' diff --git a/spec/requests/users/registrations_spec.rb b/spec/requests/users/registrations_spec.rb index aa7562f..821db72 100644 --- a/spec/requests/users/registrations_spec.rb +++ b/spec/requests/users/registrations_spec.rb @@ -23,6 +23,13 @@ RSpec.describe 'users/registrations', type: :request do password: SwaggerResponseHelper::PASSWORD, password_confirmation: SwaggerResponseHelper::PASSWORD } + }, + wedding: { + type: :object, + required: %i[date], + properties: { + date: { type: :string, format: :date}, + } } } } From 7a80f1f5ef565cfc439f78a15435f420f9739899 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 1 Dec 2024 14:04:03 +0100 Subject: [PATCH 2/4] Make wedding object required for the swagger specs --- spec/requests/users/registrations_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/users/registrations_spec.rb b/spec/requests/users/registrations_spec.rb index 821db72..ae0f86d 100644 --- a/spec/requests/users/registrations_spec.rb +++ b/spec/requests/users/registrations_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'users/registrations', type: :request do parameter Swagger::Schema::SLUG parameter name: :body, in: :body, schema: { type: :object, - required: [:user], + required: [:user, :wedding], properties: { user: { type: :object, From f588b97e182f869faeb37659366ab498fc79b677 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 1 Dec 2024 13:05:22 +0000 Subject: [PATCH 3/4] Add copyright notice --- app/views/users/mailer/confirmation_instructions.html.erb | 2 ++ app/views/users/mailer/email_changed.html.erb | 2 ++ app/views/users/mailer/password_change.html.erb | 2 ++ app/views/users/mailer/reset_password_instructions.html.erb | 2 ++ app/views/users/mailer/unlock_instructions.html.erb | 2 ++ 5 files changed, 10 insertions(+) diff --git a/app/views/users/mailer/confirmation_instructions.html.erb b/app/views/users/mailer/confirmation_instructions.html.erb index 7b71e44..448e3d9 100644 --- a/app/views/users/mailer/confirmation_instructions.html.erb +++ b/app/views/users/mailer/confirmation_instructions.html.erb @@ -1,3 +1,5 @@ +<%# Copyright (C) 2024 Manuel Bustillo %> +

Welcome <%= @email %>!

You can confirm your account email through the link below:

diff --git a/app/views/users/mailer/email_changed.html.erb b/app/views/users/mailer/email_changed.html.erb index 32f4ba8..51cfa7a 100644 --- a/app/views/users/mailer/email_changed.html.erb +++ b/app/views/users/mailer/email_changed.html.erb @@ -1,3 +1,5 @@ +<%# Copyright (C) 2024 Manuel Bustillo %> +

Hello <%= @email %>!

<% if @resource.try(:unconfirmed_email?) %> diff --git a/app/views/users/mailer/password_change.html.erb b/app/views/users/mailer/password_change.html.erb index b41daf4..7bfd484 100644 --- a/app/views/users/mailer/password_change.html.erb +++ b/app/views/users/mailer/password_change.html.erb @@ -1,3 +1,5 @@ +<%# Copyright (C) 2024 Manuel Bustillo %> +

Hello <%= @resource.email %>!

We're contacting you to notify you that your password has been changed.

diff --git a/app/views/users/mailer/reset_password_instructions.html.erb b/app/views/users/mailer/reset_password_instructions.html.erb index 010a079..3bc6fcd 100644 --- a/app/views/users/mailer/reset_password_instructions.html.erb +++ b/app/views/users/mailer/reset_password_instructions.html.erb @@ -1,3 +1,5 @@ +<%# Copyright (C) 2024 Manuel Bustillo %> +

Hello <%= @resource.email %>!

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

diff --git a/app/views/users/mailer/unlock_instructions.html.erb b/app/views/users/mailer/unlock_instructions.html.erb index 313fe8e..6df5c0a 100644 --- a/app/views/users/mailer/unlock_instructions.html.erb +++ b/app/views/users/mailer/unlock_instructions.html.erb @@ -1,3 +1,5 @@ +<%# Copyright (C) 2024 Manuel Bustillo %> +

Hello <%= @resource.email %>!

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

From 9d08ef6f18ca56e571dc36d1e68055d12ad622e5 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 1 Dec 2024 18:17:07 +0100 Subject: [PATCH 4/4] Update wedding slug rules to accept numbers and other chars --- app/models/wedding.rb | 4 +++- config/routes.rb | 2 +- spec/models/wedding_spec.rb | 17 ++++++++++++++++- spec/requests/schemas.rb | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/models/wedding.rb b/app/models/wedding.rb index abc0999..cee2fb7 100644 --- a/app/models/wedding.rb +++ b/app/models/wedding.rb @@ -15,6 +15,8 @@ # index_weddings_on_slug (slug) UNIQUE # class Wedding < ApplicationRecord + SLUG_REGEX = /[a-z\d-]+/ + validates :date, presence: true - validates :slug, presence: true, uniqueness: true, format: { with: /\A[a-z]+\z/ } + validates :slug, presence: true, uniqueness: true, format: { with: /\A#{SLUG_REGEX}\z/ } end diff --git a/config/routes.rb b/config/routes.rb index 9659d05..10810fe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,7 +2,7 @@ Rails.application.routes.draw do mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? - scope ":slug", constraints: { slug: /[a-z]+/ } do + scope ":slug", constraints: { slug: Wedding::SLUG_REGEX } do devise_for :users, skip: [:registration, :session, :confirmation] devise_scope :user do post 'users', to: 'users/registrations#create' diff --git a/spec/models/wedding_spec.rb b/spec/models/wedding_spec.rb index 702769f..6ec8a3e 100644 --- a/spec/models/wedding_spec.rb +++ b/spec/models/wedding_spec.rb @@ -3,5 +3,20 @@ require 'rails_helper' RSpec.describe Wedding, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + describe 'validations' do + subject { build(:wedding) } + describe 'slug' do + it { should allow_value('foo').for(:slug) } + it { should allow_value('foo-bar').for(:slug) } + it { should allow_value('foo-123').for(:slug) } + it { should allow_value('foo-123-').for(:slug) } + it { should allow_value('foo--123').for(:slug) } + + it { should_not allow_value('Foo').for(:slug) } + it { should_not allow_value('/foo').for(:slug) } + it { should_not allow_value('foo/123').for(:slug) } + it { should_not allow_value('foo_123').for(:slug) } + it { should_not allow_value('foo/').for(:slug) } + end + end end diff --git a/spec/requests/schemas.rb b/spec/requests/schemas.rb index ce35ad7..6602db8 100644 --- a/spec/requests/schemas.rb +++ b/spec/requests/schemas.rb @@ -14,6 +14,7 @@ module Swagger name: 'slug', in: :path, type: :string, + pattern: Wedding::SLUG_REGEX, example: :default, description: 'Wedding slug' }