mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
stabilizing loading the resource form
There can be race conditions when the resource update is failing after a type swtich and the form for the updated type needs to be fetched. Sometimes, the old form is used (from the former type) which does not have information on a custom field property. In this case, a fallback is added to force fetching the form. This solution is not ideal as an additional request is sent to the server. Ideally, the changes to the table should only be rendered after the schema (as part of the form) is present.
This commit is contained in:
@@ -44,6 +44,7 @@ import isNewResource from 'core-app/features/hal/helpers/is-new-resource';
|
||||
import { HalError } from 'core-app/features/hal/services/hal-error';
|
||||
import { FormResource } from 'core-app/features/hal/resources/form-resource';
|
||||
import { HalResourceEditFieldHandler } from 'core-app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler';
|
||||
import { ISchemaProxy } from 'core-app/features/hal/schemas/schema-proxy';
|
||||
|
||||
export const activeFieldContainerClassName = 'inline-edit--active-field';
|
||||
export const activeFieldClassName = 'inline-edit--field';
|
||||
@@ -294,48 +295,46 @@ export abstract class EditForm<T extends HalResource = HalResource> {
|
||||
* @param fieldName
|
||||
*/
|
||||
protected loadFieldSchema(fieldName:string, noWarnings = false):Promise<IFieldSchema> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.loadFormAndCheck(fieldName, noWarnings);
|
||||
const fieldSchema:IFieldSchema = this.change.schema.ofProperty(fieldName);
|
||||
|
||||
return this.getFormFieldSchema(fieldName).then((fieldSchema) => {
|
||||
if (!fieldSchema) {
|
||||
this.closeEditFields([fieldName]);
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
resolve(fieldSchema);
|
||||
if (!fieldSchema.writable && !noWarnings) {
|
||||
this.halNotification.showEditingBlockedError(fieldSchema.name || fieldName);
|
||||
this.closeEditFields([fieldName]);
|
||||
}
|
||||
|
||||
return fieldSchema;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the form gets loaded and we show an error when the field cannot be opened
|
||||
* Load the form and return the field schema, reloading the form once if the schema is
|
||||
* not present in the cached version (e.g. after a type change).
|
||||
* Returns null if the schema is still absent after a forced reload.
|
||||
* @param fieldName
|
||||
* @param noWarnings
|
||||
*/
|
||||
private loadFormAndCheck(fieldName:string, noWarnings = false) {
|
||||
// Ensure the form is being loaded if necessary
|
||||
this.change
|
||||
.getForm()
|
||||
.then(() => {
|
||||
// Look up whether we're actually editable
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const fieldSchema = this.change.schema.ofProperty(fieldName);
|
||||
private getFormFieldSchema(fieldName:string):Promise<IFieldSchema|null> {
|
||||
return this.change.getForm()
|
||||
.then(():Promise<IFieldSchema|null> => {
|
||||
const fieldSchema:IFieldSchema|null = (this.change.schema as ISchemaProxy).ofProperty(fieldName);
|
||||
|
||||
// If the type changed while we tried to activate the form
|
||||
// silently close the field as it will no longer be writable
|
||||
if (!fieldSchema) {
|
||||
this.closeEditFields([fieldName]);
|
||||
return;
|
||||
if (fieldSchema) {
|
||||
return Promise.resolve(fieldSchema);
|
||||
}
|
||||
|
||||
if (!fieldSchema.writable && !noWarnings) {
|
||||
this.halNotification.showEditingBlockedError(fieldSchema.name || fieldName);
|
||||
this.closeEditFields([fieldName]);
|
||||
}
|
||||
// The cached form may be stale (e.g. type just changed); force a reload and retry once.
|
||||
return this.change.getForm(true).then(
|
||||
():IFieldSchema|null => (this.change.schema as ISchemaProxy).ofProperty(fieldName),
|
||||
);
|
||||
})
|
||||
.catch((error:any) => {
|
||||
.catch((error:unknown) => {
|
||||
console.error('Failed to build edit field: %o', error);
|
||||
this.halNotification.handleRawError(error, this.resource);
|
||||
this.closeEditFields([fieldName]);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -121,20 +121,19 @@ RSpec.describe "Switching types in work package table", :js do
|
||||
|
||||
# Switch type
|
||||
type_field.activate!
|
||||
type_field.set_value type_bug.name
|
||||
type_field.set_select_field_value type_bug.name
|
||||
|
||||
wp_table.expect_and_dismiss_toaster(
|
||||
type: :error,
|
||||
message: "#{cf_req_text.name} can't be blank."
|
||||
)
|
||||
|
||||
# Required CF requires activation
|
||||
# After a failed type switch, the required CF field is auto-opened in edit mode
|
||||
req_text_field.expect_active!
|
||||
|
||||
# Now switch back to a type without the required CF
|
||||
type_field.activate!
|
||||
type_field.openSelectField
|
||||
type_field.set_value type_task.name
|
||||
type_field.set_select_field_value type_task.name
|
||||
|
||||
wp_table.expect_and_dismiss_toaster(
|
||||
message: "Successful update."
|
||||
|
||||
Reference in New Issue
Block a user