Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(recommend): introduce Recommend API client #1280

Merged
merged 10 commits into from Jun 23, 2021
3 changes: 2 additions & 1 deletion .eslintrc.js
Expand Up @@ -61,10 +61,11 @@ module.exports = {
['@algolia/client-account', './packages/client-account/src'],
['@algolia/client-analytics', './packages/client-analytics/src'],
['@algolia/client-common', './packages/client-common/src'],
['@algolia/client-search', './packages/client-search/src'],
['@algolia/client-recommendation', './packages/client-recommendation/src'],
['@algolia/client-search', './packages/client-search/src'],
['@algolia/logger-common', './packages/logger-common/src'],
['@algolia/logger-console', './packages/logger-console/src'],
['@algolia/recommend', './packages/recommend/src'],
['@algolia/requester-browser-xhr', './packages/requester-browser-xhr/src'],
['@algolia/requester-common', './packages/requester-common/src'],
['@algolia/requester-node-http', './packages/requester-node-http/src'],
Expand Down
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -9,7 +9,7 @@
<a href="https://npmjs.org/package/algoliasearch"><img src="https://img.shields.io/npm/v/algoliasearch.svg?style=flat-square" alt="NPM version"></img></a>
<a href="http://npm-stat.com/charts.html?package=algoliasearch"><img src="https://img.shields.io/npm/dm/algoliasearch.svg?style=flat-square" alt="NPM downloads"></a>
<a href="https://www.jsdelivr.com/package/npm/algoliasearch"><img src="https://data.jsdelivr.com/v1/package/npm/algoliasearch/badge" alt="jsDelivr Downloads"></img></a>
<a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
<a href="LICENSE.md"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
</p>
</p>

Expand Down Expand Up @@ -88,4 +88,4 @@ Encountering an issue? Before reaching out to support, we recommend heading to o

## 📄 License

Algolia JavaScript API Client is an open-sourced software licensed under the [MIT license](LICENSE.txt).
Algolia JavaScript API Client is an open-sourced software licensed under the [MIT license](LICENSE.md).
4 changes: 4 additions & 0 deletions package.json
Expand Up @@ -103,6 +103,10 @@
{
"path": "packages/algoliasearch/dist/algoliasearch-lite.umd.js",
"maxSize": "4.35KB"
},
{
"path": "packages/recommend/dist/recommend.umd.js",
"maxSize": "4.1KB"
}
]
}
4 changes: 2 additions & 2 deletions packages/algoliasearch/README.md
Expand Up @@ -9,7 +9,7 @@
<a href="https://npmjs.org/package/algoliasearch"><img src="https://img.shields.io/npm/v/algoliasearch.svg?style=flat-square" alt="NPM version"></img></a>
<a href="http://npm-stat.com/charts.html?package=algoliasearch"><img src="https://img.shields.io/npm/dm/algoliasearch.svg?style=flat-square" alt="NPM downloads"></a>
<a href="https://www.jsdelivr.com/package/npm/algoliasearch"><img src="https://data.jsdelivr.com/v1/package/npm/algoliasearch/badge" alt="jsDelivr Downloads"></img></a>
<a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
<a href="LICENSE.md"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
</p>
</p>

