[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/analytics.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

const ENDPOINT = 'http://rc.vtex.com'
const METRICS_PATH = '/api/analytics/schemaless-events'
const MAX_RETRIES = 2
const RETRY_INTERVAL = 1000 // 1 second

export const B2B_METRIC_NAME = 'b2b-suite-buyerorg-data'

export interface Metric {
  readonly account: string
  readonly kind: string
  readonly description: string
  readonly name: typeof B2B_METRIC_NAME
}

export default class AnalyticsClient extends ExternalClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(ENDPOINT, context, {
      ...options,
      headers: {
        'X-Vtex-Use-Https': 'true',
      },
    })
  }

  public async sendMetric(metric: Metric, retries = 0) {
    try {
      await this.http.post(METRICS_PATH, metric)
    } catch (error) {
      if (retries < MAX_RETRIES) {
        await new Promise((resolve) => setTimeout(resolve, RETRY_INTERVAL))
        await this.sendMetric(metric, retries + 1)
      } else {
        throw new Error(`${error.message} - after ${MAX_RETRIES} retries.`)
      }
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/checkout.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { IOContext, InstanceOptions } from '@vtex/api'
import { JanusClient } from '@vtex/api'

export default class Checkout extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, { ...options })
  }

  public async requestCancellation(orderId: string, reason: string) {
    return this.http.postRaw(
      `${this.routes.requestCancellation(orderId)}`,
      { reason },
      {
        headers: {
          VtexIdclientAutCookie: this.context.authToken,
        },
        metric: 'checkout-requestCancellation',
      }
    )
  }

  private get routes() {
    const basePvt = '/api/checkout/pvt'

    return {
      requestCancellation: (orderId: string) =>
        `${basePvt}/orders/${orderId}/cancelation-request`,
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/email.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'

const MAIL_SERVICE_PATH = '/api/mail-service/pvt/sendmail'
const TEMPLATE_RENDER_PATH = '/api/template-render/pvt/templates'

export default class MailClient extends JanusClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(context, {
      ...options,
      headers: {
        VtexIdClientAutCookie: context.authToken,
      },
    })
  }

  public async sendMail(mailData: MailData): Promise<string> {
    return this.http.post(MAIL_SERVICE_PATH, mailData, {
      metric: 'mail-post-send',
    })
  }

  public getTemplate(name: string) {
    return this.http.get<Template>(`${TEMPLATE_RENDER_PATH}/${name}`, {
      metric: 'mail-get-template',
    })
  }

  public publishTemplate(template: Template) {
    return this.http.post(TEMPLATE_RENDER_PATH, template, {
      metric: 'mail-post-template',
    })
  }
}

export interface MailData {
  templateName: string
  jsonData: JsonData
}

export interface JsonData {
  message: {
    to: string
  }
  organization: {
    name: string
    admin?: string
    note?: string
    status?: string
  }
}

interface Template {
  AccountId?: string
  AccountName?: string
  ApplicationId?: string
  Description?: string
  FriendlyName: string
  IsDefaultTemplate: boolean
  IsPersisted: boolean
  IsRemoved: boolean
  Name: string
  Type: string
  Templates: {
    email: {
      To: string
      CC?: string
      BCC?: string
      Subject: string
      Message: string
      Type: string
      ProviderId: string
      ProviderName?: string
      IsActive: boolean
      withError: boolean
    }
    sms: {
      Type: string
      ProviderId?: string
      ProviderName?: string
      IsActive: boolean
      withError: boolean
      Parameters: string[]
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/vtexId.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'

interface AuthenticatedUser {
  user: string
}

export default class VtexId extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, { ...options })
  }

  public async getAuthenticatedUser(
    authToken: string
  ): Promise<AuthenticatedUser> {
    return this.http.get('/api/vtexid/pub/authenticated/user/', {
      params: { authToken },
      metric: 'authenticated-user-get',
    })
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/sellers.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'
import { isNaN, orderBy } from 'lodash'

const SELLERS_PATH = '/api/seller-register/pvt/sellers'

export interface Seller {
  id: string
  name: string
  email: string
}

export interface GetSellersResponse {
  items: Seller[]
  pagination: {
    page: number
    pageSize: number
    total: number
  }
}

export interface GetSellersOpts {
  page: number
  pageSize: number
}

export default class SellersClient extends JanusClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(context, {
      ...options,
      headers: {
        VtexIdClientAutCookie: context.authToken,
      },
    })
  }

  public async getSellers(): Promise<{ items: Seller[] }> {
    return this.http.get(SELLERS_PATH)
  }

  public async getSellersPaginated(
    opts?: GetSellersOpts
  ): Promise<GetSellersResponse> {
    const INITIAL_LIST_OPTIONS = {
      page: 1,
      pageSize: 100,
    }

    const argsWithDefaults = {
      ...INITIAL_LIST_OPTIONS,
      ...opts,
    }

    const { page, pageSize } = argsWithDefaults

    if (page < 1 || pageSize < 1) {
      throw new Error(
        `Please make sure you are passing valid values for 'page' and 'pageSize'. Received: page=${page}, pageSize=${pageSize}`
      )
    }

    const from = (page - 1) * pageSize
    const to = page * pageSize

    const result = await this.http.get<{
      items: Seller[]
      paging: {
        from: number
        to: number
        total: number
      }
    }>(SELLERS_PATH, {
      metric: 'sellers-paginated-get',
      params: { from, to },
    })

    if (!result.items) {
      return {
        items: [],
        pagination: {
          page: 1,
          pageSize: 0,
          total: 0,
        },
      }
    }

    if (result.items.length > 1) {
      result.items = this.sortSellers(result.items)
    }

    return {
      items: result.items,
      pagination: {
        page,
        pageSize,
        total: result.paging.total,
      },
    }
  }

  private sortSellers(sellers: Seller[]) {
    // Sorts the sellers first based on whether the name is a number, then sorts by the actual name
    return orderBy(
      sellers,
      [
        (seller: Seller) => {
          const name = seller.name ?? ''
          // Check if the name is a number and not an empty string
          const isNumber = !isNaN(name) && name !== ''

          // Returns 1 if the name is a number, or 0 if not, to prioritize sorting numbers first
          return isNumber ? 1 : 0
        },
        (seller: Seller) => {
          const name = seller.name ?? ''

          // If the name is not a number, return the name as a string for alphabetical sorting
          // If it is a number, convert it to a number for numeric sorting

          return isNaN(name) ? name : Number(name)
        },
      ],
      // Sort both criteria in ascending order
      ['asc', 'asc']
    )
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/Orders.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext, RequestConfig } from '@vtex/api'
import { JanusClient } from '@vtex/api'

import { statusToError } from '../utils'

export default class OrdersClient extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...options?.headers,
        VtexIdclientAutCookie: ctx.authToken,
      },
    })
  }

  public order = (id: string) =>
    this.get(this.routes.order(id), { metric: 'orders' })

  protected get = <T>(url: string, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
    }

    return this.http.get<T>(url, config).catch(statusToError)
  }

  private get routes() {
    const base = '/api/orders'

    return {
      order: (id: string) => `${base}/pvt/document/${id}`,
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/Oms.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext, RequestConfig } from '@vtex/api'
import { JanusClient } from '@vtex/api'

import { statusToError } from '../utils'

export default class OMSClient extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...options?.headers,
        VtexIdclientAutCookie: ctx.authToken,
      },
    })
  }

  public order = (id: string) =>
    this.get(this.routes.order(id), { metric: 'oms-order' })

  public search = (query: string) =>
    this.get(this.routes.search(query), { metric: 'oms-order-search' })

  protected get = <T>(url: string, config: RequestConfig = {}) => {
    config.headers = {
      ...config.headers,
    }

    return this.http.get<T>(url, config).catch(statusToError)
  }

  private get routes() {
    const base = '/api/oms'

    return {
      order: (id: string) => `${base}/pvt/admin/orders/${id}`,
      search: (query: string) => `${base}/pvt/orders?${query}`,
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/LMClient.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { InstanceOptions, IOContext } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

export default class LMClient extends ExternalClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(`http://${ctx.account}.vtexcommercestable.com.br/`, ctx, {
      ...options,
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        VtexIdclientAutCookie: ctx.authToken,
      },
    })
  }

  public getUserAdminPermissions = async (account: string, userId: string) => {
    return this.get(
      `/api/license-manager/pvt/accounts/${encodeURI(
        account
      )}/logins/${encodeURI(userId)}/granted`
    ).then((res: any) => {
      return res
    })
  }

  public checkUserSpecificRole = async (
    account: string,
    userEmail: string,
    productCode: number,
    resourceCode: string
  ) => {
    return this.get(
      `/api/license-manager/pvt/accounts/${encodeURI(
        account
      )}/products/${productCode}/logins/${encodeURI(
        userEmail
      )}/resources/${encodeURI(resourceCode)}/granted`
    ).then((res: any) => {
      return res
    })
  }

  public getAccount = async () => {
    return this.get<GetAccountResponse>(`/api/license-manager/account`).then(
      (res) => {
        return res
      }
    )
  }

  protected get = <T>(url: string) => {
    return this.http.get<T>(url)
  }
}

