Skip to content

Commit

Permalink
ClientSide types (#15)
Browse files Browse the repository at this point in the history
* Added ClientSide type

* Added changeset

* Updated types/index.ts

* Removed deprecated endpoint

* Revert "Updated types/index.ts"

This reverts commit 56a0928.

* Revert "Removed deprecated endpoint"

This reverts commit d1a769a.

* Added missing ClientSide types

* Updated types for client side publish method

* Used FixMe convention for unknown types

* Updated types

* Linting

* Updated encoding

* fixed payload & added new method & added code trimming

* removed console.logs

* updated yarn.lock to correct node

* Updated git workflows

* Updates

* Updated

* updates

* updates
  • Loading branch information
pkgacek authored Mar 10, 2021
1 parent 8628825 commit 0098874
Show file tree
Hide file tree
Showing 14 changed files with 821 additions and 510 deletions.
5 changes: 5 additions & 0 deletions .changeset/large-spiders-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@voucherify/sdk': patch
---

Added ClientSide types
5 changes: 5 additions & 0 deletions .changeset/mighty-melons-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@voucherify/sdk': patch
---

Updated types for client side publish method
5 changes: 5 additions & 0 deletions .changeset/red-kiwis-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@voucherify/sdk': patch
---

Used FixMe convention for unknown types
5 changes: 5 additions & 0 deletions .changeset/ten-cats-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@voucherify/sdk': patch
---

Added missing ClientSide types
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v2

- name: Use Node.js 14.x
- name: Use Node.js 14.15
uses: actions/setup-node@v2
with:
node-version: 14.x
node-version: 14.15

- name: Cache Yarn Dependencies
uses: c-hive/gha-yarn-cache@v1
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
# - Adjust this value if necessary once we have stable releases
fetch-depth: 100

- name: Setup Node.js 14.x And npm Org
- name: Setup Node.js 14.15 And npm Org
uses: actions/setup-node@v2
with:
node-version: 14.x
node-version: 14.15
registry-url: "https://registry.npmjs.org"
scope: "@voucherify"

Expand Down
2 changes: 1 addition & 1 deletion examples/with-nodejs/client-track-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const metadata = {
test: true,
}
voucherify
.track('Custom event 123', metadata, customer)
.track('Custom event 123', customer, metadata)
.then(result => {
console.log(result)
})
Expand Down
101 changes: 78 additions & 23 deletions packages/sdk/src/ClientSide.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { isObject, isString, isNumber, assert, toQueryParams, isOptionalObject, isOptionalString } from './helpers'
import * as T from './types/ClientSide'

import { assert, isObject, isOptionalObject, isOptionalString, isString, toQueryParams } from './helpers'

import type { RequestController } from './RequestController'

export class ClientSide {
Expand All @@ -10,13 +13,13 @@ export class ClientSide {
/**
* @see https://docs.voucherify.io/reference/#vouchers-validate
*/
public validate(params: $FixMe) {
public validate(params: T.ClientSideValidateParams | string) {
assert(
isObject(params) || isString(params),
'client.validate: expected "params" argument to be an object or a string',
)

const query: Record<string, unknown> = {}
const query: Record<string, any> = {}

if (isString(params)) {
query.code = params
Expand All @@ -30,6 +33,10 @@ export class ClientSide {
query.trackingId = this.trackingId
}

if (!!query.code) {
query.code = query.code.replace(/[\r\n\t\f\v]/g, '').trim()
}

assert(isOptionalObject(query?.customer), 'client.validate: expected "params.customer" to be an object')
assert(isOptionalString(query?.customer?.source_id), 'client.validate: expected "params.customer.source_id" to be a string') // prettier-ignore
assert(isOptionalObject(query?.customer?.metadata), 'client.validate: expected "params.customer.metadata" to be an object') // prettier-ignore
Expand All @@ -38,45 +45,93 @@ export class ClientSide {

const path = query.code ? '/validate' : '/promotions/validation'

return this.client.get(path, queryParams)
return this.client.get<T.ClientSideValidateResponse>(path, queryParams)
}
/**
* @see https://docs.voucherify.io/reference#redeem-voucher-client-side
*/
public redeem(code: string, payload: $FixMe = {}) {
public redeem(code: string, payload: T.ClientSideRedeemPayload = {}) {
assert(isString(code), 'client.redeem - please provide a valid Voucher code')
assert(isObject(payload), 'client.redeem - expected payload to be an object')
assert(isObject(payload.order), 'client.redeem - expected payload.order to be an object')
assert(isNumber(payload.order.amount), 'client.redeem - expected payload.order.amount to be a number')

code = code.trim()
code = code.replace(/[\r\n\t\f\v]/g, '').trim()

payload.customer = payload.customer ?? {}
payload.customer.source_id = payload.customer.source_id ?? this.trackingId

return this.client.post<$FixMe>('/redeem', payload, { code })
return this.client.post<T.ClientSideRedeemResponse>('/redeem', payload, { code })
}
public publish(campaign: string, payload: $FixMe = {}) {
assert(isString(campaign), 'client.publish - campaign is required to publish a voucher')
assert(isObject(payload), 'client.redeem - expected payload to be an object')
/**
* @see https://docs.voucherify.io/reference#create-publication
*/
public publish(
campaign: string,
payload: T.ClientSidePublishPayload = {},
queryParams: T.ClientSidePublishQueryParams = {},
) {
assert(isObject(payload), 'client.publish - expected payload to be an object')

payload.customer = payload.customer ?? {}
payload.customer.source_id = payload.customer.source_id ?? this.trackingId
payload.channel = payload.channel ?? 'Voucherify.js' // @todo - removed hard-coded channel
const preparedPayload: T.ClientSidePublishPreparedPayload = {}

preparedPayload.customer = payload.customer ?? {}
preparedPayload.customer.source_id = payload.customer?.source_id ?? this.trackingId
preparedPayload.channel = payload.channel ?? 'Voucherify.js' // @todo - removed hard-coded channel

assert(
isString(preparedPayload.customer?.source_id),
'client.publish - expected payload to contain customer source id or to have tracking id set up by Voucherify client',
)

queryParams.campaign = campaign.replace(/[\r\n\t\f\v]/g, '').trim()

return this.client.post<$FixMe>('/publish', payload, { campaign })
return this.client.post<T.ClientSidePublishResponse>('/publish', preparedPayload, queryParams)
}
/**
* @see https://docs.voucherify.io/reference#track-custom-event-client-side
*/
public track(event_name: $FixMe, metadata: $FixMe, customer?: $FixMe) {
const payload: $FixMe = {

public track(
event_name: string,
customer: T.ClientSideTrackCustomer,
metadata?: Record<string, any>,
referral?: T.ClientSideTrackReferral,
loyalty?: T.ClientSideTrackLoyalty,
) {
assert(isString(event_name), 'client.track - expected event name to be an string')
assert(isObject(customer), 'client.track - expected customer to be an object')

const payload: T.ClientSideTrackPayload = {
event: event_name,
metadata: metadata,
customer: customer ?? {},
metadata: metadata ?? {},
customer: customer,
referral: referral ?? {},
loyalty: loyalty ?? {},
}
payload.customer.source_id = (customer ?? {}).source_id ?? this.trackingId

return this.client.post<$FixMe>('/events', payload)
payload.customer.source_id = customer.source_id ?? this.trackingId

assert(
isString(payload.customer?.source_id),
'client.track - expected payload to contain customer source id or to have tracking id set up by Voucherify client',
)

return this.client.post<T.ClientSideTrackResponse>('/events', payload)
}
/**
* @see https://docs.voucherify.io/reference#list-vouchers
*/
public listVouchers(params: T.ClientSideListVouchersParams = {}) {
const query: Record<string, any> = {}

query.campaign = params.campaign
query.category = params.category
query.page = params.page
query.limit = params.limit
query.customer = params.customer
query.created_at = params.created_at
query.updated_at = params.updated_at

const queryParams = toQueryParams(query)

return this.client.get<T.ClientSideListVouchersResponse>('/vouchers', queryParams)
}
}
5 changes: 3 additions & 2 deletions packages/sdk/src/RequestController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios, { AxiosInstance, AxiosError } from 'axios'
import { VoucherifyError } from './VoucherifyError'
import axios, { AxiosError, AxiosInstance } from 'axios'

import Qs from 'qs'
import { VoucherifyError } from './VoucherifyError'

export interface RequestControllerOptions {
baseURL: string
Expand Down
4 changes: 2 additions & 2 deletions packages/sdk/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export function toQueryParams(obj: Record<string, unknown>): Record<string, stri
case 'number':
case 'boolean':
case 'bigint':
if (prefix) return entries.push([`${prefix}[${key}]`, val.toString()])
return entries.push([key, val.toString()])
if (prefix) return entries.push([`${prefix}[${key}]`, encode(val.toString())])
return entries.push([key, encode(val.toString())])
case 'object':
if (prefix) return mapToEntries(`${prefix}[${key}]`, <Record<string, unknown>>val)
return mapToEntries(key, <Record<string, unknown>>val)
Expand Down
120 changes: 120 additions & 0 deletions packages/sdk/src/types/ClientSide.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { DiscountAmount, DiscountPercent, DiscountUnit, VouchersListParams, VouchersResponse } from './Vouchers'
import { OrdersCreateResponse, OrdersItem } from './Orders'

import { CustomerRequest } from './Customers'
import { DistributionsPublicationsCreateResponse } from './Distributions'
import { SimplePromotionTier } from './PromotionTiers'

export interface ClientSideValidateParams {
code?: string
tracking_id?: string
amount?: number
items?: Pick<OrdersItem, 'source_id' | 'product_id' | 'sku' | 'quantity'>[]
orderMetadata?: Record<string, any>
customer?: Pick<CustomerRequest, 'source_id' | 'metadata'>
metadata?: Record<string, any>
session_type?: 'LOCK'
session_key?: string
session_ttl?: number
session_ttl_unit?: 'MILLISECONDS' | 'SECONDS' | 'MINUTES' | 'HOURS' | 'DAYS'
}

export type ClientSideListVouchersParams = VouchersListParams
export type ClientSideVoucherListing = Pick<
VouchersResponse,
'active' | 'code' | 'metadata' | 'assets' | 'object' | 'expiration_date' | 'start_date'
>

export interface ClientSideListVouchersResponse {
object: 'list'
total: number
data_ref: 'vouchers'
vouchers: ClientSideVoucherListing[]
}
export interface ClientSideValidateResponse {
code?: string
valid: boolean
discount?: DiscountUnit | DiscountAmount | DiscountPercent
order?: {
object: 'order'
amount?: number
discount_amount?: number
items: Pick<OrdersItem, 'source_id' | 'product_id' | 'sku' | 'quantity'>[]
}
tracking_id?: string
promotions?: SimplePromotionTier[]
}

export interface ClientSideRedeemPayload {
tracking_id?: string
customer?: CustomerRequest
order?: ClientSideRedeemOrder
metadata?: Record<string, any>
reward?: {
id: string
}
session?: {
key: string
}
}

export interface ClientSideRedeemResponse {
id: string
object: 'redemption'
date?: string
customer_id?: string
tracking_id?: string
order?: OrdersCreateResponse
metadata?: Record<string, any>
result: 'SUCCESS' | 'FAILURE'
voucher?: VouchersResponse
}

export interface ClientSidePublishPayload {
source_id?: string
channel?: 'Voucherify.js' | string
customer?: CustomerRequest
voucher?: string
metadata?: Record<string, any>
}

export type ClientSidePublishPreparedPayload = ClientSidePublishPayload

export interface ClientSidePublishQueryParams {
join_once?: boolean
campaign?: string
}

export interface ClientSidePublishCampaign {
name: string
count?: number
}

export type ClientSidePublishResponse = DistributionsPublicationsCreateResponse & { vouchers_id?: string[] }
export interface ClientSideTrackLoyalty {
code?: string
}
export interface ClientSideTrackReferral {
code?: string
}

export interface ClientSideTrackPayload {
event: string
metadata?: Record<string, any>
customer: CustomerRequest
loyalty?: ClientSideTrackLoyalty
referral?: ClientSideTrackReferral
}

export interface ClientSideTrackResponse {
object: 'event'
type: string
}

export type ClientSideRedeemOrder = Partial<Pick<OrdersCreateResponse, 'id' | 'source_id' | 'metadata' | 'amount'>> & {
items?: ClientSideRedeemItem[]
}

export type ClientSideRedeemItem = Pick<OrdersItem, 'source_id' | 'product_id' | 'sku' | 'quantity'>
export type ClientSideResponseItem = ClientSideRedeemItem
export type ClientSideTrackCustomer = CustomerRequest
11 changes: 11 additions & 0 deletions packages/sdk/src/types/PromotionTiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ import { OrdersCreateResponse, OrdersItem } from './Orders'
import { SimpleCustomer } from './Customers'
import { ValidationRulesListAssignmentsResponse } from './ValidationRules'

export interface SimplePromotionTier {
tracking_id: string
metadata?: Record<string, any>
valid: boolean
id: string
name: string
banner?: string
discount: DiscountUnit | DiscountPercent | DiscountAmount
hierarchy: number
object: 'promotion_tier'
}
export interface PromotionTier {
id: string
object: 'promotion_tier'
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './Customers'
export * from './Balance'
export * from './ClientSide'
export * from './Redemptions'
export * from './Validations'
export * from './Campaigns'
Expand Down
Loading

0 comments on commit 0098874

Please sign in to comment.