From 09d07aea5af3cadea6b000045de01050b6683477 Mon Sep 17 00:00:00 2001 From: simukappu Date: Wed, 8 Jan 2020 10:14:57 +0900 Subject: [PATCH] Update subscription schema of REST API backend #113 --- .../subscriptions_api_controller.rb | 23 +++++++- .../subscriptions_controller.rb | 4 +- .../apis/subscription_api.rb | 18 ++++++ .../concerns/swagger/subscriptions_api.rb | 42 ++++++++++++++ .../concerns/swagger/subscription_schema.rb | 25 +++++++-- lib/activity_notification/rails/routes.rb | 3 + .../subscriptions_api_controller.rb | 5 ++ ...ubscriptions_api_with_devise_controller.rb | 5 ++ ...riptions_api_controller_shared_examples.rb | 56 ++++++++++++++++++- 9 files changed, 171 insertions(+), 10 deletions(-) diff --git a/app/controllers/activity_notification/subscriptions_api_controller.rb b/app/controllers/activity_notification/subscriptions_api_controller.rb index 13802742..001f0c00 100644 --- a/app/controllers/activity_notification/subscriptions_api_controller.rb +++ b/app/controllers/activity_notification/subscriptions_api_controller.rb @@ -6,6 +6,8 @@ class SubscriptionsApiController < SubscriptionsController # Include CommonApiController to select target and define common methods include CommonApiController protect_from_forgery except: [:create] + before_action :set_subscription, except: [:index, :create, :find, :optional_target_names] + before_action ->{ validate_param(:key) }, only: [:find, :optional_target_names] # Returns subscription index of the target. # @@ -44,12 +46,17 @@ def create subscription: subscription_json } else + optional_target_names = (params[:subscription][:optional_targets] || {}).keys.select { |key| !key.to_s.start_with?("subscribing_to_") } + optional_target_names.each do |optional_target_name| + subscribing_param = params[:subscription][:optional_targets][optional_target_name][:subscribing] + params[:subscription][:optional_targets]["subscribing_to_#{optional_target_name}"] = subscribing_param unless subscribing_param.nil? + end super render status: 201, location: { action: :show, id: @subscription }, json: subscription_json end end - # Finds and Shows a subscription from specified key. + # Finds and shows a subscription from specified key. # # GET /:target_type/:target_id/subscriptions/find # @overload index(params) @@ -61,6 +68,20 @@ def find render json: subscription_json if @subscription end + # Finds and returns configured optional_target names from specified key. + # + # GET /:target_type/:target_id/subscriptions/optional_target_names + # @overload index(params) + # @param [Hash] params Request parameter options for subscription index + # @option params [required, String] :key (nil) Key of the subscription + # @return [JSON] Configured optional_target names + def optional_target_names + latest_notification = @target.notifications.filtered_by_key(params[:key]).latest + latest_notification ? + render(json: { configured_count: latest_notification.optional_target_names.length, optional_target_names: latest_notification.optional_target_names }) : + render_resource_not_found("Couldn't find notification with this target and 'key'=#{params[:key]}") + end + # Shows a subscription. # # GET /:target_type/:target_id/subscriptions/:id diff --git a/app/controllers/activity_notification/subscriptions_controller.rb b/app/controllers/activity_notification/subscriptions_controller.rb index 669fc5c8..078427cd 100644 --- a/app/controllers/activity_notification/subscriptions_controller.rb +++ b/app/controllers/activity_notification/subscriptions_controller.rb @@ -3,7 +3,7 @@ module ActivityNotification class SubscriptionsController < ActivityNotification.config.parent_controller.constantize # Include CommonController to select target and define common methods include CommonController - before_action :set_subscription, except: [:index, :find, :create] + before_action :set_subscription, except: [:index, :create, :find] before_action ->{ validate_param(:key) }, only: [:find] before_action ->{ validate_param(:optional_target_name) }, only: [:subscribe_to_optional_target, :unsubscribe_to_optional_target] @@ -41,7 +41,7 @@ def create return_back_or_ajax end - # Finds and Shows a subscription from specified key. + # Finds and shows a subscription from specified key. # # GET /:target_type/:target_id/subscriptions/find # @overload index(params) diff --git a/lib/activity_notification/apis/subscription_api.rb b/lib/activity_notification/apis/subscription_api.rb index 0fa583b2..ddf0d267 100644 --- a/lib/activity_notification/apis/subscription_api.rb +++ b/lib/activity_notification/apis/subscription_api.rb @@ -100,6 +100,24 @@ def to_optional_target_unsubscribed_at_key(optional_target_name) end end + # Override as_json method for optional_targets representation + # + # @param [Hash] options Options for as_json method + # @return [Hash] Hash representing the subscription model + def as_json(options = {}) + json = super(options).with_indifferent_access + optional_targets_json = {} + optional_target_names.each do |optional_target_name| + optional_targets_json[optional_target_name] = { + subscribing: json[:optional_targets][Subscription.to_optional_target_key(optional_target_name)], + subscribed_at: json[:optional_targets][Subscription.to_optional_target_subscribed_at_key(optional_target_name)], + unsubscribed_at: json[:optional_targets][Subscription.to_optional_target_unsubscribed_at_key(optional_target_name)] + } + end + json[:optional_targets] = optional_targets_json + json + end + # Subscribes to the notification and notification email. # # @param [Hash] options Options for subscribing to the notification diff --git a/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb b/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb index feddff38..a955c1fb 100644 --- a/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb +++ b/lib/activity_notification/controllers/concerns/swagger/subscriptions_api.rb @@ -154,6 +154,48 @@ module Swagger::SubscriptionsApi #:nodoc: end end + swagger_path '/{target_type}/{target_id}/subscriptions/optional_target_names' do + operation :get do + key :summary, 'Find configured optional_target names' + key :description, 'Finds and returns configured optional_target names from specified key.' + key :operationId, 'findOptionalTargetNames' + key :tags, ['subscriptions'] + + extend Swagger::SubscriptionsParameters::TargetParameters + parameter do + key :name, :key + key :in, :query + key :description, "Key of the notification and subscription to find" + key :required, true + key :type, :string + end + + response 200 do + key :description, "Found configured optional_target names" + content 'application/json' do + schema do + key :type, :object + property :configured_count do + key :type, :integer + key :description, "Number of configured optional_target names" + key :example, 1 + end + property :optional_target_names do + key :type, :array + items do + key :type, :string + key :example, "action_cable_channel" + end + key :description, "Configured optional_target names" + end + end + end + end + extend Swagger::ErrorResponses::InvalidParameterError + extend Swagger::ErrorResponses::ResourceNotFoundError + end + end + swagger_path '/{target_type}/{target_id}/subscriptions/{id}' do operation :get do key :summary, 'Get subscription' diff --git a/lib/activity_notification/models/concerns/swagger/subscription_schema.rb b/lib/activity_notification/models/concerns/swagger/subscription_schema.rb index cd5c59ac..5195ac95 100644 --- a/lib/activity_notification/models/concerns/swagger/subscription_schema.rb +++ b/lib/activity_notification/models/concerns/swagger/subscription_schema.rb @@ -80,18 +80,26 @@ module Swagger::SubscriptionSchema #:nodoc: key :additionalProperties, { type: "object", properties: { - subscribing_to_optional_target_name: { + subscribing: { type: "boolean" }, - subscribed_to_optional_target_name_at: { + subscribed_at: { type: "string", format: "date-time" } } } key :example, { - "subscribing_to_slack": true, - "subscribed_to_slack_at": Time.current + "action_cable_channel": { + "subscribing": true, + "subscribed_at": Time.current, + "unsubscribed_at": nil + }, + "slack": { + "subscribing": false, + "subscribed_at": nil, + "unsubscribed_at": Time.current + } } end property :created_at do @@ -131,13 +139,18 @@ module Swagger::SubscriptionSchema #:nodoc: key :additionalProperties, { type: "object", properties: { - subscribing_to_optional_target_name: { + subscribing: { type: "boolean" } } } key :example, { - "subscribing_to_slack": true + "action_cable_channel": { + "subscribing": true + }, + "slack": { + "subscribing": false + } } end end diff --git a/lib/activity_notification/rails/routes.rb b/lib/activity_notification/rails/routes.rb index c37b48a9..50d7f5cd 100644 --- a/lib/activity_notification/rails/routes.rb +++ b/lib/activity_notification/rails/routes.rb @@ -293,6 +293,8 @@ def notify_to(*resources) # { controller:"activity_notification/subscriptions_api", action:"index", target_type:"users" } # GET /subscriptions/find(.:format) # { controller:"activity_notification/subscriptions_api", action:"find", target_type:"users" } + # GET /subscriptions/optional_target_names(.:format) + # { controller:"activity_notification/subscriptions_api", action:"optional_target_names", target_type:"users" } # GET /subscriptions/:id(.:format) # { controller:"activity_notification/subscriptions_api", action:"show", target_type:"users" } # PUT /subscriptions(.:format) @@ -430,6 +432,7 @@ def create_subscription_routes(options = {}, resources_options = []) self.resources options[:model], resources_options do collection do get :find unless ignore_path?(:find, options) + get :optional_target_names if options[:api_mode] && !ignore_path?(:optional_target_names, options) end member do put :subscribe unless ignore_path?(:subscribe, options) diff --git a/lib/generators/templates/controllers/subscriptions_api_controller.rb b/lib/generators/templates/controllers/subscriptions_api_controller.rb index c17aae6c..c2d8b2e1 100644 --- a/lib/generators/templates/controllers/subscriptions_api_controller.rb +++ b/lib/generators/templates/controllers/subscriptions_api_controller.rb @@ -14,6 +14,11 @@ def find super end + # GET /:target_type/:target_id/subscriptions/optional_target_names + def optional_target_names + super + end + # GET /:target_type/:target_id/subscriptions/:id # def show # super diff --git a/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb b/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb index 2e0b97b3..d952ef9b 100644 --- a/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb +++ b/lib/generators/templates/controllers/subscriptions_api_with_devise_controller.rb @@ -14,6 +14,11 @@ def find super end + # GET /:target_type/:target_id/subscriptions/optional_target_names + def optional_target_names + super + end + # GET /:target_type/:target_id/subscriptions/:id # def show # super diff --git a/spec/controllers/subscriptions_api_controller_shared_examples.rb b/spec/controllers/subscriptions_api_controller_shared_examples.rb index 0c0a5291..541035e8 100644 --- a/spec/controllers/subscriptions_api_controller_shared_examples.rb +++ b/spec/controllers/subscriptions_api_controller_shared_examples.rb @@ -310,6 +310,39 @@ end end + describe "GET #optional_target_names" do + context "with key, target_type and (typed_target)_id parameters" do + before do + @notification = create(:notification, target: test_target, key: 'test_subscription_key') + @subscription = create(:subscription, target: test_target, key: 'test_subscription_key') + get_with_compatibility :optional_target_names, target_params.merge({ key: 'test_subscription_key', typed_target_param => test_target }), valid_session + end + + it "returns 200 as http status code" do + expect(response.status).to eq(200) + end + + it "returns the blank array since configurured optional targets are not configured" do + expect(JSON.parse(response.body)["optional_target_names"].is_a?(Array)).to be_truthy + end + end + + context "with wrong id and (typed_target)_id parameters" do + before do + @subscription = create(:subscription, target: create(:user)) + get_with_compatibility :find, target_params.merge({ key: 'test_subscription_key', typed_target_param => test_target }), valid_session + end + + it "returns 404 as http status code" do + expect(response.status).to eq(404) + end + + it "returns error JSON response" do + assert_error_response(404) + end + end + end + describe "GET #show" do context "with id, target_type and (typed_target)_id parameters" do before do @@ -609,7 +642,15 @@ post_with_compatibility "#{api_path}/subscriptions", params: { "subscription" => { "key" => "new_subscription_key", "subscribing"=> "true", - "subscribing_to_email"=>"true" + "subscribing_to_email"=>"true", + "optional_targets"=>{ + "action_cable_channel"=>{ + "subscribing"=>"true", + }, + "slack"=>{ + "subscribing"=>"false" + } + } } }, headers: @headers assert_all_schema_confirm(response, 201) @@ -633,6 +674,19 @@ end end + describe "GET /{target_type}/{target_id}/subscriptions/optional_target_names", type: :request do + it "returns response as API references" do + create(:notification, target: test_target, key: @subscription.key) + get_with_compatibility "#{api_path}/subscriptions/optional_target_names?key=#{@subscription.key}", headers: @headers + assert_all_schema_confirm(response, 200) + end + + it "returns response as API references when any notification with the key is not found" do + get_with_compatibility "#{api_path}/subscriptions/optional_target_names?key=#{@subscription.key}", headers: @headers + assert_all_schema_confirm(response, 404) + end + end + describe "GET /{target_type}/{target_id}/subscriptions/{id}", type: :request do it "returns response as API references" do get_with_compatibility "#{api_path}/subscriptions/#{@subscription.id}", headers: @headers