From 66ad8e1be24e59d97dd453a9a5d9c0965e2a1455 Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Sun, 28 Jun 2020 16:13:46 +0100
Subject: [PATCH 01/14] update rails

---
 Gemfile.lock | 106 +++++++++++++++++++++++++--------------------------
 1 file changed, 53 insertions(+), 53 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index de4c18f6..db7bb053 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,59 +1,59 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (6.0.3.1)
-      actionpack (= 6.0.3.1)
+    actioncable (6.0.3.2)
+      actionpack (= 6.0.3.2)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (6.0.3.1)
-      actionpack (= 6.0.3.1)
-      activejob (= 6.0.3.1)
-      activerecord (= 6.0.3.1)
-      activestorage (= 6.0.3.1)
-      activesupport (= 6.0.3.1)
+    actionmailbox (6.0.3.2)
+      actionpack (= 6.0.3.2)
+      activejob (= 6.0.3.2)
+      activerecord (= 6.0.3.2)
+      activestorage (= 6.0.3.2)
+      activesupport (= 6.0.3.2)
       mail (>= 2.7.1)
-    actionmailer (6.0.3.1)
-      actionpack (= 6.0.3.1)
-      actionview (= 6.0.3.1)
-      activejob (= 6.0.3.1)
+    actionmailer (6.0.3.2)
+      actionpack (= 6.0.3.2)
+      actionview (= 6.0.3.2)
+      activejob (= 6.0.3.2)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 2.0)
-    actionpack (6.0.3.1)
-      actionview (= 6.0.3.1)
-      activesupport (= 6.0.3.1)
+    actionpack (6.0.3.2)
+      actionview (= 6.0.3.2)
+      activesupport (= 6.0.3.2)
       rack (~> 2.0, >= 2.0.8)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (6.0.3.1)
-      actionpack (= 6.0.3.1)
-      activerecord (= 6.0.3.1)
-      activestorage (= 6.0.3.1)
-      activesupport (= 6.0.3.1)
+    actiontext (6.0.3.2)
+      actionpack (= 6.0.3.2)
+      activerecord (= 6.0.3.2)
+      activestorage (= 6.0.3.2)
+      activesupport (= 6.0.3.2)
       nokogiri (>= 1.8.5)
-    actionview (6.0.3.1)
-      activesupport (= 6.0.3.1)
+    actionview (6.0.3.2)
+      activesupport (= 6.0.3.2)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.1, >= 1.2.0)
-    activejob (6.0.3.1)
-      activesupport (= 6.0.3.1)
+    activejob (6.0.3.2)
+      activesupport (= 6.0.3.2)
       globalid (>= 0.3.6)
-    activemodel (6.0.3.1)
-      activesupport (= 6.0.3.1)
-    activerecord (6.0.3.1)
-      activemodel (= 6.0.3.1)
-      activesupport (= 6.0.3.1)
+    activemodel (6.0.3.2)
+      activesupport (= 6.0.3.2)
+    activerecord (6.0.3.2)
+      activemodel (= 6.0.3.2)
+      activesupport (= 6.0.3.2)
     activerecord-postgis-adapter (6.0.0)
       activerecord (~> 6.0)
       rgeo-activerecord (~> 6.0)
-    activestorage (6.0.3.1)
-      actionpack (= 6.0.3.1)
-      activejob (= 6.0.3.1)
-      activerecord (= 6.0.3.1)
+    activestorage (6.0.3.2)
+      actionpack (= 6.0.3.2)
+      activejob (= 6.0.3.2)
+      activerecord (= 6.0.3.2)
       marcel (~> 0.3.1)
-    activesupport (6.0.3.1)
+    activesupport (6.0.3.2)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 0.7, < 2)
       minitest (~> 5.1)
@@ -179,7 +179,7 @@ GEM
     httparty (0.18.0)
       mime-types (~> 3.0)
       multi_xml (>= 0.5.2)
-    i18n (1.8.2)
+    i18n (1.8.3)
       concurrent-ruby (~> 1.0)
     image_processing (1.11.0)
       mini_magick (>= 4.9.5, < 5)
@@ -209,7 +209,7 @@ GEM
       rb-fsevent (~> 0.9, >= 0.9.4)
       rb-inotify (~> 0.9, >= 0.9.7)
       ruby_dep (~> 1.2)
-    loofah (2.5.0)
+    loofah (2.6.0)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
     mail (2.7.1)
@@ -258,20 +258,20 @@ GEM
       rack
     rack-test (1.1.0)
       rack (>= 1.0, < 3)
