@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 +6 -0
- package/Config.graphqls +10 -0
- package/README.md +135 -1
- package/api/add-hygraph-record.ts +60 -0
- package/api/remove-hygraph-record.ts +36 -0
- package/index.ts +2 -0
- package/package.json +14 -14
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
|
|
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
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.
|
|
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.
|
|
16
|
-
"@graphcommerce/next-config": "^6.2.0-canary.
|
|
17
|
-
"@graphcommerce/prettier-config-pwa": "6.2.0-canary.
|
|
18
|
-
"@graphcommerce/typescript-config-pwa": "6.2.0-canary.
|
|
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.
|
|
22
|
-
"@graphcommerce/ecommerce-ui": "6.2.0-canary.
|
|
23
|
-
"@graphcommerce/magento-search": "6.2.0-canary.
|
|
24
|
-
"@graphcommerce/next-config": "^6.2.0-canary.
|
|
25
|
-
"@graphcommerce/next-ui": "6.2.0-canary.
|
|
26
|
-
"@graphcommerce/magento-product": "6.2.0-canary.
|
|
27
|
-
"@graphcommerce/graphql-mesh": "6.2.0-canary.
|
|
28
|
-
"@graphcommerce/magento-store": "6.2.0-canary.
|
|
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
|
+
}
|