Refactore settng route

This commit is contained in:
Stanislav Romanov 2022-10-15 12:45:36 +03:00
parent 4a8582eba9
commit 92446c3196
13 changed files with 446 additions and 240 deletions

View file

@ -1,174 +1,177 @@
import { dasherize } from '@ember/string';
import { languages } from 'monaco-editor/esm/vs/editor/editor.api.js';
// import { dasherize } from '@ember/string';
// // import { languages } from 'monaco-editor/esm/vs/editor/editor.api.js';
const urlSettingsPrefix = 'https://docs.meilisearch.com/reference/api/settings';
// const urlSettingsPrefix = 'https://docs.meilisearch.com/reference/api/settings';
export const indexSettings = [
{
name: 'displayedAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'distinctAttribute',
schema: {
type: ['string', 'null'],
},
},
{
name: 'faceting',
schema: {
type: 'object',
properties: {
maxValuesPerFacet: {
type: 'integer',
},
},
required: ['maxValuesPerFacet'],
additionalProperties: false,
},
},
{
name: 'filterableAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'pagination',
schema: {
type: 'object',
properties: {
maxTotalHits: {
type: 'integer',
},
},
required: ['maxTotalHits'],
additionalProperties: false,
},
},
{
name: 'rankingRules',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'searchableAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'sortableAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'stopWords',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'synonyms',
schema: {
type: 'object',
additionalProperties: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
},
{
name: 'typoTolerance',
schema: {
type: 'object',
properties: {
enabled: {
type: 'boolean',
},
minWordSizeForTypos: {
type: 'object',
properties: {
oneTypo: { type: 'integer' },
twoTypos: { type: 'integer' },
},
additionalProperties: false,
},
disableOnWords: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
disableOnAttributes: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
additionalProperties: false,
},
},
].map((item) => {
item.uri = item.schema.$id = `${urlSettingsPrefix}/${dasherize(item.name)}`;
item.fileMatch = [item.uri];
// export const indexSettings = [
// {
// name: 'displayedAttributes',
// schema: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// {
// name: 'distinctAttribute',
// schema: {
// type: ['string', 'null'],
// },
// },
// {
// name: 'faceting',
// schema: {
// type: 'object',
// properties: {
// maxValuesPerFacet: {
// type: 'integer',
// },
// },
// required: ['maxValuesPerFacet'],
// additionalProperties: false,
// },
// },
// {
// name: 'filterableAttributes',
// schema: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// {
// name: 'pagination',
// schema: {
// type: 'object',
// properties: {
// maxTotalHits: {
// type: 'integer',
// },
// },
// required: ['maxTotalHits'],
// additionalProperties: false,
// },
// },
// {
// name: 'rankingRules',
// schema: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// {
// name: 'searchableAttributes',
// schema: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// {
// name: 'sortableAttributes',
// schema: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// {
// name: 'stopWords',
// schema: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// {
// name: 'synonyms',
// schema: {
// type: 'object',
// additionalProperties: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// },
// {
// name: 'typoTolerance',
// schema: {
// type: 'object',
// properties: {
// enabled: {
// type: 'boolean',
// },
// minWordSizeForTypos: {
// type: 'object',
// properties: {
// oneTypo: { type: 'integer' },
// twoTypos: { type: 'integer' },
// },
// additionalProperties: false,
// },
// disableOnWords: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// disableOnAttributes: {
// type: 'array',
// items: {
// type: 'string',
// },
// uniqueItems: true,
// },
// },
// additionalProperties: false,
// },
// },
// ].map((item) => {
// item.uri = item.schema.$id = `${urlSettingsPrefix}/${dasherize(item.name)}`;
// item.fileMatch = [item.uri];
return item;
});
// return item;
// });
export const allIndexSettings = {
uri: urlSettingsPrefix,
fileMatch: [urlSettingsPrefix],
schema: {
$id: urlSettingsPrefix,
type: 'object',
properties: indexSettings.reduce((properties, item) => {
properties[item.name] = {
$ref: item.schema.$id,
};
// const allSettings = {
// name: 'allSettings',
// uri: urlSettingsPrefix,
// fileMatch: [urlSettingsPrefix],
// schema: {
// $id: urlSettingsPrefix,
// type: 'object',
// properties: indexSettings.reduce((properties, item) => {
// properties[item.name] = {
// $ref: item.schema.$id,
// };
return properties;
}, {}),
additionalProperties: false,
},
};
// return properties;
// }, {}),
// additionalProperties: false,
// },
// };
// TODO: do it async, not in main stream
languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [...indexSettings, allIndexSettings],
});
// indexSettings.unshift(allSettings);
// // TODO: do it async, not in main stream
// // languages.json.jsonDefaults.setDiagnosticsOptions({
// // validate: true,
// // schemas: indexSettings,
// // });

View file

