Skip to content

Commit 7b493d4

Browse files
committed
wip - accept non project links on wp representer
1 parent 335ab2f commit 7b493d4

File tree

8 files changed

+168
-69
lines changed

8 files changed

+168
-69
lines changed

lib/api/decorators/linked_resource.rb

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ def validate_links!(links)
8080
raise ::API::Errors::BadRequest.new(I18n.t("api_v3.errors.bad_request.invalid_link", key: invalid))
8181
end
8282

83+
def associated_resource_default_link(represented,
84+
name,
85+
v3_path:,
86+
skip_link:,
87+
title_attribute:,
88+
getter: :"#{name}_id",
89+
undisclosed: false)
90+
if undisclosed && instance_exec(&skip_link)
91+
{
92+
href: API::V3::URN_UNDISCLOSED,
93+
title: I18n.t(:"api_v3.undisclosed.#{name}")
94+
}
95+
elsif !instance_exec(&skip_link)
96+
::API::Decorators::LinkObject
97+
.new(represented,
98+
path: v3_path.is_a?(Proc) ? instance_exec(&v3_path) : v3_path,
99+
property_name: name,
100+
title_attribute:,
101+
getter:)
102+
.to_hash
103+
end
104+
end
105+
83106
module ClassMethods
84107
def resource(name,
85108
getter:,
@@ -154,11 +177,11 @@ def associated_resource(name,
154177
uncacheable_link: false,
155178
getter: associated_resource_default_getter(name, representer),
156179
setter: associated_resource_default_setter(name, as, v3_path),
157-
link: associated_resource_default_link(name,
158-
v3_path:,
159-
skip_link:,
160-
undisclosed:,
161-
title_attribute: link_title_attribute))
180+
link: associated_resource_default_link_lambda(name,
181+
v3_path:,
182+
skip_link:,
183+
undisclosed:,
184+
title_attribute: link_title_attribute))
162185
resource(as || name,
163186
getter:,
164187
setter:,
@@ -198,27 +221,20 @@ def associated_resource_default_setter(name, as, v3_path)
198221
end
199222
end
200223

201-
def associated_resource_default_link(name,
224+
def associated_resource_default_link_lambda(name,
225+
v3_path:,
226+
skip_link:,
227+
title_attribute:,
228+
getter: :"#{name}_id",
229+
undisclosed: false)
230+
->(*) do
231+
associated_resource_default_link(represented,
232+
name,
202233
v3_path:,
203234
skip_link:,
204235
title_attribute:,
205-
getter: :"#{name}_id",
206-
undisclosed: false)
207-
->(*) do
208-
if undisclosed && instance_exec(&skip_link)
209-
{
210-
href: API::V3::URN_UNDISCLOSED,
211-
title: I18n.t(:"api_v3.undisclosed.#{name}")
212-
}
213-
elsif !instance_exec(&skip_link)
214-
::API::Decorators::LinkObject
215-
.new(represented,
216-
path: v3_path.is_a?(Proc) ? instance_exec(&v3_path) : v3_path,
217-
property_name: name,
218-
title_attribute:,
219-
getter:)
220-
.to_hash
221-
end
236+
getter:,
237+
undisclosed:)
222238
end
223239
end
224240

lib/api/v3/principals/principal_representer_factory.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ def create_link_lambda(name, getter: "#{name}_id")
5959
->(*) {
6060
v3_path = API::V3::Principals::PrincipalType.for(represented.send(name))
6161

62-
instance_exec(&self.class.associated_resource_default_link(name,
63-
v3_path:,
64-
skip_link: -> { false },
65-
title_attribute: :name,
66-
getter:))
62+
# TODO: check if this can be turned into a call to
63+
# associated_resource_default_link
64+
instance_exec(&self.class.associated_resource_default_link_lambda(name,
65+
v3_path:,
66+
skip_link: -> { false },
67+
title_attribute: :name,
68+
getter:))
6769
}
6870
end
6971

lib/api/v3/projects/project_representer.rb

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ProjectRepresenter < ::API::Decorators::Single
3737
include ::API::Caching::CachedRepresenter
3838
include API::Decorators::FormattableProperty
3939
extend ::API::V3::Utilities::CustomFieldInjector::RepresenterClass
40+
include ::API::V3::Workspaces::WorkspaceRepresenterFactory::Associations
4041

4142
def self.current_user_view_allowed_lambda
4243
->(*) { current_user.allowed_in_project?(:view_project, represented) || current_user.allowed_globally?(:add_project) }
@@ -149,19 +150,7 @@ def self.current_user_view_allowed_lambda
149150
links :ancestors,
150151
uncacheable: true do
151152
represented.ancestors_from_root.map do |ancestor|
152-
# Explicitly check for admin as an archived project
153-
# will lead to the admin losing permissions in the project.
154-
if current_user.admin? || ancestor.visible?
155-
{
156-
href: strategy(ancestor).path(ancestor),
157-
title: ancestor.name
158-
}
159-
else
160-
{
161-
href: API::V3::URN_UNDISCLOSED,
162-
title: I18n.t(:"api_v3.undisclosed.ancestor")
163-
}
164-
end
153+
project_link(ancestor, name: :ancestor, getter: :id)
165154
end
166155
end
167156

@@ -186,12 +175,10 @@ def self.current_user_view_allowed_lambda
186175
{ href: api_v3_paths.favor_workspace(represented.id) }
187176
end
188177

189-
associated_resource :parent,
190-
v3_path: ->(*) { represented.parent and strategy(represented.parent).path_name },
191-
representer: ::API::V3::Projects::ProjectRepresenter,
192-
uncacheable_link: true,
193-
undisclosed: true,
194-
skip_render: ->(*) { represented.parent && !represented.parent.visible? && !current_user.admin? }
178+
associated_project :parent,
179+
skip_render: ->(*) {
180+
represented.parent && !represented.parent.visible? && !current_user.admin?
181+
}
195182

196183
property :id
197184
property :identifier,

lib/api/v3/shares/entity_representer_factory.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ def create_link_lambda(name, getter: "#{name}_id")
6969
v3_path = API::V3::Shares::EntityRepresenterFactory.representer_type(represented.send(name))
7070
title_attribute = API::V3::Shares::EntityRepresenterFactory.title_attribute(represented.send(name))
7171

72-
instance_exec(&self.class.associated_resource_default_link(name,
73-
v3_path:,
74-
skip_link: -> { false },
75-
title_attribute:,
76-
getter:))
72+
# TODO: check if this can be turned into a call to
73+
# associated_resource_default_link
74+
instance_exec(&self.class.associated_resource_default_link_lambda(name,
75+
v3_path:,
76+
skip_link: -> { false },
77+
title_attribute:,
78+
getter:))
7779
}
7880
end
7981

