mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	feat: Add Platform APIs (#1456)
This commit is contained in:
		| @@ -48,6 +48,7 @@ Rails/ApplicationController: | |||||||
|     - 'app/controllers/dashboard_controller.rb' |     - 'app/controllers/dashboard_controller.rb' | ||||||
|     - 'app/controllers/widget_tests_controller.rb' |     - 'app/controllers/widget_tests_controller.rb' | ||||||
|     - 'app/controllers/widgets_controller.rb' |     - 'app/controllers/widgets_controller.rb' | ||||||
|  |     - 'app/controllers/platform_controller.rb' | ||||||
| Style/ClassAndModuleChildren: | Style/ClassAndModuleChildren: | ||||||
|   EnforcedStyle: compact |   EnforcedStyle: compact | ||||||
|   Exclude: |   Exclude: | ||||||
|   | |||||||
| @@ -6,13 +6,6 @@ | |||||||
| # Note that changes in the inspected code, or installation of new | # Note that changes in the inspected code, or installation of new | ||||||
| # versions of RuboCop, may require this file to be generated again. | # versions of RuboCop, may require this file to be generated again. | ||||||
|  |  | ||||||
| # Offense count: 1 |  | ||||||
| # Configuration parameters: EnforcedStyle. |  | ||||||
| # SupportedStyles: native, lf, crlf |  | ||||||
| Layout/EndOfLine: |  | ||||||
|   Exclude: |  | ||||||
|     - 'deploy/after_restart.rb' |  | ||||||
|  |  | ||||||
| # Offense count: 1 | # Offense count: 1 | ||||||
| Lint/DuplicateMethods: | Lint/DuplicateMethods: | ||||||
|   Exclude: |   Exclude: | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ class ApplicationController < ActionController::Base | |||||||
|     render_unauthorized('You are not authorized to do this action') |     render_unauthorized('You are not authorized to do this action') | ||||||
|   ensure |   ensure | ||||||
|     # to address the thread variable leak issues in Puma/Thin webserver |     # to address the thread variable leak issues in Puma/Thin webserver | ||||||
|     Current.user = nil |     Current.reset | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def set_current_user |   def set_current_user | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								app/controllers/platform/api/v1/account_users_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/controllers/platform/api/v1/account_users_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | class Platform::Api::V1::AccountUsersController < PlatformController | ||||||
|  |   before_action :set_resource | ||||||
|  |   before_action :validate_platform_app_permissible | ||||||
|  |  | ||||||
|  |   def index | ||||||
|  |     render json: @resource.account_users | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def create | ||||||
|  |     @account_user = @resource.account_users.find_or_initialize_by(user_id: account_user_params[:user_id]) | ||||||
|  |     @account_user.update!(account_user_params) | ||||||
|  |     render json: @account_user | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def destroy | ||||||
|  |     @resource.account_users.find_by(user_id: account_user_params[:user_id])&.destroy | ||||||
|  |     head :ok | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def set_resource | ||||||
|  |     @resource = Account.find(params[:account_id]) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_user_params | ||||||
|  |     params.permit(:user_id, :role) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										32
									
								
								app/controllers/platform/api/v1/accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/controllers/platform/api/v1/accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | class Platform::Api::V1::AccountsController < PlatformController | ||||||
|  |   def create | ||||||
|  |     @resource = Account.new(account_params) | ||||||
|  |     @resource.save! | ||||||
|  |     @platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource) | ||||||
|  |     render json: @resource | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def show | ||||||
|  |     render json: @resource | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def update | ||||||
|  |     @resource.update!(account_params) | ||||||
|  |     render json: @resource | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def destroy | ||||||
|  |     # TODO: obfusicate account | ||||||
|  |     head :ok | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def set_resource | ||||||
|  |     @resource = Account.find(params[:id]) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def account_params | ||||||
|  |     params.permit(:name) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										43
									
								
								app/controllers/platform/api/v1/users_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/controllers/platform/api/v1/users_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | class Platform::Api::V1::UsersController < PlatformController | ||||||
