Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def pageSizeParam(params)

def self_link(project)
if project
api_v3_paths.work_packages_by_project(project.id)
api_v3_paths.work_packages_by_workspace(project.id)
else
api_v3_paths.work_packages
end
Expand Down
24 changes: 24 additions & 0 deletions docs/api/apiv3/components/schemas/project_model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ properties:
active:
type: boolean
description: Indicates whether the project is currently active or already archived
favorited:
type: boolean
description: Indicates whether the project is favorited by the current user
statusExplanation:
allOf:
- $ref: './formattable.yml'
Expand Down Expand Up @@ -71,6 +74,27 @@ properties:
# Conditions

**Permission**: admin
favor:
allOf:
- $ref: './link.yml'
- description: |-
Mark this project as favorited by the current user

# Conditions

Only present if the project is not yet favorited

Permission**: none but login is required
disfavor:
allOf:
- $ref: './link.yml'
- description: |-
Mark this project as not favorited by the current user

# Conditions
Only present if the project is favorited by the current user

Permission**: none but login is required
createWorkPackage:
allOf:
- $ref: './link.yml'
Expand Down
3 changes: 2 additions & 1 deletion docs/api/apiv3/paths/projects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ get:
+ ancestor: filters projects by their ancestor. A project is not considered to be its own ancestor.
+ available_project_attributes: filters projects based on the activated project project attributes.
+ created_at: based on the time the project was created
+ favorited: based on the favorited property of the project
+ id: based on projects' id.
+ latest_activity_at: based on the time the last activity was registered on a project.
+ project_phase_any: based on the project phases active in a project.
+ name_and_identifier: based on both the name and the identifier.
+ parent_id: filters projects by their parent.
+ principal: based on members of the project.
+ project_phase_any: based on the project phases active in a project.
+ project_status_code: based on status code of the project
+ storage_id: filters projects by linked storages
+ storage_url: filters projects by linked storages identified by the host url
Expand Down
17 changes: 10 additions & 7 deletions docs/api/apiv3/tags/projects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ description: |-

## Actions

| Link | Description | Condition |
|:--------------------------: |----------------------------------------------------------------------| --------------------------------- |
| update | Form endpoint that aids in updating this project | **Permission**: edit project |
| updateImmediately | Directly update this project | **Permission**: edit project |
| delete | Delete this project | **Permission**: admin |
| createWorkPackage | Form endpoint that aids in preparing and creating a work package | **Permission**: add work packages |
| createWorkPackageImmediately | Directly creates a work package in the project | **Permission**: add work packages |
| Link | Description | Condition |
|:--------------------------: |----------------------------------------------------------------------| --------------------------------- |
| update | Form endpoint that aids in updating this project | **Permission**: edit project |
| updateImmediately | Directly update this project | **Permission**: edit project |
| delete | Delete this project | **Permission**: admin |
| favor | Mark this project as favorited by the current user | **Permission**: none but login is required, only present if the project is not yet favorited |
| disfavor | Mark this project as no longer favorited by the current user | **Permission**: none but login is required, only present if the project is favorited |
| createWorkPackage | Form endpoint that aids in preparing and creating a work package | **Permission**: add work packages |
| createWorkPackageImmediately | Directly creates a work package in the project | **Permission**: add work packages |

## Linked Properties

Expand Down Expand Up @@ -42,6 +44,7 @@ description: |-
| identifier | | String | | READ/WRITE |
| name | | String | | READ/WRITE |
| active | Indicates whether the project is currently active or already archived | Boolean | | READ/WRITE |
| favorited | Indicates whether the project is favorited by the current user | Boolean | | READ |
| statusExplanation | A text detailing and explaining why the project has the reported status | Formattable | | READ/WRITE |
| public | Indicates whether the project is accessible for everybody | Boolean | | READ/WRITE |
| description | | Formattable | | READ/WRITE |
Expand Down
70 changes: 40 additions & 30 deletions lib/api/decorators/linked_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,29 @@ def validate_links!(links)
raise ::API::Errors::BadRequest.new(I18n.t("api_v3.errors.bad_request.invalid_link", key: invalid))
end

def associated_resource_default_link(represented,
name,
v3_path:,
skip_link:,
title_attribute:,
getter: :"#{name}_id",
undisclosed: false)
if undisclosed && instance_exec(&skip_link)
{
href: API::V3::URN_UNDISCLOSED,
title: I18n.t(:"api_v3.undisclosed.#{name}")
}
elsif !instance_exec(&skip_link)
::API::Decorators::LinkObject
.new(represented,
path: v3_path.is_a?(Proc) ? instance_exec(&v3_path) : v3_path,
property_name: name,
title_attribute:,
getter:)
.to_hash
end
end

