firestore-meilisearch 0.1.5 → 0.1.6
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 +1 -1
- package/__tests__/functions.test.ts +47 -22
- package/lib/index.js +24 -7
- package/lib/logs.js +5 -7
- package/lib/validate.js +16 -0
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/index.ts +27 -8
- package/src/logs.ts +5 -9
- package/src/validate.ts +13 -0
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ Note that this extension only listens for changes to _documents_ in a specific c
|
|
|
40
40
|
Before installing this extension, you'll need to:
|
|
41
41
|
|
|
42
42
|
- [Set up Cloud Firestore in your Firebase project](https://firebase.google.com/docs/firestore/quickstart)
|
|
43
|
-
- Run a Meilisearch instance.
|
|
43
|
+
- Run a Meilisearch instance. [Learn more about Meilisearch cloud](https://www.meilisearch.com/pricing). Alternatively there are many other easy ways [to download and run a Meilisearch instance](https://docs.meilisearch.com/learn/getting_started/installation.html#download-and-launch)
|
|
44
44
|
|
|
45
45
|
#### Data import format
|
|
46
46
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import * as firebaseFunctionsTestInit from 'firebase-functions-test'
|
|
2
2
|
import mockedEnv from 'mocked-env'
|
|
3
3
|
import { mocked } from 'ts-jest/utils'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
mockConsoleLog,
|
|
6
|
+
mockConsoleInfo,
|
|
7
|
+
mockConsoleError,
|
|
8
|
+
} from './__mocks__/console'
|
|
5
9
|
import { MeiliSearch } from 'meilisearch'
|
|
6
10
|
import defaultEnvironment from './data/environment'
|
|
7
11
|
import defaultDocument from './data/document'
|
|
@@ -19,11 +23,9 @@ describe('extension', () => {
|
|
|
19
23
|
// Mocking of Meilisearch package
|
|
20
24
|
const mockedMeilisearch = mocked(MeiliSearch, true)
|
|
21
25
|
const mockedAddDocuments = jest.fn()
|
|
22
|
-
const mockedUpdateDocuments = jest.fn()
|
|
23
26
|
const mockedDeleteDocument = jest.fn()
|
|
24
27
|
const mockedIndex = jest.fn(() => ({
|
|
25
28
|
addDocuments: mockedAddDocuments,
|
|
26
|
-
updateDocuments: mockedUpdateDocuments,
|
|
27
29
|
deleteDocument: mockedDeleteDocument,
|
|
28
30
|
}))
|
|
29
31
|
mockedMeilisearch.mockReturnValue({
|
|
@@ -107,8 +109,7 @@ describe('extension', () => {
|
|
|
107
109
|
expect(mockConsoleInfo).toBeCalledWith(
|
|
108
110
|
`Creating new document ${
|
|
109
111
|
afterSnapshot.id as string
|
|
110
|
-
} in Meilisearch index ${defaultEnvironment.MEILISEARCH_INDEX_NAME}
|
|
111
|
-
{ _firestore_id: defaultDocument.id, ...afterSnapshot.data() }
|
|
112
|
+
} in Meilisearch index: ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
112
113
|
)
|
|
113
114
|
expect(mockedAddDocuments).toHaveBeenCalledWith(
|
|
114
115
|
[
|
|
@@ -150,8 +151,7 @@ describe('extension', () => {
|
|
|
150
151
|
expect(mockConsoleInfo).toBeCalledWith(
|
|
151
152
|
`Creating new document ${
|
|
152
153
|
afterSnapshot.id as string
|
|
153
|
-
} in Meilisearch index ${defaultEnvironment.MEILISEARCH_INDEX_NAME}
|
|
154
|
-
{ _firestore_id: defaultDocument.id, ...afterSnapshot.data() }
|
|
154
|
+
} in Meilisearch index: ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
155
155
|
)
|
|
156
156
|
expect(mockedAddDocuments).toHaveBeenCalledWith(
|
|
157
157
|
[
|
|
@@ -165,6 +165,37 @@ describe('extension', () => {
|
|
|
165
165
|
)
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
+
test('function run on creating document with unauthorized chars in document id', async () => {
|
|
169
|
+
const badId = '@@ !#'
|
|
170
|
+
const beforeSnapshot = firebaseMock.firestore.makeDocumentSnapshot(
|
|
171
|
+
{},
|
|
172
|
+
`collection/{}`
|
|
173
|
+
)
|
|
174
|
+
const afterSnapshot = firebaseMock.firestore.makeDocumentSnapshot(
|
|
175
|
+
{ id: badId },
|
|
176
|
+
`collection/${badId}`
|
|
177
|
+
)
|
|
178
|
+
const documentChange = firebaseMock.makeChange(
|
|
179
|
+
beforeSnapshot,
|
|
180
|
+
afterSnapshot
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
await mockExport(documentChange, {
|
|
184
|
+
resource: {
|
|
185
|
+
name: 'test',
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
expect(mockConsoleInfo).toBeCalledWith(
|
|
190
|
+
`Creating new document ${
|
|
191
|
+
afterSnapshot.id as string
|
|
192
|
+
} in Meilisearch index: ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
193
|
+
)
|
|
194
|
+
expect(mockConsoleError).toBeCalledWith(
|
|
195
|
+
`Could not create document with id: ${badId}. The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`
|
|
196
|
+
)
|
|
197
|
+
})
|
|
198
|
+
|
|
168
199
|
test('function runs with updated data', async () => {
|
|
169
200
|
const beforeSnapshot = firebaseMock.firestore.makeDocumentSnapshot(
|
|
170
201
|
{ foo: 'bar' },
|
|
@@ -191,13 +222,12 @@ describe('extension', () => {
|
|
|
191
222
|
config
|
|
192
223
|
)
|
|
193
224
|
expect(mockConsoleInfo).toBeCalledWith(
|
|
194
|
-
`Updating document ${
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
{ _firestore_id: defaultDocument.id, ...afterSnapshot.data() }
|
|
225
|
+
`Updating document ${
|
|
226
|
+
afterSnapshot.id as string
|
|
227
|
+
} in Meilisearch index: ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
198
228
|
)
|
|
199
229
|
expect(mockConsoleLog).toBeCalledWith('Completed execution of extension')
|
|
200
|
-
expect(
|
|
230
|
+
expect(mockedAddDocuments).toHaveBeenCalledWith([
|
|
201
231
|
{
|
|
202
232
|
_firestore_id: defaultDocument.id,
|
|
203
233
|
...defaultDocument.document,
|
|
@@ -231,17 +261,12 @@ describe('extension', () => {
|
|
|
231
261
|
config
|
|
232
262
|
)
|
|
233
263
|
expect(mockConsoleInfo).toBeCalledWith(
|
|
234
|
-
`Updating document ${
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
{
|
|
238
|
-
_firestore_id: defaultDocument.id,
|
|
239
|
-
id: '12345',
|
|
240
|
-
...afterSnapshot.data(),
|
|
241
|
-
}
|
|
264
|
+
`Updating document ${
|
|
265
|
+
afterSnapshot.id as string
|
|
266
|
+
} in Meilisearch index: ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
242
267
|
)
|
|
243
268
|
expect(mockConsoleLog).toBeCalledWith('Completed execution of extension')
|
|
244
|
-
expect(
|
|
269
|
+
expect(mockedAddDocuments).toHaveBeenCalledWith([
|
|
245
270
|
{
|
|
246
271
|
_firestore_id: defaultDocument.id,
|
|
247
272
|
id: '12345',
|
|
@@ -273,7 +298,7 @@ describe('extension', () => {
|
|
|
273
298
|
config
|
|
274
299
|
)
|
|
275
300
|
expect(mockConsoleInfo).toBeCalledWith(
|
|
276
|
-
`Deleting document ${defaultDocument.id} in Meilisearch index ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
301
|
+
`Deleting document ${defaultDocument.id} in Meilisearch index: ${defaultEnvironment.MEILISEARCH_INDEX_NAME}`
|
|
277
302
|
)
|
|
278
303
|
expect(mockConsoleLog).toBeCalledWith('Completed execution of extension')
|
|
279
304
|
expect(mockedDeleteDocument).toHaveBeenCalledWith(defaultDocument.id)
|
package/lib/index.js
CHANGED
|
@@ -17,11 +17,13 @@ exports.indexingWorker = void 0;
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
const functions = require("firebase-functions");
|
|
20
|
+
const firebase_functions_1 = require("firebase-functions");
|
|
20
21
|
const create_index_1 = require("./meilisearch/create-index");
|
|
21
22
|
const util_1 = require("./util");
|
|
22
23
|
const logs = require("./logs");
|
|
23
24
|
const adapter_1 = require("./adapter");
|
|
24
25
|
const config_1 = require("./config");
|
|
26
|
+
const validate_1 = require("./validate");
|
|
25
27
|
const index = (0, create_index_1.initMeilisearchIndex)(config_1.config.meilisearch);
|
|
26
28
|
logs.init();
|
|
27
29
|
/**
|
|
@@ -52,9 +54,14 @@ exports.indexingWorker = functions.handler.firestore.document.onWrite(async (cha
|
|
|
52
54
|
*/
|
|
53
55
|
async function handleAddDocument(documentId, snapshot) {
|
|
54
56
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
logs.addDocument(documentId);
|
|
58
|
+
if ((0, validate_1.validateDocumentId)(documentId)) {
|
|
59
|
+
const document = (0, adapter_1.adaptDocument)(documentId, snapshot);
|
|
60
|
+
await index.addDocuments([document], { primaryKey: '_firestore_id' });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
firebase_functions_1.logger.error(`Could not create document with id: ${documentId}. The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`);
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
catch (e) {
|
|
60
67
|
logs.error(e);
|
|
@@ -66,8 +73,13 @@ async function handleAddDocument(documentId, snapshot) {
|
|
|
66
73
|
*/
|
|
67
74
|
async function handleDeleteDocument(documentId) {
|
|
68
75
|
try {
|
|
69
|
-
await index.deleteDocument(documentId);
|
|
70
76
|
logs.deleteDocument(documentId);
|
|
77
|
+
if ((0, validate_1.validateDocumentId)(documentId)) {
|
|
78
|
+
await index.deleteDocument(documentId);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
firebase_functions_1.logger.error(`Could not delete document with id: ${documentId}. The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`);
|
|
82
|
+
}
|
|
71
83
|
}
|
|
72
84
|
catch (e) {
|
|
73
85
|
logs.error(e);
|
|
@@ -80,9 +92,14 @@ async function handleDeleteDocument(documentId) {
|
|
|
80
92
|
*/
|
|
81
93
|
async function handleUpdateDocument(documentId, after) {
|
|
82
94
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
logs.updateDocument(documentId);
|
|
96
|
+
if ((0, validate_1.validateDocumentId)(documentId)) {
|
|
97
|
+
const document = (0, adapter_1.adaptDocument)(documentId, after);
|
|
98
|
+
await index.addDocuments([document]);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
firebase_functions_1.logger.error(`Could not update document with id: ${documentId}.The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`);
|
|
102
|
+
}
|
|
86
103
|
}
|
|
87
104
|
catch (e) {
|
|
88
105
|
logs.error(e);
|
package/lib/logs.js
CHANGED
|
@@ -50,19 +50,17 @@ exports.complete = complete;
|
|
|
50
50
|
/**
|
|
51
51
|
* Log an addition of a document.
|
|
52
52
|
* @param {string} id Document id added.
|
|
53
|
-
* @param {object} data Data contained in the document.
|
|
54
53
|
*/
|
|
55
|
-
function addDocument(id
|
|
56
|
-
firebase_functions_1.logger.info(`Creating new document ${id} in Meilisearch index ${config_1.config.meilisearch.indexUid}
|
|
54
|
+
function addDocument(id) {
|
|
55
|
+
firebase_functions_1.logger.info(`Creating new document ${id} in Meilisearch index: ${config_1.config.meilisearch.indexUid}`);
|
|
57
56
|
}
|
|
58
57
|
exports.addDocument = addDocument;
|
|
59
58
|
/**
|
|
60
59
|
* Log an update of a document.
|
|
61
60
|
* @param {string} id Document id updated.
|
|
62
|
-
* @param {object} data Data contained in the document.
|
|
63
61
|
*/
|
|
64
|
-
function updateDocument(id
|
|
65
|
-
firebase_functions_1.logger.info(`Updating document ${id} in Meilisearch index ${config_1.config.meilisearch.indexUid}
|
|
62
|
+
function updateDocument(id) {
|
|
63
|
+
firebase_functions_1.logger.info(`Updating document ${id} in Meilisearch index: ${config_1.config.meilisearch.indexUid}`);
|
|
66
64
|
}
|
|
67
65
|
exports.updateDocument = updateDocument;
|
|
68
66
|
/**
|
|
@@ -70,7 +68,7 @@ exports.updateDocument = updateDocument;
|
|
|
70
68
|
* @param {string} id Document id deleted.
|
|
71
69
|
*/
|
|
72
70
|
function deleteDocument(id) {
|
|
73
|
-
firebase_functions_1.logger.info(`Deleting document ${id} in Meilisearch index ${config_1.config.meilisearch.indexUid}`);
|
|
71
|
+
firebase_functions_1.logger.info(`Deleting document ${id} in Meilisearch index: ${config_1.config.meilisearch.indexUid}`);
|
|
74
72
|
}
|
|
75
73
|
exports.deleteDocument = deleteDocument;
|
|
76
74
|
/**
|
package/lib/validate.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateDocumentId = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Validate if the format of the document id is compliant with Meilisearch.
|
|
6
|
+
* Based on this documentation: https://docs.meilisearch.com/learn/core_concepts/primary_key.html#formatting-the-document-id
|
|
7
|
+
*
|
|
8
|
+
* @param {string} documentId Document id.
|
|
9
|
+
*
|
|
10
|
+
* @return {boolean} - Wether the document id is properly formatted (true) or not.
|
|
11
|
+
*/
|
|
12
|
+
function validateDocumentId(documentId) {
|
|
13
|
+
const wrongChars = documentId.search(/([^a-zA-Z0-9-_]+)/);
|
|
14
|
+
return wrongChars === -1;
|
|
15
|
+
}
|
|
16
|
+
exports.validateDocumentId = validateDocumentId;
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import * as functions from 'firebase-functions'
|
|
19
|
-
import { Change } from 'firebase-functions'
|
|
19
|
+
import { Change, logger } from 'firebase-functions'
|
|
20
20
|
import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore'
|
|
21
21
|
import { initMeilisearchIndex } from './meilisearch/create-index'
|
|
22
22
|
import { getChangeType, getChangedDocumentId, ChangeType } from './util'
|
|
23
23
|
import * as logs from './logs'
|
|
24
24
|
import { adaptDocument } from './adapter'
|
|
25
25
|
import { config } from './config'
|
|
26
|
+
import { validateDocumentId } from './validate'
|
|
26
27
|
|
|
27
28
|
const index = initMeilisearchIndex(config.meilisearch)
|
|
28
29
|
|
|
@@ -63,9 +64,15 @@ async function handleAddDocument(
|
|
|
63
64
|
snapshot: DocumentSnapshot
|
|
64
65
|
): Promise<void> {
|
|
65
66
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
logs.addDocument(documentId)
|
|
68
|
+
if (validateDocumentId(documentId)) {
|
|
69
|
+
const document = adaptDocument(documentId, snapshot)
|
|
70
|
+
await index.addDocuments([document], { primaryKey: '_firestore_id' })
|
|
71
|
+
} else {
|
|
72
|
+
logger.error(
|
|
73
|
+
`Could not create document with id: ${documentId}. The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`
|
|
74
|
+
)
|
|
75
|
+
}
|
|
69
76
|
} catch (e) {
|
|
70
77
|
logs.error(e as Error)
|
|
71
78
|
}
|
|
@@ -77,8 +84,14 @@ async function handleAddDocument(
|
|
|
77
84
|
*/
|
|
78
85
|
async function handleDeleteDocument(documentId: string): Promise<void> {
|
|
79
86
|
try {
|
|
80
|
-
await index.deleteDocument(documentId)
|
|
81
87
|
logs.deleteDocument(documentId)
|
|
88
|
+
if (validateDocumentId(documentId)) {
|
|
89
|
+
await index.deleteDocument(documentId)
|
|
90
|
+
} else {
|
|
91
|
+
logger.error(
|
|
92
|
+
`Could not delete document with id: ${documentId}. The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
82
95
|
} catch (e) {
|
|
83
96
|
logs.error(e as Error)
|
|
84
97
|
}
|
|
@@ -94,9 +107,15 @@ async function handleUpdateDocument(
|
|
|
94
107
|
after: DocumentSnapshot
|
|
95
108
|
): Promise<void> {
|
|
96
109
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
logs.updateDocument(documentId)
|
|
111
|
+
if (validateDocumentId(documentId)) {
|
|
112
|
+
const document = adaptDocument(documentId, after)
|
|
113
|
+
await index.addDocuments([document])
|
|
114
|
+
} else {
|
|
115
|
+
logger.error(
|
|
116
|
+
`Could not update document with id: ${documentId}.The document id can only contain case-insensitive alphanumeric characters (abcDEF), hyphens (-) or underscores(_).`
|
|
117
|
+
)
|
|
118
|
+
}
|
|
100
119
|
} catch (e) {
|
|
101
120
|
logs.error(e as Error)
|
|
102
121
|
}
|
package/src/logs.ts
CHANGED
|
@@ -50,24 +50,20 @@ export function complete() {
|
|
|
50
50
|
/**
|
|
51
51
|
* Log an addition of a document.
|
|
52
52
|
* @param {string} id Document id added.
|
|
53
|
-
* @param {object} data Data contained in the document.
|
|
54
53
|
*/
|
|
55
|
-
export function addDocument(id: string
|
|
54
|
+
export function addDocument(id: string) {
|
|
56
55
|
logger.info(
|
|
57
|
-
`Creating new document ${id} in Meilisearch index ${config.meilisearch.indexUid}
|
|
58
|
-
data
|
|
56
|
+
`Creating new document ${id} in Meilisearch index: ${config.meilisearch.indexUid}`
|
|
59
57
|
)
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
/**
|
|
63
61
|
* Log an update of a document.
|
|
64
62
|
* @param {string} id Document id updated.
|
|
65
|
-
* @param {object} data Data contained in the document.
|
|
66
63
|
*/
|
|
67
|
-
export function updateDocument(id: string
|
|
64
|
+
export function updateDocument(id: string) {
|
|
68
65
|
logger.info(
|
|
69
|
-
`Updating document ${id} in Meilisearch index ${config.meilisearch.indexUid}
|
|
70
|
-
data
|
|
66
|
+
`Updating document ${id} in Meilisearch index: ${config.meilisearch.indexUid}`
|
|
71
67
|
)
|
|
72
68
|
}
|
|
73
69
|
|
|
@@ -77,7 +73,7 @@ export function updateDocument(id: string, data: Record<string, any>) {
|
|
|
77
73
|
*/
|
|
78
74
|
export function deleteDocument(id: string) {
|
|
79
75
|
logger.info(
|
|
80
|
-
`Deleting document ${id} in Meilisearch index ${config.meilisearch.indexUid}`
|
|
76
|
+
`Deleting document ${id} in Meilisearch index: ${config.meilisearch.indexUid}`
|
|
81
77
|
)
|
|
82
78
|
}
|
|
83
79
|
|
package/src/validate.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate if the format of the document id is compliant with Meilisearch.
|
|
3
|
+
* Based on this documentation: https://docs.meilisearch.com/learn/core_concepts/primary_key.html#formatting-the-document-id
|
|
4
|
+
*
|
|
5
|
+
* @param {string} documentId Document id.
|
|
6
|
+
*
|
|
7
|
+
* @return {boolean} - Wether the document id is properly formatted (true) or not.
|
|
8
|
+
*/
|
|
9
|
+
export function validateDocumentId(documentId: string) {
|
|
10
|
+
const wrongChars = documentId.search(/([^a-zA-Z0-9-_]+)/)
|
|
11
|
+
|
|
12
|
+
return wrongChars === -1
|
|
13
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.1.
|
|
1
|
+
export const version = '0.1.6'
|