From d68eabd60a95d52680c20579b1feece0547a7678 Mon Sep 17 00:00:00 2001 From: yelinz Date: Fri, 22 Nov 2024 13:45:05 +0100 Subject: [PATCH 1/2] refactor(file): implement new alexandria search endpoint --- addon/adapters/file.js | 12 ----- addon/adapters/search-result.js | 15 +++++++ addon/components/document-view.hbs | 1 - addon/components/document-view.js | 45 ++++++++++++++----- .../{file-search.hbs => search-view.hbs} | 0 .../{file-search.js => search-view.js} | 17 +++---- addon/controllers/index.js | 2 +- addon/controllers/search.js | 1 + addon/models/search-result.js | 11 +++++ addon/serializers/search-result.js | 3 ++ addon/templates/search.hbs | 2 +- .../files/__root__/adapters/search-result.js | 5 +++ .../files/__root__/models/search-result.js | 1 + .../__root__/serializers/search-result.js | 1 + tests/dummy/app/adapters/search-result.js | 5 +++ tests/dummy/app/models/search-result.js | 1 + tests/dummy/app/serializers/search-result.js | 1 + tests/dummy/mirage/config.js | 9 ++++ tests/dummy/mirage/factories/file.js | 1 - tests/dummy/mirage/factories/search-result.js | 16 +++++++ tests/dummy/mirage/models/search-result.js | 6 +++ tests/unit/controllers/application-test.js | 2 +- 22 files changed, 117 insertions(+), 40 deletions(-) create mode 100644 addon/adapters/search-result.js rename addon/components/{file-search.hbs => search-view.hbs} (100%) rename addon/components/{file-search.js => search-view.js} (81%) create mode 100644 addon/models/search-result.js create mode 100644 addon/serializers/search-result.js create mode 100644 blueprints/ember-alexandria/files/__root__/adapters/search-result.js create mode 100644 blueprints/ember-alexandria/files/__root__/models/search-result.js create mode 100644 blueprints/ember-alexandria/files/__root__/serializers/search-result.js create mode 100644 tests/dummy/app/adapters/search-result.js create mode 100644 tests/dummy/app/models/search-result.js create mode 100644 tests/dummy/app/serializers/search-result.js create mode 100644 tests/dummy/mirage/factories/search-result.js create mode 100644 tests/dummy/mirage/models/search-result.js diff --git a/addon/adapters/file.js b/addon/adapters/file.js index d8ccd871..60476157 100644 --- a/addon/adapters/file.js +++ b/addon/adapters/file.js @@ -25,17 +25,5 @@ export default function (BaseClass) { return ajaxOptions; } - - // Overwrite and replicate the query function, - // because ember doesnt pass adapterOptions to urlForQuery - query(_, type, query, __, options) { - let url = this.buildURL(type.modelName, null, null, "query", query); - - if (options?.adapterOptions?.customEndpoint) { - url = `${this.buildURL()}/${options.adapterOptions.customEndpoint}`; - } - - return this.ajax(url, "GET", { data: query }); - } }; } diff --git a/addon/adapters/search-result.js b/addon/adapters/search-result.js new file mode 100644 index 00000000..02f492cf --- /dev/null +++ b/addon/adapters/search-result.js @@ -0,0 +1,15 @@ +export default function (BaseClass) { + return class SearchResultAdapter extends BaseClass { + // Overwrite and replicate the query function, + // because ember doesnt pass adapterOptions to urlForQuery + query(_, type, query, __, options) { + let url = this.buildURL(type.modelName, null, null, "query", query); + + if (options?.adapterOptions?.customEndpoint) { + url = `${this.buildURL()}/${options.adapterOptions.customEndpoint}`; + } + + return this.ajax(url, "GET", { data: query }); + } + }; +} diff --git a/addon/components/document-view.hbs b/addon/components/document-view.hbs index ff9f4293..b5fb5449 100644 --- a/addon/components/document-view.hbs +++ b/addon/components/document-view.hbs @@ -42,7 +42,6 @@ >
{{! List & Grid View }} {{#if this.listView}} diff --git a/addon/components/document-view.js b/addon/components/document-view.js index f680f7e0..3be71d2d 100644 --- a/addon/components/document-view.js +++ b/addon/components/document-view.js @@ -61,17 +61,44 @@ export default class DocumentViewComponent extends Component { @task *fetchDocuments() { - const documents = yield this.store.query("document", { - include: "category,files,tags", - filter: this.args.filters || {}, - sort: this.sort ? `${this.sortDirection}${this.sort}` : "", - }); + let documents = []; + const filter = this.args.filters || {}; + if (filter.query) { + filter.only_newest = true; + const searchResult = yield this.store.query( + "search-result", + { + include: "document,matched_file", + filter, + page: { number: 1 }, + }, + { + adapterOptions: { + customEndpoint: "search", + }, + }, + ); + + documents = searchResult.reduce((acc, result) => { + if (!acc.some((doc) => doc.id === result.document.id)) { + acc.push(result.document); + } + return acc; + }, []); + } else { + documents = yield this.store.query("document", { + include: "category,files,tags", + filter, + sort: this.sort ? `${this.sortDirection}${this.sort}` : "", + }); + } + + this.initialiseDocumentSelection(documents); return yield this.config.documentsPostProcess(documents); } - @task - *initialiseDocumentSelection() { + initialiseDocumentSelection(docs) { let docIds = []; if (this.router.externalRouter.currentRoute?.queryParams?.document) { docIds = decodeURIComponent( @@ -79,10 +106,6 @@ export default class DocumentViewComponent extends Component { ).split(","); } if (docIds.length !== 0) { - const docs = yield this.store.query("document", { - filter: this.args.filters || {}, - sort: this.sort ? `${this.sortDirection}${this.sort}` : "", - }); const selectedDocs = [...docs].filter((doc) => docIds.includes(doc.id)); selectedDocs.forEach((doc) => this.documents.selectDocument(doc)); } diff --git a/addon/components/file-search.hbs b/addon/components/search-view.hbs similarity index 100% rename from addon/components/file-search.hbs rename to addon/components/search-view.hbs diff --git a/addon/components/file-search.js b/addon/components/search-view.js similarity index 81% rename from addon/components/file-search.js rename to addon/components/search-view.js index ea040004..e9020ee6 100644 --- a/addon/components/file-search.js +++ b/addon/components/search-view.js @@ -5,7 +5,7 @@ import { tracked } from "@glimmer/tracking"; import { task } from "ember-concurrency"; import { trackedFunction } from "reactiveweb/function"; -export default class DocumentViewComponent extends Component { +export default class SearchViewComponent extends Component { @service store; @service("alexandria-config") config; @service("alexandria-documents") documents; @@ -22,10 +22,10 @@ export default class DocumentViewComponent extends Component { return []; } - const files = await this.store.query( - "file", + const search = await this.store.query( + "search-result", { - include: "document,renderings", + include: "document,matched_file", filter: this.args.filters || {}, page: { number: 1 }, }, @@ -36,14 +36,7 @@ export default class DocumentViewComponent extends Component { }, ); - const documents = Array.from( - new Map( - files.map((file) => [ - file.document.id, - this.store.peekRecord("document", file.document.id), - ]), - ).values(), - ); + const documents = search.map((result) => result.document); return await this.config.documentsPostProcess(documents); }); diff --git a/addon/controllers/index.js b/addon/controllers/index.js index b391ab28..dd39b741 100644 --- a/addon/controllers/index.js +++ b/addon/controllers/index.js @@ -28,7 +28,7 @@ export default class IndexController extends Controller { category: this.category, tags: this.tags.length ? this.tags.join(",") : undefined, marks: this.marks.length ? this.marks.join(",") : undefined, - search: this.search, + query: this.search, activeGroup: this.activeGroup, }; diff --git a/addon/controllers/search.js b/addon/controllers/search.js index 89de58d9..69db7d44 100644 --- a/addon/controllers/search.js +++ b/addon/controllers/search.js @@ -12,6 +12,7 @@ export default class SearchController extends Controller { get filters() { const filters = { + onlyNewest: true, query: this.search, }; diff --git a/addon/models/search-result.js b/addon/models/search-result.js new file mode 100644 index 00000000..e08aa6ac --- /dev/null +++ b/addon/models/search-result.js @@ -0,0 +1,11 @@ +import Model, { attr, belongsTo } from "@ember-data/model"; + +export default class SearchResultModel extends Model { + @attr searchRank; + @attr searchContext; + @attr fileName; + @attr documentName; + + @belongsTo("document", { inverse: null, async: false }) document; + @belongsTo("matched-file", { inverse: null, async: false }) file; +} diff --git a/addon/serializers/search-result.js b/addon/serializers/search-result.js new file mode 100644 index 00000000..fd21b6a8 --- /dev/null +++ b/addon/serializers/search-result.js @@ -0,0 +1,3 @@ +import { LocalizedSerializer } from "ember-localized-model"; + +export default class SearchResultSerializer extends LocalizedSerializer {} diff --git a/addon/templates/search.hbs b/addon/templates/search.hbs index 06d9f53c..9d0a61cd 100644 --- a/addon/templates/search.hbs +++ b/addon/templates/search.hbs @@ -1,7 +1,7 @@
- diff --git a/blueprints/ember-alexandria/files/__root__/adapters/search-result.js b/blueprints/ember-alexandria/files/__root__/adapters/search-result.js new file mode 100644 index 00000000..1529070d --- /dev/null +++ b/blueprints/ember-alexandria/files/__root__/adapters/search-result.js @@ -0,0 +1,5 @@ +import ApplicationAdapter from "./application"; + +import adapterFactory from "ember-alexandria/adapters/search-result"; + +export default adapterFactory(ApplicationAdapter); diff --git a/blueprints/ember-alexandria/files/__root__/models/search-result.js b/blueprints/ember-alexandria/files/__root__/models/search-result.js new file mode 100644 index 00000000..32f35a44 --- /dev/null +++ b/blueprints/ember-alexandria/files/__root__/models/search-result.js @@ -0,0 +1 @@ +export { default } from "ember-alexandria/models/search-result"; diff --git a/blueprints/ember-alexandria/files/__root__/serializers/search-result.js b/blueprints/ember-alexandria/files/__root__/serializers/search-result.js new file mode 100644 index 00000000..b2c52884 --- /dev/null +++ b/blueprints/ember-alexandria/files/__root__/serializers/search-result.js @@ -0,0 +1 @@ +export { default } from "ember-alexandria/serializers/search-result"; diff --git a/tests/dummy/app/adapters/search-result.js b/tests/dummy/app/adapters/search-result.js new file mode 100644 index 00000000..1529070d --- /dev/null +++ b/tests/dummy/app/adapters/search-result.js @@ -0,0 +1,5 @@ +import ApplicationAdapter from "./application"; + +import adapterFactory from "ember-alexandria/adapters/search-result"; + +export default adapterFactory(ApplicationAdapter); diff --git a/tests/dummy/app/models/search-result.js b/tests/dummy/app/models/search-result.js new file mode 100644 index 00000000..32f35a44 --- /dev/null +++ b/tests/dummy/app/models/search-result.js @@ -0,0 +1 @@ +export { default } from "ember-alexandria/models/search-result"; diff --git a/tests/dummy/app/serializers/search-result.js b/tests/dummy/app/serializers/search-result.js new file mode 100644 index 00000000..b2c52884 --- /dev/null +++ b/tests/dummy/app/serializers/search-result.js @@ -0,0 +1 @@ +export { default } from "ember-alexandria/serializers/search-result"; diff --git a/tests/dummy/mirage/config.js b/tests/dummy/mirage/config.js index 6050fbfa..4ad6a040 100644 --- a/tests/dummy/mirage/config.js +++ b/tests/dummy/mirage/config.js @@ -27,6 +27,15 @@ export default function makeServer(config) { }); this.get("/files/multi", () => new Response(200, {}, {})); + + this.get("/search", function (schema) { + const res = schema.searchResults.create({ + document: schema.documents.create(), + }); + const serialized = this.serialize(res); + serialized.data = [serialized.data]; + return serialized; + }); }, }); } diff --git a/tests/dummy/mirage/factories/file.js b/tests/dummy/mirage/factories/file.js index 02d74a21..e44b4624 100644 --- a/tests/dummy/mirage/factories/file.js +++ b/tests/dummy/mirage/factories/file.js @@ -8,5 +8,4 @@ export default Factory.extend({ name: () => faker.system.fileName(), variant: "original", downloadUrl: () => faker.internet.url(), - checksum: () => `sha256:${faker.git.commitSha({ length: 64 })}`, }); diff --git a/tests/dummy/mirage/factories/search-result.js b/tests/dummy/mirage/factories/search-result.js new file mode 100644 index 00000000..fee68932 --- /dev/null +++ b/tests/dummy/mirage/factories/search-result.js @@ -0,0 +1,16 @@ +import { faker } from "@faker-js/faker"; +import { Factory } from "miragejs"; + +export default Factory.extend({ + fileName: () => faker.system.fileName(), + documentName: () => faker.system.fileName(), + searchRank: () => faker.number.int(), + searchContext: () => faker.word.adjective(), + + afterCreate(result, server) { + result.update({ + document: server.schema.documents.create(), + matchedFile: server.schema.files.create(), + }); + }, +}); diff --git a/tests/dummy/mirage/models/search-result.js b/tests/dummy/mirage/models/search-result.js new file mode 100644 index 00000000..ed0327a9 --- /dev/null +++ b/tests/dummy/mirage/models/search-result.js @@ -0,0 +1,6 @@ +import { Model, belongsTo } from "miragejs"; + +export default Model.extend({ + document: belongsTo(), + matchedFile: belongsTo(), +}); diff --git a/tests/unit/controllers/application-test.js b/tests/unit/controllers/application-test.js index 64ceb86c..c3465bd9 100644 --- a/tests/unit/controllers/application-test.js +++ b/tests/unit/controllers/application-test.js @@ -19,7 +19,7 @@ module("Unit | Controller | index", function (hooks) { activeGroup: "group", category: 1, metainfo: JSON.stringify([{ key: "instance_id", value: "1" }]), - search: "test", + query: "test", tags: undefined, marks: undefined, }); From 276887e85063ad11fe09cba6e7535488b308b1da Mon Sep 17 00:00:00 2001 From: yelinz Date: Mon, 9 Dec 2024 13:49:32 +0100 Subject: [PATCH 2/2] feat(search): add clear button --- addon/components/search.hbs | 10 ++++++- addon/components/search.js | 10 ++++++- addon/controllers/index.js | 2 +- tests/integration/components/search-test.js | 31 +++++++++++++++++++-- tests/unit/controllers/application-test.js | 2 +- translations/de.yaml | 1 + translations/en.yaml | 1 + translations/it.yaml | 1 + 8 files changed, 51 insertions(+), 7 deletions(-) diff --git a/addon/components/search.hbs b/addon/components/search.hbs index 761322ee..8383677b 100644 --- a/addon/components/search.hbs +++ b/addon/components/search.hbs @@ -3,7 +3,7 @@ class="uk-background-default uk-search uk-search-default uk-width-1" {{on "submit" this.onSubmit}} > - + + {{!}} +
\ No newline at end of file diff --git a/addon/components/search.js b/addon/components/search.js index c47cdeda..08d11490 100644 --- a/addon/components/search.js +++ b/addon/components/search.js @@ -21,8 +21,16 @@ export default class SearchComponent extends Component { } this.router.transitionTo(this.router.currentRouteName, { - queryParams: { search: search || undefined, category: undefined }, + queryParams: { search: search || undefined }, }); }, ); + + @action + resetSearch(event) { + event.preventDefault(); + this.router.transitionTo(this.router.currentRouteName, { + queryParams: { search: undefined }, + }); + } } diff --git a/addon/controllers/index.js b/addon/controllers/index.js index dd39b741..edf1b0a9 100644 --- a/addon/controllers/index.js +++ b/addon/controllers/index.js @@ -25,7 +25,7 @@ export default class IndexController extends Controller { get documentFilters() { let filters = { - category: this.category, + categories: this.category, tags: this.tags.length ? this.tags.join(",") : undefined, marks: this.marks.length ? this.marks.join(",") : undefined, query: this.search, diff --git a/tests/integration/components/search-test.js b/tests/integration/components/search-test.js index 2d2a810d..5dda657c 100644 --- a/tests/integration/components/search-test.js +++ b/tests/integration/components/search-test.js @@ -1,4 +1,4 @@ -import { render, fillIn } from "@ember/test-helpers"; +import { render, fillIn, click } from "@ember/test-helpers"; import { setupRenderingTest } from "dummy/tests/helpers"; import { hbs } from "ember-cli-htmlbars"; import { module, test } from "qunit"; @@ -7,7 +7,7 @@ import { fake, stub } from "sinon"; module("Integration | Component | search", function (hooks) { setupRenderingTest(hooks); - test("it renders", async function (assert) { + test("it searches", async function (assert) { const router = this.engine.lookup("service:router"); stub(router, "currentRouteName").get(() => null); @@ -25,7 +25,32 @@ module("Integration | Component | search", function (hooks) { [ null, { - queryParams: { search: "new search", category: undefined }, + queryParams: { search: "new search" }, + }, + ], + "transitionTo was called with the correct arguments", + ); + }); + + test("it clears search", async function (assert) { + const router = this.engine.lookup("service:router"); + + stub(router, "currentRouteName").get(() => null); + router.transitionTo = fake(); + + await render(hbs``, { owner: this.engine }); + + assert.dom("[data-test-search-input]").hasValue("test"); + + await click("[data-test-search-clear]"); + + assert.ok(router.transitionTo.called, "transitionTo was called"); + assert.deepEqual( + router.transitionTo.firstCall.args, + [ + null, + { + queryParams: { search: undefined }, }, ], "transitionTo was called with the correct arguments", diff --git a/tests/unit/controllers/application-test.js b/tests/unit/controllers/application-test.js index c3465bd9..94fdccf3 100644 --- a/tests/unit/controllers/application-test.js +++ b/tests/unit/controllers/application-test.js @@ -17,7 +17,7 @@ module("Unit | Controller | index", function (hooks) { assert.deepEqual(controller.documentFilters, { activeGroup: "group", - category: 1, + categories: 1, metainfo: JSON.stringify([{ key: "instance_id", value: "1" }]), query: "test", tags: undefined, diff --git a/translations/de.yaml b/translations/de.yaml index 00ec24b5..0530c80c 100644 --- a/translations/de.yaml +++ b/translations/de.yaml @@ -1,6 +1,7 @@ alexandria: loading: "Lädt..." search: "Suchen..." + search-clear: "Suche löschen" nothing-found: "Wir haben nichts gefunden..." delete: "Löschen" upload-file: "Datei hochladen" diff --git a/translations/en.yaml b/translations/en.yaml index a20f19d5..5237663b 100644 --- a/translations/en.yaml +++ b/translations/en.yaml @@ -1,6 +1,7 @@ alexandria: loading: "Loading..." search: "Search..." + search-clear: "Clear search" nothing-found: "We found nothing..." delete: "Delete" upload-file: "Upload file" diff --git a/translations/it.yaml b/translations/it.yaml index 9f5ddf5a..ae7bff82 100644 --- a/translations/it.yaml +++ b/translations/it.yaml @@ -1,6 +1,7 @@ alexandria: loading: "Sto caricando i risultati..." search: "Cerca..." + search-clear: "Ricerca chiara" nothing-found: "Nessun risultato trovato..." delete: "Elimina" upload-file: "Carica file"