Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [6.51.0] - 2026-06-23
### Added
- Base `IOClients` getter `janusCatalogSystem` (Janus Catalog) and
`licenseManager.listBindings`, plus binding-mapping helpers
(getCanonicalAndAlternateAddresses, inferTargetProduct) used by the
Tenant API migration. Backport of #617 to the 6.x line.

## [6.50.1] - 2025-09-10
### Fixed
- Axios vuln GHSA-jr5f-v2jv-69x6 by using axios@^0.30.1
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vtex/api",
"version": "6.50.2-beta.0",
"version": "6.51.0",
"description": "VTEX I/O API client",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
6 changes: 5 additions & 1 deletion src/clients/IOClients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CatalogGraphQL } from './apps/catalogGraphQL/index'
import { ID, MasterData, PaymentProvider } from './external'
import { Apps, Assets, BillingMetrics, Events, Registry, Router, VBase, Workspaces } from './infra'
import { IOClient, IOClientConstructor } from './IOClient'
import { LicenseManager, Segment, Session, TenantClient } from './janus'
import { Catalog, LicenseManager, Segment, Session, TenantClient } from './janus'

export type ClientsImplementation<T extends IOClients> = new (
clientOptions: Record<string, InstanceOptions>,
Expand Down Expand Up @@ -93,6 +93,10 @@ export class IOClients {
return this.getOrSet('catalogGraphQL', CatalogGraphQL)
}

public get janusCatalogSystem() {
return this.getOrSet('janusCatalogSystem', Catalog)
}

public get paymentProvider() {
return this.getOrSet('paymentProvider', PaymentProvider)
}
Expand Down
38 changes: 38 additions & 0 deletions src/clients/janus/Catalog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { inflightUrlWithQuery, RequestConfig } from '../../../HttpClient'
import { JanusClient } from '../JanusClient'
import { SalesChannel } from './types'

const BASE_URL = '/api/catalog_system'

const routes = {
defaultSalesChannel: () => `${BASE_URL}/pub/saleschannel/default`,
salesChannel: (salesChannelId: number) => `${BASE_URL}/pub/saleschannel/${salesChannelId}`,
}

export class Catalog extends JanusClient {
public getSalesChannel(id: number, config?: RequestConfig) {
const metric = 'catalog-saleschannel'
return this.http.get<SalesChannel>(routes.salesChannel(id), {
inflightKey: inflightUrlWithQuery,
metric,
...config,
tracing: {
requestSpanNameSuffix: metric,
...config?.tracing,
},
})
}

public getDefaultSalesChannel(config?: RequestConfig) {
const metric = 'catalog-saleschannel-default'
return this.http.get<SalesChannel>(routes.defaultSalesChannel(), {
inflightKey: inflightUrlWithQuery,
metric,
...config,
tracing: {
requestSpanNameSuffix: metric,
...config?.tracing,
},
})
}
}
5 changes: 5 additions & 0 deletions src/clients/janus/Catalog/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface SalesChannel {
Id: number
CultureInfo: string
CurrencyCode: string
}
64 changes: 0 additions & 64 deletions src/clients/janus/LicenseManager.ts

This file was deleted.

85 changes: 85 additions & 0 deletions src/clients/janus/LicenseManager/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { inflightUrlWithQuery, RequestConfig, RequestTracingConfig } from '../../../HttpClient'
import { JanusClient } from '../JanusClient'
import { APIBindingRes } from './types'

export * from './types'
export * from './utils'

const TWO_MINUTES_S = 2 * 60

const BASE_URL = '/api/license-manager'

const routes = {
accountData: () => `${BASE_URL}/account`,
listBindings: (tenant: string) => `${BASE_URL}/binding/site/${encodeURIComponent(tenant)}`,
resourceAccess: (resourceKey: string) => `${BASE_URL}/resources/${encodeURIComponent(resourceKey)}/access`,
topbarData: () => `${BASE_URL}/site/pvt/newtopbar`,
}

export class LicenseManager extends JanusClient {
public getAccountData(VtexIdclientAutCookie: string, tracingConfig?: RequestTracingConfig) {
const metric = 'lm-account-data'
return this.http.get(routes.accountData(), {
forceMaxAge: TWO_MINUTES_S,
headers: {
VtexIdclientAutCookie,
},
inflightKey: inflightUrlWithQuery,
metric,
tracing: {
requestSpanNameSuffix: metric,
...tracingConfig?.tracing,
},
})
}

public getTopbarData(VtexIdclientAutCookie: string, tracingConfig?: RequestTracingConfig) {
const metric = 'lm-topbar-data'
return this.http.get(routes.topbarData(), {
headers: {
VtexIdclientAutCookie,
},
metric,
tracing: {
requestSpanNameSuffix: metric,
...tracingConfig?.tracing,
},
})
}

public canAccessResource(VtexIdclientAutCookie: string, resourceKey: string, tracingConfig?: RequestTracingConfig) {
const metric = 'lm-resource-access'
return this.http
.get(routes.resourceAccess(resourceKey), {
headers: {
VtexIdclientAutCookie,
},
metric,
tracing: {
requestSpanNameSuffix: metric,
...tracingConfig?.tracing,
},
})
.then(
() => true,
() => false
)
}

public listBindings(tenant: string, config?: RequestConfig) {
const metric = 'lm-list-bindings'
return this.http.get<APIBindingRes[]>(routes.listBindings(tenant), {
headers: {
VtexIdclientAutCookie: this.context.authToken,
},
inflightKey: inflightUrlWithQuery,
memoizeable: true,
metric,
...config,
tracing: {
requestSpanNameSuffix: metric,
...config?.tracing,
},
})
}
}
17 changes: 17 additions & 0 deletions src/clients/janus/LicenseManager/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface APIAddress {
Host: string
IsCanonical: boolean
BasePath: string
Localization: {
[k: string]: string
}
}

export interface APIBindingRes {
Id: string
Addresses: APIAddress[]
SiteName: string
DefaultSalesChannelId: number | null
DefaultLocale: string
SupportedLocales: string[]
}
53 changes: 53 additions & 0 deletions src/clients/janus/LicenseManager/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { APIBindingRes } from './types'

const buildAddressPath = (basePath: string, localizationKey: string): string => {
if (basePath === '') {
return `/${localizationKey}`
}

if (localizationKey === '') {
return `/${basePath}`
}

return `/${basePath}/${localizationKey}`
}

export const getCanonicalAndAlternateAddresses = (
lmBinding: APIBindingRes
): { canonicalBaseAddress: string; alternateBaseAddresses: string[] } => {
let canonicalBaseAddress = ''
const alternateBaseAddresses: string[] = []

for (const addressEntry of lmBinding.Addresses) {
const localizationKeys = Object.keys(addressEntry.Localization)

for (const localizationKey of localizationKeys) {
alternateBaseAddresses.push(addressEntry.Host + buildAddressPath(addressEntry.BasePath, localizationKey))
}

if (addressEntry.IsCanonical) {
// The canonical address is built from the shortest localization key.
const [shortestLocalizationKey = ''] = [...localizationKeys].sort((a, b) => a.length - b.length)
canonicalBaseAddress = addressEntry.Host + buildAddressPath(addressEntry.BasePath, shortestLocalizationKey)
}
}

// The canonical address must not also appear in the alternates list.
const canonicalIndexInAlternates = alternateBaseAddresses.indexOf(canonicalBaseAddress)
if (canonicalIndexInAlternates !== -1) {
alternateBaseAddresses.splice(canonicalIndexInAlternates, 1)
}

return { canonicalBaseAddress, alternateBaseAddresses }
}

export const inferTargetProduct = (
canonicalBaseAddress: string,
alternateBaseAddresses: string[]
): 'vtex-admin' | 'vtex-storefront' => {
const isAdmin =
canonicalBaseAddress.endsWith('myvtex.com/admin') ||
alternateBaseAddresses.some((address) => address.endsWith('myvtex.com/admin'))

return isAdmin ? 'vtex-admin' : 'vtex-storefront'
}
1 change: 1 addition & 0 deletions src/clients/janus/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './LicenseManager'
export * from './Segment'
export * from './Session'
export * from './Tenant'
export * from './Catalog'