Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 12 additions & 5 deletions app/services/base_services/set_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ def set_default_attributes(_params)
def set_custom_values_to_validate(params)
return model.deactivate_custom_field_validations! if contract_options[:skip_custom_field_validation]

custom_field_ids = custom_field_ids_from(params)
custom_field_ids = custom_field_ids_to_validate(params)

# Only update custom_values_to_validate when custom field params are provided.
# Only update custom_values_to_validate when the custom field params are provided.
# Otherwise keep them intact, so other services can still set them.
return if custom_field_ids.empty?
return unless custom_field_ids.any?

# Validate only the custom values being updated via the params.
model.custom_values_to_validate = model.custom_values.filter do |cv|
# Validate the custom values updated via the params only.
model.custom_values_to_validate = model.custom_field_values.filter do |cv|
custom_field_ids.include?(cv.custom_field_id)
end
end
Expand All @@ -98,6 +98,13 @@ def prepare_model(model)
model
end

def custom_field_ids_to_validate(params)
# Leave custom_field_ids_to_validate empty when the model is not persisted,
# allowing the default behaviour to set the id's to be validated in the
# model.custom_values_to_validate method.
model.persisted? ? custom_field_ids_from(params) : []
end

def custom_field_ids_from(params)
# 1. Retrieve custom fields set via the accessor `wp.custom_field_1 = 1`
custom_field_ids = params.keys.filter_map { |k| k[/^custom_field_(\d+)$/, 1]&.to_i }
Expand Down
11 changes: 10 additions & 1 deletion app/services/projects/set_attributes_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

module Projects
class SetAttributesService < ::BaseServices::SetAttributes

private

def set_attributes(params)
Expand Down Expand Up @@ -111,5 +110,15 @@ def faulty_code?(status_code)
def first_not_set_code
(Project.status_codes.keys - [model.status_code]).first
end

def custom_field_ids_to_validate(params)
# In case of new records, validate custom fields that are enabled for all projects
# and also required.
if model.new_record?
model.available_custom_fields.for_all.required.pluck(:id)
else
custom_field_ids_from(params)
end
end
end
end
52 changes: 52 additions & 0 deletions spec/requests/api/v3/projects/create_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,58 @@

it_behaves_like "creates a project with a custom value", "Engineering"
end

context "with another custom field present" do
shared_let(:other_custom_field) do
create(:text_project_custom_field,
name: "Other CF")
end

context "when a value for the other cf is provided but the required one is missing (regression #70107)" do
let(:body) do
{
identifier: "new_project_identifier",
name: "Project name",
other_custom_field.attribute_name(:camel_case) => {
raw: "Other value"
}
}.to_json
end

it "responds with 422 and explains the custom field error" do
expect(last_response).to have_http_status(:unprocessable_entity)

expect(last_response.body)
.to be_json_eql("Department can't be blank.".to_json)
.at_path("message")
end
end
end

context "with another custom field present that is required but not for_all" do
shared_let(:required_not_for_all_custom_field) do
create(:text_project_custom_field,
name: "Not for all CF",
is_required: true,
is_for_all: false)
end

context "when a value for the required field is provided, but no value for the required not for_all custom_field" do
let(:body) do
{
identifier: "new_project_identifier",
name: "Project name",
shared_custom_field.attribute_name(:camel_case) => {
raw: "Engineering"
}
}.to_json
end

it "responds with 201 and does not validate the required not for_all custom_field" do
expect(last_response).to have_http_status(:created)
end
end
end
end

context "with a visible custom field" do
Expand Down
Loading