-    rails (6.0.3.1)
-      actioncable (= 6.0.3.1)
-      actionmailbox (= 6.0.3.1)
-      actionmailer (= 6.0.3.1)
-      actionpack (= 6.0.3.1)
-      actiontext (= 6.0.3.1)
-      actionview (= 6.0.3.1)
-      activejob (= 6.0.3.1)
-      activemodel (= 6.0.3.1)
-      activerecord (= 6.0.3.1)
-      activestorage (= 6.0.3.1)
-      activesupport (= 6.0.3.1)
+    rails (6.0.3.2)
+      actioncable (= 6.0.3.2)
+      actionmailbox (= 6.0.3.2)
+      actionmailer (= 6.0.3.2)
+      actionpack (= 6.0.3.2)
+      actiontext (= 6.0.3.2)
+      actionview (= 6.0.3.2)
+      activejob (= 6.0.3.2)
+      activemodel (= 6.0.3.2)
+      activerecord (= 6.0.3.2)
+      activestorage (= 6.0.3.2)
+      activesupport (= 6.0.3.2)
       bundler (>= 1.3.0)
-      railties (= 6.0.3.1)
+      railties (= 6.0.3.2)
       sprockets-rails (>= 2.0.0)
     rails-dom-testing (2.0.3)
       activesupport (>= 4.2.0)
@@ -281,9 +281,9 @@ GEM
     rails-i18n (6.0.0)
       i18n (>= 0.7, < 2)
       railties (>= 6.0.0, < 7)
-    railties (6.0.3.1)
-      actionpack (= 6.0.3.1)
-      activesupport (= 6.0.3.1)
+    railties (6.0.3.2)
+      actionpack (= 6.0.3.2)
+      activesupport (= 6.0.3.2)
       method_source
       rake (>= 0.8.7)
       thor (>= 0.20.3, < 2.0)
@@ -387,7 +387,7 @@ GEM
     spring-watcher-listen (2.0.1)
       listen (>= 2.7, < 4.0)
       spring (>= 1.2, < 3.0)
-    sprockets (4.0.0)
+    sprockets (4.0.2)
       concurrent-ruby (~> 1.0)
       rack (> 1, < 3)
     sprockets-rails (3.2.1)

From 7b992a8816b9ae3e4c60cd3adca0b761f1ca6e75 Mon Sep 17 00:00:00 2001
From: Tiago Santos <santos.tiago@gmail.com>
Date: Sun, 28 Jun 2020 22:52:55 +0100
Subject: [PATCH 02/14] Created the model for favorites

---
 app/models/favorite.rb                        | 14 ++++++++++++++
 db/migrate/20200628215106_create_favorites.rb | 10 ++++++++++
 db/schema.rb                                  | 13 ++++++++++++-
 spec/factories/favorites.rb                   | 16 ++++++++++++++++
 spec/models/favorite_spec.rb                  | 15 +++++++++++++++
 5 files changed, 67 insertions(+), 1 deletion(-)
 create mode 100644 app/models/favorite.rb
 create mode 100644 db/migrate/20200628215106_create_favorites.rb
 create mode 100644 spec/factories/favorites.rb
 create mode 100644 spec/models/favorite_spec.rb

diff --git a/app/models/favorite.rb b/app/models/favorite.rb
new file mode 100644
index 00000000..9d312519
--- /dev/null
+++ b/app/models/favorite.rb
@@ -0,0 +1,14 @@
+# == Schema Information
+#
+# Table name: favorites
+#
+#  id         :bigint           not null, primary key
+#  store_id   :bigint           not null
+#  user_id    :bigint           not null
+#  created_at :datetime         not null
+#  updated_at :datetime         not null
+#
+class Favorite < ApplicationRecord
+  belongs_to :store
+  belongs_to :user
+end
diff --git a/db/migrate/20200628215106_create_favorites.rb b/db/migrate/20200628215106_create_favorites.rb
new file mode 100644
index 00000000..6de45b1f
--- /dev/null
+++ b/db/migrate/20200628215106_create_favorites.rb
@@ -0,0 +1,10 @@
+class CreateFavorites < ActiveRecord::Migration[6.0]
+  def change
+    create_table :favorites do |t|
+      t.references :store, null: false, foreign_key: true
+      t.references :user, null: false, foreign_key: true
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 65f9d507..50168f29 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2020_06_24_110119) do
+ActiveRecord::Schema.define(version: 2020_06_28_215106) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -85,6 +85,15 @@
     t.index ["water_quality"], name: "index_beach_configurations_on_water_quality"
   end
 
+  create_table "favorites", force: :cascade do |t|
+    t.bigint "store_id", null: false
+    t.bigint "user_id", null: false
+    t.datetime "created_at", precision: 6, null: false
+    t.datetime "updated_at", precision: 6, null: false
+    t.index ["store_id"], name: "index_favorites_on_store_id"
+    t.index ["user_id"], name: "index_favorites_on_user_id"
+  end
+
   create_table "phones", force: :cascade do |t|
     t.string "phone_number", null: false
     t.string "name"
@@ -265,6 +274,8 @@
   add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
   add_foreign_key "api_keys", "users", on_delete: :cascade
   add_foreign_key "beach_configurations", "stores", on_delete: :cascade
+  add_foreign_key "favorites", "stores"
+  add_foreign_key "favorites", "users"
   add_foreign_key "phones", "stores", on_delete: :cascade
   add_foreign_key "status_crowdsource_users", "stores", on_delete: :cascade
   add_foreign_key "status_crowdsource_users", "users", on_delete: :cascade
diff --git a/spec/factories/favorites.rb b/spec/factories/favorites.rb
new file mode 100644
index 00000000..a83c0806
--- /dev/null
+++ b/spec/factories/favorites.rb
@@ -0,0 +1,16 @@
+# == Schema Information
+#
+# Table name: favorites
+#
+#  id         :bigint           not null, primary key
+#  store_id   :bigint           not null
+#  user_id    :bigint           not null
+#  created_at :datetime         not null
+#  updated_at :datetime         not null
+#
+FactoryBot.define do
+  factory :favorite do
+    store { nil }
+    user { nil }
+  end
+end
diff --git a/spec/models/favorite_spec.rb b/spec/models/favorite_spec.rb
new file mode 100644
index 00000000..631247a7
--- /dev/null
+++ b/spec/models/favorite_spec.rb
@@ -0,0 +1,15 @@
+# == Schema Information
+#
+# Table name: favorites
+#
+#  id         :bigint           not null, primary key
+#  store_id   :bigint           not null
+#  user_id    :bigint           not null
+#  created_at :datetime         not null
+#  updated_at :datetime         not null
+#
+require 'rails_helper'
+
+RSpec.describe Favorite, type: :model do
+  pending "add some examples to (or delete) #{__FILE__}"
+end

From 28c3b82386a3316060fae9b5f9bcb5c28e3ac606 Mon Sep 17 00:00:00 2001
From: Tiago Santos <santos.tiago@gmail.com>
Date: Mon, 29 Jun 2020 00:13:13 +0100
Subject: [PATCH 03/14] Finished the crud for favorites

---
 .../api/v1/favorites_controller.rb            | 56 +++++++++++++++++++
 app/controllers/api_controller.rb             |  5 +-
 app/models/favorite.rb                        |  2 +
 app/models/user.rb                            |  1 +
 app/resources/api/v1/favorite_resource.rb     | 21 +++++++
 app/serializers/favorite_serializer.rb        | 40 +++++++++++++
 config/routes.rb                              |  2 +
 db/migrate/20200628215106_create_favorites.rb |  2 +
 db/schema.rb                                  |  1 +
 9 files changed, 128 insertions(+), 2 deletions(-)
 create mode 100644 app/controllers/api/v1/favorites_controller.rb
 create mode 100644 app/resources/api/v1/favorite_resource.rb
 create mode 100644 app/serializers/favorite_serializer.rb

diff --git a/app/controllers/api/v1/favorites_controller.rb b/app/controllers/api/v1/favorites_controller.rb
new file mode 100644
index 00000000..2d1e3a3e
--- /dev/null
+++ b/app/controllers/api/v1/favorites_controller.rb
@@ -0,0 +1,56 @@
+module Api
+  module V1
+    class FavoritesController < ApiController
+      def create
+        return if no_user
+        return if no_store
+
+        favorite = Favorite.new(store: @store, user: @user)
+        if favorite.save
+          render json: FavoriteSerializer.new(favorite).serialized_json, status: :created
+        else
+          render json: {errors: favorite.errors.full_messages.join(', ')}, status: :conflict
+        end
+      end
+
+      def destroy
+        return if no_user
+
+        favorite = Favorite.find_by id: params[:id]
+        return render json: {errors: "Couldn't find favorite"}, status: :not_found unless favorite
+        unless @user == favorite.user
+          return render json: {errors: "Favorite doesn't belong to user"}, status: :forbidden
+        end
+
+        if favorite.destroy
+          render json: {}, status: :no_content
+        else
+          render json: {errors: favorite.errors.full_messages.join(', ')}, status: :conflict
+        end
+      end
+
+      def index
+        @favorites = context[:current_user].favorites
+        super
+      end
+
+      private
+
+      def no_user
+        @user = context[:current_user]
+        return false if @user&.confirmed?
+
+        render json: {error: 'You must be authenticated'}, status: :forbidden
+        true
+      end
+
+      def no_store
+        @store = Store.find params.dig(:data, :attributes, :"store-id")
+        false
+      rescue ActiveRecord::RecordNotFound
+        render json: {error: 'Store not found'}
+        true
+      end
+    end
+  end
+end
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index fdfe8855..2b77da49 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -18,7 +18,7 @@ def context
   private
 
   def authenticate_with_jwt!
