Merge pull request #11437 from opf/implementation/43677-extend-notification-representer-to-include-the-date-lines

[#43677] Extend Notification representer to include the date lines
pull/11536/head
Dombi Attila 2 years ago committed by GitHub
commit 2b09649caf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      config/locales/en.yml
  2. 45
      docs/api/apiv3/components/examples/date_alert_notification.yml
  3. 48
      docs/api/apiv3/components/examples/mentioned_notification.yml
  4. 48
      docs/api/apiv3/components/examples/notification_collection.yml
  5. 13
      docs/api/apiv3/components/examples/values_property_schema.yml
  6. 11
      docs/api/apiv3/components/examples/values_property_start_date.yml
  7. 37
      docs/api/apiv3/components/schemas/notification_collection_model.yml
  8. 84
      docs/api/apiv3/components/schemas/notification_model.yml
  9. 2
      docs/api/apiv3/components/schemas/root_model.yml
  10. 7
      docs/api/apiv3/components/schemas/schema_model.yml
  11. 39
      docs/api/apiv3/components/schemas/values_property_model.yml
  12. 17
      docs/api/apiv3/openapi-spec.yml
  13. 5
      docs/api/apiv3/paths/notification.yml
  14. 47
      docs/api/apiv3/paths/notification_details.yml
  15. 7
      docs/api/apiv3/paths/notifications.yml
  16. 51
      docs/api/apiv3/paths/values_schema.yml
  17. 18
      docs/api/apiv3/tags/notifications.yml
  18. 23
      docs/api/apiv3/tags/values_property.yml
  19. 4
      lib/api/decorators/date_property.rb
  20. 74
      lib/api/decorators/self_link.rb
  21. 30
      lib/api/decorators/single.rb
  22. 52
      lib/api/v3/notifications/details_factory.rb
  23. 43
      lib/api/v3/notifications/details_factory/date_alert_due_date.rb
  24. 43
      lib/api/v3/notifications/details_factory/date_alert_start_date.rb
  25. 37
      lib/api/v3/notifications/details_factory/default.rb
  26. 5
      lib/api/v3/notifications/notification_collection_representer.rb
  27. 19
      lib/api/v3/notifications/notification_representer.rb
  28. 10
      lib/api/v3/notifications/notifications_api.rb
  29. 3
      lib/api/v3/root.rb
  30. 8
      lib/api/v3/utilities/path_helper.rb
  31. 35
      lib/api/v3/values/property_date_representer.rb
  32. 39
      lib/api/v3/values/property_model.rb
  33. 64
      lib/api/v3/values/property_representer.rb
  34. 44
      lib/api/v3/values/schemas/model.rb
  35. 39
      lib/api/v3/values/schemas/property_schema_representer.rb
  36. 43
      lib/api/v3/values/schemas/value_schema_api.rb
  37. 73
      lib/api/v3/values/schemas/value_schema_factory.rb
  38. 35
      lib/api/v3/values/values_api.rb
  39. 1
      lib/api/v3/work_packages/work_package_sums_representer.rb
  40. 80
      spec/lib/api/v3/notifications/details_factory_spec.rb
  41. 5
      spec/lib/api/v3/notifications/notification_collection_representer_spec.rb
  42. 79
      spec/lib/api/v3/notifications/notification_representer_rendering_spec.rb
  43. 14
      spec/lib/api/v3/utilities/path_helper_spec.rb
  44. 84
      spec/lib/api/v3/values/property_date_representer_rendering_spec.rb
  45. 90
      spec/lib/api/v3/values/schemas/property_schema_representer_rendering_spec.rb
  46. 91
      spec/lib/api/v3/values/schemas/value_schema_factory_spec.rb
  47. 141
      spec/requests/api/v3/notifications/details_resource_spec.rb
  48. 18
      spec/requests/api/v3/notifications/show_resource_examples.rb
  49. 48
      spec/requests/api/v3/notifications/show_resource_spec.rb
  50. 100
      spec/requests/api/v3/values/schemas/value_schema_resource_spec.rb

@ -3132,6 +3132,7 @@ en:
api_v3:
attributes:
lock_version: "Lock Version"
property: 'Property'
errors:
code_400: "Bad request: %{message}"
code_401: "You need to be authenticated to access this resource."

@ -0,0 +1,45 @@
# Example: Mentioned notification
---
value:
_type: Notification
id: 1
readIAN: false
reason: dateAlert
createdAt: '2022-04-05T14:38:28Z'
updatedAt: '2022-04-06T09:03:24Z'
_embedded:
project:
_hint: Project resource shortened for brevity
_type: Project
id: 11
name: Jedi Remnant Locator
resource:
_hint: WorkPackage resource shortened for brevity
_type: WorkPackage
id: 77
subject: Educate Visas Marr
details:
- _type: 'Values::Property'
property: 'startDate'
value: '2021-01-01'
_links:
self:
href: 'api/v3/notifications/123/details/0'
schema:
href: 'api/v3/values/schemas/startDate'
_links:
self:
href: '/api/v3/notifications/1'
readIAN:
href: '/api/v3/notifications/1/read_ian'
method: post
actor:
href: null
project:
href: '/api/v3/projects/11'
title: Jedi Remnant Locator
activity:
href: null
resource:
href: '/api/v3/work_packages/77'
title: Educate Visas Marr

@ -0,0 +1,48 @@
# Example: Mentioned notification
---
value:
_type: Notification
id: 1
readIAN: false
reason: mentioned
createdAt: '2022-04-05T14:38:28Z'
updatedAt: '2022-04-06T09:03:24Z'
_embedded:
author:
_hint: User resource shortened for brevity
_type: User
id: 13
name: Darth Nihilus
project:
_hint: Project resource shortened for brevity
_type: Project
id: 11
name: Jedi Remnant Locator
activity:
_hint: Activity resource shortened for brevity
_type: Activity::Comment
id: 180
version: 3
resource:
_hint: WorkPackage resource shortened for brevity
_type: WorkPackage
id: 77
subject: Educate Visas Marr
details: []
_links:
self:
href: '/api/v3/notifications/1'
readIAN:
href: '/api/v3/notifications/1/read_ian'
method: post
actor:
href: '/api/v3/users/13'
title: Darth Nihilus
project:
href: '/api/v3/projects/11'
title: Jedi Remnant Locator
activity:
href: '/api/v3/activities/180'
resource:
href: '/api/v3/work_packages/77'
title: Educate Visas Marr

@ -0,0 +1,48 @@
# Example: Notification collection
---
value:
_type: Collection
count: 2
total: 2
offset: 1
pageSize: 20
_embedded:
elements:
- _hint: Notification resource shortened for brevity
id: 1
readIAN: false
reason: mentioned
- _hint: Notification resource shortened for brevity
id: 2
readIAN: false
reason: dateAlert
_embedded:
details:
- _type: 'Values::Property'
property: 'startDate'
value: '2021-01-01'
_links:
self:
href: 'api/v3/notifications/123/details/0'
schema:
href: 'api/v3/values/schemas/startDate'
detailsSchemas:
- _type: 'Schema'
property:
name: 'Property'
type: 'String'
value:
name: 'Start date'
type: 'Date'
_links:
self:
href: '/api/v3/values/schemas/startDate'
_links:
self:
href: '/api/v3/notifications?offset=1&pageSize=20'
jumpTo:
href: '/api/v3/notifications?filters=%5B%5D&offset=%7Boffset%7D&pageSize=20'
templated: true
changeSize:
href: '/api/v3/notifications?filters=%5B%5D&offset=1&pageSize=%7Bsize%7D'
templated: true

@ -0,0 +1,13 @@
# Example: Values::Property schema
---
value:
_type: 'Schema'
property:
name: 'Property'
type: 'String'
value:
name: 'Start date'
type: 'Date'
_links:
self:
href: 'api/v3/values/schemas/startDate'

@ -0,0 +1,11 @@
# Example: Values::Property
---
value:
_type: 'Values::Property'
property: 'startDate'
value: '2021-01-01'
_links:
self:
href: 'api/v3/notifications/123/details/0'
schema:
href: 'api/v3/values/schemas/startDate'

@ -17,54 +17,33 @@ allOf:
- $ref: "./link.yml"
- description: |-
This notification collection
**Resource**: NotificationCollectionModel
jumpTo:
allOf:
- $ref: "./link.yml"
- description: |-
The notification collection at another offset
**Resource**: NotificationCollectionModel
changeSize:
allOf:
- $ref: "./link.yml"
- description: |-
The notification collection with another size
**Resource**: NotificationCollectionModel
_embedded:
type: object
required:
- elements
- detailsSchemas
properties:
elements:
type: array
items:
$ref: './notification_model.yml'
example:
_type: Collection
count: 2
total: 2
offset: 1
pageSize: 20
_embedded:
elements:
- _hint: Notification resource shortened for brevity
id: 1
readIAN: false
reason: mentioned
- _hint: Notification resource shortened for brevity
id: 2
readIAN: false
reason: mentioned
_links:
self:
href: '/api/v3/notifications?offset=1&pageSize=20'
jumpTo:
href: '/api/v3/notifications?filters=%5B%5D&offset=%7Boffset%7D&pageSize=20'
templated: true
changeSize:
href: '/api/v3/notifications?filters=%5B%5D&offset=1&pageSize=%7Bsize%7D'
templated: true
detailsSchemas:
type: array
items:
$ref: './schema_model.yml'

@ -12,10 +12,28 @@ properties:
minimum: 1
reason:
type: string
description: The reason for the notification (such as mentioned, involved, watched)
description: The reason for the notification
enum:
- assigned
- commented
- created
- dateAlert
- mentioned
- prioritized
- processed
- responsible
- subscribed
- scheduled
- watched
readIAN:
type: boolean
description: Whether the notification is marked as read
details:
type: array
items:
oneOf:
- $ref: './values_property_model.yml'
description: A list of objects including detailed information about the notification.
createdAt:
type: string
format: date-time
@ -27,9 +45,7 @@ properties:
_embedded:
type: object
required:
- actor
- project
- activity
- resource
properties:
actor:
@ -49,6 +65,7 @@ properties:
- actor
- activity
- resource
- details
properties:
self:
allOf:
@ -78,7 +95,8 @@ properties:
allOf:
- "$ref": "./link.yml"
- description: |-
The user that caused the notification
The user that caused the notification. This might be null in
case no user triggered the notification.
**Resource**: User
resource:
@ -92,51 +110,17 @@ properties:
allOf:
- "$ref": "./link.yml"
- description: |-
The journal activity, if the notification originated from a journal entry
The journal activity, if the notification originated from a journal entry. This might be null in
case no activity triggered the notification.
**Resource**: Activity
example:
_type: Notification
id: 1
readIAN: false
reason: mentioned
createdAt: '2022-04-05T14:38:28Z'
updatedAt: '2022-04-06T09:03:24Z'
_embedded:
author:
_hint: User resource shortened for brevity
_type: User
id: 13
name: Darth Nihilus
project:
_hint: Project resource shortened for brevity
_type: Project
id: 11
name: Jedi Remnant Locator
activity:
_hint: Activity resource shortened for brevity
_type: Activity::Comment
id: 180
version: 3
resource:
_hint: WorkPackage resource shortened for brevity
_type: WorkPackage
id: 77
subject: Educate Visas Marr
_links:
self:
href: '/api/v3/notifications/1'
readIAN:
href: '/api/v3/notifications/1/read_ian'
method: post
actor:
href: '/api/v3/users/13'
title: Darth Nihilus
project:
href: '/api/v3/projects/11'
title: Jedi Remnant Locator
activity:
href: '/api/v3/activities/180'
resource:
href: '/api/v3/work_packages/77'
title: Educate Visas Marr
details:
type: array
items:
allOf:
- "$ref": "./link.yml"
- description: |-
A list of objects including detailed information about the notification. The contents of
and type of this can vary widely between different notification reasons.
**Resource**: Polymorphic (e.g. Values::Property)

@ -141,4 +141,4 @@ example:
userPreferences:
href: '/api/v3/users/3/preferences'
workPackages:
href: '/pi/v3/work_packages'
href: '/api/v3/work_packages'

@ -1,7 +1,14 @@
# Schema: SchemaModel
---
type: object
required:
- _type
- _links
properties:
_type:
type: string
enum:
- Schema
_dependencies:
type: array
description: A list of dependencies between one property's value and another property

@ -0,0 +1,39 @@
# Schema: Values::PropertyModel
---
type: object
required:
- _type
- property
- value
- _links
properties:
_type:
type: string
enum:
- Values::Property
property:
type: string
description: The key of the key - value pair represented by the Values::Property
value:
type: string
description: The value of the key - value pair represented by the Values::Property
_links:
type: object
required:
- self
- schema
properties:
self:
allOf:
- $ref: './link.yml'
- description: |-
This key - value pair.
**Resource**: Storage
schema:
allOf:
- $ref: './link.yml'
- description: |-
The schema describing the key - value pair.
**Resource**: Schema

@ -245,6 +245,8 @@ paths:
"$ref": "./paths/notifications_unread.yml"
"/api/v3/notifications/{id}":
"$ref": "./paths/notification.yml"
"/api/v3/notifications/{notification_id}/details/{id}":
"$ref": "./paths/notification_details.yml"
"/api/v3/notifications/{id}/read_ian":
"$ref": "./paths/notification_read.yml"
"/api/v3/notifications/{id}/unread_ian":
@ -386,6 +388,8 @@ paths:
"$ref": "./paths/user_form.yml"
"/api/v3/users/{id}/lock":
"$ref": "./paths/user_lock.yml"
"/api/v3/values/schema/{id}":
"$ref": "./paths/values_schema.yml"
"/api/v3/versions":
"$ref": "./paths/versions.yml"
"/api/v3/versions/available_projects":
@ -445,6 +449,12 @@ paths:
components:
examples:
DateAlertNotification:
$ref: './components/examples/date_alert_notification.yml'
MentionedNotification:
$ref: './components/examples/mentioned_notification.yml'
NotificationCollection:
$ref: './components/examples/notification_collection.yml'
Project:
$ref: "./components/examples/project.yml"
ProjectBody:
@ -455,6 +465,10 @@ components:
"$ref": "./components/examples/query.yml"
QuerySchemaModel:
"$ref": "./components/examples/query_schema.yml"
ValuesPropertySchema:
"$ref": "./components/examples/values_property_schema.yml"
ValuesPropertyStartDate:
"$ref": "./components/examples/values_property_start_date.yml"
Views:
"$ref": "./components/examples/views.yml"
ViewWorkPackagesTable:
@ -764,6 +778,8 @@ components:
"$ref": "./components/schemas/user_create_form.yml"
User_update_form:
"$ref": "./components/schemas/user_update_form.yml"
ValuesPropertyModel:
"$ref": "./components/schemas/values_property_model.yml"
VersionModel:
"$ref": "./components/schemas/version_model.yml"
Version_create_form:
@ -870,6 +886,7 @@ tags:
- "$ref": "./tags/types.yml"
- "$ref": "./tags/userpreferences.yml"
- "$ref": "./tags/users.yml"
- "$ref": "./tags/values_property.yml"
- "$ref": "./tags/versions.yml"
- "$ref": "./tags/views.yml"
- "$ref": "./tags/wiki_pages.yml"

@ -20,6 +20,11 @@ get:
application/hal+json:
schema:
$ref: '../components/schemas/notification_model.yml'
examples:
'Date alert notification':
$ref: '../components/examples/date_alert_notification.yml'
'Mentioned notification':
$ref: '../components/examples/mentioned_notification.yml'
description: OK
'404':
content:

@ -0,0 +1,47 @@
# /api/v3/notifications/{notification_id}/details/{id}
---
get:
summary: Get a notification detail
operationId: view_notification_detail
tags:
- Notifications
- Values::Property
description: Returns an individual detail of a notification identified by the notification id and the id of the detail.
parameters:
- name: notification_id
in: path
description: notification id
example: '1'
required: true
schema:
type: integer
- name: id
in: path
description: detail id
example: '0'
required: true
schema:
type: integer
responses:
'200':
content:
application/hal+json:
schema:
$ref: '../components/schemas/values_property_model.yml'
examples:
'Start date notification detail':
$ref: '../components/examples/values_property_start_date.yml'
description: OK
'404':
content:
application/hal+json:
schema:
$ref: '../components/schemas/error_response.yml'
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the notification or the detail of it does not exist or if the user does not have permission to view it.
**Required permission** being recipient of the notification

@ -8,6 +8,10 @@ get:
description: |-
Returns the collection of available in-app notifications. The notifications returned depend on the provided
parameters and also on the requesting user's permissions.
Contrary to most collections, this one also links to and embeds schemas for the `details` properties of the notifications returned.
This is an optimization. Clients will receive the information necessary to display the various types of details that a notification
can carry.
parameters:
- name: offset
description: Page number inside the requested collection.
@ -79,6 +83,9 @@ get:
application/hal+json:
schema:
$ref: '../components/schemas/notification_collection_model.yml'
examples:
'Collection of notifications':
$ref: '../components/examples/notification_collection.yml'
description: OK
'403':
content:

@ -0,0 +1,51 @@
# /api/v3/values/schemas/{id}
---
get:
parameters:
- name: id
in: path
description: |-
The identifier of the value. This is typically the value of the `property` property of
the `Values` resource. It should be in lower camelcase format.
example: 'startDate'
required: true
schema:
type: string
responses:
'200':
content:
application/hal+json:
schema:
"$ref": "../components/schemas/schema_model.yml"
examples:
'Values::Property Start date schema':
$ref: '../components/examples/values_property_schema.yml'
description: OK
headers: {}
'404':
content:
application/hal+json:
schema:
$ref: '../components/schemas/error_response.yml'
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the schema does not exist.
'400':
content:
application/hal+json:
schema:
$ref: '../components/schemas/error_response.yml'
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:BadRequest
message: "Bad request: property is invalid"
description: |-
Returned if the requested property id is not in a lower camel case format.
tags:
- Values::Property
description: The schema of a `Values` resource.
operationId: View_values_schema
summary: View Values schema

@ -1,6 +1,9 @@
---
description: |-
Notifications are created through notifiable actions in OpenProject.
Notifications are triggered by actions carried out in the system by users, e.g. editing a work package, but can also be send out because
of time passing e.g. when a user is notified of a work package that is overdue.
This endpoint only returns in-app notifications.
## Actions
@ -12,13 +15,14 @@ description: |-
## Linked Properties
| Link | Description | Type | Constraints | Supported operations | Condition |
| :-----------: | ---------------------------------------- | -------------- | --------------------- | -------------------- | ----------------------------------------- |
| self | This notification | Notification | not null | READ | |
| project | The project containng the resource | Project | not null | READ | |
| actor | The user that caused the notification | User | not null | READ | |
| resource | The resource the notification belongs to | Polymorphic | not null | READ | |
| activity | The journal the notification belongs to | Polymorphic | | READ | optional |
| Link | Description | Type | Constraints | Supported operations | Condition |
| :-----------: | ---------------------------------------- | -------------- | --------------------- | -------------------- | ----------------------------------------- |
| self | This notification | Notification | not null | READ | |
| project | The project containing the resource | Project | not null | READ | |
| actor | The user that caused the notification | User | | READ | optional |
| resource | The resource the notification belongs to | Polymorphic | not null | READ | |
| activity | The journal the notification belongs to | Polymorphic | | READ | optional |
| details | A list of objects including detailed information | Polymorphic | | READ | optional |
## Local Properties

@ -0,0 +1,23 @@
---
description: |-
`Values::Property` represents a single key - value pair. That pair typically is an
excerpt of the properties of a resource. `Values::Property` itself is not an independent resource. It will always be nested and as such
will only exist as part of another resource.
It is currently used e.g. in the Notification resource.
## Linked Properties
| Property | Description | Type | Constraints | Supported operations | Condition |
| :--------------: | ----------------------------| ----------- | --------------------- | -------------------- | ------------------------- |
| self | This value | Values::Property | not null | READ | |
| schema | This value's schema | Schema | not null | READ | |
## Local Properties
| Property | Description | Type | Constraints | Supported operations | Condition |
| :--------------: | ------------------------------------------------------ | ----------- | -------------- | -------------------- | ---------------------------- |
| property | The pairs' key name | String | not null | READ | |
| value | The pairs' value | Polymorphic | | READ | |
name: Values::Property

@ -37,6 +37,10 @@ module API
base.extend ClassMethods
end
def datetime_formatter
::API::V3::Utilities::DateTimeFormatter
end
module ClassMethods
def date_property(name,
getter: default_date_getter(name),

@ -0,0 +1,74 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API
module Decorators
module SelfLink
def self.included(base)
base.extend ClassMethods
end
def self.prepended(base)
base.extend ClassMethods
end
def self_v3_path(path, id_attribute)
path ||= _type.underscore
id = if id_attribute.respond_to?(:call)
instance_eval(&id_attribute)
else
represented.send(id_attribute)
end
id = [nil] if id.nil?
api_v3_paths.send(path, *Array(id))
end
module ClassMethods
def self_link(path: nil,
id_attribute: :id,
title: true,
title_getter: ->(*) { represented.name })
link :self do
self_path = self_v3_path(path, id_attribute)
link_object = { href: self_path }
if title
title_string = instance_eval(&title_getter)
link_object[:title] = title_string if title_string
end
link_object
end
end
end
end
end
end

@ -35,6 +35,7 @@ module API
class Single < ::Roar::Decorator
include ::Roar::JSON::HAL
include ::Roar::Hypermedia
include ::API::Decorators::SelfLink
include ::API::V3::Utilities::PathHelper
attr_reader :current_user, :embed_links
@ -64,17 +65,6 @@ module API
render_nil: false,
writable: false
def self.self_link(path: nil, id_attribute: :id, title_getter: ->(*) { represented.name })
link :self do
self_path = self_v3_path(path, id_attribute)
link_object = { href: self_path }
title = instance_eval(&title_getter)
link_object[:title] = title if title
link_object
end
end
class_attribute :to_eager_load
class_attribute :checked_permissions
@ -102,30 +92,12 @@ module API
end
end
def datetime_formatter
::API::V3::Utilities::DateTimeFormatter
end
# If a subclass does not depend on a model being passed to this class, it can override
# this method and return false. Otherwise it will be enforced that the model of each
# representer is non-nil.
def model_required?
true
end
def self_v3_path(path, id_attribute)
path ||= _type.underscore
id = if id_attribute.respond_to?(:call)
instance_eval(&id_attribute)
else
represented.send(id_attribute)
end
id = [nil] if id.nil?
api_v3_paths.send(path, *Array(id))
end
end
end
end

@ -0,0 +1,52 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Notifications
module DetailsFactory
extend ::API::V3::Utilities::PathHelper
module_function
def for(notification)
concrete_factory_for(notification.reason)
.for(notification)
end
def concrete_factory_for(reason)
@concrete_factory_for ||= Hash.new do |h, reason_key|
h[reason_key] = if API::V3::Notifications::DetailsFactory.const_defined?(reason_key.camelcase)
"API::V3::Notifications::DetailsFactory::#{reason_key.camelcase}".constantize
else
API::V3::Notifications::DetailsFactory::Default
end
end
@concrete_factory_for[reason]
end
end
end

@ -0,0 +1,43 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Notifications::DetailsFactory
module DateAlertDueDate
extend ::API::V3::Utilities::PathHelper
module_function
def for(notification)
[
::API::V3::Values::PropertyDateRepresenter
.new(::API::V3::Values::PropertyModel.new(:due_date, notification.resource.due_date),
self_link: api_v3_paths.notification_detail(notification.id, 0))
]
end
end
end

@ -0,0 +1,43 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Notifications::DetailsFactory
module DateAlertStartDate
extend ::API::V3::Utilities::PathHelper
module_function
def for(notification)
[
::API::V3::Values::PropertyDateRepresenter
.new(::API::V3::Values::PropertyModel.new(:start_date, notification.resource.start_date),
self_link: api_v3_paths.notification_detail(notification.id, 0))
]
end
end
end

@ -0,0 +1,37 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Notifications::DetailsFactory
module Default
module_function
def for(_notification)
[]
end
end
end

@ -30,6 +30,11 @@ module API
module V3
module Notifications
class NotificationCollectionRepresenter < ::API::Decorators::OffsetPaginatedCollection
property :detailsSchemas,
getter: ->(*) { ::API::V3::Values::Schemas::ValueSchemaFactory.all },
exec_context: :decorator,
embedded: true
def initialize(models, self_link:, current_user:, query: {}, page: nil, per_page: nil, groups: nil)
super

@ -34,19 +34,34 @@ module API
include API::Decorators::LinkedResource
extend API::Decorators::PolymorphicResource
self_link title_getter: ->(*) {}
self_link title: false
property :id
property :read_ian,
as: :readIAN
property :reason
property :reason,
getter: ->(*) do
case reason
when 'date_alert_start_date', 'date_alert_due_date'
'dateAlert'
else
reason
end
end
date_time_property :created_at
date_time_property :updated_at
property :details,
embedded: true,
exec_context: :decorator,
getter: ->(*) do
DetailsFactory.for(represented)
end
link :readIAN do
next if represented.read_ian

@ -93,6 +93,16 @@ module API
post :unread_ian do
update_status(read_ian: false)
end
namespace :details do
route_param :detail_id, type: Integer, desc: 'Notification Detail ID' do
get do
DetailsFactory.for(@notification).at(params[:detail_id]).tap do |detail|
raise API::Errors::NotFound unless detail
end
end
end
end
end
end
end

@ -53,6 +53,7 @@ module API
mount ::API::V3::CustomActions::CustomActionsAPI
mount ::API::V3::CustomOptions::CustomOptionsAPI
mount ::API::V3::Days::DaysAPI
mount ::API::V3::Grids::GridsAPI
mount ::API::V3::Notifications::NotificationsAPI
mount ::API::V3::HelpTexts::HelpTextsAPI
mount ::API::V3::Memberships::MembershipsAPI
@ -74,11 +75,11 @@ module API
mount ::API::V3::PlaceholderUsers::PlaceholderUsersAPI
mount ::API::V3::UserPreferences::UserPreferencesAPI
mount ::API::V3::Groups::GroupsAPI
mount ::API::V3::Values::ValuesAPI
mount ::API::V3::Versions::VersionsAPI
mount ::API::V3::Views::ViewsAPI
mount ::API::V3::WorkPackages::WorkPackagesAPI
mount ::API::V3::WikiPages::WikiPagesAPI
mount ::API::V3::Grids::GridsAPI
get '/' do
RootRepresenter.new({}, current_user:)

@ -263,6 +263,10 @@ module API
"#{notification(id)}/unread_ian"
end
def self.notification_detail(notification_id, detail_id)
"#{notification(notification_id)}/details/#{detail_id}"
end
index :placeholder_user
show :placeholder_user
@ -441,6 +445,10 @@ module API
index :group
show :group
def self.value_schema(property)
"#{root}/values/schemas/#{property}"
end
resources :version
index :view

@ -0,0 +1,35 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values
class PropertyDateRepresenter < PropertyRepresenter
include API::Decorators::DateProperty
date_property :value
end
end

@ -0,0 +1,39 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values
class PropertyModel
attr_reader :property,
:value
def initialize(property, value)
@property = property
@value = value
end
end
end

@ -0,0 +1,64 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values
class PropertyRepresenter < ::Roar::Decorator
include ::Roar::JSON::HAL
include ::Roar::Hypermedia
include ::API::Decorators::SelfLink
include ::API::V3::Utilities::PathHelper
def initialize(model, self_link:)
@self_link = self_link
super(model)
end
property :_type,
getter: ->(*) { 'Values::Property' }
property :property,
getter: ->(*) { property.to_s.camelcase(:lower) }
self_link title: false
link :schema do
{
href: api_v3_paths.value_schema(represented.property.to_s.camelcase(:lower))
}
end
def self_v3_path(*_args)
self_link
end
private
attr_reader :self_link
end
end

@ -0,0 +1,44 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values::Schemas
class Model
attr_reader :name,
:type
def initialize(name, type)
@name = name
@type = type
end
def ==(other)
name == other.name &&
type == other.type
end
end
end

@ -0,0 +1,39 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values::Schemas
class PropertySchemaRepresenter < ::API::Decorators::SchemaRepresenter
schema :property,
type: 'String',
name_source: ->(*) { I18n.t(:'api_v3.attributes.property') }
schema :value,
type: ->(*) { represented.type },
name_source: ->(*) { represented.name }
end
end

@ -0,0 +1,43 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values::Schemas
class ValueSchemaAPI < ::API::OpenProjectAPI
resources :schemas do
params do
requires :property, desc: 'Name of the schema property', regexp: /^[a-z][a-zA-Z0-9]*$/
end
get ':property' do
ValueSchemaFactory.for(params[:property].underscore).tap do |representer|
raise API::Errors::NotFound unless representer
end
end
end
end
end

@ -0,0 +1,73 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values::Schemas
module ValueSchemaFactory
extend ::API::V3::Utilities::PathHelper
SUPPORTED = %w(start_date due_date).freeze
module_function
def for(property)
return nil unless supported?(property)
::API::V3::Values::Schemas::PropertySchemaRepresenter
.new(model_for(property),
current_user: nil,
self_link: api_v3_paths.value_schema(property.camelcase(:lower)))
end
def all
SUPPORTED.map { |property| self.for(property) }
end
def supported?(property)
# This is but a stub. Currently, only 'start_date' and 'due_date'
# need to be supported so this simple approach works.
SUPPORTED.include?(property)
end
def model_for(property)
API::V3::Values::Schemas::Model
.new(i18n_for(property),
type_for(property))
end
def i18n_for(property)
# This is but a stub. Currently, only 'start_date' and 'due_date'
# need to be supported so this simple approach works.
I18n.t("attributes.#{property}")
end
def type_for(_property)
# This is but a stub. Currently, only 'start_date' and 'due_date'
# need to be supported so this simple approach works.
'Date'
end
end
end

@ -0,0 +1,35 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
module API::V3::Values
class ValuesAPI < ::API::OpenProjectAPI
resources :values do
mount API::V3::Values::Schemas::ValueSchemaAPI
end
end
end

@ -6,6 +6,7 @@ module API
class WorkPackageSumsRepresenter < ::API::Decorators::Single
extend ::API::V3::Utilities::CustomFieldInjector::RepresenterClass
include ActionView::Helpers::NumberHelper
include ::API::Decorators::DateProperty
custom_field_injector(injector_class: ::API::V3::Utilities::CustomFieldSumInjector)

@ -0,0 +1,80 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require 'spec_helper'
describe ::API::V3::Notifications::DetailsFactory do
let(:resource) { build_stubbed(:work_package) }
let(:notification) do
build_stubbed(:notification,
resource:,
reason:)
end
describe '.for' do
context 'for a date_alert_start_date notification' do
let(:reason) { :date_alert_start_date }
it 'returns one detail' do
expect(described_class.for(notification).size)
.to eq 1
end
it 'returns an array of `Values::Property` representers' do
expect(described_class.for(notification)[0])
.to be_a ::API::V3::Values::PropertyDateRepresenter
end
end
context 'for a date_alert_due_date notification' do
let(:reason) { :date_alert_due_date }
it 'returns one detail' do
expect(described_class.for(notification).size)
.to eq 1
end
it 'returns an array of `Values::Property` representers' do
expect(described_class.for(notification)[0])
.to be_a ::API::V3::Values::PropertyDateRepresenter
end
end
(Notification::REASONS.keys - %i[date_alert_start_date date_alert_due_date]).each do |possible_reason|
context "for a #{possible_reason} notification" do
let(:reason) { possible_reason }
it 'is an empty array' do
expect(described_class.for(notification))
.to eq []
end
end
end
end
end

@ -79,6 +79,11 @@ describe ::API::V3::Notifications::NotificationCollectionRepresenter do
it_behaves_like 'offset-paginated APIv3 collection', 3, 'notifications', 'Notification'
it 'renders the available detailsSchemas' do
details_schemas = ::API::V3::Values::Schemas::ValueSchemaFactory.all
expect(subject).to be_json_eql(details_schemas.to_json).at_path('_embedded/detailsSchemas')
end
context 'when passing groups' do
let(:groups) do
[

@ -34,11 +34,12 @@ describe ::API::V3::Notifications::NotificationRepresenter, 'rendering' do
subject(:generated) { representer.to_json }
shared_let(:project) { create :project }
shared_let(:resource) { create :work_package, project: }
let(:resource) { build_stubbed(:work_package, project:) }
let(:recipient) { build_stubbed(:user) }
let(:journal) { nil }
let(:actor) { nil }
let(:reason) { :mentioned }
let(:notification) do
build_stubbed :notification,
recipient:,
@ -46,6 +47,7 @@ describe ::API::V3::Notifications::NotificationRepresenter, 'rendering' do
resource:,
journal:,
actor:,
reason:,
read_ian:
end
let(:representer) do
@ -101,8 +103,26 @@ describe ::API::V3::Notifications::NotificationRepresenter, 'rendering' do
let(:value) { notification.id }
end
it_behaves_like 'property', :reason do
let(:value) { notification.reason }
describe 'reason' do
(Notification::REASONS.keys - %i[date_alert_start_date date_alert_due_date]).each do |notification_reason|
context "for a #{notification_reason} reason" do
let(:reason) { notification_reason }
it_behaves_like 'property', :reason do
let(:value) { notification_reason }
end
end
end
%i[date_alert_start_date date_alert_due_date].each do |notification_reason|
context "for a #{notification_reason} reason" do
let(:reason) { notification_reason }
it_behaves_like 'property', :reason do
let(:value) { 'dateAlert' }
end
end
end
end
it_behaves_like 'datetime property', :createdAt do
@ -112,6 +132,10 @@ describe ::API::V3::Notifications::NotificationRepresenter, 'rendering' do
it_behaves_like 'datetime property', :updatedAt do
let(:value) { notification.updated_at }
end
it 'is expected to not have a message' do
expect(subject).not_to have_json_path('message')
end
end
describe 'project' do
@ -180,7 +204,7 @@ describe ::API::V3::Notifications::NotificationRepresenter, 'rendering' do
end
context 'when set' do
let(:journal) { resource.journals.last }
let(:journal) { build_stubbed(:work_package_journal) }
it_behaves_like 'has an untitled link' do
let(:link) { 'activity' }
@ -198,4 +222,51 @@ describe ::API::V3::Notifications::NotificationRepresenter, 'rendering' do
end
end
end
describe 'details' do
shared_examples_for 'embeds a Values::Property for startDate' do
it 'embeds a Values::Property' do
expect(generated)
.to be_json_eql('Values::Property'.to_json)
.at_path("_embedded/details/0/_type")
end
it 'has a startDate value for the `property` property' do
expect(generated)
.to be_json_eql('startDate'.to_json)
.at_path("_embedded/details/0/property")
end
it 'has a work_package`s start_date for the value' do
expect(generated)
.to be_json_eql(resource.start_date.to_json)
.at_path("_embedded/details/0/value")
end
end
context 'for a dateAlert when embedding' do
let(:reason) { :date_alert_start_date }
let(:embed_links) { true }
it_behaves_like 'embeds a Values::Property for startDate'
end
context 'for a dateAlert when not embedding' do
let(:reason) { :date_alert_start_date }
let(:embed_links) { false }
it_behaves_like 'embeds a Values::Property for startDate'
end
context 'for a mention when embedding' do
let(:reason) { :mentioned }
let(:embed_links) { true }
it 'has an empty details array' do
expect(generated)
.to have_json_size(0)
.at_path("_embedded/details")
end
end
end
end

@ -245,6 +245,12 @@ describe ::API::V3::Utilities::PathHelper do
it_behaves_like 'api v3 path', '/notifications/42/unread_ian'
end
describe '#notification_detail' do
subject { helper.notification_detail(42, 0) }
it_behaves_like 'api v3 path', '/notifications/42/details/0'
end
end
describe 'markup paths' do
@ -477,6 +483,14 @@ describe ::API::V3::Utilities::PathHelper do
it_behaves_like 'show', :group
end
describe 'values paths' do
describe '#values_schema' do
subject { helper.value_schema('bogus_value') }
it_behaves_like 'api v3 path', '/values/schemas/bogus_value'
end
end
describe 'version paths' do
it_behaves_like 'resource', :version

@ -0,0 +1,84 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require 'spec_helper'
describe ::API::V3::Values::PropertyDateRepresenter, 'rendering' do
subject(:generated) { representer.to_json }
let(:property) { 'abc' }
let(:date_value) { Date.current }
let(:key_value) { Struct.new(:property, :value, keyword_init: true).new(property:, value: date_value) }
let(:self_link) { 'api/bogus/value' }
let(:representer) do
described_class.new key_value, self_link:
end
describe 'self link' do
it_behaves_like 'has an untitled link' do
let(:link) { 'self' }
let(:href) { self_link }
end
end
describe 'properties' do
describe '_type' do
it_behaves_like 'property', :_type do
let(:value) { 'Values::Property' }
end
end
describe 'property' do
it_behaves_like 'property', :property do
let(:value) { property }
end
context 'with a snake_case property' do
let(:property) { 'snake_case' }
it_behaves_like 'property', :property do
let(:value) { 'snakeCase' }
end
end
end
describe 'value' do
it_behaves_like 'date property', :value do
let(:value) { date_value }
end
context 'with an empty value' do
let(:date_value) { nil }
it_behaves_like 'date property', :value do
let(:value) { nil }
end
end
end
end
end

@ -0,0 +1,90 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require 'spec_helper'
describe ::API::V3::Values::Schemas::PropertySchemaRepresenter, 'rendering' do
include API::V3::Utilities::PathHelper
let(:current_user) { build_stubbed(:user) }
let(:self_link) { '/a/self/link' }
let(:model) do
API::V3::Values::Schemas::Model
.new('The start date to that object',
'ADate')
end
let(:representer) do
described_class.create(model,
self_link:,
current_user:)
end
subject(:generated) { representer.to_json }
describe '_type' do
it 'is indicated as Schema' do
expect(subject)
.to be_json_eql('Schema'.to_json)
.at_path('_type')
end
end
describe 'property' do
let(:path) { 'property' }
it_behaves_like 'has basic schema properties' do
let(:type) { 'String' }
let(:name) { I18n.t(:'api_v3.attributes.property') }
let(:required) { true }
let(:writable) { false }
end
end
describe 'value' do
let(:path) { 'value' }
it_behaves_like 'has basic schema properties' do
let(:type) { model.type }
let(:name) { model.name }
let(:required) { true }
let(:writable) { false }
end
end
describe '_links' do
describe 'self' do
it_behaves_like 'has an untitled link' do
let(:link) { 'self' }
let(:href) { self_link }
end
end
end
end

@ -0,0 +1,91 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require 'spec_helper'
describe ::API::V3::Values::Schemas::ValueSchemaFactory do
include ::API::V3::Utilities::PathHelper
describe '.for' do
let!(:representer_instance) do
instance_double(::API::V3::Values::Schemas::PropertySchemaRepresenter)
end
let!(:representer_class) do
allow(::API::V3::Values::Schemas::PropertySchemaRepresenter)
.to receive(:new)
.and_return(representer_instance)
end
context 'for the start_date property' do
let(:property) { 'start_date' }
it 'returns a schema representer' do
expect(described_class.for(property))
.to eq representer_instance
end
it 'instantiates the representer with the proper params' do
described_class.for(property)
expect(::API::V3::Values::Schemas::PropertySchemaRepresenter)
.to have_received(:new)
.with(API::V3::Values::Schemas::Model.new(I18n.t('attributes.start_date'), 'Date'),
current_user: nil,
self_link: api_v3_paths.value_schema(property.camelcase(:lower)))
end
end
context 'for the due_date property' do
let(:property) { 'due_date' }
it 'returns a schema representer' do
expect(described_class.for(property))
.to eq representer_instance
end
it 'instantiates the representer with the proper params' do
described_class.for(property)
expect(::API::V3::Values::Schemas::PropertySchemaRepresenter)
.to have_received(:new)
.with(API::V3::Values::Schemas::Model.new(I18n.t('attributes.due_date'), 'Date'),
current_user: nil,
self_link: api_v3_paths.value_schema(property.camelcase(:lower)))
end
end
context 'for another property' do
let(:property) { 'bogus' }
it 'returns nil' do
expect(described_class.for(property))
.to be_nil
end
end
end
end

@ -0,0 +1,141 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
require 'spec_helper'
describe ::API::V3::Notifications::NotificationsAPI,
'fetch notification details',
content_type: :json do
include API::V3::Utilities::PathHelper
shared_let(:project) { create(:project) }
shared_let(:resource) do
create(:work_package,
project:,
start_date: Date.yesterday,
due_date: Date.tomorrow)
end
shared_let(:recipient) do
create(:user,
member_in_project: project,
member_with_permissions: %i[view_work_packages])
end
let(:notification) { create(:notification, recipient:, resource:, project:, reason:) }
let(:reason) { :date_alert_start_date }
# We have 1 detail item at maximum, and the id is coming
# from the detail's array index which is 0
let(:detail_id) { 0 }
let(:notification_detail_path) { api_v3_paths.notification_detail(notification.id, detail_id) }
let(:send_request) do
get notification_detail_path
end
let(:parsed_response) { JSON.parse(last_response.body) }
before do
login_as current_user
end
describe 'recipient user' do
let(:current_user) { recipient }
context 'for a non dateAlert notification' do
let(:reason) { :mentioned }
it 'returns a 404 response' do
send_request
expect(last_response.status).to eq(404)
end
end
context 'for a start date alert notification' do
let(:reason) { :date_alert_start_date }
it 'can get the notification details for a start date' do
send_request
expect(last_response.body)
.to be_json_eql('startDate'.to_json)
.at_path('property')
expect(last_response.body)
.to be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_date(resource.start_date).to_json)
.at_path('value')
expect(last_response.body)
.to be_json_eql(notification_detail_path.to_json)
.at_path('_links/self/href')
expect(last_response.body)
.to be_json_eql("/api/v3/values/schemas/startDate".to_json)
.at_path('_links/schema/href')
end
end
context 'for a due date alert notification' do
let(:reason) { :date_alert_due_date }
it 'can get the notification details for a due date' do
send_request
expect(last_response.body)
.to be_json_eql('dueDate'.to_json)
.at_path('property')
expect(last_response.body)
.to be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_date(resource.due_date).to_json)
.at_path('value')
expect(last_response.body)
.to be_json_eql(notification_detail_path.to_json)
.at_path('_links/self/href')
expect(last_response.body)
.to be_json_eql("/api/v3/values/schemas/dueDate".to_json)
.at_path('_links/schema/href')
end
end
end
describe 'admin user' do
current_user { build_stubbed(:admin) }
before do
send_request
end
it 'returns a 404 response' do
expect(last_response.status).to eq(404)
end
end
describe 'unauthorized user' do
current_user { build_stubbed(:user) }
before do
send_request
end
it 'returns a 404 response' do
expect(last_response.status).to eq(404)
end
end
end

@ -27,20 +27,26 @@
shared_examples 'represents the notification' do
it 'represents the notification', :aggregate_failures do
expect(last_response.status).to eq(200)
expect(last_response.status)
.to eq(200)
expect(last_response.body)
.to(be_json_eql('Notification'.to_json).at_path('_type'))
.to be_json_eql('Notification'.to_json)
.at_path('_type')
expect(last_response.body)
.to(be_json_eql(notification.read_ian.to_json).at_path('readIAN'))
.to be_json_eql(notification.read_ian.to_json)
.at_path('readIAN')
expect(last_response.body)
.to(be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_datetime(notification.created_at).to_json).at_path('createdAt'))
.to be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_datetime(notification.created_at).to_json)
.at_path('createdAt')
expect(last_response.body)
.to(be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_datetime(notification.updated_at).to_json).at_path('updatedAt'))
.to be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_datetime(notification.updated_at).to_json)
.at_path('updatedAt')
expect(last_response.body)
.to(be_json_eql(notification.id.to_json).at_path('id'))
.to be_json_eql(notification.id.to_json)
.at_path('id')
end
end

