firestore-meilisearch 0.1.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.
Files changed (65) hide show
  1. package/.editorconfig +20 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  4. package/.github/ISSUE_TEMPLATE/other.md +7 -0
  5. package/.github/dependatbot.yml +23 -0
  6. package/.github/release-draft-template.yml +33 -0
  7. package/.github/scripts/check-release.sh +42 -0
  8. package/.github/workflows/publish.yml +30 -0
  9. package/.github/workflows/release-drafter.yml +16 -0
  10. package/.github/workflows/test.yml +42 -0
  11. package/CHANGELOG.md +3 -0
  12. package/CONTRIBUTING.md +236 -0
  13. package/LICENSE +201 -0
  14. package/POSTINSTALL.md +40 -0
  15. package/PREINSTALL.md +42 -0
  16. package/README.md +128 -0
  17. package/bors.toml +8 -0
  18. package/dataset/firebase-export-metadata.json +8 -0
  19. package/dataset/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata +0 -0
  20. package/dataset/firestore_export/all_namespaces/all_kinds/output-0 +0 -0
  21. package/dataset/firestore_export/firestore_export.overall_export_metadata +0 -0
  22. package/extension.yaml +176 -0
  23. package/firebase.json +20 -0
  24. package/functions/.eslintignore +2 -0
  25. package/functions/.eslintrc.js +54 -0
  26. package/functions/__tests__/__mocks__/console.ts +7 -0
  27. package/functions/__tests__/adapter.test.ts +98 -0
  28. package/functions/__tests__/config.test.ts +130 -0
  29. package/functions/__tests__/data/document.ts +11 -0
  30. package/functions/__tests__/data/environment.ts +9 -0
  31. package/functions/__tests__/functions.test.ts +280 -0
  32. package/functions/__tests__/jest.setup.ts +1 -0
  33. package/functions/__tests__/test.types.d.ts +5 -0
  34. package/functions/__tests__/tsconfig.json +5 -0
  35. package/functions/__tests__/util.test.ts +200 -0
  36. package/functions/jest.config.js +12 -0
  37. package/functions/lib/adapter.js +61 -0
  38. package/functions/lib/config.js +13 -0
  39. package/functions/lib/import/config.js +127 -0
  40. package/functions/lib/import/index.js +93 -0
  41. package/functions/lib/index.js +90 -0
  42. package/functions/lib/logs.js +97 -0
  43. package/functions/lib/meilisearch/create-index.js +17 -0
  44. package/functions/lib/meilisearch-index.js +17 -0
  45. package/functions/lib/types.js +2 -0
  46. package/functions/lib/util.js +47 -0
  47. package/functions/lib/version.js +4 -0
  48. package/functions/package.json +53 -0
  49. package/functions/src/adapter.ts +106 -0
  50. package/functions/src/config.ts +34 -0
  51. package/functions/src/import/config.ts +207 -0
  52. package/functions/src/import/index.ts +115 -0
  53. package/functions/src/index.ts +103 -0
  54. package/functions/src/logs.ts +107 -0
  55. package/functions/src/meilisearch/create-index.ts +20 -0
  56. package/functions/src/types.ts +8 -0
  57. package/functions/src/util.ts +63 -0
  58. package/functions/src/version.ts +1 -0
  59. package/functions/tsconfig.eslint.json +13 -0
  60. package/functions/tsconfig.json +23 -0
  61. package/functions/yarn.lock +5306 -0
  62. package/guides/IMPORT_EXISTING_DOCUMENTS.md +74 -0
  63. package/package.json +21 -0
  64. package/script/version.sh +51 -0
  65. package/test-params-example.env +9 -0