|  |   # ref: https://stackoverflow.com/a/45190318/939299 | ||||||
|  |   # set resource is called for other actions already in platform controller | ||||||
|  |   # we want to add login to that chain as well | ||||||
|  |   before_action(only: [:login]) { set_resource } | ||||||
|  |   before_action(only: [:login]) { validate_platform_app_permissible } | ||||||
|  |  | ||||||
|  |   def create | ||||||
|  |     @resource = (User.find_by(email: user_params[:email]) || User.new(user_params)) | ||||||
|  |     @resource.confirm | ||||||
|  |     @resource.save! | ||||||
|  |     @platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource) | ||||||
|  |     render json: @resource | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def login | ||||||
|  |     render json: { url: "#{ENV['FRONTEND_URL']}/app/login?email=#{@resource.email}&sso_auth_token=#{@resource.generate_sso_auth_token}" } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def show | ||||||
|  |     render json: @resource | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def update | ||||||
|  |     @resource.update!(user_params) | ||||||
|  |     render json: @resource | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def destroy | ||||||
|  |     # TODO: obfusicate user | ||||||
|  |     head :ok | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def set_resource | ||||||
|  |     @resource = User.find(params[:id]) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def user_params | ||||||
|  |     params.permit(:name, :email, :password) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										37
									
								
								app/controllers/platform_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/controllers/platform_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | class PlatformController < ActionController::Base | ||||||
|  |   protect_from_forgery with: :null_session | ||||||
|  |  | ||||||
|  |   before_action :ensure_access_token | ||||||
|  |   before_action :set_platform_app | ||||||
|  |   before_action :set_resource, only: [:update, :show, :destroy] | ||||||
|  |   before_action :validate_platform_app_permissible, only: [:update, :show, :destroy] | ||||||
|  |  | ||||||
|  |   def show; end | ||||||
|  |  | ||||||
|  |   def update; end | ||||||
|  |  | ||||||
|  |   def destroy; end | ||||||
|  |  | ||||||
|  |   private | ||||||
|  |  | ||||||
|  |   def ensure_access_token | ||||||
|  |     token = request.headers[:api_access_token] || request.headers[:HTTP_API_ACCESS_TOKEN] | ||||||
|  |     @access_token = AccessToken.find_by(token: token) if token.present? | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def set_platform_app | ||||||
|  |     @platform_app = @access_token.owner if @access_token && @access_token.owner.is_a?(PlatformApp) | ||||||
|  |     render json: { error: 'Invalid access_token' }, status: :unauthorized if @platform_app.blank? | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def set_resource | ||||||
|  |     # set @resource in your controller | ||||||
|  |     raise 'Overwrite this method your controller' | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def validate_platform_app_permissible | ||||||
|  |     return if @platform_app.platform_app_permissibles.find_by(permissible: @resource) | ||||||
|  |  | ||||||
|  |     render json: { error: 'Non permissible resource' }, status: :unauthorized | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -32,7 +32,6 @@ class Inbox < ApplicationRecord | |||||||
|  |  | ||||||
|   belongs_to :account |   belongs_to :account | ||||||
|  |  | ||||||
|   # TODO: should add associations for the channel types |  | ||||||
|   belongs_to :channel, polymorphic: true, dependent: :destroy |   belongs_to :channel, polymorphic: true, dependent: :destroy | ||||||
|  |  | ||||||
|   has_many :contact_inboxes, dependent: :destroy |   has_many :contact_inboxes, dependent: :destroy | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								app/models/platform_app.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/models/platform_app.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # == Schema Information | ||||||
|  | # | ||||||
|  | # Table name: platform_apps | ||||||
|  | # | ||||||
|  | #  id         :bigint           not null, primary key | ||||||
|  | #  name       :string           not null | ||||||
|  | #  created_at :datetime         not null | ||||||
|  | #  updated_at :datetime         not null | ||||||
|  | # | ||||||
|  | class PlatformApp < ApplicationRecord | ||||||
|  |   include AccessTokenable | ||||||
|  |  | ||||||
|  |   validates :name, presence: true | ||||||
|  |  | ||||||
|  |   has_many :platform_app_permissibles, dependent: :destroy | ||||||
|  | end | ||||||
							
								
								
									
										26
									
								
								app/models/platform_app_permissible.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/models/platform_app_permissible.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | # == Schema Information | ||||||
|  | # | ||||||
|  | # Table name: platform_app_permissibles | ||||||
|  | # | ||||||
|  | #  id               :bigint           not null, primary key | ||||||
|  | #  permissible_type :string           not null | ||||||
|  | #  created_at       :datetime         not null | ||||||
|  | #  updated_at       :datetime         not null | ||||||
|  | #  permissible_id   :bigint           not null | ||||||
|  | #  platform_app_id  :bigint           not null | ||||||
|  | # | ||||||
|  | # Indexes | ||||||
|  | # | ||||||
|  | #  index_platform_app_permissibles_on_permissibles     (permissible_type,permissible_id) | ||||||
|  | #  index_platform_app_permissibles_on_platform_app_id  (platform_app_id) | ||||||
|  | #  unique_permissibles_index                           (platform_app_id,permissible_id,permissible_type) UNIQUE | ||||||
|  | # | ||||||
|  | class PlatformAppPermissible < ApplicationRecord | ||||||
|  |   include AccessTokenable | ||||||
|  |  | ||||||
|  |   validates :platform_app, presence: true | ||||||
|  |   validates :platform_app_id, uniqueness: { scope: [:permissible_id, :permissible_type] } | ||||||
|  |  | ||||||
|  |   belongs_to :platform_app | ||||||
|  |   belongs_to :permissible, polymorphic: true, dependent: :destroy | ||||||
|  | end | ||||||
| @@ -155,13 +155,25 @@ Rails.application.routes.draw do | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   namespace :twitter do |   # ---------------------------------------------------------------------- | ||||||
|     resource :authorization, only: [:create] |   # Routes for platform APIs | ||||||
|     resource :callback, only: [:show] |   namespace :platform, defaults: { format: 'json' } do | ||||||
|  |     namespace :api do | ||||||
|  |       namespace :v1 do | ||||||
|  |         resources :users, only: [:create, :show, :update, :destroy] do | ||||||
|  |           member do | ||||||
|  |             get :login | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |         resources :accounts, only: [:create, :show, :update, :destroy] do | ||||||
|  |           resources :account_users, only: [:index, :create] do | ||||||
|  |             collection do | ||||||
|  |               delete :destroy | ||||||
|  |             end | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   namespace :twilio do |  | ||||||
|     resources :callback, only: [:create] |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   # ---------------------------------------------------------------------- |   # ---------------------------------------------------------------------- | ||||||
| @@ -173,14 +185,19 @@ Rails.application.routes.draw do | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   # ---------------------------------------------------------------------- |   # ---------------------------------------------------------------------- | ||||||
|   # Routes for social integrations |   # Routes for channel integrations | ||||||
|   mount Facebook::Messenger::Server, at: 'bot' |   mount Facebook::Messenger::Server, at: 'bot' | ||||||
|   get 'webhooks/twitter', to: 'api/v1/webhooks#twitter_crc' |   get 'webhooks/twitter', to: 'api/v1/webhooks#twitter_crc' | ||||||
|   post 'webhooks/twitter', to: 'api/v1/webhooks#twitter_events' |   post 'webhooks/twitter', to: 'api/v1/webhooks#twitter_events' | ||||||
|  |  | ||||||
|   # ---------------------------------------------------------------------- |   namespace :twitter do | ||||||
|   # Routes for testing |     resource :authorization, only: [:create] | ||||||
|   resources :widget_tests, only: [:index] unless Rails.env.production? |     resource :callback, only: [:show] | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   namespace :twilio do | ||||||
|  |     resources :callback, only: [:create] | ||||||
|  |   end | ||||||
|  |  | ||||||
|   # ---------------------------------------------------------------------- |   # ---------------------------------------------------------------------- | ||||||
|   # Routes for external service verifications |   # Routes for external service verifications | ||||||
| @@ -216,4 +233,8 @@ Rails.application.routes.draw do | |||||||
|   # Routes for swagger docs |   # Routes for swagger docs | ||||||
|   get '/swagger/*path', to: 'swagger#respond' |   get '/swagger/*path', to: 'swagger#respond' | ||||||
|   get '/swagger', to: 'swagger#respond' |   get '/swagger', to: 'swagger#respond' | ||||||
|  |  | ||||||
|  |   # ---------------------------------------------------------------------- | ||||||
|  |   # Routes for testing | ||||||
|  |   resources :widget_tests, only: [:index] unless Rails.env.production? | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								db/migrate/20201123195011_create_platform_apps.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								db/migrate/20201123195011_create_platform_apps.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | class CreatePlatformApps < ActiveRecord::Migration[6.0] | ||||||
|  |   def change | ||||||
|  |     create_table :platform_apps do |t| | ||||||
|  |       t.string :name, null: false | ||||||
|  |       t.timestamps | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | class CreatePlatformAppPermissibles < ActiveRecord::Migration[6.0] | ||||||
|  |   def change | ||||||
|  |     create_table :platform_app_permissibles do |t| | ||||||
|  |       t.references :platform_app, index: true, null: false | ||||||
|  |       t.references :permissible, null: false, polymorphic: true, index: { name: :index_platform_app_permissibles_on_permissibles } | ||||||
|  |       t.timestamps | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     add_index :platform_app_permissibles, [:platform_app_id, :permissible_id, :permissible_type], unique: true, name: 'unique_permissibles_index' | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										18
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								db/schema.rb
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ | |||||||
| # | # | ||||||
| # 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_01_09_211805) do | ActiveRecord::Schema.define(version: 2021_01_09_211805) 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 | ||||||
| @@ -428,6 +429,23 @@ ActiveRecord::Schema.define(version: 2021_01_09_211805) do | |||||||
|     t.index ["user_id"], name: "index_notifications_on_user_id" |     t.index ["user_id"], name: "index_notifications_on_user_id" | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   create_table "platform_app_permissibles", force: :cascade do |t| | ||||||
|  |     t.bigint "platform_app_id", null: false | ||||||
|  |     t.string "permissible_type", null: false | ||||||
|  |     t.bigint "permissible_id", null: false | ||||||
|  |     t.datetime "created_at", precision: 6, null: false | ||||||
|  |     t.datetime "updated_at", precision: 6, null: false | ||||||
|  |     t.index ["permissible_type", "permissible_id"], name: "index_platform_app_permissibles_on_permissibles" | ||||||
|  |     t.index ["platform_app_id", "permissible_id", "permissible_type"], name: "unique_permissibles_index", unique: true | ||||||
|  |     t.index ["platform_app_id"], name: "index_platform_app_permissibles_on_platform_app_id" | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   create_table "platform_apps", force: :cascade do |t| | ||||||
|  |     t.string "name", null: false | ||||||
|  |     t.datetime "created_at", precision: 6, null: false | ||||||
|  |     t.datetime "updated_at", precision: 6, null: false | ||||||
|  |   end | ||||||
|  |  | ||||||
|   create_table "super_admins", force: :cascade do |t| |   create_table "super_admins", 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 | ||||||
|   | |||||||
| @@ -7,4 +7,10 @@ module Current | |||||||
|     super |     super | ||||||
|     Time.zone = account.timezone |     Time.zone = account.timezone | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def self.reset | ||||||
|  |     Current.user = nil | ||||||
|  |     Current.account = nil | ||||||
|  |     Current.account_user = nil | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -0,0 +1,93 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe 'Platform Account Users API', type: :request do | ||||||
|  |   let!(:account) { create(:account) } | ||||||
|  |  | ||||||
|  |   describe 'GET /platform/api/v1/accounts/{account_id}/account_users' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/accounts/#{account.id}/account_users" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |       let!(:account_user) { create(:account_user, account: account) } | ||||||
|  |  | ||||||
|  |       it 'returns all the account users for the account' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: account) | ||||||
|  |  | ||||||
|  |         get "/platform/api/v1/accounts/#{account.id}/account_users", | ||||||
|  |             headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         expect(response.body).to include(account_user.id.to_s) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'POST /platform/api/v1/accounts/{account_id}/account_users' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         post "/platform/api/v1/accounts/#{account.id}/account_users" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'creates a new account user for the account' do | ||||||
|  |         user = create(:user) | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: account) | ||||||
|  |  | ||||||
|  |         post "/platform/api/v1/accounts/#{account.id}/account_users", | ||||||
|  |              params: { user_id: user.id, role: 'administrator' }, | ||||||
|  |              headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['user_id']).to eq(user.id) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'updates the new account user for the account' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: account) | ||||||
|  |         account_user = create(:account_user, account: account, role: 'agent') | ||||||
|  |  | ||||||
|  |         post "/platform/api/v1/accounts/#{account.id}/account_users", | ||||||
|  |              params: { user_id: account_user.user_id, role: 'administrator' }, | ||||||
|  |              headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['role']).to eq('administrator') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'DELETE /platform/api/v1/accounts/{account_id}/account_users' do | ||||||
|  |     let(:account_user) { create(:account_user, account: account, role: 'agent') } | ||||||
|  |  | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         delete "/platform/api/v1/accounts/#{account.id}/account_users", params: { user_id: account_user.user_id } | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'returns deletes the account user' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: account) | ||||||
|  |  | ||||||
|  |         delete "/platform/api/v1/accounts/#{account.id}/account_users", params: { user_id: account_user.user_id }, | ||||||
|  |                                                                         headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         expect(account.account_users.count).to eq 0 | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										107
									
								
								spec/controllers/platform/api/v1/accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								spec/controllers/platform/api/v1/accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe 'Platform Accounts API', type: :request do | ||||||