@ -55,19 +55,49 @@ describe ::API::V3::Notifications::NotificationsAPI,
get api_v3_paths.notification(notification.id)
end
before do
login_as current_user
send_request
describe 'recipient user' do
current_user { recipient }
before do
send_request
end
it_behaves_like 'represents the notification'
end
describe 'recipient user' do
let(:current_user) { recipient }
describe 'recipient user for a dateAlert notification' do
current_user { recipient }
before do
notification.reason = :date_alert_due_date
notification.journal = nil
notification.actor = nil
notification.save!
resource.update_column(:due_date, Date.current)
send_request
end
it_behaves_like 'represents the notification'
it 'includes the value of the work package associated in the details', :aggregate_failures do
expect(last_response.body)
.to be_json_eql('dueDate'.to_json)
.at_path('_embedded/details/0/property')
expect(last_response.body)
.to be_json_eql(::API::V3::Utilities::DateTimeFormatter.format_date(resource.due_date).to_json)
.at_path('_embedded/details/0/value')
end
end
describe 'admin user' do
let(:current_user) { build(:admin) }
current_user { build_stubbed(:admin) }
before do
send_request
end
it 'returns a 404 response' do
expect(last_response.status).to eq(404)
@ -75,7 +105,11 @@ describe ::API::V3::Notifications::NotificationsAPI,
end
describe 'unauthorized user' do
let(:current_user) { build(:user) }
current_user { build_stubbed(:user) }
before do
send_request
end
it 'returns a 404 response' do
expect(last_response.status).to eq(404)