lib/api/v3/workspaces/workspace_representer_factory.rb

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,65 @@ module API
3232
module V3
3333
module Workspaces
3434
module WorkspaceRepresenterFactory
35+
module Associations
36+
extend ActiveSupport::Concern
37+
include API::Decorators::LinkedResource
38+
39+
def project_link(project, name:, getter: "#{name}_id")
40+
# Explicitly check for admin as an archived project
41+
# will lead to the admin losing permissions in the project.
42+
if project && !project.visible? && !current_user.admin?
43+
{
44+
href: API::V3::URN_UNDISCLOSED,
45+
title: I18n.t(:"api_v3.undisclosed.#{name}")
46+
}
47+
elsif !project
48+
{
49+
href: nil
50+
}
51+
else
52+
associated_resource_default_link(project,
53+
:itself,
54+
v3_path: project&.workspace_type,
55+
skip_link: -> { false },
56+
title_attribute: :name,
57+
getter:)
58+
end
59+
end
60+
61+
class_methods do
62+
def associated_project(name, skip_render: false)
63+
associated_resource name,
64+
representer: ::API::V3::Projects::ProjectRepresenter,
65+
uncacheable_link: true,
66+
skip_render:,
67+
link: ::API::V3::Workspaces::WorkspaceRepresenterFactory
68+
.create_link_lambda(name),
69+
setter: ::API::V3::Workspaces::WorkspaceRepresenterFactory
70+
.create_setter_lambda(name)
71+
end
72+
end
73+
end
74+
3575
module_function
3676