interface GetAccountResponse {
  isActive: boolean
  id: string
  name: string
  accountName: string
  lv: unknown
  isOperating: boolean
  defaultUrl: unknown
  district: unknown
  country: unknown
  complement: unknown
  compunknownName: string
  cnpj: string
  haveParentAccount: boolean
  parentAccountId: unknown
  parentAccountName: unknown
  city: unknown
  address: unknown
  logo: unknown
  hasLogo: boolean
  number: unknown
  postalCode: unknown
  state: unknown
  telephone: string
  tradingName: string
  licenses: unknown[]
  sponsor: {
    name: string
    email: string
    phone: string
  }
  contact: {
    name: string
    email: string
    phone: string
  }
  operationDate: unknown
  inactivationDate: unknown
  creationDate: string
  hosts: unknown[]
  sites: unknown[]
  appKeys: unknown[]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/index.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients } from '@vtex/api'

import LMClient from './LMClient'
import AnalyticsClient from './analytics'
import VtexId from './vtexId'
import PaymentsClient from './payments'
import MailClient from './email'
import Checkout from './checkout'
import OMSClient from './Oms'
import OrdersClient from './Orders'
import StorefrontPermissions from './storefrontPermissions'
import IdentityClient from './IdentityClient'
import Catalog from './catalog'
import SellersClient from './sellers'

// Extend the default IOClients implementation with our own custom clients.
export class Clients extends IOClients {
  public get lm() {
    return this.getOrSet('lm', LMClient)
  }

  public get analytics() {
    return this.getOrSet('analytics', AnalyticsClient)
  }

  public get checkout() {
    return this.getOrSet('checkout', Checkout)
  }

  public get payments() {
    return this.getOrSet('payments', PaymentsClient)
  }

  public get mail() {
    return this.getOrSet('mail', MailClient)
  }

  public get oms() {
    return this.getOrSet('oms', OMSClient)
  }

  public get orders() {
    return this.getOrSet('orders', OrdersClient)
  }

  public get storefrontPermissions() {
    return this.getOrSet('storefrontPermissions', StorefrontPermissions)
  }

  public get vtexId() {
    return this.getOrSet('vtexId', VtexId)
  }

  public get identity() {
    return this.getOrSet('identity', IdentityClient)
  }

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

  public get sellers() {
    return this.getOrSet('sellers', SellersClient)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/IdentityClient.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'

export default class IdentityClient extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        ...options?.headers,
      },
    })
  }

  public async validateToken({ token }: { token: string }): Promise<any> {
    return this.http.post('/api/vtexid/credential/validate', { token })
  }

  public async getToken({
    appkey,
    apptoken,
  }: {
    appkey: string
    apptoken: string
  }): Promise<any> {
    return this.http.post('/api/vtexid/apptoken/login', { appkey, apptoken })
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/payments.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'

import { CREDIT_CARDS } from '../constants'

export default class PaymentsClient extends JanusClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(context, {
      ...options,
      headers: {
        ...options?.headers,
        VtexIdclientAutCookie: context.authToken,
      },
    })
  }

  public rules = () =>
    this.http.get<Rule[]>(`api/payments/pvt/rules`, {
      metric: 'payments-get-rules',
    })

  public async getPaymentTerms() {
    const paymentRules = await this.rules()

    const enabledConnectors = paymentRules.filter(
      (rule) => rule.enabled === true
    )

    const enabledPaymentSystems = enabledConnectors.map(
      (connector) => connector.paymentSystem
    )

    const uniquePaymentSystems = enabledPaymentSystems.filter(
      (value, index, self) => {
        return (
          index ===
          self.findIndex((t) => t.id === value.id && t.name === value.name)
        )
      }
    )

    const uniquePaymentSystemsWithoutCreditCards = uniquePaymentSystems.filter(
      (value) => {
        return !CREDIT_CARDS.includes(value.name)
      }
    )

    uniquePaymentSystemsWithoutCreditCards.unshift({
      id: 999999,
      implementation: null,
      name: 'Credit card',
    })

    return uniquePaymentSystemsWithoutCreditCards
  }
}

interface Rule {
  id: string
  name: string
  salesChannels: Array<{
    id: string
  }>
  paymentSystem: { id: number; name: string; implementation: string | null }
  connector: {
    implementation: string
    affiliationId: string
  }
  issuer: { name: string | null }
  antifraud: { implementation: string | null; affiliationId: string | null }
  installmentOptions: string | null
  isSelfAuthorized: string | null
  requiresAuthentication: string | null
  enabled: boolean
  installmentsService: boolean
  isDefault: string | null
  beginDate: string | null
  endDate: string | null
  condition: string | null
  multiMerchantList: string | null
  country: string | null
  dateIntervals: string | null
  externalInterest: boolean
  minimumValue: string | null
  deadlines: string[]
  cobrand: { name: string | null }
  cardLevel: { name: string | null }
  excludedBinsRanges: string | null
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/clients/catalog.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { JanusClient } from '@vtex/api'

export default class Catalog extends JanusClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(ctx, {
      ...options,
      headers: {
        VtexIdClientAutCookie: ctx.authToken,
      },
    })
  }

  public async salesChannelAvailable(email: string) {
    return this.http.get<SalesChannelAvailable[]>(
      `/api/catalog_system/pub/saleschannel/available?email=${encodeURIComponent(
        email
      )}`,
      {
        metric: 'catalog-sales-channel-available',
      }
    )
  }

  public async collectionsAvailable(searchTerm: string) {
    return this.http.get<PagedCollectionsResponseAPI>(
      `/api/catalog_system/pvt/collection/search/${encodeURI(searchTerm)}`,
      {
        metric: 'catalog-collections-available',
      }
    )
  }
}

interface SalesChannelAvailable {
  Id: number
  Name: string
  IsActive: boolean
  ProductClusterId: string | null
  CountryCode: string
  CultureInfo: string
  TimeZone: string
  CurrencyCode: string
  CurrencySymbol: string
  CurrencyLocale: number
  CurrencyFormatInfo: unknown
  Position: number
  ConditionRule: string | null
  CurrencyDecimalDigits: null | number
}

interface Paging {
  pages: number
  page: number
  perPage: number
  total: number
}

interface CollectionItem {
  id: number
  name: string
  searchable: boolean
  highlight: boolean
  dateFrom: string
  dateTo: string
  totalProducts: number
  type: 'Hybrid' | 'Manual' | 'Automatic'
  lastModifiedBy: string | null
}