Expand Down Expand Up @@ -79,4 +79,4 @@ For full documentation, visit the **[online documentation](https://www.algolia.c

## 📄 License

Algolia JavaScript API Client is an open-sourced software licensed under the [MIT license](LICENSE.txt).
Algolia JavaScript API Client is an open-sourced software licensed under the [MIT license](LICENSE.md).
7 changes: 7 additions & 0 deletions packages/client-common/src/__tests__/TestSuite.ts
Expand Up @@ -12,6 +12,8 @@ import { addMethods } from '..';
import algoliasearchForBrowser from '../../../algoliasearch/src/builds/browser';
import algoliasearchForBrowserLite from '../../../algoliasearch/src/builds/browserLite';
import algoliasearchForNode from '../../../algoliasearch/src/builds/node';
import recommendForBrowser from '../../../recommend/src/builds/browser';
import recommendForNode from '../../../recommend/src/builds/node';

/* eslint functional/no-class: 0 */
export class TestSuite {
Expand All @@ -29,6 +31,11 @@ export class TestSuite {
? algoliasearchForBrowser
: algoliasearchForNode;

// @ts-ignore `destroy` only exists on the Node build
public readonly recommend: typeof recommendForNode = this.isBrowser
? recommendForBrowser
: recommendForNode;

public indicesCount = 0;

public constructor(testName?: string) {
Expand Down
72 changes: 72 additions & 0 deletions packages/recommend/README.md
@@ -0,0 +1,72 @@
<p align="center">
<h1>Algolia Recommend</h1>

<h4>The perfect starting point to integrate <a href="https://www.algolia.com/products/recommendations" target="_blank">Algolia Recommend</a> within your JavaScript project</h4>

<p align="center">
<a href="https://npmjs.org/package/@algolia/recommend"><img src="https://img.shields.io/npm/v/@algolia/recommend.svg?style=flat-square" alt="NPM version"></img></a>
<a href="LICENSE.md"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
</p>
</p>

<p align="center">
<a href="https://www.algolia.com/doc/api-client/methods/recommend/" target="_blank">Documentation</a> •
<a href="https://www.algolia.com/doc/ui-libraries/recommend/introduction/what-is-recommend/" target="_blank">UI library</a> •
<a href="https://discourse.algolia.com" target="_blank">Community Forum</a> •
<a href="http://stackoverflow.com/questions/tagged/algolia" target="_blank">Stack Overflow</a> •
<a href="https://github.com/algolia/algoliasearch-client-javascript/issues" target="_blank">Report a bug</a> •
<a href="https://www.algolia.com/support" target="_blank">Support</a>
</p>

## ✨ Features

- Thin & **minimal low-level HTTP client** to interact with Algolia's Recommend API
- Works both on the **browser** and **node.js**
- **UMD compatible**, you can use it with any module loader
- Built with TypeScript

## 💡 Getting Started

First, install Algolia Recommend API Client via the [npm](https://www.npmjs.com/get-npm) package manager:

```bash
npm install @algolia/recommend
```

Then, let's retrieve recommendations:

```js
const algoliarecommend = require('@algolia/recommend');

const client = algoliarecommend('YourApplicationID', 'YourAdminAPIKey');

client
.getFrequentlyBoughtTogether({
indexName: 'your_index_name',
objectID: 'your_object_id',
})
.then(({ results }) => {
console.log(results);
})
.catch(err => {
console.log(err);
});

client
.getRelatedProducts({
indexName: 'your_index_name',
objectID: 'your_object_id',
})
.then(({ results }) => {
console.log(results);
})
.catch(err => {
console.log(err);
});
```

For full documentation, visit the **[online documentation](https://www.algolia.com/doc/api-client/methods/recommend/)**.

## 📄 License

Algolia Recommend API Client is an open-sourced software licensed under the [MIT license](LICENSE.md).
7 changes: 7 additions & 0 deletions packages/recommend/api-extractor.json
@@ -0,0 +1,7 @@
{
"extends": "../../api-extractor.json",
"mainEntryPointFilePath": "./dist/packages/recommend/src/builds/node.d.ts",
"dtsRollup": {
"untrimmedFilePath": "./dist/recommend.d.ts"
}
}
3 changes: 3 additions & 0 deletions packages/recommend/index.d.ts
@@ -0,0 +1,3 @@
/* eslint-disable import/no-unresolved*/
export * from './dist/recommend';
export { default } from './dist/recommend';
15 changes: 15 additions & 0 deletions packages/recommend/index.js
@@ -0,0 +1,15 @@
/* eslint-disable functional/immutable-data, import/no-commonjs */
const recommend = require('./dist/recommend.cjs.js');

/**
* The Common JS build is the default entry point for the Node environment. Keep in
* in mind, that for the browser environment, we hint the bundler to use the UMD
* build instead as specified on the key `browser` of our `package.json` file.
*/
module.exports = recommend;

/**
* In addition, we also set explicitly the default export below making
* this Common JS module in compliance with es6 modules specification.
*/
module.exports.default = recommend;
37 changes: 37 additions & 0 deletions packages/recommend/package.json
@@ -0,0 +1,37 @@
{
"name": "@algolia/recommend",
"version": "4.9.3",
"private": false,
"description": "The perfect starting point to integrate Algolia Recommend within your JavaScript project.",
"repository": {
"type": "git",
"url": "git://github.com/algolia/algoliasearch-client-javascript.git"
},
"license": "MIT",
"sideEffects": false,
"main": "index.js",
"jsdelivr": "./dist/recommend.umd.js",
"unpkg": "./dist/recommend.umd.js",
"browser": {
"./index.js": "./dist/recommend.umd.js"
},
"types": "index.d.ts",
"files": [
"dist",
"index.js",
"index.d.ts"
],
"dependencies": {
"@algolia/cache-browser-local-storage": "4.9.3",
"@algolia/cache-common": "4.9.3",
"@algolia/cache-in-memory": "4.9.3",
"@algolia/client-common": "4.9.3",
"@algolia/client-search": "4.9.3",
"@algolia/logger-common": "4.9.3",
"@algolia/logger-console": "4.9.3",
"@algolia/requester-browser-xhr": "4.9.3",
"@algolia/requester-common": "4.9.3",
"@algolia/requester-node-http": "4.9.3",
"@algolia/transporter": "4.9.3"
}
}
@@ -0,0 +1,77 @@
import { TestSuite } from '../../../client-common/src/__tests__/TestSuite';

const recommend = new TestSuite('recommend').recommend;

function createMockedClient() {
const client = recommend('appId', 'apiKey');
jest.spyOn(client.transporter, 'read').mockImplementation(() => Promise.resolve());

return client;
}

describe('getFrequentlyBoughtTogether', () => {
test('builds the request', async () => {
const client = createMockedClient();

await client.getFrequentlyBoughtTogether(
{
indexName: 'products',
objectID: 'B018APC4LE',
},
{}
Haroenv marked this conversation as resolved.
Show resolved Hide resolved
);

expect(client.transporter.read).toHaveBeenCalledTimes(1);
expect(client.transporter.read).toHaveBeenCalledWith(
{
cacheable: true,
data: {
requests: [
{
fallbackParameters: {},
indexName: 'products',
model: 'bought-together',
objectID: 'B018APC4LE',
threshold: 0,
},
],
},
method: 'POST',
path: '1/indexes/*/recommendations',
},
{}
);
});

test('ignores `fallbackParameters`', async () => {
const client = createMockedClient();

await client.getFrequentlyBoughtTogether({
// @ts-ignore `fallbackParameters` are not supposed to be passed
// according to the types
fallbackParameters: {
facetFilters: [],
},
indexName: 'products',
objectID: 'B018APC4LE',
});

expect(client.transporter.read).toHaveBeenCalledTimes(1);
expect(client.transporter.read).toHaveBeenCalledWith(
expect.objectContaining({
data: {
requests: [
{
fallbackParameters: {},
indexName: 'products',
model: 'bought-together',
objectID: 'B018APC4LE',
threshold: 0,
},
],
},
}),
undefined
);
});
});
78 changes: 78 additions & 0 deletions packages/recommend/src/__tests__/getRecommendations.test.ts
@@ -0,0 +1,78 @@
import { TestSuite } from '../../../client-common/src/__tests__/TestSuite';

const recommend = new TestSuite('recommend').recommend;

function createMockedClient() {
const client = recommend('appId', 'apiKey');
jest.spyOn(client.transporter, 'read').mockImplementation(() => Promise.resolve());

return client;
}

describe('getRecommendations', () => {
test('builds the request for "bought-together" model', async () => {
const client = createMockedClient();

await client.getRecommendations(
{
model: 'bought-together',
indexName: 'products',
objectID: 'B018APC4LE',
},
{}
);

expect(client.transporter.read).toHaveBeenCalledTimes(1);
expect(client.transporter.read).toHaveBeenCalledWith(
{
cacheable: true,
data: {
requests: [
{
indexName: 'products',
model: 'bought-together',
objectID: 'B018APC4LE',
threshold: 0,
},
],
},
method: 'POST',
path: '1/indexes/*/recommendations',
},
{}
);
});

test('builds the request for "related-products" model', async () => {
const client = createMockedClient();

await client.getRecommendations(
{
model: 'related-products',
indexName: 'products',
objectID: 'B018APC4LE',
},
{}
);

expect(client.transporter.read).toHaveBeenCalledTimes(1);
expect(client.transporter.read).toHaveBeenCalledWith(
{
cacheable: true,
data: {
requests: [
{
indexName: 'products',
model: 'related-products',
objectID: 'B018APC4LE',
threshold: 0,
},
],
},
method: 'POST',
path: '1/indexes/*/recommendations',
},
{}
);
});
});