|  |   let!(:account) { create(:account) } | ||||||
|  |  | ||||||
|  |   describe 'POST /platform/api/v1/accounts' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         post '/platform/api/v1/accounts' | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         post '/platform/api/v1/accounts', params: { name: 'Test Account' }, | ||||||
|  |                                           headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'creates an account when and its permissible relationship' do | ||||||
|  |         post '/platform/api/v1/accounts', params: { name: 'Test Account' }, | ||||||
|  |                                           headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         expect(response.body).to include('Test Account') | ||||||
|  |         expect(platform_app.platform_app_permissibles.first.permissible.name).to eq('Test Account') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'GET /platform/api/v1/accounts/{account_id}' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/accounts/#{account.id}" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/accounts/#{account.id}", headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'returns unauthorized when its not a permissible object' do | ||||||
|  |         get "/platform/api/v1/accounts/#{account.id}", headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'shows an account when its permissible object' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: account) | ||||||
|  |  | ||||||
|  |         get "/platform/api/v1/accounts/#{account.id}", | ||||||
|  |             headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         expect(response.body).to include(account.name) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'PATCH /platform/api/v1/accounts/{account_id}' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         patch "/platform/api/v1/accounts/#{account.id}" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         patch "/platform/api/v1/accounts/#{account.id}", params: { name: 'Test Account' }, | ||||||
|  |                                                          headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'returns unauthorized when its not a permissible object' do | ||||||
|  |         patch "/platform/api/v1/accounts/#{account.id}", params: { name: 'Test Account' }, | ||||||
|  |                                                          headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'updates an account when its permissible object' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: account) | ||||||
|  |  | ||||||
|  |         patch "/platform/api/v1/accounts/#{account.id}", params: { name: 'Test Account' }, | ||||||
|  |                                                          headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         expect(account.reload.name).to eq('Test Account') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										154
									
								
								spec/controllers/platform/api/v1/users_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								spec/controllers/platform/api/v1/users_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe 'Platform Users API', type: :request do | ||||||