@ -9,8 +9,12 @@
<listbox.Button
class='inline-flex items-center px-3 py-2 border rounded cursor-pointer'
>
{{@listboxLabel}}
{{@value}}
{{#if (has-block)}}
{{yield @value}}
{{else}}
{{@listboxLabel}}
{{@value}}
{{/if}}
</listbox.Button>
<listbox.Options
class='absolute z-dropdown right-0 w-28 min-w-min mt-2 py-1 origin-top-right bg-base-7 border rounded shadow-sm outline-none'
@ -21,7 +25,11 @@
<div
class='flex items-center px-4 py-2 text-sm hover:bg-base-6 hover:text-base-1 cursor-pointer'
>
{{item}}
{{#if (has-block)}}
{{yield item}}
{{else}}
{{item}}
{{/if}}
</div>
</options.Option>
{{/each}}

View file

@ -1,10 +1,15 @@
<div class='mb-4'>
{{!-- TODO: add link to meilisearch's documentation --}}
<div
class='mb-4'
{{did-insert this.setValue}}
{{did-update this.setValue @setting}}
>
{{! TODO: add link to meilisearch's documentation }}
<Form::Element @label={{@label}} @errors={{array this.error}}>
<MonacoEditor
@value={{json-stringify this.value}}
@uri={{@setting.uri}}
@language='json'
@schema={{@setting}}
@sendValue={{this.save}}
@invoker={{this.invoker}}
@onDidChangeValidation={{this.onDidChangeValidation}}
@ -18,6 +23,6 @@
@size='sm'
@onClick={{fn this.invoker.invoke 'invokeSendValue'}}
/>
<Button @text='Cancel' @size='sm' @onClick={{this.setInitialValue}} />
<Button @text='Cancel' @size='sm' @onClick={{this.setValue}} />
<Button @text='Reset' @size='sm' @onClick={{this.reset}} />
</div>

View file

@ -14,8 +14,6 @@ export default class FormIndexSettingComponent extends Component {
super(...arguments);
this.invoker = new ActionInvoker();
this.setInitialValue();
}
@action
@ -32,7 +30,7 @@ export default class FormIndexSettingComponent extends Component {
}
@action
async setInitialValue() {
async setValue() {
this.value = await this.args.index.getSetting(this.args.setting.name);
this.error = null;
}

View file

@ -1,6 +1,6 @@
<div
{{did-insert this.initEditor}}
{{did-update this.updateValue @value}}
{{did-update this.updateValue @value @uri}}
class='border'
...attributes
></div>

View file

@ -1,6 +1,10 @@
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { Uri, editor } from 'monaco-editor/esm/vs/editor/editor.api.js';
import {
Uri,
editor,
languages,
} from 'monaco-editor/esm/vs/editor/editor.api.js';
import { registerDestructor } from '@ember/destroyable';
// This is needed because the SimpleWorker.js in monaco-editor has the following code:
@ -13,6 +17,28 @@ window.requirejs.s = {
},
};
function getModelUri(uri) {
return Uri.parse(uri);
}
function getModel(value, language, uri, schema) {
const modelUri = getModelUri(uri);
let model = editor.getModel(modelUri);
if (model === null) {
model = editor.createModel(value, language, modelUri);
if (language === 'json' && schema) {
languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [schema],
});
}
}
return model;
}
export default class MonacoEditorComponent extends Component {
constructor(owner, args) {
super(owner, args);
@ -20,21 +46,14 @@ export default class MonacoEditorComponent extends Component {
this.args.invoker?.subscribe(this);
}
get modelUri() {
return Uri.parse(this.args.uri);
}
@action
initEditor(el) {
let model = editor.getModel(this.modelUri);
if (model === null) {
model = editor.createModel(
this.args.value,
this.args.language,
this.modelUri
);
}
const model = getModel(
this.args.value,
this.args.language,
this.args.uri,
this.args.schema
);
this.editor = editor.create(el, {
model,
@ -50,13 +69,6 @@ export default class MonacoEditorComponent extends Component {
},
});
// Add onChange event
// if (this.args.onChange) {
// this.editor.onDidChangeModelContent(() => {
// this.args.onChange(this.editor.getValue());
// });
// }
// Autoresize height of element
const onDidContentSizeChangeHandler = this.editor.onDidContentSizeChange(
() => {
@ -71,22 +83,32 @@ export default class MonacoEditorComponent extends Component {
);
registerDestructor(this, onDidContentSizeChangeHandler.dispose);
const onDidChangeMarkersHandler = editor.onDidChangeMarkers((uris) => {
const error = uris.find(
(item) => item.toString() === this.modelUri.toString()
);
// const onDidChangeMarkersHandler = editor.onDidChangeMarkers((uris) => {
// const error = uris.find((item) => item.toString() === this.args.uri);
this.args.onDidChangeValidation?.(
error ? 'Error: the value is invalid' : false
);
});
registerDestructor(this, onDidChangeMarkersHandler.dispose);
// this.args.onDidChangeValidation?.(
// error ? 'Error: the value is invalid' : false
// );
// });
// registerDestructor(this, onDidChangeMarkersHandler.dispose);
}
@action
updateValue(el, [value]) {
if (value !== this.editor.getValue()) {
this.editor?.setValue(value);
updateValue() {
const model = this.editor.getModel();
if (model.uri.toString() !== this.args.uri) {
const newModel = getModel(
this.args.value,
this.args.language,
this.args.uri,
this.args.schema
);
this.editor.setModel(newModel);
model.dispose();
} else {
this.editor.setValue(this.args.value);
}
}
@ -96,7 +118,8 @@ export default class MonacoEditorComponent extends Component {
}
willDestroy() {
this.editor?.dispose();
this.editor.getModel()?.dispose();
this.editor.dispose();
super.willDestroy(...arguments);
}

View file

@ -10,9 +10,7 @@ Router.map(function () {
this.route('admin', function () {
this.route('indexes', function () {
this.route('item', { path: '/:index_uid' }, function () {
this.route('settings', function () {
this.route('specific');
});
this.route('settings', function () {});
this.route('info');
this.route('edit');
this.route('documents', function () {

View file

@ -1,3 +1,182 @@
import Route from '@ember/routing/route';
import { dasherize } from '@ember/string';
export default class AdminIndexesItemSettingsIndexRoute extends Route {}
const urlSettingsPrefix = 'https://docs.meilisearch.com/reference/api/settings';
export const settings = [
{
name: 'displayedAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'distinctAttribute',
schema: {
type: ['string', 'null'],
},
},
{
name: 'faceting',
schema: {
type: 'object',
properties: {
maxValuesPerFacet: {
type: 'integer',
},
},
required: ['maxValuesPerFacet'],
additionalProperties: false,
},
},
{
name: 'filterableAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'pagination',
schema: {
type: 'object',
properties: {
maxTotalHits: {
type: 'integer',
},
},
required: ['maxTotalHits'],
additionalProperties: false,
},
},
{
name: 'rankingRules',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'searchableAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'sortableAttributes',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'stopWords',
schema: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
{
name: 'synonyms',
schema: {
type: 'object',
additionalProperties: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
},
{
name: 'typoTolerance',
schema: {
type: 'object',
properties: {
enabled: {
type: 'boolean',
},
minWordSizeForTypos: {
type: 'object',
properties: {
oneTypo: { type: 'integer' },
twoTypos: { type: 'integer' },
},
additionalProperties: false,
},
disableOnWords: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
disableOnAttributes: {
type: 'array',
items: {
type: 'string',
},
uniqueItems: true,
},
},
additionalProperties: false,
},
},
].map((item) => {
item.uri = item.schema.$id = `${urlSettingsPrefix}/${dasherize(item.name)}`;
item.fileMatch = [item.uri];
return item;
});
const allSettings = {
uri: urlSettingsPrefix,
fileMatch: [urlSettingsPrefix],
schema: {
$id: urlSettingsPrefix,
type: 'object',
properties: settings.reduce((properties, item) => {
properties[item.name] = item.schema;
return properties;
}, {}),
additionalProperties: false,
},
};
settings.unshift(allSettings);
// TODO: do it async, not in main stream
// languages.json.jsonDefaults.setDiagnosticsOptions({
// validate: true,
// schemas: indexSettings,
// });
export default class AdminIndexesItemSettingsIndexRoute extends Route {
model() {
const index = this.modelFor('admin.indexes.item');
return { index, settings, currentSetting: settings[0] };
}
}

View file

@ -1,3 +0,0 @@
import Route from '@ember/routing/route';
export default class AdminIndexesItemSettingsSpecificRoute extends Route {}

View file

@ -1,6 +1,6 @@
<Tabs>
{{!-- <Tabs>
<Tab @route="admin.indexes.item.settings.index" @text="All" />
<Tab @route="admin.indexes.item.settings.specific" @text="Specific" />
</Tabs>
</Tabs> --}}
{{outlet}}

View file

@ -1 +1,14 @@
<IndexSetting @setting={{@model.allSettings}} @index={{@model}} />
<Form::Listbox
@value={{this.model.currentSetting}}
@values={{this.model.settings}}
@onChange={{set this.model 'currentSetting'}}
class="w-72"
as |setting|
>
{{if setting.name (namelize setting.name) 'All settings'}}
</Form::Listbox>
<IndexSetting
@setting={{this.model.currentSetting}}
@index={{this.model.index}}
/>

View file

@ -1,7 +0,0 @@
{{#each @model.settings as |setting|}}
<IndexSetting
@setting={{setting}}
@index={{@model}}
@label={{namelize setting.name}}
/>
{{/each}}

View file

@ -1,11 +0,0 @@
import { module, test } from 'qunit';
import { setupTest } from 'meiliadmin/tests/helpers';
module('Unit | Route | admin/indexes/item/settings/specific', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:admin/indexes/item/settings/specific');
assert.ok(route);
});
});