diff --git a/config/locales/en.yml b/config/locales/en.yml index a85533c971..57b937b535 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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." diff --git a/docs/api/apiv3/components/examples/date_alert_notification.yml b/docs/api/apiv3/components/examples/date_alert_notification.yml new file mode 100644 index 0000000000..78fc0dd7d6 --- /dev/null +++ b/docs/api/apiv3/components/examples/date_alert_notification.yml @@ -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 diff --git a/docs/api/apiv3/components/examples/mentioned_notification.yml b/docs/api/apiv3/components/examples/mentioned_notification.yml new file mode 100644 index 0000000000..fac25f67d1 --- /dev/null +++ b/docs/api/apiv3/components/examples/mentioned_notification.yml @@ -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 diff --git a/docs/api/apiv3/components/examples/notification_collection.yml b/docs/api/apiv3/components/examples/notification_collection.yml new file mode 100644 index 0000000000..88c05479c6 --- /dev/null +++ b/docs/api/apiv3/components/examples/notification_collection.yml @@ -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 diff --git a/docs/api/apiv3/components/examples/values_property_schema.yml b/docs/api/apiv3/components/examples/values_property_schema.yml new file mode 100644 index 0000000000..ed3b588389 --- /dev/null +++ b/docs/api/apiv3/components/examples/values_property_schema.yml @@ -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' diff --git a/docs/api/apiv3/components/examples/values_property_start_date.yml b/docs/api/apiv3/components/examples/values_property_start_date.yml new file mode 100644 index 0000000000..1769572bfb --- /dev/null +++ b/docs/api/apiv3/components/examples/values_property_start_date.yml @@ -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' diff --git a/docs/api/apiv3/components/schemas/notification_collection_model.yml b/docs/api/apiv3/components/schemas/notification_collection_model.yml index b3c6be3e60..ec248ab778 100644 --- a/docs/api/apiv3/components/schemas/notification_collection_model.yml +++ b/docs/api/apiv3/components/schemas/notification_collection_model.yml @@ -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' diff --git a/docs/api/apiv3/components/schemas/notification_model.yml b/docs/api/apiv3/components/schemas/notification_model.yml index 776d7f93a5..6b62404758 100644 --- a/docs/api/apiv3/components/schemas/notification_model.yml +++ b/docs/api/apiv3/components/schemas/notification_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) diff --git a/docs/api/apiv3/components/schemas/root_model.yml b/docs/api/apiv3/components/schemas/root_model.yml index fb060c3ccc..a2abf83243 100644 --- a/docs/api/apiv3/components/schemas/root_model.yml +++ b/docs/api/apiv3/components/schemas/root_model.yml @@ -141,4 +141,4 @@ example: userPreferences: href: '/api/v3/users/3/preferences' workPackages: - href: '/pi/v3/work_packages' + href: '/api/v3/work_packages' diff --git a/docs/api/apiv3/components/schemas/schema_model.yml b/docs/api/apiv3/components/schemas/schema_model.yml index 4c2e1d18cb..3ed6811646 100644 --- a/docs/api/apiv3/components/schemas/schema_model.yml +++ b/docs/api/apiv3/components/schemas/schema_model.yml @@ -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 diff --git a/docs/api/apiv3/components/schemas/values_property_model.yml b/docs/api/apiv3/components/schemas/values_property_model.yml new file mode 100644 index 0000000000..9d5d4ceb62 --- /dev/null +++ b/docs/api/apiv3/components/schemas/values_property_model.yml @@ -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 diff --git a/docs/api/apiv3/openapi-spec.yml b/docs/api/apiv3/openapi-spec.yml index 190381ef1e..b4231164e5 100644 --- a/docs/api/apiv3/openapi-spec.yml +++ b/docs/api/apiv3/openapi-spec.yml @@ -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" diff --git a/docs/api/apiv3/paths/notification.yml b/docs/api/apiv3/paths/notification.yml index 1b1f29a540..915f5a6a1d 100644 --- a/docs/api/apiv3/paths/notification.yml +++ b/docs/api/apiv3/paths/notification.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: diff --git a/docs/api/apiv3/paths/notification_details.yml b/docs/api/apiv3/paths/notification_details.yml new file mode 100644 index 0000000000..9e318db2e9 --- /dev/null +++ b/docs/api/apiv3/paths/notification_details.yml @@ -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 diff --git a/docs/api/apiv3/paths/notifications.yml b/docs/api/apiv3/paths/notifications.yml index 2d78fd8cc5..24e29d70f4 100644 --- a/docs/api/apiv3/paths/notifications.yml +++ b/docs/api/apiv3/paths/notifications.yml @@ -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: diff --git a/docs/api/apiv3/paths/values_schema.yml b/docs/api/apiv3/paths/values_schema.yml new file mode 100644 index 0000000000..391220546a --- /dev/null +++ b/docs/api/apiv3/paths/values_schema.yml @@ -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 diff --git a/docs/api/apiv3/tags/notifications.yml b/docs/api/apiv3/tags/notifications.yml index ba7fb0c497..8b6a353124 100644 --- a/docs/api/apiv3/tags/notifications.yml +++ b/docs/api/apiv3/tags/notifications.yml @@ -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 diff --git a/docs/api/apiv3/tags/values_property.yml b/docs/api/apiv3/tags/values_property.yml new file mode 100644 index 0000000000..36e6728c65 --- /dev/null +++ b/docs/api/apiv3/tags/values_property.yml @@ -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 diff --git a/lib/api/decorators/date_property.rb b/lib/api/decorators/date_property.rb index d012e7f272..fd1e9d4407 100644 --- a/lib/api/decorators/date_property.rb +++ b/lib/api/decorators/date_property.rb @@ -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), diff --git a/lib/api/decorators/self_link.rb b/lib/api/decorators/self_link.rb new file mode 100644 index 0000000000..9a4166798a --- /dev/null +++ b/lib/api/decorators/self_link.rb @@ -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 diff --git a/lib/api/decorators/single.rb b/lib/api/decorators/single.rb index a72b1b7d24..43dae9a591 100644 --- a/lib/api/decorators/single.rb +++ b/lib/api/decorators/single.rb @@ -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 diff --git a/lib/api/v3/notifications/details_factory.rb b/lib/api/v3/notifications/details_factory.rb new file mode 100644 index 0000000000..c1f3d0daab --- /dev/null +++ b/lib/api/v3/notifications/details_factory.rb @@ -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 diff --git a/lib/api/v3/notifications/details_factory/date_alert_due_date.rb b/lib/api/v3/notifications/details_factory/date_alert_due_date.rb new file mode 100644 index 0000000000..1d0d6f68d0 --- /dev/null +++ b/lib/api/v3/notifications/details_factory/date_alert_due_date.rb @@ -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 diff --git a/lib/api/v3/notifications/details_factory/date_alert_start_date.rb b/lib/api/v3/notifications/details_factory/date_alert_start_date.rb new file mode 100644 index 0000000000..4dc0d936e6 --- /dev/null +++ b/lib/api/v3/notifications/details_factory/date_alert_start_date.rb @@ -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 diff --git a/lib/api/v3/notifications/details_factory/default.rb b/lib/api/v3/notifications/details_factory/default.rb new file mode 100644 index 0000000000..bddf4c3a45 --- /dev/null +++ b/lib/api/v3/notifications/details_factory/default.rb @@ -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 diff --git a/lib/api/v3/notifications/notification_collection_representer.rb b/lib/api/v3/notifications/notification_collection_representer.rb index 728506f34d..2df15c5d2f 100644 --- a/lib/api/v3/notifications/notification_collection_representer.rb +++ b/lib/api/v3/notifications/notification_collection_representer.rb @@ -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 diff --git a/lib/api/v3/notifications/notification_representer.rb b/lib/api/v3/notifications/notification_representer.rb index 42eb4c05b2..64e782320f 100644 --- a/lib/api/v3/notifications/notification_representer.rb +++ b/lib/api/v3/notifications/notification_representer.rb @@ -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 diff --git a/lib/api/v3/notifications/notifications_api.rb b/lib/api/v3/notifications/notifications_api.rb index 83dc9325eb..942bb40b5d 100644 --- a/lib/api/v3/notifications/notifications_api.rb +++ b/lib/api/v3/notifications/notifications_api.rb @@ -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 diff --git a/lib/api/v3/root.rb b/lib/api/v3/root.rb index db5199e780..8cbf58a123 100644 --- a/lib/api/v3/root.rb +++ b/lib/api/v3/root.rb @@ -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:) diff --git a/lib/api/v3/utilities/path_helper.rb b/lib/api/v3/utilities/path_helper.rb index 53ee693e0b..04a0fab2e1 100644 --- a/lib/api/v3/utilities/path_helper.rb +++ b/lib/api/v3/utilities/path_helper.rb @@ -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 diff --git a/lib/api/v3/values/property_date_representer.rb b/lib/api/v3/values/property_date_representer.rb new file mode 100644 index 0000000000..187320e2fc --- /dev/null +++ b/lib/api/v3/values/property_date_representer.rb @@ -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 diff --git a/lib/api/v3/values/property_model.rb b/lib/api/v3/values/property_model.rb new file mode 100644 index 0000000000..238d8ef50a --- /dev/null +++ b/lib/api/v3/values/property_model.rb @@ -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 diff --git a/lib/api/v3/values/property_representer.rb b/lib/api/v3/values/property_representer.rb new file mode 100644 index 0000000000..a3949fcd9f --- /dev/null +++ b/lib/api/v3/values/property_representer.rb @@ -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 diff --git a/lib/api/v3/values/schemas/model.rb b/lib/api/v3/values/schemas/model.rb new file mode 100644 index 0000000000..568e82e8ac --- /dev/null +++ b/lib/api/v3/values/schemas/model.rb @@ -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 diff --git a/lib/api/v3/values/schemas/property_schema_representer.rb b/lib/api/v3/values/schemas/property_schema_representer.rb new file mode 100644 index 0000000000..cb729725c3 --- /dev/null +++ b/lib/api/v3/values/schemas/property_schema_representer.rb @@ -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 diff --git a/lib/api/v3/values/schemas/value_schema_api.rb b/lib/api/v3/values/schemas/value_schema_api.rb new file mode 100644 index 0000000000..866296e7fb --- /dev/null +++ b/lib/api/v3/values/schemas/value_schema_api.rb @@ -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 diff --git a/lib/api/v3/values/schemas/value_schema_factory.rb b/lib/api/v3/values/schemas/value_schema_factory.rb new file mode 100644 index 0000000000..bb427a1cc5 --- /dev/null +++ b/lib/api/v3/values/schemas/value_schema_factory.rb @@ -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 diff --git a/lib/api/v3/values/values_api.rb b/lib/api/v3/values/values_api.rb new file mode 100644 index 0000000000..403f01cc3f --- /dev/null +++ b/lib/api/v3/values/values_api.rb @@ -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 diff --git a/lib/api/v3/work_packages/work_package_sums_representer.rb b/lib/api/v3/work_packages/work_package_sums_representer.rb index 69b05b1aca..33742794e5 100644 --- a/lib/api/v3/work_packages/work_package_sums_representer.rb +++ b/lib/api/v3/work_packages/work_package_sums_representer.rb @@ -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) diff --git a/spec/lib/api/v3/notifications/details_factory_spec.rb b/spec/lib/api/v3/notifications/details_factory_spec.rb new file mode 100644 index 0000000000..8d832fc080 --- /dev/null +++ b/spec/lib/api/v3/notifications/details_factory_spec.rb @@ -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 diff --git a/spec/lib/api/v3/notifications/notification_collection_representer_spec.rb b/spec/lib/api/v3/notifications/notification_collection_representer_spec.rb index c00bd0cef7..81fe6ca29f 100644 --- a/spec/lib/api/v3/notifications/notification_collection_representer_spec.rb +++ b/spec/lib/api/v3/notifications/notification_collection_representer_spec.rb @@ -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 [ diff --git a/spec/lib/api/v3/notifications/notification_representer_rendering_spec.rb b/spec/lib/api/v3/notifications/notification_representer_rendering_spec.rb index 29537dd57c..7fdeb5d9db 100644 --- a/spec/lib/api/v3/notifications/notification_representer_rendering_spec.rb +++ b/spec/lib/api/v3/notifications/notification_representer_rendering_spec.rb @@ -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 diff --git a/spec/lib/api/v3/utilities/path_helper_spec.rb b/spec/lib/api/v3/utilities/path_helper_spec.rb index 5fb04195e7..0484db06b6 100644 --- a/spec/lib/api/v3/utilities/path_helper_spec.rb +++ b/spec/lib/api/v3/utilities/path_helper_spec.rb @@ -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 diff --git a/spec/lib/api/v3/values/property_date_representer_rendering_spec.rb b/spec/lib/api/v3/values/property_date_representer_rendering_spec.rb new file mode 100644 index 0000000000..6f84374bf4 --- /dev/null +++ b/spec/lib/api/v3/values/property_date_representer_rendering_spec.rb @@ -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 diff --git a/spec/lib/api/v3/values/schemas/property_schema_representer_rendering_spec.rb b/spec/lib/api/v3/values/schemas/property_schema_representer_rendering_spec.rb new file mode 100644 index 0000000000..ea2cc986ce --- /dev/null +++ b/spec/lib/api/v3/values/schemas/property_schema_representer_rendering_spec.rb @@ -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 diff --git a/spec/lib/api/v3/values/schemas/value_schema_factory_spec.rb b/spec/lib/api/v3/values/schemas/value_schema_factory_spec.rb new file mode 100644 index 0000000000..5d0594b867 --- /dev/null +++ b/spec/lib/api/v3/values/schemas/value_schema_factory_spec.rb @@ -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 diff --git a/spec/requests/api/v3/notifications/details_resource_spec.rb b/spec/requests/api/v3/notifications/details_resource_spec.rb new file mode 100644 index 0000000000..9ae0fd51b1 --- /dev/null +++ b/spec/requests/api/v3/notifications/details_resource_spec.rb @@ -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 diff --git a/spec/requests/api/v3/notifications/show_resource_examples.rb b/spec/requests/api/v3/notifications/show_resource_examples.rb index 7ba28512dd..fae6dedc41 100644 --- a/spec/requests/api/v3/notifications/show_resource_examples.rb +++ b/spec/requests/api/v3/notifications/show_resource_examples.rb @@ -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 diff --git a/spec/requests/api/v3/notifications/show_resource_spec.rb b/spec/requests/api/v3/notifications/show_resource_spec.rb index f59d5daf3e..d584ab7e3e 100644 --- a/spec/requests/api/v3/notifications/show_resource_spec.rb +++ b/spec/requests/api/v3/notifications/show_resource_spec.rb @@ -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) diff --git a/spec/requests/api/v3/values/schemas/value_schema_resource_spec.rb b/spec/requests/api/v3/values/schemas/value_schema_resource_spec.rb new file mode 100644 index 0000000000..5385eee29d --- /dev/null +++ b/spec/requests/api/v3/values/schemas/value_schema_resource_spec.rb @@ -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