|  |   let!(:user) { create(:user) } | ||||||
|  |  | ||||||
|  |   describe 'GET /platform/api/v1/users/{user_id}' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/users/#{user.id}" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/users/#{user.id}", headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'returns unauthorized when its not a permissible object' do | ||||||
|  |         get "/platform/api/v1/users/#{user.id}", headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'shows a user when its permissible object' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: user) | ||||||
|  |  | ||||||
|  |         get "/platform/api/v1/users/#{user.id}", | ||||||
|  |             headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['email']).to eq(user.email) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'GET /platform/api/v1/users/{user_id}/login' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/users/#{user.id}/login" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         get "/platform/api/v1/users/#{user.id}/login", headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'returns unauthorized when its not a permissible object' do | ||||||
|  |         get "/platform/api/v1/users/#{user.id}/login", headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'return login link for user' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: user) | ||||||
|  |  | ||||||
|  |         get "/platform/api/v1/users/#{user.id}/login", | ||||||
|  |             headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['url']).to include('sso_auth_token') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'POST /platform/api/v1/users/' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         post '/platform/api/v1/users' | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         post '/platform/api/v1/users/', headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'creates a new user and permissible for the user' do | ||||||
|  |         post '/platform/api/v1/users/', params: { name: 'test', email: 'test@test.com', password: 'password123' }, | ||||||
|  |                                         headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['email']).to eq('test@test.com') | ||||||
|  |         expect(platform_app.platform_app_permissibles.first.permissible_id).to eq data['id'] | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'fetch existing user and creates permissible for the user' do | ||||||
|  |         create(:user, name: 'old test', email: 'test@test.com') | ||||||
|  |         post '/platform/api/v1/users/', params: { name: 'test', email: 'test@test.com', password: 'password123' }, | ||||||
|  |                                         headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['name']).to eq('old test') | ||||||
|  |         expect(platform_app.platform_app_permissibles.first.permissible_id).to eq data['id'] | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'PATCH /platform/api/v1/users/{user_id}' do | ||||||
|  |     context 'when it is an unauthenticated platform app' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         patch "/platform/api/v1/users/#{user.id}" | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an invalid platform app token' do | ||||||
|  |       it 'returns unauthorized' do | ||||||
|  |         patch "/platform/api/v1/users/#{user.id}", headers: { api_access_token: 'invalid' }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'when it is an authenticated platform app' do | ||||||
|  |       let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |       it 'returns unauthorized when its not a permissible object' do | ||||||
|  |         patch "/platform/api/v1/users/#{user.id}", params: { name: 'test' }, | ||||||
|  |                                                    headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |         expect(response).to have_http_status(:unauthorized) | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'updates the user' do | ||||||
|  |         create(:platform_app_permissible, platform_app: platform_app, permissible: user) | ||||||
|  |         patch "/platform/api/v1/users/#{user.id}", params: { name: 'test123' }, | ||||||
|  |                                                    headers: { api_access_token: platform_app.access_token.token }, as: :json | ||||||
|  |  | ||||||
|  |         expect(response).to have_http_status(:success) | ||||||
|  |         data = JSON.parse(response.body) | ||||||
|  |         expect(data['name']).to eq('test123') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										5
									
								
								spec/factories/platform_apps.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/factories/platform_apps.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | FactoryBot.define do | ||||||
