Skip to content

Commit

Permalink
Merge #1081
Browse files Browse the repository at this point in the history
1081: Provide the possibility to give your own httpClient and request configs r=bidoubiwa a=bidoubiwa

Fixes: #1060 
Fixes: #928

Co-authored-by: Charlotte Vermandel <charlottevermandel@gmail.com>
  • Loading branch information
meili-bors[bot] and bidoubiwa committed Apr 27, 2023
2 parents ce8e9c4 + 8ae81a7 commit fe74e1e
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/curvy-ravens-walk.md
@@ -0,0 +1,5 @@
---
"@meilisearch/instant-meilisearch": patch
---

Add the possibility to provide a custom HTTP request and custom requests configurations
37 changes: 37 additions & 0 deletions README.md
Expand Up @@ -94,6 +94,8 @@ const searchClient = instantMeiliSearch(
- [`primaryKey`](#primary-key): Specify the primary key of your documents (default `undefined`).
- [`keepZeroFacets`](#keep-zero-facets): Show the facets value even when they have 0 matches (default `false`).
- [`matchingStrategy`](#matching-strategy): Determine the search strategy on words matching (default `last`).
- [`requestConfig`](#request-config): Use custom request configurations.
- ['httpClient'](#custom-http-client): Use a custom HTTP client.

The options are added as the third parameter of the `instantMeilisearch` function.

Expand Down Expand Up @@ -178,6 +180,41 @@ The other strategy is `all`, where both `hello` and `world` **must** be present
}
```

### Request Config

You can provide a custom request configuration. Available field can be [found here](https://fetch.spec.whatwg.org/#requestinit).

for example, with custom headers.

```ts
{
requestConfig: {
headers: {
Authorization: AUTH_TOKEN
},
credentials: 'include'
}
}
```

### Custom HTTP client

You can use your own HTTP client, for example, with [`axios`](https://github.com/axios/axios).

```ts
{
httpClient: async (url, opts) => {
const response = await $axios.request({
url,
data: opts?.body,
headers: opts?.headers,
method: (opts?.method?.toLocaleUpperCase() as Method) ?? 'GET'
})
return response.data
}
}
```

## 🪡 Example with InstantSearch

The open-source [InstantSearch](https://www.algolia.com/doc/api-reference/widgets/js/) library powered by Algolia provides all the front-end tools you need to highly customize your search bar environment.
Expand Down
37 changes: 37 additions & 0 deletions packages/instant-meilisearch/README.md
Expand Up @@ -94,6 +94,8 @@ const searchClient = instantMeiliSearch(
- [`primaryKey`](#primary-key): Specify the primary key of your documents (default `undefined`).
- [`keepZeroFacets`](#keep-zero-facets): Show the facets value even when they have 0 matches (default `false`).
- [`matchingStrategy`](#matching-strategy): Determine the search strategy on words matching (default `last`).
- [`requestConfig`](#request-config): Use custom request configurations.
- ['httpClient'](#custom-http-client): Use a custom HTTP client.

The options are added as the third parameter of the `instantMeilisearch` function.

Expand Down Expand Up @@ -178,6 +180,41 @@ The other strategy is `all`, where both `hello` and `world` **must** be present
}
```

### Request Config

You can provide a custom request configuration. Available field can be [found here](https://fetch.spec.whatwg.org/#requestinit).

for example, with custom headers.

```ts
{
requestConfig: {
headers: {
Authorization: AUTH_TOKEN
},
credentials: 'include'
}
}
```

### Custom HTTP client

You can use your own HTTP client, for example, with [`axios`](https://github.com/axios/axios).

```ts
{
httpClient: async (url, opts) => {
const response = await $axios.request({
url,
data: opts?.body,
headers: opts?.headers,
method: (opts?.method?.toLocaleUpperCase() as Method) ?? 'GET'
})
return response.data
}
}
```

## 🪡 Example with InstantSearch

The open-source [InstantSearch](https://www.algolia.com/doc/api-reference/widgets/js/) library powered by Algolia provides all the front-end tools you need to highly customize your search bar environment.
Expand Down
41 changes: 41 additions & 0 deletions packages/instant-meilisearch/__tests__/custom-http-client.test.ts
@@ -0,0 +1,41 @@
import { instantMeiliSearch } from '../src'
import { meilisearchClient, dataset } from './assets/utils'

describe('Custom HTTP client tests', () => {
beforeAll(async () => {
const deleteTask = await meilisearchClient.deleteIndex('movies')
await meilisearchClient.waitForTask(deleteTask.taskUid)

const documentsTask = await meilisearchClient
.index('movies')
.addDocuments(dataset)
await meilisearchClient.index('movies').waitForTask(documentsTask.taskUid)
})

test('a custom HTTP client', async () => {
const httpClient = jest.fn(async (url: string, init?: RequestInit) => {
const result = await fetch(url, init)
return await result.json()
})

const searchClient = instantMeiliSearch(
'http://localhost:7700',
'masterKey',
{
httpClient,
}
)

const response = await searchClient.search([
{
indexName: 'movies',
params: {
attributesToRetrieve: [],
query: 'ariel',
},
},
])
expect(httpClient).toHaveBeenCalledTimes(2)
expect(response.results[0].hits.length).toEqual(1)
})
})
54 changes: 54 additions & 0 deletions packages/instant-meilisearch/__tests__/instantiation.test.ts
Expand Up @@ -40,4 +40,58 @@ describe('InstantMeiliSearch instantiation', () => {
})
}).toThrow(TypeError)
})

test('instantiation with custom request config with correct type', () => {
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
requestConfig: {},
})

expect(searchClient).toBeTruthy()
})

test('instantiation with custom request config set to undefined', () => {
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
requestConfig: undefined,
})

expect(searchClient).toBeTruthy()
})