-    return @current_user = User.first if Rails.env.development?
+    # return @current_user = User.first if Rails.env.development?
 
     payload = JwtService.decode(token: token)
     if DateTime.parse(payload['expiration_date']) <= DateTime.current
@@ -36,7 +36,8 @@ def current_user
     @current_user ||= if store_owner_code
                         User.find_by(store_owner_code: store_owner_code, role: :store_owner)
                       else
-                        User.find_by(app_uuid: JwtService.decode(token: token)['uuid'])
+                        tmp_uuid = JwtService.decode(token: token)['uuid']
+                        tmp_uuid.present? ? User.find_by(app_uuid: tmp_uuid) : nil
                       end
   rescue StandardError
     @current_user = nil
diff --git a/app/models/favorite.rb b/app/models/favorite.rb
index 9d312519..e96b4a58 100644
--- a/app/models/favorite.rb
+++ b/app/models/favorite.rb
@@ -11,4 +11,6 @@
 class Favorite < ApplicationRecord
   belongs_to :store
   belongs_to :user
+
+  validates :store_id, uniqueness: {scope: :user_id}
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index e160233e..762206b4 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -40,6 +40,7 @@ class User < ApplicationRecord
   has_many :stores, through: :user_stores
   has_many :created_stores, class_name: 'Store', foreign_key: :created_by_id, inverse_of: :created_by
   has_many :status_crowdsource_users
+  has_many :favorites
   has_one :api_key
 
   has_secure_token :store_owner_code
diff --git a/app/resources/api/v1/favorite_resource.rb b/app/resources/api/v1/favorite_resource.rb
new file mode 100644
index 00000000..932dc9ab
--- /dev/null
+++ b/app/resources/api/v1/favorite_resource.rb
@@ -0,0 +1,21 @@
+module Api
+  module V1
+    class FavoriteResource < ApplicationResource
+      caching
+
+      attributes :store_id, :created_at
+
+      has_one :user
+      has_one :store
+
+      filters :user, :store
+
+      def self.records(options = {})
+        current_user = options[:context][:current_user]
+        current_user&.favorites
+      end
+
+      exclude_links :default
+    end
+  end
+end
diff --git a/app/serializers/favorite_serializer.rb b/app/serializers/favorite_serializer.rb
new file mode 100644
index 00000000..6f017cc3
--- /dev/null
+++ b/app/serializers/favorite_serializer.rb
@@ -0,0 +1,40 @@
+# == Schema Information
+#
+# Table name: stores
+#
+#  id                  :bigint           not null, primary key
+#  name                :string
+#  group               :string
+#  street              :string
+#  city                :string
+#  district            :string
+#  country             :string
+#  zip_code            :string
+#  latitude            :float
+#  longitude           :float
+#  capacity            :integer
+#  details             :text
+#  store_type          :integer          default("1"), not null
+#  created_at          :datetime         not null
+#  updated_at          :datetime         not null
+#  lonlat              :geometry         point, 0
+#  state               :integer          default("1")
+#  reason_to_delete    :text
+#  open                :boolean          default("true")
+#  created_by_id       :bigint
+#  updated_by_id       :bigint
+#  from_osm            :boolean          default("false")
+#  original_id         :bigint
+#  source              :string
+#  make_phone_calls    :boolean          default("false")
+#  phone_call_interval :integer          default("60")
+#  municipality        :string
+#
+
+class FavoriteSerializer
+  include FastJsonapi::ObjectSerializer
+  set_key_transform :dash
+  set_type :favorites
+
+  attributes :store_id, :user_id
+end
diff --git a/config/routes.rb b/config/routes.rb
index c7f18d01..865acfec 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -34,6 +34,8 @@
       jsonapi_resources :status_crowdsources, only: [:index] do end
       jsonapi_resources :status_generals, only: [:index] do end
       jsonapi_resources :random_status_generals, only: [:index] do end
+      jsonapi_resources :favorites, only: [:index, :create, :destroy] do end
+
 
       get 'beach-status', to: 'beaches#index'
       get 'beach-general-status', to: 'beaches#general_status'
diff --git a/db/migrate/20200628215106_create_favorites.rb b/db/migrate/20200628215106_create_favorites.rb
index 6de45b1f..ef5e5236 100644
--- a/db/migrate/20200628215106_create_favorites.rb
+++ b/db/migrate/20200628215106_create_favorites.rb
@@ -5,6 +5,8 @@ def change
       t.references :user, null: false, foreign_key: true
 
       t.timestamps
+
+      t.index [:store_id, :user_id]
     end
   end
 end
diff --git a/db/schema.rb b/db/schema.rb
index 50168f29..4d7fbca2 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -90,6 +90,7 @@
     t.bigint "user_id", null: false
     t.datetime "created_at", precision: 6, null: false
     t.datetime "updated_at", precision: 6, null: false
