drop invalid filter values on wp init

bug/36827-creating-work-package-in-status-not-available-for-work-package-type
ulferts 4 years ago
parent 1a58e2d793
commit 58ddd1bc57
No known key found for this signature in database
GPG Key ID: A205708DE1284017
  1. 13
      frontend/src/app/core/apiv3/endpoints/work_packages/apiv3-work-package-form.ts
  2. 2
      frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.ts
  3. 87
      frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts
  4. 29
      spec/features/work_packages/new/attributes_from_filter_spec.rb

@ -4,19 +4,6 @@ import { Observable } from 'rxjs';
import { HalSource } from 'core-app/features/hal/resources/hal-resource';
export class APIv3WorkPackageForm extends APIv3FormResource {
/**
* Returns a promise to post `/api/v3/work_packages/form` with only the type part of the
* provided payload being sent to the backend.
*
* @param payload: The payload to be sent to the backend
* @returns A work package form resource prefilled with the provided payload.
*/
public forTypePayload(payload:HalSource):Observable<FormResource> {
const typePayload = payload._links.type ? { _links: { type: payload._links.type } } : { _links: {} };
return this.post(payload);
}
/**
* Returns a promise to post `/api/v3/work_packages/form` where the
* payload sent to the backend has been provided.

@ -64,7 +64,7 @@ export class WorkPackageFilterValues {
}
/**
* Set a value no null for a none type filter (!*)
* Set a value to null for a none type filter (!*)
*
* @param filter A none '!*' filter
* @private

@ -48,6 +48,7 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link';
import { SchemaResource } from 'core-app/features/hal/resources/schema-resource';
import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service';
import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service';
import { ErrorResource } from 'core-app/features/hal/resources/error-resource';
export const newWorkPackageHref = '/api/v3/work_packages/new';
@ -228,20 +229,20 @@ export class WorkPackageCreateService extends UntilDestroyedMixin {
return this
.withFiltersPayload(projectIdentifier, defaults)
.then((filterDefaults) => {
const mergedPayload = _.merge({ _links: {} }, filterDefaults, defaults);
return this.createNewWorkPackage(projectIdentifier, mergedPayload).then((change:WorkPackageChangeset) => {
if (!change) {
throw new Error('No new work package was created');
}
// We need to apply the defaults again (after them being applied in the form requests)
// here as the initial form requests might have led to some default
// values not being carried over. This can happen when custom fields not available in one type are filter values.
this.defaultsFromFilters(change, defaults);
return change;
});
return this
.createNewWorkPackage(projectIdentifier, filterDefaults)
.then((change:WorkPackageChangeset) => {
if (!change) {
throw new Error('No new work package was created');
}
// We need to apply the defaults again (after them being applied in the form requests)
// here as the initial form requests might have led to some default
// values not being carried over. This can happen when custom fields not available in one type are filter values.
this.defaultsFromFilters(change, filterDefaults);
return change;
});
});
}
@ -277,8 +278,10 @@ export class WorkPackageCreateService extends UntilDestroyedMixin {
* top level.
*/
private withFiltersPayload(projectIdentifier:string|null|undefined, defaults?:HalSource):Promise<HalSource> {
const fromFilter = { _links: {} };
let fromFilter:HalSource = { _links: {} };
this.defaultsFromFilters(fromFilter, defaults);
this.toApiPayload(fromFilter);
fromFilter = _.merge(fromFilter, defaults)
const filtersApplied = Object.keys(fromFilter).length > 1 || Object.keys(fromFilter._links).length > 0;
@ -288,33 +291,53 @@ export class WorkPackageCreateService extends UntilDestroyedMixin {
.withOptionalProject(projectIdentifier)
.work_packages
.form
.forTypePayload(defaults || { _links: {} })
.forPayload(fromFilter)
.toPromise()
.then((form:FormResource) => {
this.toApiPayload(fromFilter, form.schema);
const errors = form.getErrors()?.errors;
(errors || []).forEach((error:ErrorResource) => {
const attribute = error.details.attribute;
if (fromFilter[attribute]) {
delete (fromFilter[attribute])
} else if (fromFilter._links[attribute]) {
delete (fromFilter._links[attribute])
}
});
return fromFilter;
});
}
return Promise.resolve(fromFilter);
}
private toApiPayload(payload:HalSource, schema:SchemaResource) {
private toApiPayload(payload:HalSource, schema?:SchemaResource) {
const links:string[] = [];
Object.keys(schema.$source).forEach((attribute) => {
if (!['Integer',
'Float',
'Date',
'DateTime',
'Duration',
'Formattable',
'Boolean',
'String',
'Text',
undefined].includes(schema.$source[attribute].type)) {
links.push(attribute);
}
});
// TODO: is the schema chase still needed?
if (schema) {
Object.keys(schema.$source).forEach((attribute) => {
if (!['Integer',
'Float',
'Date',
'DateTime',
'Duration',
'Formattable',
'Boolean',
'String',
'Text',
undefined].includes(schema.$source[attribute].type)) {
links.push(attribute);
}
});
} else {
Object.keys(payload).forEach(attribute => {
if (payload[attribute] instanceof HalResource) {
links.push(attribute);
}
});
}
links.forEach((attribute) => {
const value = payload[attribute];

@ -199,4 +199,33 @@ RSpec.feature 'Work package create uses attributes from filters', js: true, sele
expect(wp.type_id).to eq type_bug.id
end
end
context 'with a status for which no workflow exists' do
let(:other_status) { FactoryBot.create(:status) }
let(:filters) do
[['status_id', '=', [other_status.id]]]
end
it 'falls back to the first status for which a workflow exists' do
split_page = wp_table.create_wp_by_button type_bug
# Instead of the status selected by the filters another status is used.
# This is done silently. The field does not get set into edit mode.
expect(page)
.to have_selector('.inline-edit--display-field.status', text: status.name)
subject = split_page.edit_field(:subject)
subject.expect_active!
subject.set_value 'Foobar!'
split_page.save!
wp_table.expect_and_dismiss_notification(
message: 'Successful creation. Click here to open this work package in fullscreen view.'
)
wp = WorkPackage.last
expect(wp.status)
.to eql status
end
end
end

Loading…
Cancel
Save