37-
def create_link_lambda(name, getter: "#{name}_id")
77+
def create_link_lambda(name, property_name: name)
3878
->(*) {
39-
instance_exec(&self.class.associated_resource_default_link(name,
40-
v3_path: represented.project&.workspace_type,
41-
skip_link: -> { false },
42-
title_attribute: :name,
43-
getter:))
79+
project_link(represented.public_send(name),
80+
name: property_name,
81+
getter: :id)
82+
}
83+
end
84+
85+
def create_setter_lambda(name, property_name: name, namespaces: %i(projects programs portfolios))
86+
->(fragment:, **) {
87+
::API::Decorators::LinkObject
88+
.new(represented,
89+
property_name:,
90+
namespace: namespaces,
91+
getter: :"#{name}_id",
92+
setter: :"#{name}_id=")
93+
.from_hash(fragment)
4494
}
4595
end
4696
end

modules/costs/lib/api/v3/cost_entries/entity_representer_factory.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,13 @@ def create_link_lambda(name, getter: "#{name}_id")
7373
v3_path = API::V3::CostEntries::EntityRepresenterFactory.representer_type(represented.send(name))
7474
title_attribute = API::V3::CostEntries::EntityRepresenterFactory.title_attribute(represented.send(name))
7575

76-
instance_exec(&self.class.associated_resource_default_link(name,
77-
v3_path:,
78-
skip_link: -> { false },
79-
title_attribute:,
80-
getter:))
76+
# TODO: check if this can be turned into a call to
77+
# associated_resource_default_link
78+
instance_exec(&self.class.associated_resource_default_link_lambda(name,
79+
v3_path:,
80+
skip_link: -> { false },
81+
title_attribute:,
82+
getter:))
8183
}
8284
end
8385

modules/costs/lib/api/v3/time_entries/entity_representer_factory.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,13 @@ def create_link_lambda(name, getter: "#{name}_id")
7979
v3_path = API::V3::TimeEntries::EntityRepresenterFactory.representer_type(represented.send(name))
8080
title_attribute = API::V3::TimeEntries::EntityRepresenterFactory.title_attribute(represented.send(name))
8181

82-
instance_exec(&self.class.associated_resource_default_link(name,
83-
v3_path:,
84-
skip_link: -> { false },
85-
title_attribute:,
86-
getter:))
82+
# TODO: check if this can be turned into a call to
83+
# associated_resource_default_link
84+
instance_exec(&self.class.associated_resource_default_link_lambda(name,
85+
v3_path:,
86+
skip_link: -> { false },
87+
title_attribute:,
88+
getter:))
8789
}
8890
end
8991

spec/lib/api/v3/projects/project_payload_representer_parsing_spec.rb

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135

136136
describe "_links" do
137137
context "with a parent link" do
138-
context "with the href being an url" do
138+
context "with the href being a project url" do
139139
let(:hash) do
140140
{
141141
"_links" => {
@@ -154,6 +154,44 @@
154154
end
155155
end
156156

157+
context "with the href being a program url" do
158+
let(:hash) do
159+
{
160+
"_links" => {
161+
"parent" => {
162+
"href" => api_v3_paths.program(5)
163+
}
164+
}
165+
}
166+
end
167+
168+
it "sets the parent_id to the value" do
169+
project = representer.from_hash(hash)
170+
171+
expect(project[:parent_id])
172+
.to eq "5"
173+
end
174+
end
175+
176+
context "with the href being a portfolio url" do
177+
let(:hash) do
178+
{
179+
"_links" => {
180+
"parent" => {
181+
"href" => api_v3_paths.portfolio(5)
182+
}
183+
}
184+
}
185+
end
186+
187+
it "sets the parent_id to the value" do
188+
project = representer.from_hash(hash)
189+
190+
expect(project[:parent_id])
191+
.to eq "5"
192+
end
193+
end
194+
157195
context "with the href being nil" do
158196
let(:hash) do
159197
{

0 commit comments

Comments
 (0)