+    t.index ["store_id", "user_id"], name: "index_favorites_on_store_id_and_user_id"
     t.index ["store_id"], name: "index_favorites_on_store_id"
     t.index ["user_id"], name: "index_favorites_on_user_id"
   end

From 41ccdfae2d766b75f8f34f65871dd8e5cc070d4e Mon Sep 17 00:00:00 2001
From: Tiago Santos <santos.tiago@gmail.com>
Date: Mon, 29 Jun 2020 00:24:39 +0100
Subject: [PATCH 04/14] Lists users' favorites on login in

---
 app/controllers/api/v1/auth_controller.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb
index 3892ed65..26dab6dc 100644
--- a/app/controllers/api/v1/auth_controller.rb
+++ b/app/controllers/api/v1/auth_controller.rb
@@ -40,7 +40,7 @@ def login
           # rubocop:disable Rails/SkipsModelValidations
           User.where.not(id: user.id).where(app_uuid: user.app_uuid).update_all(app_uuid: nil)
           # rubocop:enable Rails/SkipsModelValidations
-          render json: {success: 'login successful'}, status: :ok
+          render json: FavoriteSerializer.new(user.favorites).serialized_json, status: :ok
         else
           render json: {error: 'wrong password'}, status: :unauthorized
         end

From 8f52319c8730f53b769021e8b96eb54cb07c4fd2 Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Mon, 29 Jun 2020 09:15:08 +0100
Subject: [PATCH 05/14] Makes sure favorites index on store_id and user_id are
 unique

---
 db/migrate/20200629081037_set_index_unique_on_favorites.rb | 6 ++++++
 db/schema.rb                                               | 4 ++--
 2 files changed, 8 insertions(+), 2 deletions(-)
 create mode 100644 db/migrate/20200629081037_set_index_unique_on_favorites.rb

diff --git a/db/migrate/20200629081037_set_index_unique_on_favorites.rb b/db/migrate/20200629081037_set_index_unique_on_favorites.rb
new file mode 100644
index 00000000..ba032dd0
--- /dev/null
+++ b/db/migrate/20200629081037_set_index_unique_on_favorites.rb
@@ -0,0 +1,6 @@
+class SetIndexUniqueOnFavorites < ActiveRecord::Migration[6.0]
+  def change
+    remove_index :favorites, name: :index_favorites_on_store_id_and_user_id
+    add_index :favorites, [:store_id, :user_id], unique: true
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4d7fbca2..7f6d64ac 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2020_06_28_215106) do
+ActiveRecord::Schema.define(version: 2020_06_29_081037) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -90,7 +90,7 @@
     t.bigint "user_id", null: false
     t.datetime "created_at", precision: 6, null: false
     t.datetime "updated_at", precision: 6, null: false
-    t.index ["store_id", "user_id"], name: "index_favorites_on_store_id_and_user_id"
+    t.index ["store_id", "user_id"], name: "index_favorites_on_store_id_and_user_id", unique: true
     t.index ["store_id"], name: "index_favorites_on_store_id"
     t.index ["user_id"], name: "index_favorites_on_user_id"
   end

From 65893ce04e5955d00102d3e8536635a3a06d6edf Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Mon, 29 Jun 2020 11:11:36 +0100
Subject: [PATCH 06/14] use different status code

---
 app/controllers/api/v1/auth_controller.rb | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb
index 26dab6dc..30f62a4b 100644
--- a/app/controllers/api/v1/auth_controller.rb
+++ b/app/controllers/api/v1/auth_controller.rb
@@ -5,7 +5,7 @@ class AuthController < ApiController
 
       def register
         if User.find_by(email: @attrs[:email]) || @attrs[:email].blank?
-          render json: {error: 'email already registered'}, status: :unauthorized and return
+          render json: {error: 'email already registered'}, status: :forbidden and return
         end
 
         new_user = nil
@@ -42,10 +42,10 @@ def login
           # rubocop:enable Rails/SkipsModelValidations
           render json: FavoriteSerializer.new(user.favorites).serialized_json, status: :ok
         else
-          render json: {error: 'wrong password'}, status: :unauthorized
+          render json: {error: 'wrong password'}, status: :forbidden
         end
       rescue StandardError
-        render json: {error: 'authentication failed'}, status: :unauthorized
+        render json: {error: 'authentication failed'}, status: :forbidden
       end
 
       def logout
@@ -53,7 +53,7 @@ def logout
           context[:current_user].update(app_uuid: "#{context[:current_user].app_uuid}_old_#{Time.current.to_i}")
           render json: {success: 'user logged out'}, status: :ok
         else
