Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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'