From c06e4a0b9fcf798cad3c66099647e5416f1db60d Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 9 Nov 2023 12:38:06 +0000 Subject: [PATCH] Add widget meta-data - v0.11.0 (#44) * Add widget meta-data * Fix wrong schema * Add tests * Export the latest version of the widget metadata --- src/generatedTypes/index.ts | 10 ++- src/generatedTypes/v0.11.0.ts | 140 +++++++++++++++++++++++++++++++++ src/schemas/v0.11.0.json | 62 +++++++++++++++ src/schemas/widget/v0.1.0.json | 23 ++++++ src/scripts/compile.ts | 4 +- test/schema.spec.ts | 75 ++++++++++++++++++ 6 files changed, 310 insertions(+), 4 deletions(-) create mode 100644 src/generatedTypes/v0.11.0.ts create mode 100644 src/schemas/v0.11.0.json create mode 100644 src/schemas/widget/v0.1.0.json diff --git a/src/generatedTypes/index.ts b/src/generatedTypes/index.ts index e9f40af..d762afd 100644 --- a/src/generatedTypes/index.ts +++ b/src/generatedTypes/index.ts @@ -2,6 +2,7 @@ import * as v0_1_0 from './v0.1.0' import * as v0_10_0 from './v0.10.0' +import * as v0_11_0 from './v0.11.0' import * as v0_2_0 from './v0.2.0' import * as v0_3_0 from './v0.3.0' import * as v0_4_0 from './v0.4.0' @@ -11,18 +12,20 @@ import * as v0_7_0 from './v0.7.0' import * as v0_8_0 from './v0.8.0' import * as v0_9_0 from './v0.9.0' -export * as latest from './v0.10.0' +export * as latest from './v0.11.0' -export const LATEST_APP_DATA_VERSION = '0.10.0' +export const LATEST_APP_DATA_VERSION = '0.11.0' export const LATEST_QUOTE_METADATA_VERSION = '0.3.0' export const LATEST_REFERRER_METADATA_VERSION = '0.2.0' export const LATEST_ORDER_CLASS_METADATA_VERSION = '0.3.0' export const LATEST_UTM_METADATA_VERSION = '0.2.0' export const LATEST_HOOKS_METADATA_VERSION = '0.1.0' export const LATEST_SIGNER_METADATA_VERSION = '0.1.0' +export const LATEST_WIDGET_METADATA_VERSION = '0.1.0' -export type LatestAppDataDocVersion = v0_10_0.AppDataRootSchema +export type LatestAppDataDocVersion = v0_11_0.AppDataRootSchema export type AnyAppDataDocVersion = + | v0_11_0.AppDataRootSchema | v0_10_0.AppDataRootSchema | v0_9_0.AppDataRootSchema | v0_8_0.AppDataRootSchema @@ -35,6 +38,7 @@ export type AnyAppDataDocVersion = | v0_1_0.AppDataRootSchema export { + v0_11_0, v0_10_0, v0_9_0, v0_8_0, diff --git a/src/generatedTypes/v0.11.0.ts b/src/generatedTypes/v0.11.0.ts new file mode 100644 index 0000000..2e0d42c --- /dev/null +++ b/src/generatedTypes/v0.11.0.ts @@ -0,0 +1,140 @@ +/* tslint:disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Semantic versioning of document. + */ +export type Version = string; +/** + * The code identifying the CLI, UI, service generating the order. + */ +export type AppCode = string; +/** + * Environment from which the order came from. + */ +export type Environment = string; +/** + * The address of the trader who signs the CoW Swap order. This field should normally be omitted; it is recommended to use it if the signer is a smart-contract wallet using EIP-1271 signatures. + */ +export type Signer = string; +export type ReferrerAddress = string; +/** + * Tracks in which medium the traffic originated from (twitter, facebook, etc.) + */ +export type UTMSource = string; +/** + * Tracks in which medium the traffic originated from (mail, CPC, social, etc.) + */ +export type UTMMedium = string; +/** + * Track the performance of a specific campaign + */ +export type UTMCampaign = string; +/** + * Track which link was clicked + */ +export type UTMContent = string; +/** + * Track which keyword term a website visitor came from + */ +export type UTMKeywordTerm = string; +/** + * Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BIPS) + */ +export type SlippageBips = string; +/** + * Indicator of the order class. + */ +export type OrderClass1 = "market" | "limit" | "liquidity" | "twap"; +/** + * Semantic versioning of document. + */ +export type Version1 = string; +/** + * The contract to call for the hook + */ +export type HookTarget = string; +/** + * The calldata to use when calling the hook + */ +export type HookCallData = string; +/** + * The gas limit (in gas units) for the hook + */ +export type HookGasLimit = string; +/** + * CoW Hooks to call before an order executes + */ +export type PreHooks = CoWHook[]; +/** + * CoW Hooks to call after an order executes + */ +export type PostHooks = CoWHook[]; +/** + * The code identifying the UI powering the widget + */ +export type AppCode1 = string; +/** + * Environment from which the order came from. + */ +export type Environment1 = string; + +/** + * Metadata JSON document for adding information to orders. + */ +export interface AppDataRootSchema { + version: Version; + appCode?: AppCode; + environment?: Environment; + metadata: Metadata; +} +/** + * Each metadata will specify one aspect of the order. + */ +export interface Metadata { + signer?: Signer; + referrer?: Referrer; + utm?: UTMCodes; + quote?: Quote; + orderClass?: OrderClass; + hooks?: OrderInteractionHooks; + widget?: Widget; +} +export interface Referrer { + address: ReferrerAddress; +} +export interface UTMCodes { + utmSource?: UTMSource; + utmMedium?: UTMMedium; + utmCampaign?: UTMCampaign; + utmContent?: UTMContent; + utmTerm?: UTMKeywordTerm; +} +export interface Quote { + slippageBips: SlippageBips; +} +export interface OrderClass { + orderClass: OrderClass1; +} +/** + * Optional Pre and Post order interaction hooks attached to a single order + */ +export interface OrderInteractionHooks { + version?: Version1; + pre?: PreHooks; + post?: PostHooks; +} +export interface CoWHook { + target: HookTarget; + callData: HookCallData; + gasLimit: HookGasLimit; +} +export interface Widget { + appCode: AppCode1; + environment?: Environment1; + [k: string]: unknown; +} diff --git a/src/schemas/v0.11.0.json b/src/schemas/v0.11.0.json new file mode 100644 index 0000000..5dcd00f --- /dev/null +++ b/src/schemas/v0.11.0.json @@ -0,0 +1,62 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v0.11.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": ["version", "metadata"], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.11.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "signer": { + "$ref": "signer/v0.1.0.json#" + }, + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v0.3.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + }, + "widget": { + "$ref": "widget/v0.1.0.json#" + } + } + } + } +} diff --git a/src/schemas/widget/v0.1.0.json b/src/schemas/widget/v0.1.0.json new file mode 100644 index 0000000..47a442e --- /dev/null +++ b/src/schemas/widget/v0.1.0.json @@ -0,0 +1,23 @@ +{ + "$id": "#widget/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["appCode"], + "title": "Widget", + "type": "object", + "properties": { + "appCode": { + "$id": "#/properties/appCodeWidget", + "description": "The code identifying the UI powering the widget", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environmentWidget", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + } + } +} diff --git a/src/scripts/compile.ts b/src/scripts/compile.ts index a738734..85aa083 100644 --- a/src/scripts/compile.ts +++ b/src/scripts/compile.ts @@ -63,6 +63,7 @@ async function compile(): Promise { const latestUtmVersion = await getLatestMetadataDocVersion('utm') const latestHooksVersion = await getLatestMetadataDocVersion('hooks') const latestSignerVersion = await getLatestMetadataDocVersion('signer') + const latestWidgetVersion = await getLatestMetadataDocVersion('widget') const additionalTypesExport = ` export * as latest from './${latest}' @@ -74,6 +75,7 @@ export const LATEST_ORDER_CLASS_METADATA_VERSION = '${extractSemver(latestOrderC export const LATEST_UTM_METADATA_VERSION = '${extractSemver(latestUtmVersion)}' export const LATEST_HOOKS_METADATA_VERSION = '${extractSemver(latestHooksVersion)}' export const LATEST_SIGNER_METADATA_VERSION = '${extractSemver(latestSignerVersion)}' +export const LATEST_WIDGET_METADATA_VERSION = '${extractSemver(latestWidgetVersion)}' export type LatestAppDataDocVersion = ${latestExport}.AppDataRootSchema export type AnyAppDataDocVersion = ${allVersions} @@ -98,7 +100,7 @@ function extractSemver(name: string): string { } async function getLatestMetadataDocVersion( - metadataDocName: 'quote' | 'referrer' | 'orderClass' | 'utm' | 'hooks' | 'signer' + metadataDocName: 'quote' | 'referrer' | 'orderClass' | 'utm' | 'hooks' | 'signer' | 'widget' ): Promise { const metadataPath = path.join(SCHEMAS_SRC_PATH, metadataDocName) const versions = await fs.promises.readdir(metadataPath) diff --git a/test/schema.spec.ts b/test/schema.spec.ts index fc9941e..9c9de7a 100644 --- a/test/schema.spec.ts +++ b/test/schema.spec.ts @@ -8,6 +8,7 @@ import schemaV0_5_0 from '../schemas/v0.5.0.json' import schemaV0_6_0 from '../schemas/v0.6.0.json' import schemaV0_9_0 from '../schemas/v0.9.0.json' import schemaV0_10_0 from '../schemas/v0.10.0.json' +import schemaV0_11_0 from '../schemas/v0.11.0.json' const ADDRESS = '0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9' const REFERRER_V0_1_0 = { address: ADDRESS, version: '0.1.0' } @@ -552,6 +553,80 @@ describe('Schema v0.10.0', () => { ) }) +describe('Schema v0.11.0', () => { + const ajv = new Ajv() + const validator = ajv.compile(schemaV0_11_0) + + const BASE_DOCUMENT = { + version: '0.11.0', + metadata: {}, + } + + test('Minimal valid schema', _buildAssertValidFn(validator, BASE_DOCUMENT)) + + test( + 'With widget v0.11.0', + _buildAssertValidFn(validator, { + ...BASE_DOCUMENT, + metadata: { widget: { appCode: 'Pig Swap', environment: 'production' } }, + }) + ) + + test( + 'With widget and no environment v0.11.0', + _buildAssertValidFn(validator, { + ...BASE_DOCUMENT, + metadata: { widget: { appCode: 'Pig Swap' } }, + }) + ) + + test( + 'Widget with no appCode v0.11.0', + _buildAssertInvalidFn( + validator, + { + ...BASE_DOCUMENT, + metadata: { + widget: { + environment: 'production', + }, + }, + }, + [ + { + instancePath: '/metadata/widget', + keyword: 'required', + message: "must have required property 'appCode'", + params: { missingProperty: 'appCode' }, + schemaPath: '#/properties/metadata/properties/widget/required', + }, + ] + ) + ) + + test( + 'Signer with invalid address', + _buildAssertInvalidFn( + validator, + { + ...BASE_DOCUMENT, + metadata: { + signer: '0xinvalid', + }, + }, + [ + { + instancePath: '/metadata/signer', + keyword: 'pattern', + message: 'must match pattern "^0x[a-fA-F0-9]{40}$"', + params: { pattern: '^0x[a-fA-F0-9]{40}$' }, + schemaPath: '#/properties/metadata/properties/signer/pattern', + }, + ] + ) + ) +}) + function _buildAssertValidFn(validator: ValidateFunction, doc: any) { return () => { // when