@graphcommerce/algolia-search 6.2.0-canary.91 → 6.2.0-canary.92

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @graphcommerce/algolia-search
2
2
 
3
+ ## 6.2.0-canary.92
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2026](https://github.com/graphcommerce-org/graphcommerce/pull/2026) [`c1b76eb4d`](https://github.com/graphcommerce-org/graphcommerce/commit/c1b76eb4d70cf5cef9f768bb1f4986e639b31932) - Improved documentation @graphcommerce/algolia-search ([@mikekeehnen](https://github.com/mikekeehnen))
8
+
3
9
  ## 6.2.0-canary.91
4
10
 
5
11
  ## 6.2.0-canary.90
package/Config.graphqls CHANGED
@@ -69,4 +69,14 @@ extend input GraphCommerceConfig {
69
69
  Configures algolia search debounce time. This will slow down the search response.
70
70
  """
71
71
  algoliaSearchDebounceTime: Int
72
+
73
+ """
74
+ Configure your Algolia HyGraph index. This will create a new index for HyGraph data if it doesn't exist in Algolia.
75
+ """
76
+ algoliaHygraphIndex: String
77
+
78
+ """
79
+ Configure your Algolia HyGraph secret. This will be used to authenticate the HyGraph data.
80
+ """
81
+ hygraphSecret: String
72
82
  }
package/README.md CHANGED
@@ -26,9 +26,77 @@ Storefront configuration values:
26
26
  - filterAttributes (containing a list of the following values)
27
27
  - aggregation
28
28
  - toAlgoliaAttribute
29
+ - sortOptions
30
+ - label
31
+ - value
32
+
33
+ ## When to use filterAttributes?
34
+
35
+ The filterAttributes configuration is used to map the filter attributes from the
36
+ Magento 2 API to Algolia API. This is needed because some of the attributes in
37
+ Algolia don't match with the indexed attributes in Algolia. We currently support
38
+ some default attributes that are Magento 2 native, which means you don't have to
39
+ add them to the filterAttributes configuration. These attributes are:
40
+
41
+ - price
42
+ - category_uid
43
+
44
+ If you want to map other attributes, you can add them to the filterAttributes
45
+ configuration. For example, if you want to map the `color` attribute, you can
46
+ add the following configuration:
47
+
48
+ ```
49
+ filterAttributes: [
50
+ {
51
+ aggregation: 'color',
52
+ toAlgoliaAttribute: 'color',
53
+ },
54
+ ]
55
+ ```
56
+
57
+ Filling in the aggregation (Magento 2) enables this plugin to read the
58
+ aggregation properties, such as the label and the options. The
59
+ toAlgoliaAttribute (Algolia) is the attribute that is used in the Algolia index
60
+ and connects the correct values to the aggregation.
61
+
62
+ ## When to use sortOptions?
63
+
64
+ You can use the sortOptions to define new sorting options inside the Algolia
65
+ plugin. The label is the name of the sorting option that is shown in the UI. The
66
+ value is the value that is used to find the sort index in the Algolia API. For
67
+ example, if you want to add a new sorting option called `Newest`, you can add
68
+ the following configuration:
69
+
70
+ ```
71
+ sortOptions: [
72
+ {
73
+ label: 'Newest',
74
+ value: 'newest-product-index',
75
+ },
76
+ ]
77
+ ```
78
+
79
+ ## What is the recommended algoliaSearchDebounceTime?
80
+
81
+ We've added a debounce time to the search feature to prevent it from being
82
+ called too frequently. This means that the search function will wait for a
83
+ specified amount of time before executing, which reduces the number of queries
84
+ sent to the Algolia API and can reduce cost. The default debounce time is 0
85
+ milliseconds for optimal responsiveness, but you can adjust it by adding the
86
+ `algoliaSearchDebounceTime` parameter to your Graphcommerce configuration as
87
+ following:
88
+
89
+ ```
90
+ algoliaSearchDebounceTime: 500
91
+
92
+ ```
29
93
 
30
94
  ## Add server side hydration to Algolia Search
31
95
 
96
+ **_NOTE:_** Server side hydration is currently not supported due to the current
97
+ requirement of `useRouter` inside this plugin. We are working on a solution to
98
+ this problem.
99
+
32
100
  1. Add `react-instantsearch-hooks-server` package to your project
33
101
 
34
102
  ```
@@ -56,12 +124,15 @@ type SearchResultProps = DefaultPageQuery &
56
124
  ```
57
125
 
58
126
  3. Add the `getServerState` method from the `react-instantsearch-hooks-server`
59
- package to the imports of your search page
127
+ package and the `renderToString` method from the `react-dom/server` package
128
+ to the imports of your search page.
60
129
 
61
130
  ```
62
131
  ...
63
132
  import { getServerState } from 'react-instantsearch-hooks-server'
133
+ import {renderToString } from 'react-dom/server'
64
134
  ...
135
+
65
136
  ```
66
137
 
67
138
  4. Assign the result of the `getServerState` method to the `serverState`
@@ -109,3 +180,66 @@ return {
109
180
 
110
181
  + <SearchContext serverProps={serverState}>
111
182
  ```
183
+
184
+ ## Add Algolia indexing to Magento 2
185
+
186
+ 1. Follow the algolia docs to enable the Algolia integration in Magento 2
187
+ [here](https://www.algolia.com/doc/integration/magento-2/getting-started/quick-start/?client=php).
188
+
189
+ ## Add Algolia indexing to HyGraph
190
+
191
+ 1. Add the `algoliasearch` package to your project
192
+
193
+ ```
194
+ yarn add algoliasearch
195
+ ```
196
+
197
+ 2. Add the `@hygraph/utils` package to your project
198
+
199
+ ```
200
+ yarn add @hygraph/utils
201
+ ```
202
+
203
+ 3. Create two new api routes in your project, one for adding an entry and one
204
+ for removing an entry from the Algolia index.
205
+
206
+ ```
207
+ - pages
208
+ - api
209
+ - algolia
210
+ - add.ts
211
+ - remove.ts
212
+ ```
213
+
214
+ **_NOTE:_** Both the `add.ts` and `remove.ts` files are implemented with
215
+ signature verification. This is to prevent unwanted requests to your api routes.
216
+ You can read more about signature verification for webhooks
217
+ [here](https://hygraph.com/blog/introducing-signed-webhooks).
218
+
219
+ 4. The following code is an example of how the `add.ts` file could look like.
220
+ This file is responsible for adding an entry to the Algolia index.
221
+
222
+ ```ts
223
+ import { addHygraphRecord } from '@graphcommerce/algolia-search'
224
+ import { NextApiRequest, NextApiResponse } from 'next'
225
+
226
+ export default (req: NextApiRequest, res: NextApiResponse) =>
227
+ addHygraphRecord(req, res)
228
+ ```
229
+
230
+ 5. The following code is an example of how the `remove.ts` file could look like.
231
+ This file is responsible for removing an entry from the Algolia index.
232
+
233
+ ```ts
234
+ import { removeHygraphRecord } from '@graphcommerce/algolia-search'
235
+ import { NextApiRequest, NextApiResponse } from 'next'
236
+
237
+ export default (req: NextApiRequest, res: NextApiResponse) =>
238
+ await removeHygraphRecord(req, res)
239
+ ```
240
+
241
+ 6. Follow the webhook setup guide to add the webhooks to your HyGraph project.
242
+
243
+ You can follow
244
+ [these](https://hygraph.com/docs/guides/webhooks/webhooks-overview#configure-webhooks)
245
+ instructions to configure the webhooks in your HyGraph project.
@@ -0,0 +1,60 @@
1
+ import { Page } from '@graphcommerce/graphql-mesh'
2
+ import { verifyWebhookSignature } from '@hygraph/utils'
3
+ import algoliasearch from 'algoliasearch'
4
+ import { NextApiRequest, NextApiResponse } from 'next'
5
+
6
+ type AlgoliaResult = {
7
+ objectID: string
8
+ slug: string
9
+ name: string
10
+ content: string
11
+ url: string
12
+ algoliaLastUpdateAtCET: string
13
+ }
14
+
15
+ const algolia = algoliasearch(
16
+ import.meta.graphCommerce.algoliaApplicationId,
17
+ import.meta.graphCommerce.algoliaSearchOnlyApiKey,
18
+ )
19
+
20
+ const indexName = import.meta.graphCommerce.algoliaHygraphIndex
21
+ const index = typeof indexName === 'string' ? algolia.initIndex(indexName) : undefined
22
+
23
+ export function addHygraphRecord(req: NextApiRequest, res: NextApiResponse) {
24
+ if (req.method !== 'POST') res.end()
25
+
26
+ const { body } = req
27
+ const gcms = req.headers['gcms-signature']
28
+ const signature = Array.isArray(gcms) ? gcms[0] : gcms
29
+ const secret = import.meta.graphCommerce.hygraphSecret
30
+
31
+ if (!index) return res.status(400).send('Missing "algoliaHygraphIndex" config variable')
32
+ if (!secret) return res.status(400).send('Missing "hygraphSecret" config variable')
33
+ if (!signature) return res.status(400).send('Missing gcms-signature header')
34
+
35
+ if (!verifyWebhookSignature({ body, signature, secret })) res.status(401).end()
36
+
37
+ try {
38
+ const { data } = body
39
+
40
+ const { id: objectID, localizations } = data
41
+
42
+ const objects = localizations.map((localization: Page & { updatedAt: string }) => {
43
+ const url = `${import.meta.graphCommerce.canonicalBaseUrl}/${localization?.url}`
44
+ const algoliaObject: AlgoliaResult = {
45
+ name: localization?.title ?? '',
46
+ content: localization?.metaDescription,
47
+ url,
48
+ slug: localization?.url,
49
+ algoliaLastUpdateAtCET: localization?.updatedAt,
50
+ objectID,
51
+ }
52
+
53
+ return index.saveObject(algoliaObject)
54
+ })
55
+
56
+ return res.status(201).send(objects)
57
+ } catch (err) {
58
+ return res.status(400).send(err)
59
+ }
60
+ }
@@ -0,0 +1,36 @@
1
+ import { verifyWebhookSignature } from '@hygraph/utils'
2
+ import algoliasearch from 'algoliasearch'
3
+ import { NextApiRequest, NextApiResponse } from 'next'
4
+
5
+ const algolia = algoliasearch(
6
+ import.meta.graphCommerce.algoliaApplicationId,
7
+ import.meta.graphCommerce.algoliaSearchOnlyApiKey,
8
+ )
9
+
10
+ const indexName = import.meta.graphCommerce.algoliaHygraphIndex
11
+ const index = indexName ? algolia.initIndex(indexName) : undefined
12
+
13
+ export async function removeHygraphRecord(req: NextApiRequest, res: NextApiResponse) {
14
+ if (req.method !== 'DELETE') return res.end()
15
+
16
+ const { body } = req
17
+ const gcms = req.headers['gcms-signature']
18
+ const signature = Array.isArray(gcms) ? gcms[0] : gcms
19
+ const secret = import.meta.graphCommerce.hygraphSecret
20
+
21
+ if (!index) return res.status(400).send('Missing "algoliaHygraphIndex" config variable')
22
+ if (!secret) return res.status(400).send('Missing "hygraphSecret" config variable')
23
+ if (!signature) return res.status(400).send('Missing gcms-signature header')
24
+
25
+ if (!verifyWebhookSignature({ body, signature, secret })) res.status(401).end()
26
+
27
+ try {
28
+ const { id: objectID, ...rest } = body
29
+
30
+ await index.deleteObject(objectID as string)
31
+
32
+ return res.status(201).send(rest)
33
+ } catch (err) {
34
+ return res.status(400).send(err)
35
+ }
36
+ }
package/index.ts CHANGED
@@ -0,0 +1,2 @@
1
+ export * from './api/add-hygraph-record'
2
+ export * from './api/remove-hygraph-record'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/algolia-search",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "6.2.0-canary.91",
5
+ "version": "6.2.0-canary.92",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,20 +12,20 @@
12
12
  }
13
13
  },
14
14
  "devDependencies": {
15
- "@graphcommerce/eslint-config-pwa": "6.2.0-canary.91",
16
- "@graphcommerce/next-config": "^6.2.0-canary.91",
17
- "@graphcommerce/prettier-config-pwa": "6.2.0-canary.91",
18
- "@graphcommerce/typescript-config-pwa": "6.2.0-canary.91"
15
+ "@graphcommerce/eslint-config-pwa": "6.2.0-canary.92",
16
+ "@graphcommerce/next-config": "^6.2.0-canary.92",
17
+ "@graphcommerce/prettier-config-pwa": "6.2.0-canary.92",
18
+ "@graphcommerce/typescript-config-pwa": "6.2.0-canary.92"
19
19
  },
20
20
  "dependencies": {
21
- "@graphcommerce/graphql": "6.2.0-canary.91",
22
- "@graphcommerce/ecommerce-ui": "6.2.0-canary.91",
23
- "@graphcommerce/magento-search": "6.2.0-canary.91",
24
- "@graphcommerce/next-config": "^6.2.0-canary.91",
25
- "@graphcommerce/next-ui": "6.2.0-canary.91",
26
- "@graphcommerce/magento-product": "6.2.0-canary.91",
27
- "@graphcommerce/graphql-mesh": "6.2.0-canary.91",
28
- "@graphcommerce/magento-store": "6.2.0-canary.91",
21
+ "@graphcommerce/graphql": "6.2.0-canary.92",
22
+ "@graphcommerce/ecommerce-ui": "6.2.0-canary.92",
23
+ "@graphcommerce/magento-search": "6.2.0-canary.92",
24
+ "@graphcommerce/next-config": "^6.2.0-canary.92",
25
+ "@graphcommerce/next-ui": "6.2.0-canary.92",
26
+ "@graphcommerce/magento-product": "6.2.0-canary.92",
27
+ "@graphcommerce/graphql-mesh": "6.2.0-canary.92",
28
+ "@graphcommerce/magento-store": "6.2.0-canary.92",
29
29
  "algoliasearch": "^4.15.0",
30
30
  "react-instantsearch-hooks-web": "^6.41.0"
31
31
  },
@@ -38,4 +38,4 @@
38
38
  "react": "^18.2.0",
39
39
  "react-dom": "^18.2.0"
40
40
  }
41
- }
41
+ }