DEV: Use Category's async method in param-input (#310)
This commit refactors the category id normalization of param-input to use `Category.asyncFindBySlugPath`. Now, the category id input will use an async query when the default/initial value is category slug. Co-authored-by: Natalie <1555215+nattsw@users.noreply.github.com>
This commit is contained in:
parent
1d991c6192
commit
b47ba7ea60
|
@ -1,8 +1,9 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { action } from "@ember/object";
|
import EmberObject, { action } from "@ember/object";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||||
import Form from "discourse/components/form";
|
import Form from "discourse/components/form";
|
||||||
import Category from "discourse/models/category";
|
import Category from "discourse/models/category";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
@ -49,22 +50,20 @@ const ERRORS = {
|
||||||
function digitalizeCategoryId(value) {
|
function digitalizeCategoryId(value) {
|
||||||
value = String(value || "");
|
value = String(value || "");
|
||||||
const isPositiveInt = /^\d+$/.test(value);
|
const isPositiveInt = /^\d+$/.test(value);
|
||||||
if (!isPositiveInt) {
|
if (!isPositiveInt && value.trim()) {
|
||||||
if (/\//.test(value)) {
|
return Category.asyncFindBySlugPath(dasherize(value))
|
||||||
const match = /(.*)\/(.*)/.exec(value);
|
.then((res) => res.id)
|
||||||
if (!match) {
|
.catch((err) => {
|
||||||
value = null;
|
if (err.jqXHR?.status === 404) {
|
||||||
} else {
|
throw new ParamValidationError(
|
||||||
value = Category.findBySlug(
|
`${ERRORS.NO_SUCH_CATEGORY}: ${value}`
|
||||||
dasherize(match[2]),
|
);
|
||||||
dasherize(match[1])
|
} else {
|
||||||
)?.id;
|
throw new Error(err.errorThrow || err.message);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
value = Category.findBySlug(dasherize(value))?.id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return value?.toString();
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeValue(info, value) {
|
function normalizeValue(info, value) {
|
||||||
|
@ -158,36 +157,72 @@ export default class ParamInputForm extends Component {
|
||||||
infoOf = {};
|
infoOf = {};
|
||||||
form = null;
|
form = null;
|
||||||
|
|
||||||
|
promiseNormalizations = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
|
this.initializeParams();
|
||||||
const initialValues = this.args.initialValues;
|
|
||||||
for (const info of this.args.paramInfo) {
|
|
||||||
const identifier = info.identifier;
|
|
||||||
|
|
||||||
// access parsed params if present to update values to previously ran values
|
|
||||||
let initialValue;
|
|
||||||
if (initialValues && identifier in initialValues) {
|
|
||||||
initialValue = initialValues[identifier];
|
|
||||||
} else {
|
|
||||||
// if no parsed params then get and set default values
|
|
||||||
initialValue = info.default;
|
|
||||||
}
|
|
||||||
this.data[identifier] = normalizeValue(info, initialValue);
|
|
||||||
this.paramInfo.push({
|
|
||||||
...info,
|
|
||||||
validation: validationOf(info),
|
|
||||||
validate: this.validatorOf(info),
|
|
||||||
component: componentOf(info),
|
|
||||||
});
|
|
||||||
this.infoOf[identifier] = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.args.onRegisterApi?.({
|
this.args.onRegisterApi?.({
|
||||||
submit: this.submit,
|
submit: this.submit,
|
||||||
|
allNormalized: Promise.allSettled(this.promiseNormalizations),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeParams() {
|
||||||
|
this.args.paramInfo.forEach((info) => {
|
||||||
|
const identifier = info.identifier;
|
||||||
|
const pinfo = this.createParamInfo(info);
|
||||||
|
|
||||||
|
this.paramInfo.push(pinfo);
|
||||||
|
this.infoOf[identifier] = info;
|
||||||
|
|
||||||
|
const normalized = this.getNormalizedValue(info);
|
||||||
|
|
||||||
|
if (normalized instanceof Promise) {
|
||||||
|
this.handlePromiseNormalization(normalized, pinfo);
|
||||||
|
} else {
|
||||||
|
this.data[identifier] = normalized;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createParamInfo(info) {
|
||||||
|
return EmberObject.create({
|
||||||
|
...info,
|
||||||
|
validation: validationOf(info),
|
||||||
|
validate: this.validatorOf(info),
|
||||||
|
component: componentOf(info),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getNormalizedValue(info) {
|
||||||
|
const initialValues = this.args.initialValues;
|
||||||
|
const identifier = info.identifier;
|
||||||
|
return normalizeValue(
|
||||||
|
info,
|
||||||
|
initialValues && identifier in initialValues
|
||||||
|
? initialValues[identifier]
|
||||||
|
: info.default
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePromiseNormalization(promise, pinfo) {
|
||||||
|
this.promiseNormalizations.push(promise);
|
||||||
|
pinfo.set("loading", true);
|
||||||
|
this.data[pinfo.identifier] = null;
|
||||||
|
|
||||||
|
promise
|
||||||
|
.then((res) => this.form.set(pinfo.identifier, res))
|
||||||
|
.catch((err) =>
|
||||||
|
this.form.addError(pinfo.identifier, {
|
||||||
|
title: pinfo.identifier,
|
||||||
|
message: err.message,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.finally(() => pinfo.set("loading", false));
|
||||||
|
}
|
||||||
|
|
||||||
getErrorFn(info) {
|
getErrorFn(info) {
|
||||||
const isPositiveInt = (value) => /^\d+$/.test(value);
|
const isPositiveInt = (value) => /^\d+$/.test(value);
|
||||||
const VALIDATORS = {
|
const VALIDATORS = {
|
||||||
|
@ -321,6 +356,10 @@ export default class ParamInputForm extends Component {
|
||||||
as |field|
|
as |field|
|
||||||
>
|
>
|
||||||
<info.component @field={{field}} @info={{info}} />
|
<info.component @field={{field}} @info={{info}} />
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@condition={{info.loading}}
|
||||||
|
@size="small"
|
||||||
|
/>
|
||||||
</form.Field>
|
</form.Field>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
@ -1,26 +1,20 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import CategoryChooser from "select-kit/components/category-chooser";
|
import CategoryChooser from "select-kit/components/category-chooser";
|
||||||
|
|
||||||
export default class GroupListInput extends Component {
|
export default class CategoryIdInput extends Component {
|
||||||
@tracked value;
|
// CategoryChooser will try to modify the value of value,
|
||||||
constructor() {
|
// triggering a setting-on-hash error. So we have to do the dirty work.
|
||||||
super(...arguments);
|
get data() {
|
||||||
this.value = this.args.field.value;
|
return {
|
||||||
}
|
value: this.args.field.value,
|
||||||
|
};
|
||||||
@action
|
|
||||||
update(id) {
|
|
||||||
this.value = id;
|
|
||||||
this.args.field.set(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<@field.Custom id={{@field.id}}>
|
<@field.Custom id={{@field.id}}>
|
||||||
<CategoryChooser
|
<CategoryChooser
|
||||||
@value={{this.value}}
|
@value={{this.data.value}}
|
||||||
@onChange={{this.update}}
|
@onChange={{@field.set}}
|
||||||
name={{@info.identifier}}
|
name={{@info.identifier}}
|
||||||
/>
|
/>
|
||||||
</@field.Custom>
|
</@field.Custom>
|
||||||
|
|
|
@ -359,11 +359,6 @@ export default class PluginsExplorerController extends Controller {
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
updateParams(identifier, value) {
|
|
||||||
this.selectedItem.set(`params.${identifier}`, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
updateSearch(value) {
|
updateSearch(value) {
|
||||||
this.search = value;
|
this.search = value;
|
||||||
|
|
|
@ -130,13 +130,6 @@ export default class GroupReportsShowController extends Controller {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is necessary with glimmer's one way data stream to get the child's
|
|
||||||
// changes of 'params' to bubble up.
|
|
||||||
@action
|
|
||||||
updateParams(identifier, value) {
|
|
||||||
this.set(`model.params.${identifier}`, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onRegisterApi(form) {
|
onRegisterApi(form) {
|
||||||
this.form = form;
|
this.form = form;
|
||||||
|
|
|
@ -60,7 +60,7 @@ const InputTestCases = [
|
||||||
tests: [
|
tests: [
|
||||||
{
|
{
|
||||||
input: null,
|
input: null,
|
||||||
data_null: undefined,
|
data_null: "",
|
||||||
error: ERRORS.REQUIRED,
|
error: ERRORS.REQUIRED,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -111,8 +111,9 @@ module("Data Explorer Plugin | Component | param-input", function (hooks) {
|
||||||
initialValues: config.initial
|
initialValues: config.initial
|
||||||
? { [testcase.type]: config.initial }
|
? { [testcase.type]: config.initial }
|
||||||
: {},
|
: {},
|
||||||
onRegisterApi: ({ submit }) => {
|
onRegisterApi: ({ submit, allNormalized }) => {
|
||||||
this.submit = submit;
|
this.submit = submit;
|
||||||
|
this.allNormalized = allNormalized;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -124,6 +125,8 @@ module("Data Explorer Plugin | Component | param-input", function (hooks) {
|
||||||
@onRegisterApi={{this.onRegisterApi}}
|
@onRegisterApi={{this.onRegisterApi}}
|
||||||
/>`);
|
/>`);
|
||||||
|
|
||||||
|
await this.allNormalized;
|
||||||
|
|
||||||
if (config.initial || config.default) {
|
if (config.initial || config.default) {
|
||||||
const data = await this.submit();
|
const data = await this.submit();
|
||||||
const val = config.initial || config.default;
|
const val = config.initial || config.default;
|
||||||
|
@ -201,4 +204,34 @@ module("Data Explorer Plugin | Component | param-input", function (hooks) {
|
||||||
await fillIn(`[name="string"]`, "");
|
await fillIn(`[name="string"]`, "");
|
||||||
assert.rejects(this.submit());
|
assert.rejects(this.submit());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("async normalizion", async function (assert) {
|
||||||
|
this.setProperties({
|
||||||
|
param_info: [
|
||||||
|
{
|
||||||
|
identifier: "category_id",
|
||||||
|
type: "category_id",
|
||||||
|
default: "support",
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initialValues: {},
|
||||||
|
onRegisterApi: (paramInputApi) => {
|
||||||
|
this.paramInputApi = paramInputApi;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await render(hbs`
|
||||||
|
<ParamInputForm
|
||||||
|
@initialValues={{this.initialValues}}
|
||||||
|
@paramInfo={{this.param_info}}
|
||||||
|
@onRegisterApi={{this.onRegisterApi}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
await this.paramInputApi.allNormalized;
|
||||||
|
|
||||||
|
this.paramInputApi.submit().then((res) => {
|
||||||
|
assert.strictEqual(res.category_id, "1003");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue