Skip to content
This repository has been archived by the owner on Sep 10, 2021. It is now read-only.

Commit

Permalink
Add API mode - simukappu#108 simukappu#113
Browse files Browse the repository at this point in the history
  • Loading branch information
simukappu authored and irondnb committed Dec 10, 2019
1 parent 5c5e64b commit 3d423b4
Show file tree
Hide file tree
Showing 46 changed files with 3,378 additions and 262 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/InstalledFiles
/pkg/
/spec/reports/
/spec/openapi.json
/spec/examples.txt
/spec/rails_app/log/*
/spec/rails_app/tmp/*
Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ group :test do
gem 'action-cable-testing'
gem 'ammeter'
gem 'timecop'
gem 'committee'
gem 'committee-rails'
gem 'coveralls', require: false
end

gem 'rack-cors', groups: [:production, :development]
gem 'dotenv-rails', groups: [:development, :test]
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
## About

*activity_notification* provides following functions:
* Notification API (creating notifications, query for notifications and managing notification parameters)
* Notification API for your Rails application (creating and managing notifications, query for notifications)
* Notification models (stored with ActiveRecord, Mongoid or Dynamoid ORM)
* Notification controllers (managing open/unopen of notifications, providing link to notifiable activity page)
* Notification views (presentation of notifications)
Expand All @@ -26,6 +26,7 @@
* Batch email notification (event driven or periodical email notification, daily or weekly etc)
* Push notification with [Action Cable](https://guides.rubyonrails.org/action_cable_overview.html)
* Subscription management (subscribing and unsubscribing for each target and notification type)
* REST API backend and [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification)
* Integration with [Devise](https://github.com/plataformatec/devise) authentication
* Activity notifications stream integrated into cloud computing using [Amazon DynamoDB Streams](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html)
* Optional notification targets (Configurable optional notification targets like [Amazon SNS](https://aws.amazon.com/sns), [Slack](https://slack.com), SMS and so on)
Expand Down Expand Up @@ -59,6 +60,8 @@ The deployed demo application is included in this gem's source code as a test ap
### Slack as optional notification target
<kbd>![optional-target-slack-image](https://raw.githubusercontent.com/simukappu/activity_notification/images/activity_notification_optional_target_slack.png)</kbd>

### REST API document with OpenAPI Specification
**https://app.swaggerhub.com/apis/simukappu/activity-notification/**

## Table of contents

Expand All @@ -77,6 +80,7 @@ The deployed demo application is included in this gem's source code as a test ap
- [Configuring views](#configuring-views)
- [Configuring routes](#configuring-routes)
- [Routes with scope](#routes-with-scope)
- [Routes as REST API backend](#routes-as-rest-api-backend)
- [Creating notifications](#creating-notifications)
- [Notification API](#notification-api)
- [Asynchronous notification API with ActiveJob](#asynchronous-notification-api-with-activejob)
Expand Down Expand Up @@ -105,6 +109,9 @@ The deployed demo application is included in this gem's source code as a test ap
- [Configuring subscriptions](#configuring-subscriptions)
- [Managing subscriptions](#managing-subscriptions)
- [Customizing subscriptions](#customizing-subscriptions)
- [REST API backend](#rest-api-backend)
- [Configuring REST API backend](#configuring-rest-api-backend)
- [API reference as OpenAPI Specification](#api-reference-as-openapi-specification)
- [Integration with Devise](#integration-with-devise)
- [Configuring integration with Devise authentication](#configuring-integration-with-devise-authentication)
- [Using different model as target](#using-different-model-as-target)
Expand Down Expand Up @@ -488,6 +495,22 @@ end

Then, pages are shown as */myscope/users/1/notifications*.

#### Routes as REST API backend

You can configure *activity_notification* routes as REST API backend with *api_mode* option like this:

```ruby
Rails.application.routes.draw do
scope :api do
scope :"v2" do
notify_to :users, api_mode: true
end
end
end
```