@@ -0,0 +1,103 @@
1
+ 'use strict'
2
+ /*
3
+ * Copyright 2022 Meilisearch
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import * as functions from 'firebase-functions'
19
+ import { Change } from 'firebase-functions'
20
+ import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore'
21
+ import { initMeilisearchIndex } from './meilisearch/create-index'
22
+ import { getChangeType, getChangedDocumentId, ChangeType } from './util'
23
+ import * as logs from './logs'
24
+ import { adaptDocument } from './adapter'
25
+ import { config } from './config'
26
+
27
+ const index = initMeilisearchIndex(config.meilisearch)
28
+
29
+ logs.init()
30
+
31
+ /**
32
+ * IndexingWorker is responsible for aggregating a defined field from a Firestore collection into a Meilisearch index.
33
+ * It is controlled by a Firestore handler.
34
+ */
35
+ export const indexingWorker = functions.handler.firestore.document.onWrite(
36
+ async (change: Change<DocumentSnapshot>): Promise<void> => {
37
+ logs.start()
38
+ const changeType = getChangeType(change)
39
+ const documentId = getChangedDocumentId(change)
40
+
41
+ switch (changeType) {
42
+ case ChangeType.CREATE:
43
+ await handleAddDocument(documentId, change.after)
44
+ break
45
+ case ChangeType.DELETE:
46
+ await handleDeleteDocument(documentId)
47
+ break
48
+ case ChangeType.UPDATE:
49
+ await handleUpdateDocument(documentId, change.after)
50
+ break
51
+ }
52
+ logs.complete()
53
+ }
54
+ )
55
+
56
+ /**
57
+ * Handle addition of a document in the Meilisearch index.
58
+ * @param {string} documentId Document id to add.
59
+ * @param {Change} snapshot Snapshot of the data contained in the document read from your Firestore database.
60
+ */
61
+ async function handleAddDocument(
62
+ documentId: string,
63
+ snapshot: DocumentSnapshot
64
+ ): Promise<void> {
65
+ try {
66
+ const document = adaptDocument(documentId, snapshot)
67
+ await index.addDocuments([document], { primaryKey: '_firestore_id' })
68
+ logs.addDocument(documentId, document)
69
+ } catch (e) {
70
+ logs.error(e as Error)
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Handle deletion of a document in the Meilisearch index.
76
+ * @param {string} documentId Document id to delete.
77
+ */
78
+ async function handleDeleteDocument(documentId: string): Promise<void> {
79
+ try {
80
+ await index.deleteDocument(documentId)
81
+ logs.deleteDocument(documentId)
82
+ } catch (e) {
83
+ logs.error(e as Error)
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Handle update of a document in the Meilisearch index.
89
+ * @param {string} documentId Document id to update.
90
+ * @param {Change} after Snapshot of the data contained in the document read from your Firestore database.
91
+ */
92
+ async function handleUpdateDocument(
93
+ documentId: string,
94
+ after: DocumentSnapshot
95
+ ): Promise<void> {
96
+ try {
97
+ const document = adaptDocument(documentId, after)
98
+ await index.updateDocuments([document])
99
+ logs.updateDocument(documentId, document)
100
+ } catch (e) {
101
+ logs.error(e as Error)
102
+ }
103
+ }
@@ -0,0 +1,107 @@
1
+ 'use strict'
2
+ /*
3
+ * Copyright 2022 Meilisearch
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import { logger } from 'firebase-functions'
19
+ import { config } from './config'
20
+
21
+ /**
22
+ * Initialization logger.
23
+ */
24
+ export function init() {
25
+ logger.log('Initializing extension with configuration', config)
26
+ }
27
+
28
+ /**
29
+ * Start logger.
30
+ */
31
+ export function start() {
32
+ logger.log('Started execution of extension with configuration', config)
33
+ }
34
+
35
+ /**
36
+ * Error logger.
37
+ * @param {Error} err
38
+ */
39
+ export function error(err: Error) {
40
+ logger.error('Unhandled error occurred during processing:', err)
41
+ }
42
+
43
+ /**
44
+ * Complete logger.
45
+ */
46
+ export function complete() {
47
+ logger.log('Completed execution of extension')
48
+ }
49
+
50
+ /**
51
+ * Log an addition of a document.
52
+ * @param {string} id Document id added.
53
+ * @param {object} data Data contained in the document.
54
+ */
55
+ export function addDocument(id: string, data: Record<string, any>) {
56
+ logger.info(
57
+ `Creating new document ${id} in Meilisearch index ${config.meilisearch.indexUid}`,
58
+ data
59
+ )
60
+ }
61
+
62
+ /**
63
+ * Log an update of a document.
64
+ * @param {string} id Document id updated.
65
+ * @param {object} data Data contained in the document.
66
+ */
67
+ export function updateDocument(id: string, data: Record<string, any>) {
68
+ logger.info(
69
+ `Updating document ${id} in Meilisearch index ${config.meilisearch.indexUid}`,
70
+ data
71
+ )
72
+ }
73
+
74
+ /**
75
+ * Log a deletion of a document.
76
+ * @param {string} id Document id deleted.
77
+ */
78
+ export function deleteDocument(id: string) {
79
+ logger.info(
80
+ `Deleting document ${id} in Meilisearch index ${config.meilisearch.indexUid}`
81
+ )
82
+ }
83
+
84
+ /**
85
+ * Log a modification of geoPoint based on whether or not it has the correct naming to enable `geosearch` in Meilisearch.
86
+ * @param {boolean} hasGeoField a boolean value that indicates whether the field is correctly named to enable `geosearch` in Meilisearch.
87
+ */
88
+ export function infoGeoPoint(hasGeoField: boolean) {
89
+ if (hasGeoField) {
90
+ logger.info(
91
+ `A GeoPoint was found with the field name '_geo' for compatibility with Meilisearch the field 'latitude' was renamed to 'lat' and the field 'longitude' to 'lng'`
92
+ )
93
+ } else {
94
+ logger.info(
95
+ `A GeoPoint was found without the field name '_geo' if you want to use the geoSearch with Meilisearch rename it to '_geo'`
96
+ )
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Importation data logger.
102
+ * @param {number} total
103
+ * @param {number} batches
104
+ */
105
+ export function importData(total: number, batches: number) {
106
+ logger.info(`Imported ${total} documents in ${batches} batches.`)
107
+ }
@@ -0,0 +1,20 @@
1
+ import { MeiliSearch, Index } from 'meilisearch'
2
+ import { MeilisearchConfig } from '../types'
3
+
4
+ /**
5
+ * initMeilisearchIndex
6
+ * @param {MeilisearchConfig} - Meilisearch configuration
7
+ * @return {Index}
8
+ */
9
+ export function initMeilisearchIndex({
10
+ host,
11
+ apiKey,
12
+ indexUid,
13
+ }: MeilisearchConfig): Index {
14
+ const client = new MeiliSearch({
15
+ host,
16
+ apiKey,
17
+ })
18
+
19
+ return client.index(indexUid)
20
+ }
@@ -0,0 +1,8 @@
1
+ export type { Index } from 'meilisearch'
2
+
3
+ export type MeilisearchConfig = {
4
+ host: string
5
+ apiKey: string
6
+ indexUid: string
7
+ fieldsToIndex?: string
8
+ }
@@ -0,0 +1,63 @@
1
+ 'use strict'
2
+ /*
3
+ * Copyright 2022 Meilisearch
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore'
19
+ import { Change } from 'firebase-functions'
20
+ import { config } from './config'
21
+
22
+ export enum ChangeType {
23
+ CREATE,
24
+ DELETE,
25
+ UPDATE,
26
+ }
27
+
28
+ /**
29
+ * Get type of the modification perform on a document.
30
+ * @param {Change<T>} change The Functions interface for events that change state.
31
+ * @return {ChangeType} Final type of the event.
32
+ */
33
+ export function getChangeType(change: Change<DocumentSnapshot>): ChangeType {
34
+ if (!change.after.exists) {
35
+ return ChangeType.DELETE
36
+ }
37
+ if (!change.before.exists) {
38
+ return ChangeType.CREATE
39
+ }
40
+ return ChangeType.UPDATE
41
+ }
42
+
43
+ /**
44
+ * Get final id of a document after modification.
45
+ * @param {Change<T>} change The Functions interface for events that change state.
46
+ * @return {string} Final state type of the event.
47
+ */
48
+ export function getChangedDocumentId(change: Change<DocumentSnapshot>): string {
49
+ if (change.after.exists) {
50
+ return change.after.id
51
+ }
52
+ return change.before.id
53
+ }
54
+
55
+ /**
56
+ * Returns the MEILISEARCH_FIELDS_TO_INDEX value from the config file and formats it.
57
+ * @return {string[]} An array of fields.
58
+ */
59
+ export function getFieldsToIndex(): string[] {
60
+ return config.meilisearch.fieldsToIndex
61
+ ? config.meilisearch.fieldsToIndex.split(/[ ,]+/)
62
+ : []
63
+ }
@@ -0,0 +1 @@
1
+ export const version = '0.1.0'
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ },
6
+ "include": [
7
+ "./src/**/*.ts",
8
+ "./__tests__/**/*.ts",
9
+ "./**/*.js",
10
+ ".eslintrc.js",
11
+ ],
12
+ "exclude": []
13
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "skipLibCheck": true,
4
+ "module": "commonjs",
5
+ "noImplicitReturns": true,
6
+ "noImplicitAny": false,
7
+ "outDir": "lib",
8
+ "sourceMap": false,
9
+ "strict": true,
10
+ "target": "es2018",
11
+ "types": [
12
+ "jest",
13
+ "node",
14
+ ]
15
+ },
16
+ "compileOnSave": true,
17
+ "include": [
18
+ "src",
19
+ ],
20
+ "exclude": [
21
+ "node_modules",
22
+ ]
23
+ }