|  |   factory :platform_app do | ||||||
|  |     name { Faker::Book.name } | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										6
									
								
								spec/factories/platform_apps_permissibles.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								spec/factories/platform_apps_permissibles.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | FactoryBot.define do | ||||||
|  |   factory :platform_app_permissible do | ||||||
|  |     platform_app | ||||||
|  |     permissible { create(:user) } | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -1,8 +1,13 @@ | |||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
|  | require Rails.root.join 'spec/models/concerns/access_tokenable_spec.rb' | ||||||
|  |  | ||||||
| RSpec.describe AgentBot, type: :model do | RSpec.describe AgentBot, type: :model do | ||||||
|   describe 'associations' do |   describe 'associations' do | ||||||
|     it { is_expected.to have_many(:agent_bot_inboxes) } |     it { is_expected.to have_many(:agent_bot_inboxes) } | ||||||
|     it { is_expected.to have_many(:inboxes) } |     it { is_expected.to have_many(:inboxes) } | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   describe 'concerns' do | ||||||
|  |     it_behaves_like 'access_tokenable' | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								spec/models/concerns/access_tokenable_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								spec/models/concerns/access_tokenable_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | shared_examples_for 'access_tokenable' do | ||||||
|  |   let(:obj) { create(described_class.to_s.underscore) } | ||||||
|  |  | ||||||
|  |   it 'creates access token on create' do | ||||||
|  |     expect(obj.access_token).not_to eq(nil) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										20
									
								
								spec/models/platform_app_permissible_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								spec/models/platform_app_permissible_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe PlatformAppPermissible do | ||||||