module ClassMethods
def resource(name,
getter:,
Expand All @@ -90,7 +113,6 @@ def resource(name,
show_if: ->(*) { true },
skip_render: nil,
embedded: true)

link(link_attr(name, uncacheable_link, link_cache_if), &link)

property name,
Expand All @@ -113,7 +135,6 @@ def resources(name,
show_if: ->(*) { true },
skip_render: nil,
embedded: true)

links(link_attr(name, uncacheable_link, link_cache_if), &link)

property name,
Expand All @@ -131,7 +152,6 @@ def resource_link(name,
setter:,
getter:,
show_if: ->(*) { true })

resource(name,
getter: ->(*) {},
setter:,
Expand All @@ -157,13 +177,12 @@ def associated_resource(name,
uncacheable_link: false,
getter: associated_resource_default_getter(name, representer),
setter: associated_resource_default_setter(name, as, v3_path),
link: associated_resource_default_link(name,
v3_path:,
skip_link:,
undisclosed:,
title_attribute: link_title_attribute))

resource((as || name),
link: associated_resource_default_link_lambda(name,
v3_path:,
skip_link:,
undisclosed:,
title_attribute: link_title_attribute))
resource(as || name,
getter:,
setter:,
link:,
Expand Down Expand Up @@ -202,27 +221,20 @@ def associated_resource_default_setter(name, as, v3_path)
end
end

def associated_resource_default_link(name,
def associated_resource_default_link_lambda(name,
v3_path:,
skip_link:,
title_attribute:,
getter: :"#{name}_id",
undisclosed: false)
->(*) do
associated_resource_default_link(represented,
name,
v3_path:,
skip_link:,
title_attribute:,
getter: :"#{name}_id",
undisclosed: false)
->(*) do
if undisclosed && instance_exec(&skip_link)
{
href: API::V3::URN_UNDISCLOSED,
title: I18n.t(:"api_v3.undisclosed.#{name}")
}
elsif !instance_exec(&skip_link)
::API::Decorators::LinkObject
.new(represented,
path: v3_path,
property_name: name,
title_attribute:,
getter:)
.to_hash
end
getter:,
undisclosed:)
end
end

Expand All @@ -240,7 +252,6 @@ def associated_resources(name,
v3_path:,
skip_link:,
title_attribute: link_title_attribute))

resources(as,
getter:,
setter:,
Expand All @@ -251,7 +262,6 @@ def associated_resources(name,

def associated_resources_default_getter(name,
representer)

representer ||= default_representer(name.to_s.singularize)

->(*) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,20 @@
# See COPYRIGHT and LICENSE files for more details.
#++

require "api/v3/categories/category_collection_representer"

module API
module V3
module Categories
class CategoriesByProjectAPI < ::API::OpenProjectAPI
class CategoriesByWorkspaceAPI < ::API::OpenProjectAPI
resources :categories do
after_validation do
@categories = @project.categories
end

get do
self_link = api_v3_paths.categories_by_project(@project.identifier)

CategoryCollectionRepresenter
.new(@categories, self_link:, current_user:)
.new(@categories,
self_link: api_v3_paths.categories_by_workspace(@project.identifier),
current_user:)
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/api/v3/memberships/membership_representer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ class MembershipRepresenter < ::API::Decorators::Single

property :id

associated_resource :project
associated_resource :project,
link: ::API::V3::Workspaces::WorkspaceRepresenterFactory
.create_link_lambda(:project)

associated_resource :principal,
getter: ::API::V3::Principals::PrincipalRepresenterFactory
Expand Down
59 changes: 59 additions & 0 deletions lib/api/v3/portfolios/portfolios_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 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 V3
module Portfolios
class PortfoliosAPI < ::API::OpenProjectAPI
resources :portfolios do
get &::API::V3::Utilities::Endpoints::SqlFallbackedIndex.new(model: Project,
scope: -> {
Project
.includes(API::V3::Projects::ProjectRepresenter.to_eager_load)
.portfolio
})
.mount

route_param :id do
after_validation do
@project = if current_user.admin?
Project.portfolio
else
Project.portfolio.visible(current_user)
end.find(params[:id])
end

get &::API::V3::Utilities::Endpoints::Show.new(model: Project).mount
end
end
end
end
end
end
14 changes: 9 additions & 5 deletions lib/api/v3/principals/principal_representer_factory.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
Expand Down Expand Up @@ -57,11 +59,13 @@ def create_link_lambda(name, getter: "#{name}_id")
->(*) {
v3_path = API::V3::Principals::PrincipalType.for(represented.send(name))

instance_exec(&self.class.associated_resource_default_link(name,
v3_path:,
skip_link: -> { false },
title_attribute: :name,
getter:))
# TODO: check if this can be turned into a call to
# associated_resource_default_link
instance_exec(&self.class.associated_resource_default_link_lambda(name,
v3_path:,
skip_link: -> { false },
title_attribute: :name,
getter:))
}
end

Expand Down
Loading
Loading