strapi-plugin-meilisearch 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,7 +19,6 @@
19
19
  <a href="https://github.com/meilisearch/strapi-plugin-meilisearch/actions"><img src="https://github.com/meilisearch/strapi-plugin-meilisearch/workflows/Tests/badge.svg" alt="Tests"></a>
20
20
  <a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/styled_with-prettier-ff69b4.svg" alt="Prettier"></a>
21
21
  <a href="https://github.com/meilisearch/strapi-plugin-meilisearch/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-informational" alt="License"></a>
22
- <a href="https://ms-bors.herokuapp.com/repositories/7"><img src="https://bors.tech/images/badge_small.svg" alt="Bors enabled"></a>
23
22
  </p>
24
23
 
25
24
  <p align="center">⚡ The Meilisearch plugin for Strapi</p>
@@ -614,7 +613,7 @@ yarn playground:build # Build the playground
614
613
  yarn playground:dev # Start the development server
615
614
  ```
616
615
 
617
- This command will install the required dependencies and launch the app in development mode. You should be able to reach it on the [port 8000 of your localhost](http://localhost:8000/admin/).
616
+ This command will install the required dependencies and launch the app in development mode. You should be able to reach it on the [port 1337 of your localhost](http://localhost:1337/admin/).
618
617
 
619
618
  ## 🤖 Compatibility with Meilisearch and Strapi
620
619
 
@@ -4,7 +4,7 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
5
  const reactRouterDom = require("react-router-dom");
6
6
  const admin = require("@strapi/strapi/admin");
7
- const index = require("./index-MhpvuJMV.js");
7
+ const index = require("./index-ClWytIVb.js");
8
8
  const designSystem = require("@strapi/design-system");
9
9
  function _interopNamespace(e) {
10
10
  if (e && e.__esModule) return e;
@@ -456,6 +456,21 @@ function useAlert() {
456
456
  blockTransition = true,
457
457
  title
458
458
  }) {
459
+ const docsLabel = i18n(
460
+ "plugin.message.error.meilisearchDocsLink",
461
+ "See more"
462
+ );
463
+ let normalizedLink = link;
464
+ if (link && link.label && typeof link.label === "object") {
465
+ const { id, defaultMessage } = link.label;
466
+ const normalizedId = id?.replace(new RegExp(`^${index.pluginId}\\.`), "") || id;
467
+ normalizedLink = {
468
+ ...link,
469
+ label: i18n(normalizedId, defaultMessage)
470
+ };
471
+ } else if (link && link.url && !link.label) {
472
+ normalizedLink = { ...link, label: docsLabel };
473
+ }
459
474
  toggleNotification({
460
475
  // optional
461
476
  title,
@@ -465,7 +480,7 @@ function useAlert() {
465
480
  // required
466
481
  message: i18n("notification.meilisearch.message", message),
467
482
  // optional
468
- link,
483
+ link: normalizedLink,
469
484
  // optional: default = false
470
485
  blockTransition,
471
486
  // optional
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { useState, useEffect, memo } from "react";
4
4
  import { Routes, Route } from "react-router-dom";
5
5
  import { useNotification, useFetchClient, useRBAC, private_useAutoReloadOverlayBlocker, private_AutoReloadOverlayBlockerProvider, Page, Layouts, BackButton } from "@strapi/strapi/admin";
6
- import { p as pluginId, P as PERMISSIONS } from "./index-EQZ5QLS6.mjs";
6
+ import { p as pluginId, P as PERMISSIONS } from "./index-CpAkqqV6.mjs";
7
7
  import { Tr, Td, Checkbox, Typography, Flex, Box, Button, Thead, Th, VisuallyHidden, Table, Tbody, Field, Link, Tabs } from "@strapi/design-system";
8
8
  var __assign = function() {
9
9
  __assign = Object.assign || function __assign2(t) {
@@ -437,6 +437,21 @@ function useAlert() {
437
437
  blockTransition = true,
438
438
  title
439
439
  }) {
440
+ const docsLabel = i18n(
441
+ "plugin.message.error.meilisearchDocsLink",
442
+ "See more"
443
+ );
444
+ let normalizedLink = link;
445
+ if (link && link.label && typeof link.label === "object") {
446
+ const { id, defaultMessage } = link.label;
447
+ const normalizedId = id?.replace(new RegExp(`^${pluginId}\\.`), "") || id;
448
+ normalizedLink = {
449
+ ...link,
450
+ label: i18n(normalizedId, defaultMessage)
451
+ };
452
+ } else if (link && link.url && !link.label) {
453
+ normalizedLink = { ...link, label: docsLabel };
454
+ }
440
455
  toggleNotification({
441
456
  // optional
442
457
  title,
@@ -446,7 +461,7 @@ function useAlert() {
446
461
  // required
447
462
  message: i18n("notification.meilisearch.message", message),
448
463
  // optional
449
- link,
464
+ link: normalizedLink,
450
465
  // optional: default = false
451
466
  blockTransition,
452
467
  // optional
@@ -31,7 +31,8 @@ const en = {
31
31
  "plugin.message.success.credentials": "Credentials successfully updated!",
32
32
  "plugin.message.something": "Something occurred in Meilisearch",
33
33
  "plugin.message.forbidden.title": "Forbidden",
34
- "plugin.message.forbidden.description": "You do not have permission to do this action"
34
+ "plugin.message.forbidden.description": "You do not have permission to do this action",
35
+ "plugin.message.error.meilisearchDocsLink": "See more"
35
36
  };
36
37
  export {
37
38
  en as default
@@ -33,6 +33,7 @@ const en = {
33
33
  "plugin.message.success.credentials": "Credentials successfully updated!",
34
34
  "plugin.message.something": "Something occurred in Meilisearch",
35
35
  "plugin.message.forbidden.title": "Forbidden",
36
- "plugin.message.forbidden.description": "You do not have permission to do this action"
36
+ "plugin.message.forbidden.description": "You do not have permission to do this action",
37
+ "plugin.message.error.meilisearchDocsLink": "See more"
37
38
  };
38
39
  exports.default = en;
@@ -33,6 +33,7 @@ const es = {
33
33
  "plugin.message.success.credentials": "¡Credenciales actualizadas con éxito!",
34
34
  "plugin.message.something": "Algo ha ocurrido en Meilisearch",
35
35
  "plugin.message.forbidden.title": "Prohibido",
36
- "plugin.message.forbidden.description": "No tiene permiso para realizar esta acción"
36
+ "plugin.message.forbidden.description": "No tiene permiso para realizar esta acción",
37
+ "plugin.message.error.meilisearchDocsLink": "Ver más"
37
38
  };
38
39
  exports.default = es;
@@ -31,7 +31,8 @@ const es = {
31
31
  "plugin.message.success.credentials": "¡Credenciales actualizadas con éxito!",
32
32
  "plugin.message.something": "Algo ha ocurrido en Meilisearch",
33
33
  "plugin.message.forbidden.title": "Prohibido",
34
- "plugin.message.forbidden.description": "No tiene permiso para realizar esta acción"
34
+ "plugin.message.forbidden.description": "No tiene permiso para realizar esta acción",
35
+ "plugin.message.error.meilisearchDocsLink": "Ver más"
35
36
  };
36
37
  export {
37
38
  es as default
@@ -19,21 +19,23 @@ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
19
19
  });
20
20
  };
21
21
  const name$1 = "strapi-plugin-meilisearch";
22
- const version = "0.13.3";
22
+ const version = "0.14.0";
23
23
  const description = "Synchronise and search in your Strapi content-types with Meilisearch";
24
24
  const scripts = {
25
25
  build: "strapi-plugin build",
26
26
  verify: "strapi-plugin verify",
27
27
  watch: "strapi-plugin watch",
28
28
  "watch:link": "strapi-plugin watch:link",
29
- "playground:dev": "yarn --cwd ./playground && yarn --cwd ./playground dev",
29
+ "playground:setup": "yarn --cwd ./playground setup",
30
+ "playground:dev": "yarn --cwd ./playground dev",
30
31
  "playground:build": "yarn --cwd ./playground && yarn --cwd ./playground build",
31
32
  "playground:ci": "yarn --cwd ./playground ci",
32
33
  style: "eslint --ext .js,.test.js .",
33
34
  "style:fix": "eslint --ext .js,.test.js . --fix",
34
35
  test: "jest --testPathPattern=tests",
35
36
  "test:watch": "jest --watch --testPathPattern=tests --verbose",
36
- "test:e2e:watch": 'concurrently --kill-others -s first "NODE_ENV=test yarn playground:dev" "cypress open --env env=watch"'
37
+ "test:e2e": 'concurrently --kill-others -s first "yarn playground:dev" "cypress run"',
38
+ "test:integrations": "jest -c jest.integration.config.js"
37
39
  };
38
40
  const strapi = {
39
41
  name: "meilisearch",
@@ -81,7 +83,7 @@ const devDependencies = {
81
83
  "@types/jest": "^29.5.12",
82
84
  "babel-jest": "^29.7.0",
83
85
  concurrently: "^8.2.2",
84
- cypress: "^14.0.1",
86
+ cypress: "^15.3.0",
85
87
  eslint: "^8.2.0",
86
88
  "eslint-config-prettier": "^9.1.0",
87
89
  "eslint-plugin-cypress": "^2.12.1",
@@ -211,7 +213,7 @@ const index = {
211
213
  defaultMessage: name
212
214
  },
213
215
  Component: async () => {
214
- const { App } = await Promise.resolve().then(() => require("./App-Bx6jJvL9.js"));
216
+ const { App } = await Promise.resolve().then(() => require("./App-Dn6EGme8.js"));
215
217
  return App;
216
218
  },
217
219
  permissions: PERMISSIONS.main
@@ -220,7 +222,7 @@ const index = {
220
222
  async registerTrads({ locales }) {
221
223
  const importedTranslations = await Promise.all(
222
224
  locales.map((locale) => {
223
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-CByedxRT.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-DA_jpbS7.js")) }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
225
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-CQvxkk9b.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-D61ltG8N.js")) }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
224
226
  return {
225
227
  data: getTranslation(data),
226
228
  locale
@@ -18,21 +18,23 @@ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
18
18
  });
19
19
  };
20
20
  const name$1 = "strapi-plugin-meilisearch";
21
- const version = "0.13.3";
21
+ const version = "0.14.0";
22
22
  const description = "Synchronise and search in your Strapi content-types with Meilisearch";
23
23
  const scripts = {
24
24
  build: "strapi-plugin build",
25
25
  verify: "strapi-plugin verify",
26
26
  watch: "strapi-plugin watch",
27
27
  "watch:link": "strapi-plugin watch:link",
28
- "playground:dev": "yarn --cwd ./playground && yarn --cwd ./playground dev",
28
+ "playground:setup": "yarn --cwd ./playground setup",
29
+ "playground:dev": "yarn --cwd ./playground dev",
29
30
  "playground:build": "yarn --cwd ./playground && yarn --cwd ./playground build",
30
31
  "playground:ci": "yarn --cwd ./playground ci",
31
32
  style: "eslint --ext .js,.test.js .",
32
33
  "style:fix": "eslint --ext .js,.test.js . --fix",
33
34
  test: "jest --testPathPattern=tests",
34
35
  "test:watch": "jest --watch --testPathPattern=tests --verbose",
35
- "test:e2e:watch": 'concurrently --kill-others -s first "NODE_ENV=test yarn playground:dev" "cypress open --env env=watch"'
36
+ "test:e2e": 'concurrently --kill-others -s first "yarn playground:dev" "cypress run"',
37
+ "test:integrations": "jest -c jest.integration.config.js"
36
38
  };
37
39
  const strapi = {
38
40
  name: "meilisearch",
@@ -80,7 +82,7 @@ const devDependencies = {
80
82
  "@types/jest": "^29.5.12",
81
83
  "babel-jest": "^29.7.0",
82
84
  concurrently: "^8.2.2",
83
- cypress: "^14.0.1",
85
+ cypress: "^15.3.0",
84
86
  eslint: "^8.2.0",
85
87
  "eslint-config-prettier": "^9.1.0",
86
88
  "eslint-plugin-cypress": "^2.12.1",
@@ -210,7 +212,7 @@ const index = {
210
212
  defaultMessage: name
211
213
  },
212
214
  Component: async () => {
213
- const { App } = await import("./App-B5q9mEPQ.mjs");
215
+ const { App } = await import("./App-d1i42zIj.mjs");
214
216
  return App;
215
217
  },
216
218
  permissions: PERMISSIONS.main
@@ -219,7 +221,7 @@ const index = {
219
221
  async registerTrads({ locales }) {
220
222
  const importedTranslations = await Promise.all(
221
223
  locales.map((locale) => {
222
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-D4dIehJl.mjs"), "./translations/es.json": () => import("./es-B-4TrEJr.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
224
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-BKKKU9na.mjs"), "./translations/es.json": () => import("./es-DvPal5Gh.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
223
225
  return {
224
226
  data: getTranslation(data),
225
227
  locale
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-MhpvuJMV.js");
2
+ const index = require("../_chunks/index-ClWytIVb.js");
3
3
  module.exports = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "../_chunks/index-EQZ5QLS6.mjs";
1
+ import { i } from "../_chunks/index-CpAkqqV6.mjs";
2
2
  export {
3
3
  i as default
4
4
  };
@@ -90,7 +90,84 @@ const bootstrap = async ({ strapi: strapi2 }) => {
90
90
  };
91
91
  const destroy = () => {
92
92
  };
93
- const register = () => {
93
+ async function registerDocumentMiddleware({ strapi: strapi2 }) {
94
+ if (!strapi2?.documents || typeof strapi2.documents.use !== "function") {
95
+ return;
96
+ }
97
+ strapi2.documents.use(async (ctx, next) => {
98
+ let result;
99
+ try {
100
+ const plugin = strapi2.plugin("meilisearch");
101
+ const store2 = plugin.service("store");
102
+ const meilisearch2 = plugin.service("meilisearch");
103
+ const contentTypeService2 = plugin.service("contentType");
104
+ const listenedContentTypes2 = await store2.getListenedContentTypes();
105
+ if (!listenedContentTypes2.includes(ctx.uid)) {
106
+ return next();
107
+ }
108
+ const contentType2 = ctx.uid;
109
+ const updateActions = ["create", "update", "publish"];
110
+ const deleteActions = [
111
+ "delete",
112
+ "deleteMany",
113
+ "deleteOne",
114
+ "deleteDocument",
115
+ "unpublish",
116
+ "discardDraft"
117
+ ];
118
+ const preDeleteDocumentId = deleteActions.includes(ctx.action) && (ctx?.params?.documentId ?? ctx?.params?.id) ? ctx.params.documentId ?? ctx.params.id : null;
119
+ const preDeleteEntry = preDeleteDocumentId != null ? await contentTypeService2.getEntry({
120
+ contentType: contentType2,
121
+ documentId: preDeleteDocumentId,
122
+ entriesQuery: {}
123
+ }) : null;
124
+ result = await next();
125
+ const id = result?.id ?? preDeleteEntry?.id;
126
+ const documentId = result?.documentId ?? preDeleteEntry?.documentId ?? preDeleteDocumentId ?? null;
127
+ if (updateActions.includes(ctx.action) && documentId != null) {
128
+ const entriesQuery = meilisearch2.entriesQuery({ contentType: contentType2 });
129
+ const entry = await contentTypeService2.getEntry({
130
+ contentType: contentType2,
131
+ documentId,
132
+ entriesQuery: { ...entriesQuery }
133
+ });
134
+ if (entry) {
135
+ await meilisearch2.updateEntriesInMeilisearch({
136
+ contentType: contentType2,
137
+ entries: [{ ...entry, id, documentId }]
138
+ });
139
+ } else if (id != null) {
140
+ await meilisearch2.deleteEntriesFromMeiliSearch({
141
+ contentType: contentType2,
142
+ entriesId: [id]
143
+ });
144
+ }
145
+ } else if (deleteActions.includes(ctx.action)) {
146
+ if (id != null) {
147
+ strapi2.log.info(
148
+ `Meilisearch document middleware deleting ${contentType2} ids=${id}`
149
+ );
150
+ await meilisearch2.deleteEntriesFromMeiliSearch({
151
+ contentType: contentType2,
152
+ entriesId: [id]
153
+ });
154
+ } else {
155
+ strapi2.log.info(
156
+ `Meilisearch document middleware could not delete ${contentType2} for action ${ctx.action}: missing id`
157
+ );
158
+ }
159
+ }
160
+ return result;
161
+ } catch (error2) {
162
+ strapi2.log.error(
163
+ `Meilisearch document middleware error: ${error2.message}`
164
+ );
165
+ return result;
166
+ }
167
+ });
168
+ }
169
+ const register = async ({ strapi: strapi2 }) => {
170
+ await registerDocumentMiddleware({ strapi: strapi2 });
94
171
  };
95
172
  function isObject(elem) {
96
173
  return typeof elem === "object" && !Array.isArray(elem) && elem !== null;
@@ -724,20 +801,23 @@ const contentTypeService = ({ strapi: strapi2 }) => ({
724
801
  * @param {object} [options.entriesQuery={}] - Options to apply when fetching entries from the database.
725
802
  * @param {string | string[]} [options.entriesQuery.fields] - Fields present in the returned entry.
726
803
  * @param {object} [options.entriesQuery.populate] - Relations, components and dynamic zones to populate.
727
- * @param {object} [options.entriesQuery.locale] - When using internalization, the language to fetch.
804
+ * @param {string} [options.entriesQuery.status] - Publication state: draft or published.
805
+ * @param {string} [options.entriesQuery.locale] - When using internationalization (i18n), the language to fetch.
728
806
  * @param {string} options.contentType - Content type.
729
807
  *
730
808
  * @returns {Promise<object>} - Entries.
731
809
  */
732
810
  async getEntry({ contentType: contentType2, documentId, entriesQuery = {} }) {
733
- const { populate = "*", fields = "*" } = entriesQuery;
811
+ const {
812
+ populate = "*",
813
+ fields = "*",
814
+ status = "published",
815
+ locale
816
+ } = entriesQuery;
817
+ const queryOptions = { documentId, fields, populate, status, locale };
734
818
  const contentTypeUid = this.getContentTypeUid({ contentType: contentType2 });
735
819
  if (contentTypeUid === void 0) return {};
736
- const entry = await strapi2.documents(contentTypeUid).findOne({
737
- documentId,
738
- fields,
739
- populate
740
- });
820
+ const entry = await strapi2.documents(contentTypeUid).findOne(queryOptions);
741
821
  if (entry == null) {
742
822
  strapi2.log.error(
743
823
  `Could not find entry with id ${documentId} in ${contentType2}`
@@ -758,7 +838,7 @@ const contentTypeService = ({ strapi: strapi2 }) => ({
758
838
  * @param {object} [options.populate] - Relations, components and dynamic zones to populate.
759
839
  * @param {object} [options.status] - Publication state: draft or published.
760
840
  * @param {string} options.contentType - Content type.
761
- * @param {string} [options.locale] - When using internalization, the language to fetch.
841
+ * @param {string} [options.locale] - When using internationalization (i18n), the language to fetch.
762
842
  *
763
843
  * @returns {Promise<object[]>} - Entries.
764
844
  */
@@ -782,11 +862,9 @@ const contentTypeService = ({ strapi: strapi2 }) => ({
782
862
  filters,
783
863
  sort,
784
864
  populate,
785
- status
865
+ status,
866
+ locale
786
867
  };
787
- if (locale) {
788
- queryOptions.locale = locale;
789
- }
790
868
  const entries = await strapi2.documents(contentTypeUid).findMany(queryOptions);
791
869
  if (entries && !Array.isArray(entries)) return [entries];
792
870
  return entries || [];
@@ -1341,7 +1419,9 @@ const configurationService = ({ strapi: strapi2 }) => {
1341
1419
  if (entriesQuery.status === "draft") {
1342
1420
  return entries;
1343
1421
  } else {
1344
- return entries.filter((entry) => !(entry?.publishedAt === null));
1422
+ return entries.filter(
1423
+ (entry) => !(entry?.publishedAt === void 0 || entry?.publishedAt === null)
1424
+ );
1345
1425
  }
1346
1426
  },
1347
1427
  /**
@@ -1369,7 +1449,7 @@ const configurationService = ({ strapi: strapi2 }) => {
1369
1449
  }
1370
1450
  };
1371
1451
  };
1372
- const version = "0.13.3";
1452
+ const version = "0.14.0";
1373
1453
  const Meilisearch = (config2) => {
1374
1454
  return new meilisearch$1.MeiliSearch({
1375
1455
  ...config2,
@@ -1809,119 +1889,11 @@ const lifecycleService = ({ strapi: strapi2 }) => {
1809
1889
  const contentTypeService2 = strapi2.plugin("meilisearch").service("contentType");
1810
1890
  const store2 = strapi2.plugin("meilisearch").service("store");
1811
1891
  return {
1812
- /**
1813
- * Subscribe the content type to all required lifecycles
1814
- *
1815
- * @param {object} options
1816
- * @param {string} options.contentType
1817
- *
1818
- * @returns {Promise<object>}
1819
- */
1820
1892
  async subscribeContentType({ contentType: contentType2 }) {
1821
1893
  const contentTypeUid = contentTypeService2.getContentTypeUid({
1822
1894
  contentType: contentType2
1823
1895
  });
1824
- await strapi2.db.lifecycles.subscribe({
1825
- models: [contentTypeUid],
1826
- async afterCreate(event) {
1827
- const { result } = event;
1828
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1829
- await meilisearch2.addEntriesToMeilisearch({
1830
- contentType: contentTypeUid,
1831
- entries: [result]
1832
- }).catch((e) => {
1833
- strapi2.log.error(
1834
- `Meilisearch could not add entry with id: ${result.id}: ${e.message}`
1835
- );
1836
- });
1837
- },
1838
- async afterCreateMany(event) {
1839
- const { result } = event;
1840
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1841
- const nbrEntries = result.count;
1842
- const ids = result.ids;
1843
- const entries = [];
1844
- const BATCH_SIZE = 500;
1845
- for (let pos = 0; pos < nbrEntries; pos += BATCH_SIZE) {
1846
- const batch = await contentTypeService2.getEntries({
1847
- contentType: contentTypeUid,
1848
- start: pos,
1849
- limit: BATCH_SIZE,
1850
- filters: {
1851
- id: {
1852
- $in: ids
1853
- }
1854
- }
1855
- });
1856
- entries.push(...batch);
1857
- }
1858
- meilisearch2.updateEntriesInMeilisearch({
1859
- contentType: contentTypeUid,
1860
- entries
1861
- }).catch((e) => {
1862
- strapi2.log.error(
1863
- `Meilisearch could not update the entries: ${e.message}`
1864
- );
1865
- });
1866
- },
1867
- async afterUpdate(event) {
1868
- const { result } = event;
1869
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1870
- await meilisearch2.updateEntriesInMeilisearch({
1871
- contentType: contentTypeUid,
1872
- entries: [result]
1873
- }).catch((e) => {
1874
- strapi2.log.error(
1875
- `Meilisearch could not update entry with id: ${result.id}: ${e.message}`
1876
- );
1877
- });
1878
- },
1879
- async afterUpdateMany(event) {
1880
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1881
- const nbrEntries = await contentTypeService2.numberOfEntries({
1882
- contentType: contentTypeUid,
1883
- filters: event.params.where
1884
- });
1885
- const entries = [];
1886
- const BATCH_SIZE = 500;
1887
- for (let pos = 0; pos < nbrEntries; pos += BATCH_SIZE) {
1888
- const batch = await contentTypeService2.getEntries({
1889
- contentType: contentTypeUid,
1890
- filters: event.params.where,
1891
- start: pos,
1892
- limit: BATCH_SIZE
1893
- });
1894
- entries.push(...batch);
1895
- }
1896
- meilisearch2.updateEntriesInMeilisearch({
1897
- contentType: contentTypeUid,
1898
- entries
1899
- }).catch((e) => {
1900
- strapi2.log.error(
1901
- `Meilisearch could not update the entries: ${e.message}`
1902
- );
1903
- });
1904
- },
1905
- async afterDelete(event) {
1906
- const { result, params } = event;
1907
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1908
- let entriesId = [];
1909
- if (params?.where?.$and && params?.where?.$and[0] && params?.where?.$and[0].id?.$in)
1910
- entriesId = params?.where?.$and[0].id.$in;
1911
- else entriesId = [result.id];
1912
- meilisearch2.deleteEntriesFromMeiliSearch({
1913
- contentType: contentTypeUid,
1914
- entriesId
1915
- }).catch((e) => {
1916
- strapi2.log.error(
1917
- `Meilisearch could not delete entry with id: ${result.id}: ${e.message}`
1918
- );
1919
- });
1920
- },
1921
- async afterDeleteMany(event) {
1922
- this.afterDelete(event);
1923
- }
1924
- });
1896
+ if (!contentTypeUid) return;
1925
1897
  return store2.addListenedContentType({
1926
1898
  contentType: contentTypeUid
1927
1899
  });
@@ -1943,13 +1915,7 @@ const error$1 = ({ strapi: strapi2 }) => {
1943
1915
  return {
1944
1916
  error: {
1945
1917
  message: e.message,
1946
- link: {
1947
- url: e.link || "https://www.meilisearch.com/docs",
1948
- label: {
1949
- id: "notification.meilisearch",
1950
- defaultMessage: "See more"
1951
- }
1952
- }
1918
+ link: e.link ? { url: e.link } : { url: "https://www.meilisearch.com/docs" }
1953
1919
  }
1954
1920
  };
1955
1921
  } else if (e.type === "MeiliSearchCommunicationError") {
@@ -89,7 +89,84 @@ const bootstrap = async ({ strapi: strapi2 }) => {
89
89
  };
90
90
  const destroy = () => {
91
91
  };
92
- const register = () => {
92
+ async function registerDocumentMiddleware({ strapi: strapi2 }) {
93
+ if (!strapi2?.documents || typeof strapi2.documents.use !== "function") {
94
+ return;
95
+ }
96
+ strapi2.documents.use(async (ctx, next) => {
97
+ let result;
98
+ try {
99
+ const plugin = strapi2.plugin("meilisearch");
100
+ const store2 = plugin.service("store");
101
+ const meilisearch2 = plugin.service("meilisearch");
102
+ const contentTypeService2 = plugin.service("contentType");
103
+ const listenedContentTypes2 = await store2.getListenedContentTypes();
104
+ if (!listenedContentTypes2.includes(ctx.uid)) {
105
+ return next();
106
+ }
107
+ const contentType2 = ctx.uid;
108
+ const updateActions = ["create", "update", "publish"];
109
+ const deleteActions = [
110
+ "delete",
111
+ "deleteMany",
112
+ "deleteOne",
113
+ "deleteDocument",
114
+ "unpublish",
115
+ "discardDraft"
116
+ ];
117
+ const preDeleteDocumentId = deleteActions.includes(ctx.action) && (ctx?.params?.documentId ?? ctx?.params?.id) ? ctx.params.documentId ?? ctx.params.id : null;
118
+ const preDeleteEntry = preDeleteDocumentId != null ? await contentTypeService2.getEntry({
119
+ contentType: contentType2,
120
+ documentId: preDeleteDocumentId,
121
+ entriesQuery: {}
122
+ }) : null;
123
+ result = await next();
124
+ const id = result?.id ?? preDeleteEntry?.id;
125
+ const documentId = result?.documentId ?? preDeleteEntry?.documentId ?? preDeleteDocumentId ?? null;
126
+ if (updateActions.includes(ctx.action) && documentId != null) {
127
+ const entriesQuery = meilisearch2.entriesQuery({ contentType: contentType2 });
128
+ const entry = await contentTypeService2.getEntry({
129
+ contentType: contentType2,
130
+ documentId,
131
+ entriesQuery: { ...entriesQuery }
132
+ });
133
+ if (entry) {
134
+ await meilisearch2.updateEntriesInMeilisearch({
135
+ contentType: contentType2,
136
+ entries: [{ ...entry, id, documentId }]
137
+ });
138
+ } else if (id != null) {
139
+ await meilisearch2.deleteEntriesFromMeiliSearch({
140
+ contentType: contentType2,
141
+ entriesId: [id]
142
+ });
143
+ }
144
+ } else if (deleteActions.includes(ctx.action)) {
145
+ if (id != null) {
146
+ strapi2.log.info(
147
+ `Meilisearch document middleware deleting ${contentType2} ids=${id}`
148
+ );
149
+ await meilisearch2.deleteEntriesFromMeiliSearch({
150
+ contentType: contentType2,
151
+ entriesId: [id]
152
+ });
153
+ } else {
154
+ strapi2.log.info(
155
+ `Meilisearch document middleware could not delete ${contentType2} for action ${ctx.action}: missing id`
156
+ );
157
+ }
158
+ }
159
+ return result;
160
+ } catch (error2) {
161
+ strapi2.log.error(
162
+ `Meilisearch document middleware error: ${error2.message}`
163
+ );
164
+ return result;
165
+ }
166
+ });
167
+ }
168
+ const register = async ({ strapi: strapi2 }) => {
169
+ await registerDocumentMiddleware({ strapi: strapi2 });
93
170
  };
94
171
  function isObject(elem) {
95
172
  return typeof elem === "object" && !Array.isArray(elem) && elem !== null;
@@ -723,20 +800,23 @@ const contentTypeService = ({ strapi: strapi2 }) => ({
723
800
  * @param {object} [options.entriesQuery={}] - Options to apply when fetching entries from the database.
724
801
  * @param {string | string[]} [options.entriesQuery.fields] - Fields present in the returned entry.
725
802
  * @param {object} [options.entriesQuery.populate] - Relations, components and dynamic zones to populate.
726
- * @param {object} [options.entriesQuery.locale] - When using internalization, the language to fetch.
803
+ * @param {string} [options.entriesQuery.status] - Publication state: draft or published.
804
+ * @param {string} [options.entriesQuery.locale] - When using internationalization (i18n), the language to fetch.
727
805
  * @param {string} options.contentType - Content type.
728
806
  *
729
807
  * @returns {Promise<object>} - Entries.
730
808
  */
731
809
  async getEntry({ contentType: contentType2, documentId, entriesQuery = {} }) {
732
- const { populate = "*", fields = "*" } = entriesQuery;
810
+ const {
811
+ populate = "*",
812
+ fields = "*",
813
+ status = "published",
814
+ locale
815
+ } = entriesQuery;
816
+ const queryOptions = { documentId, fields, populate, status, locale };
733
817
  const contentTypeUid = this.getContentTypeUid({ contentType: contentType2 });
734
818
  if (contentTypeUid === void 0) return {};
735
- const entry = await strapi2.documents(contentTypeUid).findOne({
736
- documentId,
737
- fields,
738
- populate
739
- });
819
+ const entry = await strapi2.documents(contentTypeUid).findOne(queryOptions);
740
820
  if (entry == null) {
741
821
  strapi2.log.error(
742
822
  `Could not find entry with id ${documentId} in ${contentType2}`
@@ -757,7 +837,7 @@ const contentTypeService = ({ strapi: strapi2 }) => ({
757
837
  * @param {object} [options.populate] - Relations, components and dynamic zones to populate.
758
838
  * @param {object} [options.status] - Publication state: draft or published.
759
839
  * @param {string} options.contentType - Content type.
760
- * @param {string} [options.locale] - When using internalization, the language to fetch.
840
+ * @param {string} [options.locale] - When using internationalization (i18n), the language to fetch.
761
841
  *
762
842
  * @returns {Promise<object[]>} - Entries.
763
843
  */
@@ -781,11 +861,9 @@ const contentTypeService = ({ strapi: strapi2 }) => ({
781
861
  filters,
782
862
  sort,
783
863
  populate,
784
- status
864
+ status,
865
+ locale
785
866
  };
786
- if (locale) {
787
- queryOptions.locale = locale;
788
- }
789
867
  const entries = await strapi2.documents(contentTypeUid).findMany(queryOptions);
790
868
  if (entries && !Array.isArray(entries)) return [entries];
791
869
  return entries || [];
@@ -1340,7 +1418,9 @@ const configurationService = ({ strapi: strapi2 }) => {
1340
1418
  if (entriesQuery.status === "draft") {
1341
1419
  return entries;
1342
1420
  } else {
1343
- return entries.filter((entry) => !(entry?.publishedAt === null));
1421
+ return entries.filter(
1422
+ (entry) => !(entry?.publishedAt === void 0 || entry?.publishedAt === null)
1423
+ );
1344
1424
  }
1345
1425
  },
1346
1426
  /**
@@ -1368,7 +1448,7 @@ const configurationService = ({ strapi: strapi2 }) => {
1368
1448
  }
1369
1449
  };
1370
1450
  };
1371
- const version = "0.13.3";
1451
+ const version = "0.14.0";
1372
1452
  const Meilisearch = (config2) => {
1373
1453
  return new MeiliSearch({
1374
1454
  ...config2,
@@ -1808,119 +1888,11 @@ const lifecycleService = ({ strapi: strapi2 }) => {
1808
1888
  const contentTypeService2 = strapi2.plugin("meilisearch").service("contentType");
1809
1889
  const store2 = strapi2.plugin("meilisearch").service("store");
1810
1890
  return {
1811
- /**
1812
- * Subscribe the content type to all required lifecycles
1813
- *
1814
- * @param {object} options
1815
- * @param {string} options.contentType
1816
- *
1817
- * @returns {Promise<object>}
1818
- */
1819
1891
  async subscribeContentType({ contentType: contentType2 }) {
1820
1892
  const contentTypeUid = contentTypeService2.getContentTypeUid({
1821
1893
  contentType: contentType2
1822
1894
  });
1823
- await strapi2.db.lifecycles.subscribe({
1824
- models: [contentTypeUid],
1825
- async afterCreate(event) {
1826
- const { result } = event;
1827
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1828
- await meilisearch2.addEntriesToMeilisearch({
1829
- contentType: contentTypeUid,
1830
- entries: [result]
1831
- }).catch((e) => {
1832
- strapi2.log.error(
1833
- `Meilisearch could not add entry with id: ${result.id}: ${e.message}`
1834
- );
1835
- });
1836
- },
1837
- async afterCreateMany(event) {
1838
- const { result } = event;
1839
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1840
- const nbrEntries = result.count;
1841
- const ids = result.ids;
1842
- const entries = [];
1843
- const BATCH_SIZE = 500;
1844
- for (let pos = 0; pos < nbrEntries; pos += BATCH_SIZE) {
1845
- const batch = await contentTypeService2.getEntries({
1846
- contentType: contentTypeUid,
1847
- start: pos,
1848
- limit: BATCH_SIZE,
1849
- filters: {
1850
- id: {
1851
- $in: ids
1852
- }
1853
- }
1854
- });
1855
- entries.push(...batch);
1856
- }
1857
- meilisearch2.updateEntriesInMeilisearch({
1858
- contentType: contentTypeUid,
1859
- entries
1860
- }).catch((e) => {
1861
- strapi2.log.error(
1862
- `Meilisearch could not update the entries: ${e.message}`
1863
- );
1864
- });
1865
- },
1866
- async afterUpdate(event) {
1867
- const { result } = event;
1868
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1869
- await meilisearch2.updateEntriesInMeilisearch({
1870
- contentType: contentTypeUid,
1871
- entries: [result]
1872
- }).catch((e) => {
1873
- strapi2.log.error(
1874
- `Meilisearch could not update entry with id: ${result.id}: ${e.message}`
1875
- );
1876
- });
1877
- },
1878
- async afterUpdateMany(event) {
1879
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1880
- const nbrEntries = await contentTypeService2.numberOfEntries({
1881
- contentType: contentTypeUid,
1882
- filters: event.params.where
1883
- });
1884
- const entries = [];
1885
- const BATCH_SIZE = 500;
1886
- for (let pos = 0; pos < nbrEntries; pos += BATCH_SIZE) {
1887
- const batch = await contentTypeService2.getEntries({
1888
- contentType: contentTypeUid,
1889
- filters: event.params.where,
1890
- start: pos,
1891
- limit: BATCH_SIZE
1892
- });
1893
- entries.push(...batch);
1894
- }
1895
- meilisearch2.updateEntriesInMeilisearch({
1896
- contentType: contentTypeUid,
1897
- entries
1898
- }).catch((e) => {
1899
- strapi2.log.error(
1900
- `Meilisearch could not update the entries: ${e.message}`
1901
- );
1902
- });
1903
- },
1904
- async afterDelete(event) {
1905
- const { result, params } = event;
1906
- const meilisearch2 = strapi2.plugin("meilisearch").service("meilisearch");
1907
- let entriesId = [];
1908
- if (params?.where?.$and && params?.where?.$and[0] && params?.where?.$and[0].id?.$in)
1909
- entriesId = params?.where?.$and[0].id.$in;
1910
- else entriesId = [result.id];
1911
- meilisearch2.deleteEntriesFromMeiliSearch({
1912
- contentType: contentTypeUid,
1913
- entriesId
1914
- }).catch((e) => {
1915
- strapi2.log.error(
1916
- `Meilisearch could not delete entry with id: ${result.id}: ${e.message}`
1917
- );
1918
- });
1919
- },
1920
- async afterDeleteMany(event) {
1921
- this.afterDelete(event);
1922
- }
1923
- });
1895
+ if (!contentTypeUid) return;
1924
1896
  return store2.addListenedContentType({
1925
1897
  contentType: contentTypeUid
1926
1898
  });
@@ -1942,13 +1914,7 @@ const error$1 = ({ strapi: strapi2 }) => {
1942
1914
  return {
1943
1915
  error: {
1944
1916
  message: e.message,
1945
- link: {
1946
- url: e.link || "https://www.meilisearch.com/docs",
1947
- label: {
1948
- id: "notification.meilisearch",
1949
- defaultMessage: "See more"
1950
- }
1951
- }
1917
+ link: e.link ? { url: e.link } : { url: "https://www.meilisearch.com/docs" }
1952
1918
  }
1953
1919
  };
1954
1920
  } else if (e.type === "MeiliSearchCommunicationError") {
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "strapi-plugin-meilisearch",
3
- "version": "0.13.3",
3
+ "version": "0.14.0",
4
4
  "description": "Synchronise and search in your Strapi content-types with Meilisearch",
5
5
  "scripts": {
6
6
  "build": "strapi-plugin build",
7
7
  "verify": "strapi-plugin verify",
8
8
  "watch": "strapi-plugin watch",
9
9
  "watch:link": "strapi-plugin watch:link",
10
- "playground:dev": "yarn --cwd ./playground && yarn --cwd ./playground dev",
10
+ "playground:setup": "yarn --cwd ./playground setup",
11
+ "playground:dev": "yarn --cwd ./playground dev",
11
12
  "playground:build": "yarn --cwd ./playground && yarn --cwd ./playground build",
12
13
  "playground:ci": "yarn --cwd ./playground ci",
13
14
  "style": "eslint --ext .js,.test.js .",
14
15
  "style:fix": "eslint --ext .js,.test.js . --fix",
15
16
  "test": "jest --testPathPattern=tests",
16
17
  "test:watch": "jest --watch --testPathPattern=tests --verbose",
17
- "test:e2e:watch": "concurrently --kill-others -s first \"NODE_ENV=test yarn playground:dev\" \"cypress open --env env=watch\""
18
+ "test:e2e": "concurrently --kill-others -s first \"yarn playground:dev\" \"cypress run\"",
19
+ "test:integrations": "jest -c jest.integration.config.js"
18
20
  },
19
21
  "strapi": {
20
22
  "name": "meilisearch",
@@ -62,7 +64,7 @@
62
64
  "@types/jest": "^29.5.12",
63
65
  "babel-jest": "^29.7.0",
64
66
  "concurrently": "^8.2.2",
65
- "cypress": "^14.0.1",
67
+ "cypress": "^15.3.0",
66
68
  "eslint": "^8.2.0",
67
69
  "eslint-config-prettier": "^9.1.0",
68
70
  "eslint-plugin-cypress": "^2.12.1",