-          render json: {error: 'user not logged in'}, status: :unauthorized
+          render json: {error: 'user not logged in'}, status: :forbidden
         end
       end
 

From 3b11bf5aeaaf2b6a36c721f3ede756e2b2766f5d Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Thu, 2 Jul 2020 10:47:54 +0100
Subject: [PATCH 07/14] don't verify authenticity token

---
 app/controllers/api/v1/auth_controller.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb
index 30f62a4b..2e81c658 100644
--- a/app/controllers/api/v1/auth_controller.rb
+++ b/app/controllers/api/v1/auth_controller.rb
@@ -2,6 +2,7 @@ module Api
   module V1
     class AuthController < ApiController
       before_action :set_attrs
+      skip_before_action :verify_authenticity_token
 
       def register
         if User.find_by(email: @attrs[:email]) || @attrs[:email].blank?

From 15737e80101cc3cae87a849d99bd65cfdb06f361 Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Thu, 2 Jul 2020 11:00:59 +0100
Subject: [PATCH 08/14] enable emails with sendgrid ons taging

---
 config/environments/staging.rb | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/config/environments/staging.rb b/config/environments/staging.rb
index 20ee76fe..4f49043e 100644
--- a/config/environments/staging.rb
+++ b/config/environments/staging.rb
@@ -104,4 +104,15 @@
   # config.active_record.database_selector = { delay: 2.seconds }
   # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
   # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
+  config.action_mailer.default_url_options = {host: 'posso-ir-staging.herokuapp.com'}
+
+  ActionMailer::Base.smtp_settings = {
+    :user_name => ENV['SENDGRID_USERNAME'],
+    :password => ENV['SENDGRID_PASSWORD'],
+    :domain => 'yourdomain.com',
+    :address => 'smtp.sendgrid.net',
+    :port => 587,
+    :authentication => :plain,
+    :enable_starttls_auto => true
+  }
 end

From 4f18f102d31e2499539b21a2f6b9b32a4eae0a30 Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Thu, 2 Jul 2020 22:44:25 +0100
Subject: [PATCH 09/14] confirm users when created on staging

---
 app/controllers/api/v1/auth_controller.rb | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb
index 2e81c658..218eb1a1 100644
--- a/app/controllers/api/v1/auth_controller.rb
+++ b/app/controllers/api/v1/auth_controller.rb
@@ -27,6 +27,8 @@ def register
         if new_user.errors.any?
           render json: {error: "unable to create user: #{new_user.errors.full_messages.join(', ')}"}, status: :conflict
         else
+          # confirm user straight away if using staging
+          new_user.confirm if Rails.env.staging?
           render json: {success: 'User created successfully'}, status: :created
         end
       end

From 791118ddf3431a54a023d523e36b92228162db5d Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Thu, 2 Jul 2020 23:00:56 +0100
Subject: [PATCH 10/14] adds environments

---
 .rubocop.yml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/.rubocop.yml b/.rubocop.yml
index ac71352a..f85d993b 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -100,3 +100,10 @@ Style/HashTransformKeys:
 
 Style/HashTransformValues:
   Enabled: true
+
+Rails/UnknownEnv:
+  Environments:
+    - production
+    - development
+    - test
+    - staging

From 91c3e615df561819bc93b5864687803f20a7802b Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Sun, 5 Jul 2020 22:50:42 +0100
Subject: [PATCH 11/14] Enables editing season start and season end

---
 app/views/beaches/_form.html.erb | 19 +++++++++++++++----
 config/locales/pt.yml            |  2 +-
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/app/views/beaches/_form.html.erb b/app/views/beaches/_form.html.erb
index b490bbd3..e4b701d5 100644
--- a/app/views/beaches/_form.html.erb
+++ b/app/views/beaches/_form.html.erb
@@ -127,8 +127,19 @@
       <%= form.fields_for :beach_configuration do |builder| %>
         <div class="form-row">
           <div class="form-group col-md-6">
-            <%= builder.label :quality_flag, class: "col-sm-4 col-form-label" %>
-            <%= builder.check_box :quality_flag,  class: "form-control" %>
+            <%= builder.label :season_start, class: "col-sm-4 col-form-label" %>
+            <%= builder.date_field :season_start,  class: "form-control" %>
+          </div>
+          <div class="form-group col-md-6">
+            <%= builder.label :season_end, class: "col-sm-6 col-form-label" %>
+            <%= builder.date_field :season_end,  class: "form-control" %>
+          </div>
+        </div>
+
+        <div class="form-row">
+          <div class="form-group col-md-6">
+            <%= builder.label :water_classification, class: "col-sm-8 col-form-label" %>
+            <%= builder.text_field :water_classification,  class: "form-control" %>
           </div>
           <div class="form-group col-md-6">
             <%= builder.label :sapo_code, class: "col-sm-6 col-form-label" %>