interface PagedCollectionsResponseAPI {
  paging: Paging
  items: CollectionItem[]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/mutations/addUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  mutation (
    $id: ID
    $userId: ID
    $roleId: ID!
    $orgId: ID!
    $costId: ID!
    $name: String!
    $canImpersonate: Boolean!
    $email: String!
  ) {
    addUser(
      id: $id
      userId: $userId
      roleId: $roleId
      orgId: $orgId
      costId: $costId
      name: $name
      canImpersonate: $canImpersonate
      email: $email
    ) {
      id
      status
      message
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/mutations/deleteUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  mutation deleteUser($id: ID!, $userId: ID, $email: String!) {
    deleteUser(id: $id, userId: $userId, email: $email) {
      id
      status
      message
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/mutations/updateUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  mutation (
    $id: ID
    $clId: ID!
    $userId: ID
    $roleId: ID!
    $orgId: ID!
    $costId: ID!
    $canImpersonate: Boolean!
    $name: String
    $email: String
  ) {
    updateUser(
      id: $id
      clId: $clId
      userId: $userId
      roleId: $roleId
      orgId: $orgId
      costId: $costId
      canImpersonate: $canImpersonate
      name: $name
      email: $email
    ) {
      id
      status
      message
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/mutations/saveUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  mutation saveUser(
    $id: ID
    $roleId: ID!
    $userId: ID
    $orgId: ID
    $costId: ID
    $clId: ID
    $canImpersonate: Boolean
    $name: String!
    $email: String!
  ) {
    saveUser(
      id: $id
      roleId: $roleId
      userId: $userId
      orgId: $orgId
      costId: $costId
      clId: $clId
      canImpersonate: $canImpersonate
      name: $name
      email: $email
    ) {
      id
      status
      message
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/mutations/impersonateUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  mutation impersonateUser($userId: ID) {
    impersonateUser(userId: $userId) {
      id
      status
      message
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getB2BUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query user($id: ID!) {
    getB2BUser(id: $id) {
      id
      roleId
      userId
      clId
      orgId
      costId
      name
      email
      canImpersonate
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getRole.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query role($id: ID!) {
    getRole(id: $id) {
      id
      name
      slug
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getUsersByEmail.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query getUsersByEmail($orgId: ID, $costId: ID, $email: String!) {
    getUsersByEmail(orgId: $orgId, costId: $costId, email: $email) {
      id
      roleId
      userId
      orgId
      costId
      name
      email
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getOrganizationsPaginatedByEmail.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query OrganizationsPaginated($email: String!, $page: Int, $pageSize: Int) {
    getOrganizationsPaginatedByEmail(
      email: $email
      page: $page
      pageSize: $pageSize
    ) {
      data {
        costId
        orgId
        roleId
        id
        clId
      }
      pagination {
        page
        pageSize
        total
      }
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getUser.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query user($id: ID!) {
    getUser(id: $id) {
      id
      roleId
      userId
      clId
      orgId
      costId
      name
      email
      canImpersonate
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/listUsersPaginated.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query users(
    $organizationId: ID
    $costCenterId: ID
    $roleId: ID
    $page: Int
    $pageSize: Int
    $search: String
    $sortOrder: String
    $sortedBy: String
  ) {
    listUsersPaginated(
      organizationId: $organizationId
      costCenterId: $costCenterId
      roleId: $roleId
      page: $page
      pageSize: $pageSize
      search: $search
      sortOrder: $sortOrder
      sortedBy: $sortedBy
    ) {
      data {
        id
        roleId
        userId
        clId
        orgId
        costId
        name
        email
        canImpersonate
      }
      pagination {
        total
      }
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getPermission.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query permissions {
    checkUserPermission {
      role {
        id
        name
        slug
      }
      permissions
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/listRoles.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query roles {
    listRoles {
      id
      name
      slug
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/listUsers.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query users($organizationId: ID, $costCenterId: ID, $roleId: ID) {
    listUsers(
      organizationId: $organizationId
      costCenterId: $costCenterId
      roleId: $roleId
    ) {
      id
      roleId
      userId
      clId
      orgId
      costId
      name
      email
      canImpersonate
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/checkImpersonation.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query checkImpersonation {
    checkImpersonation {
      firstName
      lastName
      email
      userId
      error
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/listAllUsers.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query users {
    listAllUsers {
      id
      orgId
      costId
      name
      email
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/queries/getOrganizationsByEmail.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { print } from 'graphql'
import gql from 'graphql-tag'

export default print(gql`
  query organizations($email: String!) {
    getOrganizationsByEmail(email: $email) {
      costId
      orgId
      roleId
      id
      clId
    }
  }
`)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/readFile.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import fs from 'fs'
import path from 'path'

const readFile = (filePath: string) =>
  fs.readFileSync(path.resolve(__dirname, filePath), 'utf8')

export default readFile

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/constants.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const ORGANIZATION_STATUSES = {
  ACTIVE: 'active',
  INACTIVE: 'inactive',
  ON_HOLD: 'on-hold',
  SUCCESS: 'success',
}

export const ORGANIZATION_REQUEST_STATUSES = {
  APPROVED: 'approved',
  DECLINED: 'declined',
  PENDING: 'pending',
}

export const MARKETING_TAGS = {
  VBASE_BUCKET: 'b2b_marketing_tags',
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/GraphQLError.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
export default class GraphQLError extends Error {
  public extensions: any

  constructor(message: string, details?: any) {
    super(message)
    this.extensions = { message, ...details }
  }
}

export const getErrorMessage = (e: any) => {
  return e.message ?? e.response?.data?.message ?? e
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/index.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as crypto from 'crypto'

import { AuthenticationError, ForbiddenError, UserInputError } from '@vtex/api'
import type { AxiosError } from 'axios'

import { schemas } from '../mdSchema'

export const toHash = (obj: any) => {
  return crypto.createHash('md5').update(JSON.stringify(obj)).digest('hex')
}

export const currentSchema = (entity: string) => {
  return schemas.find((item: any) => {
    return item.name === entity
  })
}

export function statusToError(e: any) {
  if (!e.response) {
    throw e
  }

  const { response } = e as AxiosError
  const { status } = response!

  if (status === 401) {
    throw new AuthenticationError(e)
  }

  if (status === 403) {
    throw new ForbiddenError(e)
  }

  if (status === 400) {
    throw new UserInputError(e)
  }

  throw e
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/directives.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { WithSession } from './directives/withSession'
import { WithPermissions } from './directives/withPermissions'
import { ValidateAdminUserAccess } from './directives/validateAdminUserAccess'
import { ValidateStoreUserAccess } from './directives/validateStoreUserAccess'
import { AuditAccess } from './directives/auditAccess'

export const schemaDirectives = {
  validateAdminUserAccess: ValidateAdminUserAccess as any,
  validateStoreUserAccess: ValidateStoreUserAccess as any,
  withPermissions: WithPermissions as any,
  withSession: WithSession as any,
  auditAccess: AuditAccess as any,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/fieldResolvers.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  COST_CENTER_DATA_ENTITY,
  COST_CENTER_FIELDS,
  ORGANIZATION_DATA_ENTITY,
  ORGANIZATION_FIELDS,
} from '../mdSchema'
import GraphQLError, { getErrorMessage } from '../utils/GraphQLError'
import type { CostCenter, Organization } from '../typings'

export const organizationName = async (
  { orgId }: { orgId: string },
  _: any,
  ctx: Context
) => {
  const {
    clients: { masterdata },
    vtex: { logger },
  } = ctx

  try {
    const organization: Organization = await masterdata.getDocument({
      dataEntity: ORGANIZATION_DATA_ENTITY,
      fields: ORGANIZATION_FIELDS,
      id: orgId,
    })

    return organization.name
  } catch (error) {
    logger.error({
      error,
      message: 'getOrganizationName-error',
    })
    throw new GraphQLError(getErrorMessage(error))
  }
}

export const organizationStatus = async (
  { orgId }: { orgId: string },
  _: any,
  ctx: Context
) => {
  const {
    clients: { masterdata },
    vtex: { logger },
  } = ctx

  try {
    const organization: Organization = await masterdata.getDocument({
      dataEntity: ORGANIZATION_DATA_ENTITY,
      fields: ORGANIZATION_FIELDS,
      id: orgId,
    })

    return organization.status
  } catch (error) {
    logger.error({
      error,
      message: 'getOrganizationStatus-error',
    })
    throw new GraphQLError(getErrorMessage(error))
  }
}

export const costCenterName = async (
  { costId }: { costId: string },
  _: any,
  ctx: Context
) => {
  const {
    clients: { masterdata },
    vtex: { logger },
  } = ctx

  try {
    const costCenter: CostCenter = await masterdata.getDocument({
      dataEntity: COST_CENTER_DATA_ENTITY,
      fields: COST_CENTER_FIELDS,
      id: costId,
    })

    return costCenter.name
  } catch (error) {
    logger.error({
      error,
      message: 'getCostCenterName-error',
    })
    throw new GraphQLError(getErrorMessage(error))
  }
}

export const role = async (
  { roleId }: { roleId: string },
  _: any,
  ctx: Context
) => {
  const {
    clients: { storefrontPermissions },
    vtex: { logger },
  } = ctx

  return storefrontPermissions
    .getRole(roleId)
    .then((result: any) => {
      return result.data.getRole
    })
    .catch((error: any) => {
      logger.error({
        error,
        message: 'getRoleError',
      })
    })
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/message.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable max-params */
import type { Logger } from '@vtex/api'

import type MailClient from '../clients/email'
import type StorefrontPermissions from '../clients/storefrontPermissions'

const getUsers = async (
  storefrontPermissions: StorefrontPermissions,
  roleSlug: string,
  organizationId?: string
) => {
  const {
    data: { listRoles },
  }: any = await storefrontPermissions.listRoles()

  const roles = listRoles.filter((r: any) => r.slug.match(roleSlug))

  if (!roles.length) {
    return []
  }

  return Promise.all(
    roles.map(async (role: any) => {
      const {
        data: { listUsers },
      }: any = await storefrontPermissions.listUsers({
        roleId: role.id,
        ...(organizationId && { organizationId }),
      })

      return listUsers
    })
  ).then((users: any) => users.flat())
}

const message = ({
  storefrontPermissions,
  logger,
  mail,
}: {
  storefrontPermissions: StorefrontPermissions
  logger: Logger
  mail: MailClient
}) => {
  const organizationCreated = async (name: string) => {
    let users: any[] = []

    try {
      users = await getUsers(storefrontPermissions, 'sales-admin')
    } catch (err) {
      logger.error(err)
    }

    const promises = []

    for (const user of users) {
      promises.push(
        mail
          .sendMail({
            jsonData: {
              message: { to: user.email },
              organization: { name, admin: user.name },
            },
            templateName: 'organization-created',
          })
          .catch((err) =>
            logger.error({
              message: {
                error: err,
                message: 'Error sending organization created email',
              },
            })
          )
      )
    }

    return Promise.all(promises)
  }

  const organizationApproved = async (
    name: string,
    admin: string,
    email: string,
    note: string
  ) => {
    return mail
      .sendMail({
        jsonData: {
          message: { to: email },
          organization: { name, admin, note },
        },
        templateName: 'organization-approved',
      })
      .catch((err) =>
        logger.error({
          message: {
            error: err,
            message: 'Error sending organization approved email',
          },
        })
      )
  }

  const organizationRequestCreated = async (
    name: string,
    admin: string,
    email: string,
    note: string
  ) => {
    return mail
      .sendMail({
        jsonData: {
          message: { to: email },
          organization: { name, admin, note },
        },
        templateName: 'organization-request-created',
      })
      .catch((err) =>
        logger.error({
          message: {
            error: err,
            message: 'Error sending organization approved email',
          },
        })
      )
  }

  const organizationDeclined = async (
    name: string,
    admin: string,
    email: string,
    note: string
  ) => {
    return mail
      .sendMail({
        jsonData: {
          message: { to: email },
          organization: { name, admin, note },
        },
        templateName: 'organization-declined',
      })
      .catch((err) =>
        logger.error({
          message: {
            error: err,
            message: 'Error sending organization declined email',
          },
        })
      )
  }

  const organizationStatusChanged = async (
    name: string,
    id: string,
    status: string
  ) => {
    let users: any[] = []

    try {
      users = [
        ...new Map(
          (await getUsers(storefrontPermissions, 'customer-admin', id)).map(
            (user: any) => [user.email, user]
          )
        ).values(),
      ]
    } catch (err) {
      logger.error(err)
    }

    const promises = []

    for (const user of users) {
      promises.push(
        mail
          .sendMail({
            jsonData: {
              message: { to: user.email },
              organization: { name, admin: user.name, status },
            },
            templateName: 'organization-status-changed',
          })
          .catch((err) =>
            logger.error({
              message: {
                error: err,
                message: 'Error sending organization status changed email',
              },
            })
          )
      )
    }

    return Promise.all(promises)
  }

  return {
    organizationApproved,
    organizationCreated,
    organizationDeclined,
    organizationRequestCreated,
    organizationStatusChanged,
  }
}

export default message

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/index.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable @typescript-eslint/no-explicit-any */

import {
  costCenterName,
  organizationName,
  organizationStatus,
  role,
} from './fieldResolvers'
import CostCentersMutation from './Mutations/CostCenters'
import MarketingTagsMutation from './Mutations/MarketingTags'
import OrganizationsMutation from './Mutations/Organizations'
import SettingsMutation from './Mutations/Settings'
import UsersMutation from './Mutations/Users'
import CostCentersQuery from './Queries/CostCenters'
import MarketingTagsQuery from './Queries/MarketingTags'
import OrganizationsQuery from './Queries/Organizations'
import SettingsQuery from './Queries/Settings'
import UsersQuery from './Queries/Users'
import Routes from './Routes'

export const resolvers = {
  B2BOrganization: {
    costCenterName,
    organizationName,
    organizationStatus,
    role,
  },
  B2BUser: {
    costCenterName,
    organizationName,
    role,
  },
  Mutation: {
    ...CostCentersMutation,
    ...MarketingTagsMutation,
    ...OrganizationsMutation,
    ...SettingsMutation,
    ...UsersMutation,
  },
  Query: {
    ...CostCentersQuery,
    ...MarketingTagsQuery,
    ...OrganizationsQuery,
    ...SettingsQuery,
    ...UsersQuery,
  },
  Routes,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/config.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { schemas } from '../mdSchema'
import templates from '../templates'
import { toHash } from '../utils'

export interface Settings {
  schemaHash: string | null
  templateHash: string | null
}

const defaultSettings = {
  schemaHash: null,
  templateHash: null,
}

export const getAppId = (): string => {
  const app = process.env.VTEX_APP_ID
  const [appName] = String(app).split('@')

  return appName
}

const checkConfig = async (ctx: Context) => {
  const {
    vtex: { logger },
    clients: { mail, masterdata, vbase },
  } = ctx

  let settings: Settings = await vbase.getJSON('mdSchema', 'settings', true)

  let schemaChanged = false
  let templatesChanged = false

  if (!settings?.schemaHash || !settings?.templateHash) {
    settings = defaultSettings
  }

  const currSchemaHash = toHash(schemas)
  const currTemplateHash = toHash(templates)

  if (!settings?.schemaHash || settings.schemaHash !== currSchemaHash) {
    const updates: any = []

    logger.info({
      message: 'checkConfig-updatingSchema',
    })

    schemas.forEach((schema) => {
      updates.push(
        masterdata
          .createOrUpdateSchema({
            dataEntity: schema.name,
            schemaBody: schema.body,
            schemaName: schema.version,
          })
          .then(() => true)
          .catch((error: any) => {
            if (error?.response?.status !== 304) {
              logger.error({
                error,
                message: 'checkConfig-createOrUpdateSchemaError',
              })

              return false
            }

            return true
          })
      )
    })
    try {
      await Promise.all(updates).then((results) => {
        if (results.every((res) => res === true)) {
          settings.schemaHash = currSchemaHash
          schemaChanged = true
        }
      })
    } catch (error) {
      logger.error({
        error,
        message: 'checkConfig-createOrUpdateSchemaError',
      })
    }
  }

  if (!settings?.templateHash || settings.templateHash !== currTemplateHash) {
    const updates: any = []

    logger.info({
      message: 'checkConfig-updatingTemplates',
    })

    try {
      await Promise.all(
        templates.map(async (template) => {
          const existingData = await mail.getTemplate(template.Name)

          if (!existingData) {
            updates.push(mail.publishTemplate(template))
          }

          return null
        })
      )
    } catch (error) {
      logger.error({
        error,
        message: 'checkConfig-updatingTemplatesError',
      })
    }

    await Promise.all(updates)
      .then(() => {
        settings.templateHash = currTemplateHash
        templatesChanged = true
      })
      .catch((error) => {
        logger.error({
          error,
          message: 'checkConfig-publishTemplateError',
        })
        throw new Error(error)
      })
  }

  if (schemaChanged || templatesChanged) {
    try {
      await vbase.saveJSON('mdSchema', 'settings', settings)
    } catch (error) {
      logger.error({
        error,
        message: 'checkConfig-saveSettingsError',
      })
    }
  }

  return settings
}

export default checkConfig

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/templates/organizationCreated.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import readFile from '../utils/readFile'

const MESSAGE_BODY = readFile('../assets/organizationCreated.html')

export const organizationCreatedMessage = {
  Name: 'organization-created',
  FriendlyName: 'Organization Created',
  // Description: null,
  IsDefaultTemplate: false,
  // AccountId: null,
  // AccountName: null,
  // ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{message.to}}',
      // CC: null,
      // BCC: null,
      Subject: 'Organization Created',
      Message: MESSAGE_BODY,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      // ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      // ProviderId: null,
      // ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/templates/organizationAccepted.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import readFile from '../utils/readFile'

const MESSAGE_BODY = readFile('../assets/organizationAccepted.html')

export const organizationAcceptedMessage = {
  Name: 'organization-approved',
  FriendlyName: 'Organization Approved',
  // Description: null,
  IsDefaultTemplate: false,
  // AccountId: null,
  // AccountName: null,
  // ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{message.to}}',
      // CC: null,
      // BCC: null,
      Subject: 'Organization Approved',
      Message: MESSAGE_BODY,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      // ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      // ProviderId: null,
      // ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/templates/organizationStatusChanged.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import readFile from '../utils/readFile'

const MESSAGE_BODY = readFile('../assets/organizationStatusChange.html')

export const organizationStatusChangedMessage = {
  Name: 'organization-status-changed',
  FriendlyName: 'Organization Status Changed',
  // Description: null,
  IsDefaultTemplate: false,
  // AccountId: null,
  // AccountName: null,
  // ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{message.to}}',
      // CC: null,
      // BCC: null,
      Subject: 'Organization Status Change',
      Message: MESSAGE_BODY,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      // ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      // ProviderId: null,
      // ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/templates/organizationRequestCreated.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import readFile from '../utils/readFile'

const MESSAGE_BODY = readFile('../assets/organizationRequestCreated.html')

export const organizationRequestCreatedMessage = {
  Name: 'organization-request-created',
  FriendlyName: 'Organization Request Created',
  // Description: null,
  IsDefaultTemplate: false,
  // AccountId: null,
  // AccountName: null,
  // ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{message.to}}',
      // CC: null,
      // BCC: null,
      Subject: 'Organization Request Created',
      Message: MESSAGE_BODY,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      // ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      // ProviderId: null,
      // ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/templates/index.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { organizationAcceptedMessage } from './organizationAccepted'
import { organizationCreatedMessage } from './organizationCreated'
import { organizationDeclinedMessage } from './organizationDeclined'
import { organizationStatusChangedMessage } from './organizationStatusChanged'
import { organizationRequestCreatedMessage } from './organizationRequestCreated'

const templates = [
  organizationCreatedMessage,
  organizationAcceptedMessage,
  organizationDeclinedMessage,
  organizationStatusChangedMessage,
  organizationRequestCreatedMessage,
]

export default templates

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/templates/organizationDeclined.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import readFile from '../utils/readFile'

const MESSAGE_BODY = readFile('../assets/organizationDeclined.html')

export const organizationDeclinedMessage = {
  Name: 'organization-declined',
  FriendlyName: 'Organization Declined',
  // Description: null,
  IsDefaultTemplate: false,
  // AccountId: null,
  // AccountName: null,
  // ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{message.to}}',
      // CC: null,
      // BCC: null,
      Subject: 'Organization Declined',
      Message: MESSAGE_BODY,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      // ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      // ProviderId: null,
      // ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/metrics/user.test.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { randEmail, randWord } from '@ngneat/falso'
import type { Logger } from '@vtex/api/lib/service/logger/logger'

import { B2B_METRIC_NAME } from '../../clients/analytics'
import type { UserArgs } from '../../typings'
import {
  sendAddUserMetric,
  sendRemoveUserMetric,
  sendUpdateUserMetric,
} from './user'

jest.mock('@vtex/api')
jest.mock('@vtex/diagnostics-nodejs', () => ({}))

const mockContext = () => {
  return {
    clients: {
      analytics: {
        sendMetric: jest.fn(),
      },
    },
  } as unknown as Context
}

afterEach(() => {
  jest.resetAllMocks()
})

describe('given an action for a user', () => {
  describe('when add user', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const userArgs: Partial<UserArgs> = {
      email: randEmail(),
      id: randWord(),
      userId: randWord(),
    }

    beforeEach(async () => {
      await sendAddUserMetric(context, logger, account, userArgs)
    })

    it('should metrify the action', () => {
      const metricParam = {
        account,
        description: 'Add User Action - Graphql',
        fields: userArgs,
        kind: 'add-user-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })

  describe('when remove user', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const userArgs: Partial<UserArgs> = {
      email: randEmail(),
      id: randWord(),
      userId: randWord(),
    }

    beforeEach(async () => {
      await sendRemoveUserMetric(context, logger, account, userArgs)
    })

    it('should metrify the action', () => {
      const metricParam = {
        account,
        description: 'Remove User Action - Graphql',
        fields: userArgs,
        kind: 'remove-user-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })

  describe('when update user', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const userArgs: Partial<UserArgs> = {
      email: randEmail(),
      id: randWord(),
      userId: randWord(),
    }

    beforeEach(async () => {
      await sendUpdateUserMetric(context, logger, account, userArgs)
    })

    it('should metrify the action', () => {
      const metricParam = {
        account,
        description: 'Update User Action - Graphql',
        fields: userArgs,
        kind: 'update-user-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/metrics/organization.test.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  randAirportName,
  randAlpha,
  randAlphaNumeric,
  randCompanyName,
  randFirstName,
  randFullName,
  randLastName,
  randPastDate,
  randStatus,
  randSuperheroName,
  randWord,
} from '@ngneat/falso'
import type { Logger } from '@vtex/api/lib/service/logger/logger'

import { B2B_METRIC_NAME } from '../../clients/analytics'
import type { Seller } from '../../clients/sellers'
import type {
  Collection,
  CustomField,
  Organization,
  PaymentTerm,
} from '../../typings'
import { ORGANIZATION_REQUEST_STATUSES } from '../constants'
import type {
  OrganizationStatusParams,
  UpdateOrganizationParams,
} from './organization'
import {
  sendOrganizationStatusMetric,
  sendUpdateOrganizationMetric,
} from './organization'

jest.mock('@vtex/api')
jest.mock('@vtex/diagnostics-nodejs', () => ({}))

const mockContext = () => {
  return {
    clients: {
      analytics: {
        sendMetric: jest.fn(),
      },
    },
  } as unknown as Context
}

afterEach(() => {
  jest.resetAllMocks()
})

describe('given an organization to update data', () => {
  describe('when change all properties', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const currentOrganization: Organization = {
      collections: [{ name: randWord() } as Collection],
      costCenters: [],
      created: randPastDate().toISOString(),
      customFields: [{ name: randWord() } as CustomField],
      id: randAlphaNumeric().toString(),
      name: randCompanyName(),
      paymentTerms: [{ name: randAlphaNumeric() } as PaymentTerm],
      priceTables: [randAlpha()],
      salesChannel: randAlpha(),
      sellers: [{ name: randFullName() } as Seller],
      status: randAlpha(),
      tradeName: randCompanyName(),
    }

    const fieldsUpdated: Partial<Organization> = {
      collections: [{ name: randSuperheroName() } as Collection],
      costCenters: [],
      customFields: [{ name: randAirportName() } as CustomField],
      name: randCompanyName(),
      paymentTerms: [{ name: randLastName() } as PaymentTerm],
      priceTables: [randFirstName()],
      salesChannel: randAirportName(),
      sellers: [{ name: randFullName() } as Seller],
      status: randStatus(),
      tradeName: randCompanyName(),
    }

    const updateOrganizationParams: UpdateOrganizationParams = {
      account,
      currentOrganizationData: currentOrganization,
      updatedProperties: fieldsUpdated,
    }

    beforeEach(async () => {
      await sendUpdateOrganizationMetric(
        context,
        logger,
        updateOrganizationParams
      )
    })

    it('should metrify that all properties changed', () => {
      const metricParam = {
        account,
        description: 'Update Organization Action - Graphql',
        fields: {
          update_details: {
            properties: [
              'collections',
              'customFields',
              'name',
              'paymentTerms',
              'priceTables',
              'salesChannel',
              'sellers',
              'status',
              'tradeName',
            ],
          },
        },
        kind: 'update-organization-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })

  describe('when no change properties data', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const collections = [{ name: randWord() } as Collection]
    const customFields = [{ name: randWord() } as CustomField]
    const name = randWord()
    const paymentTerms = [{ name: randWord() } as PaymentTerm]
    const priceTables = [randWord()]
    const salesChannel = randWord()
    const sellers = [{ name: randFullName() } as Seller]
    const status = randWord()
    const tradeName = randWord()

    const currentOrganization: Organization = {
      collections,
      costCenters: [],
      created: randWord(),
      customFields,
      id: randWord(),
      name,
      paymentTerms,
      priceTables,
      salesChannel,
      sellers,
      status,
      tradeName,
    }

    const fieldsUpdated = {
      collections,
      customFields,
      name,
      paymentTerms,
      priceTables,
      salesChannel,
      sellers,
      status,
      tradeName,
    }

    const updateOrganizationParams: UpdateOrganizationParams = {
      account,
      currentOrganizationData: currentOrganization,
      updatedProperties: fieldsUpdated,
    }

    beforeEach(async () => {
      await sendUpdateOrganizationMetric(
        context,
        logger,
        updateOrganizationParams
      )
    })

    it('should metric no properties changed', () => {
      const metricParam = {
        account,
        description: 'Update Organization Action - Graphql',
        fields: {
          update_details: {
            properties: [],
          },
        },
        kind: 'update-organization-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })
  describe('when just the name, status and tradeName', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const currentOrganization: Organization = {
      collections: [{ id: '149', name: 'Teste 2 Jay' }],
      costCenters: [],
      created: '2023-05-26T17:59:51.665Z',
      customFields: [],
      id: '166d3921-fbef-11ed-83ab-16759f4a0add',
      name: 'Antes',
      paymentTerms: [],
      priceTables: [],
      salesChannel: '1',
      sellers: [],
      status: 'inactive',
      tradeName: 'Antes',
    }

    const fieldsUpdated = {
      collections: [{ id: '149', name: 'Teste 2 Jay' }],
      customFields: [],
      name: 'Depois',
      paymentTerms: [],
      priceTables: [],
      salesChannel: '1',
      sellers: [],
      status: 'active',
      tradeName: 'Depois',
    }

    const updateOrganizationParams: UpdateOrganizationParams = {
      account,
      currentOrganizationData: currentOrganization,
      updatedProperties: fieldsUpdated,
    }

    beforeEach(async () => {
      await sendUpdateOrganizationMetric(
        context,
        logger,
        updateOrganizationParams
      )
    })

    it('should metric just the properties changed', () => {
      const metricParam = {
        account,
        description: 'Update Organization Action - Graphql',
        fields: {
          update_details: {
            properties: ['name', 'status', 'tradeName'],
          },
        },
        kind: 'update-organization-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })

  describe('when need to update status', () => {
    const context = mockContext()

    const logger = jest.fn() as unknown as Logger

    const account = randWord()

    const updateOrganizationParams: OrganizationStatusParams = {
      account,
      status: ORGANIZATION_REQUEST_STATUSES.APPROVED,
    }

    beforeEach(async () => {
      await sendOrganizationStatusMetric(
        context,
        logger,
        updateOrganizationParams
      )
    })

    it('should metrify the status changed', () => {
      const metricParam = {
        account,
        description: 'Change Organization Status Action - Graphql',
        fields: {
          status: ORGANIZATION_REQUEST_STATUSES.APPROVED,
        },
        kind: 'change-organization-status-graphql-event',
        name: B2B_METRIC_NAME,
      }

      expect(context.clients.analytics.sendMetric).toHaveBeenCalledWith(
        metricParam
      )
    })
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/metrics/organization.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Logger } from '@vtex/api/lib/service/logger/logger'
import { isEqual } from 'lodash'

import type { Organization } from '../../typings'
import type { Metric } from '../../clients/analytics'
import { B2B_METRIC_NAME } from '../../clients/analytics'

interface UpdateOrganizationFieldsMetric {
  update_details: { properties: string[] }
}

type UpdateOrganization = Metric & {
  fields: UpdateOrganizationFieldsMetric
}

export interface UpdateOrganizationParams {
  account: string
  currentOrganizationData?: Organization
  updatedProperties: Partial<Organization>
}

export interface OrganizationStatusParams {
  account: string
  status: string
}

class UpdateOrganizationMetric implements Metric {
  public readonly description = 'Update Organization Action - Graphql'
  public readonly kind = 'update-organization-graphql-event'
  public readonly account: string
  public readonly fields: UpdateOrganizationFieldsMetric
  public readonly name = B2B_METRIC_NAME

  constructor(account: string, fields: UpdateOrganizationFieldsMetric) {
    this.account = account
    this.fields = fields
  }
}

const buildUpdateOrganizationMetric = (
  account: string,
  updatedProperties: string[]
): UpdateOrganization => {
  const updateOrganizationFields: UpdateOrganizationFieldsMetric = {
    update_details: { properties: updatedProperties },
  }

  return new UpdateOrganizationMetric(account, updateOrganizationFields)
}

const getPropNamesByUpdateParams = (
  updateOrganizationParams: UpdateOrganizationParams
): string[] => {
  const updatedPropName: string[] = []

  const { currentOrganizationData, updatedProperties } =
    updateOrganizationParams

  if (!currentOrganizationData) {
    return updatedPropName
  }

  Object.entries(updatedProperties).forEach(
    ([updatedPropertyKey, updatedPropertyValue]) => {
      if (
        !isEqual(
          updatedPropertyValue,
          currentOrganizationData[updatedPropertyKey as keyof Organization]
        )
      ) {
        updatedPropName.push(updatedPropertyKey)
      }
    }
  )

  return updatedPropName
}

export const sendUpdateOrganizationMetric = async (
  ctx: Context,
  logger: Logger,
  updateOrganizationParams: UpdateOrganizationParams
) => {
  const {
    clients: { analytics },
  } = ctx

  try {
    const fieldsNamesUpdated = getPropNamesByUpdateParams(
      updateOrganizationParams
    )

    const metric = buildUpdateOrganizationMetric(
      updateOrganizationParams.account,
      fieldsNamesUpdated
    )

    await analytics.sendMetric(metric)
  } catch (error) {
    logger.error({
      error,
      message: 'Error to send metrics from updateOrganization',
    })
  }
}

export const sendOrganizationStatusMetric = async (
  ctx: Context,
  logger: Logger,
  statusParams: OrganizationStatusParams
) => {
  const {
    clients: { analytics },
  } = ctx

  try {
    await analytics.sendMetric({
      account: statusParams.account,
      description: 'Change Organization Status Action - Graphql',
      fields: {
        status: statusParams.status,
      },
      kind: 'change-organization-status-graphql-event',
      name: B2B_METRIC_NAME,
    } as unknown as Metric)
  } catch (error) {
    logger.error({
      error,
      message: 'Error to send metrics from organization status change',
    })
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/metrics/impersonate.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Metric } from '../../clients/analytics'
import { B2B_METRIC_NAME } from '../../clients/analytics'

type ImpersonatePerson = {
  email: string
  buyer_org_id: string
  cost_center_id: string
  person_id: string
}

type ImpersonateFieldsMetric = {
  user?: ImpersonatePerson
  target: ImpersonatePerson
  date: string
}

type ImpersonateMetric = Metric & { fields: ImpersonateFieldsMetric }

type ImpersonatePersonParams = {
  costCenterId: string
  organizationId: string
  email: string
  id: string
}

export type ImpersonateMetricParams = {
  account: string
  target: ImpersonatePersonParams
  user?: ImpersonatePersonParams
}

const buildMetric = (metricParams: ImpersonateMetricParams) => {
  const { account, user, target } = metricParams

  const userParam = user
    ? {
        email: user?.email,
        buyer_org_id: user?.organizationId,
        cost_center_id: user?.costCenterId,
        person_id: user?.id,
      }
    : undefined

  const metric = {
    name: B2B_METRIC_NAME,
    account,
    fields: {
      user: userParam,
      target: {
        email: target.email,
        buyer_org_id: target.organizationId,
        cost_center_id: target.costCenterId,
        person_id: target.id,
      },
      date: new Date().toISOString(),
    },
  }

  return metric
}

const buildImpersonateUserMetric = (
  metricParams: ImpersonateMetricParams
): ImpersonateMetric => {
  return {
    kind: 'impersonate-user-graphql-event',
    description: 'Impersonate User Action - Graphql',
    ...buildMetric(metricParams),
  } as ImpersonateMetric
}

const buildImpersonateB2BUserMetric = (
  metricParams: ImpersonateMetricParams
): ImpersonateMetric => {
  return {
    kind: 'impersonate-b2b-user-graphql-event',
    description: 'Impersonate B2B User Action - Graphql',
    ...buildMetric(metricParams),
  } as ImpersonateMetric
}

export const sendImpersonateUserMetric = async (
  ctx: Context,
  metricParams: ImpersonateMetricParams
) => {
  const {
    clients: { analytics },
  } = ctx

  try {
    const metric = buildImpersonateUserMetric(metricParams)

    await analytics.sendMetric(metric)
  } catch (error) {
    console.warn('Unable to log metrics', error)
  }
}

export const sendImpersonateB2BUserMetric = async (
  ctx: Context,
  metricParams: ImpersonateMetricParams
) => {
  const {
    clients: { analytics },
  } = ctx

  try {
    const metric = buildImpersonateB2BUserMetric(metricParams)

    await analytics.sendMetric(metric)
  } catch (error) {
    console.warn('Unable to log metrics', error)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/metrics/user.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Logger } from '@vtex/api/lib/service/logger/logger'

import type { UserArgs } from '../../typings'
import type { Metric } from '../../clients/analytics'
import { B2B_METRIC_NAME } from '../../clients/analytics'

interface UserMetricType {
  description: string
  kind: string
}

const userMetricType = {
  add: {
    description: 'Add User Action - Graphql',
    kind: 'add-user-graphql-event',
  } as UserMetricType,
  remove: {
    description: 'Remove User Action - Graphql',
    kind: 'remove-user-graphql-event',
  } as UserMetricType,
  update: {
    description: 'Update User Action - Graphql',
    kind: 'update-user-graphql-event',
  } as UserMetricType,
}

class UserMetric implements Metric {
  public readonly description: string
  public readonly kind: string
  public readonly account: string
  public readonly fields: Partial<UserArgs>
  public readonly name = B2B_METRIC_NAME

  constructor(
    account: string,
    { kind, description }: UserMetricType,
    fields: Partial<UserArgs>
  ) {
    this.account = account
    this.fields = fields
    this.kind = kind
    this.description = description
  }
}

const sendUserMetric = async (
  ctx: Context,
  logger: Logger,
  userMetric: UserMetric
) => {
  const {
    clients: { analytics },
  } = ctx

  try {
    await analytics.sendMetric(userMetric)
  } catch (error) {
    logger.error({
      error,
      message: `Error to send metrics from user action ${userMetric.kind}`,
    })
  }
}

export const sendRemoveUserMetric = async (
  ctx: Context,
  logger: Logger,
  account: string,
  userArgs: Partial<UserArgs>
) => {
  await sendUserMetric(
    ctx,
    logger,
    new UserMetric(account, userMetricType.remove, userArgs)
  )
}

export const sendAddUserMetric = async (
  ctx: Context,
  logger: Logger,
  account: string,
  userArgs: Partial<UserArgs>
) => {
  await sendUserMetric(
    ctx,
    logger,
    new UserMetric(account, userMetricType.add, userArgs)
  )
}

export const sendUpdateUserMetric = async (
  ctx: Context,
  logger: Logger,
  account: string,
  userArgs: Partial<UserArgs>
) => {
  await sendUserMetric(
    ctx,
    logger,
    new UserMetric(account, userMetricType.update, userArgs)
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/utils/metrics/auth.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Logger } from '@vtex/api/lib/service/logger/logger'

import type { Metric } from '../../clients/analytics'
import { B2B_METRIC_NAME } from '../../clients/analytics'

export interface AuthAuditMetric {
  operation: string
  forwardedHost: string
  caller: string
  userAgent: string
  role?: string
  permissions?: string[]
  hasAdminToken?: boolean
  hasValidAdminToken?: boolean
  hasValidAdminRole?: boolean
  hasStoreToken?: boolean
  hasCurrentValidStoreToken?: boolean
  hasValidStoreToken?: boolean
  hasApiToken?: boolean
  hasValidApiToken?: boolean
  hasValidApiRole?: boolean
  hasAdminTokenOnHeader?: boolean
  hasValidAdminTokenOnHeader?: boolean
  hasValidAdminRoleOnHeader?: boolean
}

export class AuthMetric implements Metric {
  public readonly description: string
  public readonly kind: string
  public readonly account: string
  public readonly fields: AuthAuditMetric
  public readonly name = B2B_METRIC_NAME

  constructor(account: string, fields: AuthAuditMetric, description?: string) {
    this.account = account
    this.fields = fields
    this.kind = 'b2b-organizations-graphql-auth-event'
    this.description = description ?? 'Auth metric event'
  }
}

const sendAuthMetric = async (
  ctx: Context,
  logger: Logger,
  authMetric: AuthMetric
) => {
  const {
    clients: { analytics },
  } = ctx

  try {
    await analytics.sendMetric(authMetric)
  } catch (error) {
    logger.error({
      error,
      message: `Error to send metrics from auth metric`,
    })
  }
}

export default sendAuthMetric

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/directives/auditAccess.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { GraphQLField } from 'graphql'
import { defaultFieldResolver } from 'graphql'
import { SchemaDirectiveVisitor } from 'graphql-tools'

import sendAuthMetric, { AuthMetric } from '../../utils/metrics/auth'
import {
  validateAdminToken,
  validateAdminTokenOnHeader,
  validateApiToken,
  validateStoreToken,
} from './helper'

export class AuditAccess extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field

    field.resolve = async (
      root: any,
      args: any,
      context: Context,
      info: any
    ) => {
      this.sendAuthMetric(field, context)

      return resolve(root, args, context, info)
    }
  }

  private async sendAuthMetric(field: GraphQLField<any, any>, context: any) {
    const {
      vtex: { adminUserAuthToken, storeUserAuthToken, logger },
    } = context

    const { hasAdminToken, hasValidAdminToken } = await validateAdminToken(
      context,
      adminUserAuthToken as string
    )

    const { hasAdminTokenOnHeader, hasValidAdminTokenOnHeader } =
      await validateAdminTokenOnHeader(context)

    const { hasApiToken, hasValidApiToken } = await validateApiToken(context)

    const { hasStoreToken, hasValidStoreToken } = await validateStoreToken(
      context,
      storeUserAuthToken as string
    )

    // now we emit a metric with all the collected data before we proceed
    const operation = field?.astNode?.name?.value ?? context?.request?.url
    const userAgent = context?.request?.headers['user-agent'] as string
    const caller = context?.request?.headers['x-vtex-caller'] as string
    const forwardedHost = context?.request?.headers[
      'x-forwarded-host'
    ] as string

    const auditMetric = new AuthMetric(context?.vtex?.account, {
      operation,
      forwardedHost,
      caller,
      userAgent,
      hasAdminToken,
      hasValidAdminToken,
      hasApiToken,
      hasValidApiToken,
      hasStoreToken,
      hasValidStoreToken,
      hasAdminTokenOnHeader,
      hasValidAdminTokenOnHeader,
    })

    await sendAuthMetric(context, logger, auditMetric)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/directives/helper.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { isUserPartOfBuyerOrg } from '../Queries/Users'
import { LICENSE_MANAGER_ROLES, B2B_LM_PRODUCT_CODE } from '../../constants'

export const validateAdminToken = async (
  context: Context,
  adminUserAuthToken: string,
  requiredRole: string = LICENSE_MANAGER_ROLES.B2B_ORGANIZATIONS_VIEW
): Promise<{
  hasAdminToken: boolean
  hasValidAdminToken: boolean
  hasCurrentValidAdminToken: boolean
  hasValidAdminRole: boolean
}> => {
  const {
    clients: { identity, lm },
    vtex: { account, logger },
  } = context

  // check if has admin token and if it is valid
  const hasAdminToken = !!adminUserAuthToken
  let hasValidAdminToken = false
  // this is used to check if the token is valid by current standards
  let hasCurrentValidAdminToken = false
  let hasValidAdminRole = false

  if (hasAdminToken) {
    try {
      const authUser = await identity.validateToken({
        token: adminUserAuthToken,
      })

      // we set this flag to true if the token is valid by current standards
      // in the future we should remove this line
      hasCurrentValidAdminToken = true

      if (authUser?.audience === 'admin' && authUser?.account === account) {
        hasValidAdminToken = await lm.getUserAdminPermissions(
          account,
          authUser.id
        )
        // Check for specific role using the new endpoint
        const roleCheckResponse = await lm.checkUserSpecificRole(
          account,
          authUser.id,
          B2B_LM_PRODUCT_CODE,
          requiredRole
        )

        hasValidAdminRole = !!roleCheckResponse
      }
    } catch (err) {
      // noop so we leave hasValidAdminToken as false
      logger.warn({
        message: 'Error validating admin token',
        err,
      })
    }
  }

  return {
    hasAdminToken,
    hasValidAdminToken,
    hasCurrentValidAdminToken,
    hasValidAdminRole,
  }
}

export const validateApiToken = async (
  context: Context,
  requiredRole: string = LICENSE_MANAGER_ROLES.B2B_ORGANIZATIONS_VIEW
): Promise<{
  hasApiToken: boolean
  hasValidApiToken: boolean
  hasCurrentValidApiToken: boolean
  hasValidApiRole: boolean
}> => {
  const {
    clients: { identity, lm },
    vtex: { account, logger },
  } = context

  // check if has api token and if it is valid
  const apiToken = context?.headers['vtex-api-apptoken'] as string
  const appKey = context?.headers['vtex-api-appkey'] as string
  const hasApiToken = !!(apiToken?.length && appKey?.length)
  let hasValidApiToken = false

  // this is used to check if the token is valid by current standards
  let hasCurrentValidApiToken = false
  let hasValidApiRole = false

  if (hasApiToken) {
    try {
      const { token } = await identity.getToken({
        appkey: appKey,
        apptoken: apiToken,
      })

      const authUser = await identity.validateToken({
        token,
      })

      // we set this flag to true if the token is valid by current standards
      // in the future we should remove this line
      hasCurrentValidApiToken = true
      // keeping this behavior for now, but we should remove it in the future as well
      context.cookies.set('VtexIdclientAutCookie', token)
      context.vtex.adminUserAuthToken = token

      if (authUser?.audience === 'admin' && authUser?.account === account) {
        hasValidApiToken = await lm.getUserAdminPermissions(
          account,
          authUser.id
        )
        // Check for specific role using the new endpoint
        const roleCheckResponse = await lm.checkUserSpecificRole(
          account,
          authUser.id,
          B2B_LM_PRODUCT_CODE,
          requiredRole
        )

        hasValidApiRole = !!roleCheckResponse
      }
    } catch (err) {
      // noop so we leave hasValidApiToken as false
      logger.warn({
        message: 'Error validating API token',
        err,
      })
    }
  }

  return {
    hasApiToken,
    hasValidApiToken,
    hasCurrentValidApiToken,
    hasValidApiRole,
  }
}

export const validateStoreToken = async (
  context: Context,
  storeUserAuthToken: string
): Promise<{
  hasStoreToken: boolean
  hasValidStoreToken: boolean
  hasCurrentValidStoreToken: boolean
}> => {
  const {
    clients: { vtexId },
    vtex: { logger },
  } = context

  // check if has store token and if it is valid
  const hasStoreToken = !!storeUserAuthToken
  let hasValidStoreToken = false
  // this is used to check if the token is valid by current standards
  let hasCurrentValidStoreToken = false

  if (hasStoreToken) {
    try {
      const authUser = await vtexId.getAuthenticatedUser(storeUserAuthToken)

      if (authUser?.user) {
        // we set this flag to true if the token is valid by current standards
        // in the future we should remove this line
        hasCurrentValidStoreToken = true

        const userIsPartOfBuyerOrg = await isUserPartOfBuyerOrg(
          authUser?.user,
          context
        )

        if (userIsPartOfBuyerOrg) {
          hasValidStoreToken = true
        }

        // adding log to better understand invalid store token use cases
        // in the future we should remove this log
        if (hasCurrentValidStoreToken && !hasValidStoreToken) {
          logger.warn({
            message: 'Invalid store token:',
            operation: context?.request?.url,
            userAgent: context?.request?.headers['user-agent'] as string,
            caller: context?.request?.headers['x-vtex-caller'] as string,
            forwardedHost: context?.request?.headers[
              'x-forwarded-host'
            ] as string,
            authUser,
          })
        }
      }
    } catch (err) {
      // noop so we leave hasValidStoreToken as false
      logger.warn({
        message: 'Error validating store token:',
        err,
      })
    }
  }

  return { hasStoreToken, hasValidStoreToken, hasCurrentValidStoreToken }
}

export const validateAdminTokenOnHeader = async (
  context: Context,
  requiredRole: string = LICENSE_MANAGER_ROLES.B2B_ORGANIZATIONS_VIEW
): Promise<{
  hasAdminTokenOnHeader: boolean
  hasValidAdminTokenOnHeader: boolean
  hasCurrentValidAdminTokenOnHeader: boolean
  hasValidAdminRoleOnHeader: boolean
}> => {
  const adminUserAuthToken = context?.headers.vtexidclientautcookie as string
  const hasAdminTokenOnHeader = !!adminUserAuthToken?.length

  if (!hasAdminTokenOnHeader) {
    return {
      hasAdminTokenOnHeader: false,
      hasValidAdminTokenOnHeader: false,
      hasCurrentValidAdminTokenOnHeader: false,
      hasValidAdminRoleOnHeader: false,
    }
  }

  const {
    hasAdminToken,
    hasCurrentValidAdminToken,
    hasValidAdminToken,
    hasValidAdminRole,
  } = await validateAdminToken(context, adminUserAuthToken, requiredRole)

  return {
    hasAdminTokenOnHeader: hasAdminToken,
    hasValidAdminTokenOnHeader: hasValidAdminToken,
    hasCurrentValidAdminTokenOnHeader: hasCurrentValidAdminToken,
    hasValidAdminRoleOnHeader: hasValidAdminRole,
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of b2b-organizations-graphql/node/resolvers/directives/validateAdminUserAccess.ts from the b2b-organizations-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { SchemaDirectiveVisitor } from 'graphql-tools'
import { AuthenticationError, ForbiddenError } from '@vtex/api'
import type { GraphQLField } from 'graphql'
import { defaultFieldResolver } from 'graphql'

import type { AuthAuditMetric } from '../../utils/metrics/auth'
import sendAuthMetric, { AuthMetric } from '../../utils/metrics/auth'
import {
  validateAdminToken,
  validateAdminTokenOnHeader,
  validateApiToken,
} from './helper'
import { LICENSE_MANAGER_ROLES } from '../../constants'

export class ValidateAdminUserAccess extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field

    field.resolve = async (
      root: any,
      args: any,
      context: Context,
      info: any
    ) => {
      const {
        vtex: { adminUserAuthToken, logger },
      } = context

      // Get the admin permission from directive arguments, defaulting to VIEW permission
      const adminPermissionArg =
        this.args?.adminPermission || 'B2B_ORGANIZATIONS_VIEW'

      const requiredPermission =
        adminPermissionArg === 'B2B_ORGANIZATIONS_VIEW'
          ? LICENSE_MANAGER_ROLES.B2B_ORGANIZATIONS_VIEW
          : LICENSE_MANAGER_ROLES.B2B_ORGANIZATIONS_EDIT

      // get metrics data
      const operation = field?.astNode?.name?.value ?? context?.request?.url
      const userAgent = context?.request?.headers['user-agent'] as string
      const caller = context?.request?.headers['x-vtex-caller'] as string
      const forwardedHost = context?.request?.headers[
        'x-forwarded-host'
      ] as string

      // set metric fields with initial data
      let metricFields: AuthAuditMetric = {
        operation,
        forwardedHost,
        caller,
        userAgent,
      }

      const { hasAdminToken, hasValidAdminToken, hasValidAdminRole } =
        await validateAdminToken(
          context,
          adminUserAuthToken as string,
          requiredPermission
        )

      // add admin token metrics
      metricFields = {
        ...metricFields,
        hasAdminToken,
        hasValidAdminToken,
        hasValidAdminRole,
      }

      // allow access if has valid admin token and valid admin role
      if (hasValidAdminToken && hasValidAdminRole) {
        sendAuthMetric(
          context,
          logger,
          new AuthMetric(
            context?.vtex?.account,
            metricFields,
            'ValidateAdminUserAccessAudit'
          )
        )

        return resolve(root, args, context, info)
      }

      // If there's no valid admin token on context, search for it on header
      const {
        hasAdminTokenOnHeader,
        hasValidAdminTokenOnHeader,
        hasValidAdminRoleOnHeader,
      } = await validateAdminTokenOnHeader(context, requiredPermission)

      // add admin header token metrics
      metricFields = {
        ...metricFields,
        hasAdminTokenOnHeader,
        hasValidAdminTokenOnHeader,
        hasValidAdminRoleOnHeader,
      }

      // allow access if has valid admin token and valid admin role
      if (hasValidAdminTokenOnHeader && hasValidAdminRoleOnHeader) {
        // set adminUserAuthToken on context so it can be used later
        context.vtex.adminUserAuthToken = context?.headers
          .vtexidclientautcookie as string
        sendAuthMetric(
          context,
          logger,
          new AuthMetric(
            context?.vtex?.account,
            metricFields,
            'ValidateAdminUserAccessAudit'
          )
        )

        return resolve(root, args, context, info)
      }

      const { hasApiToken, hasValidApiToken, hasValidApiRole } =
        await validateApiToken(context, requiredPermission)

      // add API token metrics
      metricFields = {
        ...metricFields,
        hasApiToken,
        hasValidApiToken,
        hasValidApiRole,
      }

      // allow access if has valid API token and valid API role
      if (hasValidApiToken && hasValidApiRole) {
        sendAuthMetric(
          context,
          logger,
          new AuthMetric(
            context?.vtex?.account,
            metricFields,
            'ValidateAdminUserAccessAudit'
          )
        )

        return resolve(root, args, context, info)
      }

      // deny access if no tokens were provided
      if (!hasAdminToken && !hasAdminTokenOnHeader && !hasApiToken) {
        logger.warn({
          message: 'ValidateAdminUserAccess: No token provided',
          ...metricFields,
        })
        throw new AuthenticationError('No token was provided')
      }

      // deny access if no valid tokens were provided or no valid role
      logger.warn({
        message:
          'ValidateAdminUserAccess: Invalid token or insufficient role permissions',
        requiredPermission,
        ...metricFields,
      })
      throw new ForbiddenError(`Unauthorized Access`)
    }
  }
}

```