|  |   let!(:platform_app_permissible) { create(:platform_app_permissible) } | ||||||
|  |  | ||||||
|  |   context 'with validations' do | ||||||
|  |     it { is_expected.to validate_presence_of(:platform_app) } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   context 'with associations' do | ||||||
|  |     it { is_expected.to belong_to(:platform_app) } | ||||||
|  |     it { is_expected.to belong_to(:permissible) } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'with factories' do | ||||||
|  |     it { expect(platform_app_permissible).present? } | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										24
									
								
								spec/models/platform_app_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								spec/models/platform_app_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | require 'rails_helper' | ||||||
|  | require Rails.root.join 'spec/models/concerns/access_tokenable_spec.rb' | ||||||
|  |  | ||||||
|  | RSpec.describe PlatformApp do | ||||||
|  |   let(:platform_app) { create(:platform_app) } | ||||||
|  |  | ||||||
|  |   context 'with validations' do | ||||||
|  |     it { is_expected.to validate_presence_of(:name) } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   context 'with associations' do | ||||||
|  |     it { is_expected.to have_many(:platform_app_permissibles) } | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'with concerns' do | ||||||
|  |     it_behaves_like 'access_tokenable' | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   describe 'with factories' do | ||||||
|  |     it { expect(platform_app).present? } | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
|  |  | ||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
|  | require Rails.root.join 'spec/models/concerns/access_tokenable_spec.rb' | ||||||
|  |  | ||||||
| RSpec.describe User do | RSpec.describe User do | ||||||
|   let!(:user) { create(:user) } |   let!(:user) { create(:user) } | ||||||
| @@ -20,6 +21,10 @@ RSpec.describe User do | |||||||
|     it { is_expected.to have_many(:events) } |     it { is_expected.to have_many(:events) } | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   describe 'concerns' do | ||||||
|  |     it_behaves_like 'access_tokenable' | ||||||
|  |   end | ||||||
|  |  | ||||||
|   describe 'pubsub_token' do |   describe 'pubsub_token' do | ||||||
|     before { user.update(name: Faker::Name.name) } |     before { user.update(name: Faker::Name.name) } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Sojan Jose
					Sojan Jose