@@ -208,8 +219,8 @@
             <%= builder.check_box :bathing_support,  class: "form-control" %>
           </div>
           <div class="form-group col-md-4">
-            <%= builder.label :water_classification, class: "col-sm-8 col-form-label" %>
-            <%= builder.text_field :water_classification,  class: "form-control" %>
+            <%= builder.label :quality_flag, class: "col-sm-6 col-form-label" %>
+            <%= builder.check_box :quality_flag,  class: "form-control" %>
           </div>
         </div>
       <% end %>
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index a7221c8a..5b871043 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -252,7 +252,7 @@ pt:
                      <strong>Activa</strong>: Praia disponível na aplicação.<br>
                     <strong>Marcada para remoção</strong>: Remoção será revista e confirmada por um administrador."
         info_deleting: 'NOTA: Quando marcar uma praia para remoção por favor escreva o porquê dessa marcação no campo de detalhes.'
-        beaches_attributes: 'Atributos das praias'
+        beach_attributes: 'Atributos das praias'
     status_crowdsource_users:
       index:
         title: 'Estados reportados'

From a95141d07461f4299ee8db413f23e7fe96c88a7d Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Thu, 9 Jul 2020 09:35:34 +0100
Subject: [PATCH 12/14] Use stores serializer instead of specific favorites
 serializer

---
 app/controllers/api/v1/auth_controller.rb     |  2 +-
 .../api/v1/favorites_controller.rb            |  6 +--
 app/models/user.rb                            |  1 +
 app/serializers/favorite_serializer.rb        | 40 -------------------
 4 files changed, 5 insertions(+), 44 deletions(-)
 delete mode 100644 app/serializers/favorite_serializer.rb

diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb
index 218eb1a1..3fd0020e 100644
--- a/app/controllers/api/v1/auth_controller.rb
+++ b/app/controllers/api/v1/auth_controller.rb
@@ -43,7 +43,7 @@ def login
           # rubocop:disable Rails/SkipsModelValidations
           User.where.not(id: user.id).where(app_uuid: user.app_uuid).update_all(app_uuid: nil)
           # rubocop:enable Rails/SkipsModelValidations
-          render json: FavoriteSerializer.new(user.favorites).serialized_json, status: :ok
+          render json: StoreSerializer.new(user.favorite_stores).serialized_json, status: :ok
         else
           render json: {error: 'wrong password'}, status: :forbidden
         end
diff --git a/app/controllers/api/v1/favorites_controller.rb b/app/controllers/api/v1/favorites_controller.rb
index 2d1e3a3e..a0fb03db 100644
--- a/app/controllers/api/v1/favorites_controller.rb
+++ b/app/controllers/api/v1/favorites_controller.rb
@@ -7,7 +7,7 @@ def create
 
         favorite = Favorite.new(store: @store, user: @user)
         if favorite.save
-          render json: FavoriteSerializer.new(favorite).serialized_json, status: :created
+          render json: StoreSerializer.new(favorite.store).serialized_json, status: :created
         else
           render json: {errors: favorite.errors.full_messages.join(', ')}, status: :conflict
         end
@@ -30,8 +30,8 @@ def destroy
       end
 
       def index
-        @favorites = context[:current_user].favorites
-        super
+        favorites = context[:current_user].favorite_stores
+        render json: StoreSerializer.new(favorites).serialized_json
       end
 
       private
diff --git a/app/models/user.rb b/app/models/user.rb
index 762206b4..c8e52df9 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -41,6 +41,7 @@ class User < ApplicationRecord
   has_many :created_stores, class_name: 'Store', foreign_key: :created_by_id, inverse_of: :created_by
   has_many :status_crowdsource_users
   has_many :favorites
+  has_many :favorite_stores, through: :favorites, source: :store
   has_one :api_key
 
   has_secure_token :store_owner_code
diff --git a/app/serializers/favorite_serializer.rb b/app/serializers/favorite_serializer.rb
deleted file mode 100644
index 6f017cc3..00000000
--- a/app/serializers/favorite_serializer.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# == Schema Information
-#
-# Table name: stores
-#
-#  id                  :bigint           not null, primary key
-#  name                :string
-#  group               :string
-#  street              :string
-#  city                :string
-#  district            :string
-#  country             :string
-#  zip_code            :string
-#  latitude            :float
-#  longitude           :float
-#  capacity            :integer
-#  details             :text
-#  store_type          :integer          default("1"), not null
-#  created_at          :datetime         not null
-#  updated_at          :datetime         not null
-#  lonlat              :geometry         point, 0
-#  state               :integer          default("1")
-#  reason_to_delete    :text
-#  open                :boolean          default("true")
-#  created_by_id       :bigint
-#  updated_by_id       :bigint
-#  from_osm            :boolean          default("false")
-#  original_id         :bigint
-#  source              :string
-#  make_phone_calls    :boolean          default("false")
-#  phone_call_interval :integer          default("60")
-#  municipality        :string
-#
-
-class FavoriteSerializer
-  include FastJsonapi::ObjectSerializer
-  set_key_transform :dash
-  set_type :favorites
-
-  attributes :store_id, :user_id
-end

