Skip to content

Commit

Permalink
feat: add topsort search resolver (#3)
Browse files Browse the repository at this point in the history
This PR adds topsort search resolver
  • Loading branch information
barbmarcio authored Sep 10, 2024
1 parent cd367e8 commit 858329e
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 17 deletions.
22 changes: 17 additions & 5 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"vendor": "vtex",
"vendor": "topsortpartnercl",
"name": "search-resolver",
"version": "1.72.0",
"title": "GraphQL resolver for the VTEX store APIs",
"description": "GraphQL resolvers for the VTEX API for the catalog and orders.",
"version": "0.1.0",
"title": "Topsort's Fork of vtex search resolver.",
"description": "Topsort's Fork of vtex search resolver.",
"credentialType": "absolute",
"builders": {
"node": "6.x",
Expand All @@ -18,9 +18,14 @@
"vtex.intelligent-search-api": "0.x"
},
"settingsSchema": {
"title": "Intelligent Search Resolver",
"title": "Topsort Intelligent Search Resolver",
"type": "object",
"properties": {
"topsortApiKey": {
"title": "Topsort API Key",
"type": "string",
"default": ""
},
"slugifyLinks": {
"title": "Set to slugify links. Uses default catalog slug instead",
"type": "boolean",
Expand Down Expand Up @@ -64,6 +69,13 @@
"path": "/*"
}
},
{
"name": "outbound-access",
"attrs": {
"host": "api.topsort.com",
"path": "/*"
}
},
{
"name": "vtex.messages:graphql-translate-messages"
}
Expand Down
96 changes: 89 additions & 7 deletions node/clients/intelligent-search-api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Auction } from "@topsort/sdk";
import { ExternalClient, InstanceOptions, IOContext } from "@vtex/api";
import { parseState } from "../utils/searchState";
import axios from "axios";

const isPathTraversal = (str: string) => str.indexOf('..') >= 0
interface CorrectionParams {
Expand Down Expand Up @@ -107,25 +109,105 @@ export class IntelligentSearchApi extends ExternalClient {
})
}