Then, you can call *activity_notification* REST API as */api/v2/notifications* from your frontend application. See [REST API backend](#rest-api-backend) for more details.

### Creating notifications

#### Notification API
Expand Down Expand Up @@ -1151,6 +1174,51 @@ If you would like to customize subscription controllers or views, you can use ge
```


### REST API backend

*activity_notification* provides REST API backend to operate notifications and subscriptions.

#### Configuring REST API backend

You can configure *activity_notification* routes as REST API backend with *api_mode* option of *notify_to* method. See [Routes as REST API backend](#routes-as-rest-api-backend) for more details. With *api_mode* option, *activity_notification* uses *[ActivityNotification::NotificationsApiController](/app/controllers/activity_notification/notifications_api_controller.rb)* instead of *[ActivityNotification::NotificationsController](/app/controllers/activity_notification/notifications_controller.rb)*.

In addition, you can use *with_subscription* option with *api_mode* to enable subscription management like this:

```ruby
Rails.application.routes.draw do
scope :api do
scope :"v2" do
notify_to :users, api_mode: true, with_subscription: true
end
end
end
```

Then, *activity_notification* uses *[ActivityNotification::SubscriptionsApiController](/app/controllers/activity_notification/subscriptions_api_controller.rb)* instead of *[ActivityNotification::SubscriptionsController](/app/controllers/activity_notification/subscriptions_controller.rb)*, and you can call *activity_notification* REST API as */api/v2/notifications* and */api/v2/subscriptions* from your frontend application.

#### API reference as OpenAPI Specification

*activity_notification* provides API reference as [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification).

OpenAPI Specification in [online demo](https://activity-notification-example.herokuapp.com/) is published here: **https://activity-notification-example.herokuapp.com/api/v2/apidocs**

Public API reference is also hosted in [SwaggerHub](https://swagger.io/tools/swaggerhub/) here: **https://app.swaggerhub.com/apis/simukappu/activity-notification/**

You can also publish OpenAPI Specification in your own application using *[ActivityNotification::ApidocsController](/app/controllers/activity_notification/apidocs_controller.rb)* like this:

```ruby
Rails.application.routes.draw do
scope :api do
scope :"v2" do
resources :apidocs, only: [:index], controller: 'activity_notification/apidocs'
end
end
end
```

You can use [Swagger UI](https://swagger.io/tools/swagger-ui/) with this OpenAPI Specification to visualize and interact with *activity_notification* API’s resources.


### Integration with Devise

*activity_notification* supports to integrate with devise authentication.
Expand Down
1 change: 1 addition & 0 deletions activity_notification.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Gem::Specification.new do |s|
s.add_dependency 'railties', '>= 4.2.0', '< 6.1'
s.add_dependency 'i18n', '>= 0.5.0'
s.add_dependency 'jquery-rails', '>= 3.1.1'
s.add_dependency 'swagger-blocks', '>= 3.0.0'

s.add_development_dependency 'puma', '>= 3.12.0'
s.add_development_dependency 'sqlite3', '>= 1.3.13'
Expand Down
75 changes: 75 additions & 0 deletions app/controllers/activity_notification/apidocs_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module ActivityNotification
# Controller to manage Swagger API references.
# @See https://github.com/fotinakis/swagger-blocks/blob/master/spec/lib/swagger_v3_blocks_spec.rb
class ApidocsController < ActivityNotification.config.parent_controller.constantize
include ::Swagger::Blocks

swagger_root do
key :openapi, '3.0.0'
info version: ActivityNotification::VERSION do
key :description, 'A default REST API created by activity_notification which provides integrated user activity notifications for Ruby on Rails'
key :title, 'ActivityNotification'
key :termsOfService, 'https://github.com/simukappu/activity_notification'
contact do
key :name, 'activity_notification community'
key :url, 'https://github.com/simukappu/activity_notification#help'
end
license do
key :name, 'MIT'
key :url, 'https://github.com/simukappu/activity_notification/blob/master/MIT-LICENSE'
end
end

server do
key :url, 'https://activity-notification-example.herokuapp.com/api/{version}'
key :description, 'ActivityNotification online demo including REST API'

variable :version do
key :enum, ['v2']
key :default, :"v#{ActivityNotification::GEM_VERSION::MAJOR}"
end
end
server do
key :url, 'http://localhost:3000/api/{version}'
key :description, 'Example Rails application at localhost including REST API'

variable :version do
key :enum, ['v2']
key :default, :"v#{ActivityNotification::GEM_VERSION::MAJOR}"
end
end

tag do
key :name, 'notifications'
key :description, 'Operations about user activity notifications'
externalDocs do
key :description, 'Find out more'
key :url, 'https://github.com/simukappu/activity_notification#creating-notifications'
end
end

tag do
key :name, 'subscriptions'
key :description, 'Operations about subscription management'
externalDocs do
key :description, 'Find out more'
key :url, 'https://github.com/simukappu/activity_notification#subscription-management'
end
end
end

SWAGGERED_CLASSES = [
Notification,
NotificationsApiController,
Subscription,
SubscriptionsApiController,
self
].freeze

# Returns root JSON of Swagger API references.
# GET /apidocs
def index
render json: ::Swagger::Blocks.build_root_json(SWAGGERED_CLASSES)
end
end
end
136 changes: 136 additions & 0 deletions app/controllers/activity_notification/notifications_api_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
module ActivityNotification
# Controller to manage notifications API.
class NotificationsApiController < NotificationsController
# Include Swagger API reference
include Swagger::NotificationsApi
# Include CommonApiController to select target and define common methods
include CommonApiController
protect_from_forgery except: [:open_all]
rescue_from ActivityNotification::NotifiableNotFoundError, with: :render_notifiable_not_found

# Returns notification index of the target.
#
# GET /:target_type/:target_id/notifications
# @overload index(params)
# @param [Hash] params Request parameter options for notification index
# @option params [String] :filter (nil) Filter option to load notification index by their status (Nothing as auto, 'opened' or 'unopened')
# @option params [String] :limit (nil) Maximum number of notifications to return
# @option params [String] :reverse ('false') Whether notification index will be ordered as earliest first
# @option params [String] :without_grouping ('false') Whether notification index will include group members
# @option params [String] :with_group_members ('false') Whether notification index will include group members
# @option params [String] :filtered_by_type (nil) Notifiable type to filter notification index
# @option params [String] :filtered_by_group_type (nil) Group type to filter notification index, valid with :filtered_by_group_id
# @option params [String] :filtered_by_group_id (nil) Group instance ID to filter notification index, valid with :filtered_by_group_type
# @option params [String] :filtered_by_key (nil) Key of notifications to filter notification index
# @return [JSON] count: number of notification index records, notifications: notification index
def index
super
render json: {
count: @notifications.size,
notifications: @notifications.as_json(include: notification_json_include_option, methods: notification_json_methods_option)
}
end

# Opens all notifications of the target.
#
# POST /:target_type/:target_id/notifications/open_all
# @overload open_all(params)
# @param [Hash] params Request parameters
# @option params [String] :filtered_by_type (nil) Notifiable type to filter notification index
# @option params [String] :filtered_by_group_type (nil) Group type to filter notification index, valid with :filtered_by_group_id
# @option params [String] :filtered_by_group_id (nil) Group instance ID to filter notification index, valid with :filtered_by_group_type
# @option params [String] :filtered_by_key (nil) Key of notifications to filter notification index
# @return [JSON] count: number of opened notification records, notifications: opened notifications
def open_all
super
render json: {
count: @opened_notifications.size,
notifications: @opened_notifications.as_json(include: notification_json_include_option, methods: notification_json_methods_option)
}
end

# Returns a single notification.
#
# GET /:target_type/:target_id/notifications/:id
# @overload show(params)
# @param [Hash] params Request parameters
# @return [JSON] Found single notification
def show
super
render json: notification_json
end

# Deletes a notification.
#
# DELETE /:target_type/:target_id/notifications/:id
# @overload destroy(params)
# @param [Hash] params Request parameters
# @return [JSON] 204 No Content
def destroy
super
head 204
end

# Opens a notification.
#
# PUT /:target_type/:target_id/notifications/:id/open
# @overload open(params)
# @param [Hash] params Request parameters
# @option params [String] :move ('false') Whether it redirects to notifiable_path after the notification is opened
# @return [JSON] count: number of opened notification records, notification: opened notification
def open
super
unless params[:move].to_s.to_boolean(false)
render json: {
count: @opened_notifications_count,
notification: notification_json
}
end
end

# Moves to notifiable_path of the notification.
#
# GET /:target_type/:target_id/notifications/:id/move
# @overload open(params)
# @param [Hash] params Request parameters
# @option params [String] :open ('false') Whether the notification will be opened
# @return [JSON] location: notifiable path, count: number of opened notification records, notification: specified notification
def move
super
render status: 302, location: @notification.notifiable_path, json: {
location: @notification.notifiable_path,
count: (@opened_notifications_count || 0),
notification: notification_json
}
end

protected

# Returns include option for notification JSON
# @api protected
def notification_json_include_option
[:target, :notifiable, :group, :notifier, :group_members].freeze
end

# Returns methods option for notification JSON
# @api protected
def notification_json_methods_option
[:notifiable_path].freeze
end

# Returns JSON of @notification
# @api protected
def notification_json
@notification.as_json(include: notification_json_include_option, methods: notification_json_methods_option)
end

# Render associated notifiable record not found error with 500 status
# @api protected
# @param [Error] error Error object
# @return [void]
def render_notifiable_not_found(error)
render status: 500, json: error_response(code: 500, message: "Associated record not found", type: error.message)
end

end
end
Loading

0 comments on commit 3d423b4

Please sign in to comment.