test('instantiation with custom request config set to a string', () => {
expect(() => {
instantMeiliSearch('http://localhost:7700', '', {
// @ts-expect-error
requestConfig: '',
})
}).toThrow('Provided requestConfig should be an object')
})

test('instantiation with custom HTTP client with correct type', () => {
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
httpClient: async () => {
return new Promise((resolve) => {
resolve({})
})
},
})

expect(searchClient).toBeTruthy()
})

test('instantiation with custom HTTP client set to undefined', () => {
const searchClient = instantMeiliSearch('http://localhost:7700', '', {
httpClient: undefined,
})

expect(searchClient).toBeTruthy()
})

test('instantiation with custom HTTP client set to a string', () => {
expect(() => {
instantMeiliSearch('http://localhost:7700', '', {
// @ts-expect-error
httpClient: 'wrong type',
})
}).toThrow('Provided custom httpClient should be a function')
})
})
15 changes: 14 additions & 1 deletion packages/instant-meilisearch/src/client/config/index.ts
Expand Up @@ -2,6 +2,7 @@ import {
InstantMeiliSearchOptions,
InstantMeiliSearchConfig,
} from '../../types'
import { isPureObject } from '../../utils/object'

/**
* Get the configuration of instant meilisearch
Expand Down Expand Up @@ -54,8 +55,10 @@ export function getApiKey(apiKey: string | (() => string)): string {
*/
export function validateInstantMeiliSearchParams(
hostUrl: string,
apiKey: string | (() => string)
apiKey: string | (() => string),
instantMeiliSearchOptions: InstantMeiliSearchOptions
) {
const { requestConfig, httpClient } = instantMeiliSearchOptions
// Validate host url
if (typeof hostUrl !== 'string') {
throw new TypeError(
Expand All @@ -69,4 +72,14 @@ export function validateInstantMeiliSearchParams(
'Provided apiKey value (2nd parameter) is not a string or a function, expected string or function'
)
}

// Validate requestConfig
if (requestConfig !== undefined && !isPureObject(requestConfig)) {
throw new TypeError('Provided requestConfig should be an object')
}

// Validate custom HTTP client
if (httpClient && typeof httpClient !== 'function') {
throw new TypeError('Provided custom httpClient should be a function')
}
}
Expand Up @@ -7,6 +7,7 @@ import {
SearchContext,
FacetDistribution,
PaginationState,
MeilisearchConfig,
} from '../types'
import {
getApiKey,
Expand Down Expand Up @@ -39,7 +40,7 @@ export function instantMeiliSearch(
instantMeiliSearchOptions: InstantMeiliSearchOptions = {}
): InstantMeiliSearchInstance {
// Validate parameters
validateInstantMeiliSearchParams(hostUrl, apiKey)
validateInstantMeiliSearchParams(hostUrl, apiKey, instantMeiliSearchOptions)

// Resolve possible function to get apiKey
apiKey = getApiKey(apiKey)
Expand All @@ -48,11 +49,21 @@ export function instantMeiliSearch(
instantMeiliSearchOptions.clientAgents
)

const meilisearchClient = new MeiliSearch({
const meilisearchConfig: MeilisearchConfig = {
host: hostUrl,
apiKey,
clientAgents,
})
}

if (instantMeiliSearchOptions.httpClient !== undefined) {
meilisearchConfig.httpClient = instantMeiliSearchOptions.httpClient
}

if (instantMeiliSearchOptions.requestConfig !== undefined) {
meilisearchConfig.requestConfig = instantMeiliSearchOptions.requestConfig
}

const meilisearchClient = new MeiliSearch(meilisearchConfig)

const searchCache = SearchCache()
// create search resolver with included cache
Expand Down
8 changes: 6 additions & 2 deletions packages/instant-meilisearch/src/types/types.ts
@@ -1,9 +1,9 @@
import type { SearchClient } from 'instantsearch.js'
import type { MultipleQueriesQuery as AlgoliaMultipleQueriesQuery } from '@algolia/client-search'

import type {
MultiSearchQuery as MeiliSearchMultiSearchParams,
MultiSearchResult,
Config as MeilisearchConfig,
} from 'meilisearch'

export type { AlgoliaMultipleQueriesQuery, MultiSearchResult }
Expand All @@ -15,6 +15,7 @@ export type {
MeiliSearch,
FacetStats as MeiliFacetStats,
MultiSearchQuery as MeiliSearchMultiSearchParams,
Config as MeilisearchConfig,
} from 'meilisearch'

export type InstantSearchParams = AlgoliaMultipleQueriesQuery['params']
Expand All @@ -24,7 +25,10 @@ export const enum MatchingStrategies {
LAST = 'last',
}

export type InstantMeiliSearchOptions = {
export type InstantMeiliSearchOptions = Pick<
MeilisearchConfig,
'requestConfig' | 'httpClient'
> & {
placeholderSearch?: boolean
primaryKey?: string
keepZeroFacets?: boolean
Expand Down

0 comments on commit fe74e1e

Please sign in to comment.