public async productSearch(params: SearchResultArgs, path: string, shippingHeader?: string[]) {
const {query, leap, searchState} = params
public async productSearch(
params: SearchResultArgs,
path: string,
shippingHeader?: string[],
topsortApiKey?: string,
) {
const { query, leap, searchState } = params;
if (isPathTraversal(path)) {
throw new Error("Malformed URL")
throw new Error("Malformed URL");
}

return this.http.get(`/product_search/${path}`, {
const result = await this.http.get(`/product_search/${path}`, {
params: {
query: query && decodeQuery(query),
locale: this.locale,
bgy_leap: leap ? true : undefined,
...parseState(searchState),
...params,
},
metric: 'product-search',
metric: "product-search",
headers: {
'x-vtex-shipping-options': shippingHeader ?? '',
"x-vtex-shipping-options": shippingHeader ?? "",
},
})
});

if (result.products.length === 0) {
return result;
}

if (!topsortApiKey) {
return result;
}

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const productIds = result.products.map((product: any) => product.productId);
const auction: Auction = {
auctions: [
{
type: "listings",
// TODO: Set number of slots in params
slots: params.sponsoredCount || 2,
products: {
ids: productIds,
},
},
],
};

try {
const url = "https://api.topsort.com/v2/auctions";
const auctionResult = await axios(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${topsortApiKey}`,
},
data: auction,
});

// eslint-disable-next-line no-console
console.log("auctionResult", auctionResult.data);

const sponsoredProducts =
auctionResult.data.results[0].winners?.map((winner: any) => {
const product = result.products.find(
(product: any) => product.productId === winner.productId,
);
return {
...product,
sponsored: true,
topsort: {
resolvedBidId: winner.resolvedBidId,
},
};
}) || [];

for (const product of sponsoredProducts.reverse()) {
result.products.unshift(product);
}

// eslint-disable-next-line no-console
console.log("createAuction axios api test passed", result.products);
this.context.logger.info({
service: "IntelligentSearchApi",
message: "createAuction axios api test passed",
result: result.products,
});
} catch (err) {
this.context.logger.warn({
service: "IntelligentSearchApi",
error: err.message,
errorStack: err,
});
}

result.products.length =
result.products.length > Number(params.to) ? params.to : result.products.length;
return result;
}

public async sponsoredProducts(params: SearchResultArgs, path: string, shippingHeader?: string[]) {
Expand Down
3 changes: 2 additions & 1 deletion node/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ declare global {
}

interface AppSettings {
slugifyLinks: boolean
slugifyLinks: boolean,
topsortApiKey: string,
}

interface DegradedSearchError {
Expand Down
1 change: 1 addition & 0 deletions node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"dependencies": {
"@gocommerce/utils": "^0.6.11",
"@topsort/sdk": "^0.3.1",
"@vtex/vtexis-compatibility-layer": "^1.2.13",
"atob": "^2.1.2",
"axios": "^0.21.2",
Expand Down
2 changes: 1 addition & 1 deletion node/resolvers/search/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ export const FILTER_TITLE_SEP = '_'
export const CATEGORY_SEGMENT = 'c'
export const FULL_TEXT_SEGMENT = 'ft'

export const APP_NAME = 'vtex.search-resolver@1.x'
export const APP_NAME = 'topsort.search-resolver@0.x'
5 changes: 3 additions & 2 deletions node/resolvers/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { resolvers as assemblyOptionResolvers } from './assemblyOption'
import { resolvers as autocompleteResolvers } from './autocomplete'
import { resolvers as brandResolvers } from './brand'
import { resolvers as categoryResolvers } from './category'
import { MAP_VALUES_SEP, PATH_SEPARATOR } from './constants'
import { MAP_VALUES_SEP, PATH_SEPARATOR, APP_NAME } from './constants'
import { resolvers as discountResolvers } from './discount'
import { resolvers as itemMetadataResolvers } from './itemMetadata'
import { resolvers as itemMetadataPriceTableItemResolvers } from './itemMetadataPriceTableItem'
Expand Down Expand Up @@ -370,7 +370,8 @@ export const queries = {
// unnecessary field. It's is an object and breaks the @vtex/api cache
delete biggyArgs.selectedFacets

const result = await intelligentSearchApi.facets({...biggyArgs, query: args.fullText}, buildAttributePath(selectedFacets), shippingOptions)
const settings: AppSettings = await ctx.clients.apps.getAppSettings(APP_NAME)
const result = await intelligentSearchApi.facets({...biggyArgs, query: args.fullText}, buildAttributePath(selectedFacets), shippingOptions, settings.topsortApiKey)

if (ctx.vtex.tenant) {
ctx.translated = result.translated
Expand Down
7 changes: 6 additions & 1 deletion node/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,11 @@
dependencies:
type-detect "4.0.8"

"@topsort/sdk@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@topsort/sdk/-/sdk-0.3.1.tgz#41f70ebc691ea8e4003937be6444b3ed705eaad7"
integrity sha512-rXL2QlNtX+W6cwR/GGTsLCGpPyBWAltYaStMVMrTCVKnr90HmWc7bhDv6rZjf0A0OwCDLC2jNkagBDvk9N0hoQ==

"@types/accepts@*":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575"
Expand Down Expand Up @@ -4684,7 +4689,7 @@ static-extend@^0.1.1:
define-property "^0.2.5"
object-copy "^0.1.0"

"stats-lite@github:vtex/node-stats-lite#dist":
stats-lite@vtex/node-stats-lite#dist:
version "2.2.0"
resolved "https://codeload.github.com/vtex/node-stats-lite/tar.gz/1b0d39cc41ef7aaecfd541191f877887a2044797"
dependencies:
Expand Down

0 comments on commit 858329e

Please sign in to comment.