From 93ccd0c3e9533752857dcb7aa7f225e9906009ce Mon Sep 17 00:00:00 2001
From: Simao Belchior <simaobelchior@gmail.com>
Date: Thu, 9 Jul 2020 20:41:37 +0100
Subject: [PATCH 13/14] include email and user's name, plus favorite stores and
 metrics

---
 app/controllers/api/v1/auth_controller.rb      |  4 +++-
 app/controllers/api/v1/favorites_controller.rb |  2 +-
 app/models/user.rb                             |  2 +-
 app/serializers/user_serializer.rb             | 17 +++++++++++++++++
 4 files changed, 22 insertions(+), 3 deletions(-)
 create mode 100644 app/serializers/user_serializer.rb

diff --git a/app/controllers/api/v1/auth_controller.rb b/app/controllers/api/v1/auth_controller.rb
index 3fd0020e..f23b4d31 100644
--- a/app/controllers/api/v1/auth_controller.rb
+++ b/app/controllers/api/v1/auth_controller.rb
@@ -42,8 +42,10 @@ def login
           # Invalidate other users on the same devise
           # rubocop:disable Rails/SkipsModelValidations
           User.where.not(id: user.id).where(app_uuid: user.app_uuid).update_all(app_uuid: nil)
+          options = {}
+          options[:include] = [:stores]
           # rubocop:enable Rails/SkipsModelValidations
-          render json: StoreSerializer.new(user.favorite_stores).serialized_json, status: :ok
+          render json: UserSerializer.new(user, options).serialized_json, status: :ok
         else
           render json: {error: 'wrong password'}, status: :forbidden
         end
diff --git a/app/controllers/api/v1/favorites_controller.rb b/app/controllers/api/v1/favorites_controller.rb
index a0fb03db..ad251dac 100644
--- a/app/controllers/api/v1/favorites_controller.rb
+++ b/app/controllers/api/v1/favorites_controller.rb
@@ -30,7 +30,7 @@ def destroy
       end
 
       def index
-        favorites = context[:current_user].favorite_stores
+        favorites = context[:current_user].stores
         render json: StoreSerializer.new(favorites).serialized_json
       end
 
diff --git a/app/models/user.rb b/app/models/user.rb
index c8e52df9..a344e76c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -41,7 +41,7 @@ class User < ApplicationRecord
   has_many :created_stores, class_name: 'Store', foreign_key: :created_by_id, inverse_of: :created_by
   has_many :status_crowdsource_users
   has_many :favorites
-  has_many :favorite_stores, through: :favorites, source: :store
+  has_many :stores, through: :favorites, source: :store
   has_one :api_key
 
   has_secure_token :store_owner_code
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
new file mode 100644
index 00000000..30a8e41f
--- /dev/null
+++ b/app/serializers/user_serializer.rb
@@ -0,0 +1,17 @@
+class UserSerializer
+  include FastJsonapi::ObjectSerializer
+  set_key_transform :dash
+  set_type :user
+
+  attribute :email
+  attribute :name
+  attribute :reports_made do
+    rand(1..100)
+  end
+  attribute :reporter_ranking do
+    rand(1..100)
+  end
+
+  has_many :stores, type: :stores, serializer: StoreSerializer
+end
+

From e2c47707dff950bc5ba883d1851318c3679c42f5 Mon Sep 17 00:00:00 2001
From: Tiago Santos <santos.tiago@gmail.com>
Date: Thu, 9 Jul 2020 23:55:47 +0200
Subject: [PATCH 14/14] Deleting a favorite now takes the store_id instead of
 the favorite_id

---
 app/controllers/api/v1/favorites_controller.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/app/controllers/api/v1/favorites_controller.rb b/app/controllers/api/v1/favorites_controller.rb
index ad251dac..030d2fb7 100644
--- a/app/controllers/api/v1/favorites_controller.rb
+++ b/app/controllers/api/v1/favorites_controller.rb
@@ -16,11 +16,8 @@ def create
       def destroy
         return if no_user
 
-        favorite = Favorite.find_by id: params[:id]
+        favorite = Favorite.find_by user_id: @user.id, store_id: params[:id]
         return render json: {errors: "Couldn't find favorite"}, status: :not_found unless favorite
-        unless @user == favorite.user
-          return render json: {errors: "Favorite doesn't belong to user"}, status: :forbidden
-        end
 
         if favorite.destroy
           render json: {}, status: :no_content