@ -0,0 +1,100 @@
# --copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2022 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++
require 'spec_helper'
describe ::API::V3::Values::Schemas::ValueSchemaAPI,
'show',
content_type: :json do
include API::V3::Utilities::PathHelper
current_user { build_stubbed(:user) }
let(:path) { api_v3_paths.value_schema(schema_id) }
let(:schema_id) { 'bogus' }
before do
get path
end
context 'for a logged in user' do
context 'for a startDate' do
let(:schema_id) { 'startDate' }
it 'returns the schema', :aggregate_failures do
expect(last_response.status)
.to eq 200
expect(last_response.body)
.to be_json_eql('Schema'.to_json)
.at_path('_type')
expect(last_response.body)
.to be_json_eql('Date'.to_json)
.at_path('value/type')
expect(last_response.body)
.to be_json_eql('Start date'.to_json)
.at_path('value/name')
end
end
context 'for a dueDate' do
let(:schema_id) { 'dueDate' }
it 'returns the schema', :aggregate_failures do
expect(last_response.status)
.to eq 200
expect(last_response.body)
.to be_json_eql('Schema'.to_json)
.at_path('_type')
expect(last_response.body)
.to be_json_eql('Date'.to_json)
.at_path('value/type')
expect(last_response.body)
.to be_json_eql('Finish date'.to_json)
.at_path('value/name')
end
end
context 'for a non existing property' do
let(:schema_id) { 'bogus' }
it_behaves_like 'not found'
end
context 'for an underscore property' do
let(:schema_id) { 'start_date' }
it_behaves_like 'param validation error'
end
end
end
Loading…
Cancel
Save