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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@iterable/api",
"version": "0.7.0",
"version": "0.7.1",
"description": "TypeScript client library for the Iterable API",
"keywords": [
"iterable",
Expand Down
62 changes: 62 additions & 0 deletions src/client/experiments.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import {
ExperimentDetails,
ExperimentDetailsSchema,
ExperimentMetricsResponse,
GetExperimentMetricsParams,
GetExperimentParams,
GetExperimentVariantsParams,
GetExperimentVariantsResponse,
GetExperimentVariantsResponseSchema,
ListExperimentsParams,
ListExperimentsResponse,
ListExperimentsResponseSchema,
} from "../types/experiments.js";
import type { Constructor } from "./base.js";
import type { BaseIterableClient } from "./base.js";
Expand Down Expand Up @@ -44,5 +53,58 @@ export function Experiments<T extends Constructor<BaseIterableClient>>(
// Parse CSV response into array of objects
return this.parseCsv(response);
}

async listExperiments(
params?: ListExperimentsParams
): Promise<ListExperimentsResponse> {
const queryParams = new URLSearchParams();

if (params?.campaignId !== undefined) {
queryParams.append("campaignId", params.campaignId.toString());
}
if (params?.status) {
queryParams.append("state", params.status);
}
if (params?.startDate) {
queryParams.append("startDateTime", params.startDate);
}
if (params?.endDate) {
queryParams.append("endDateTime", params.endDate);
}
if (params?.limit !== undefined) {
queryParams.append("limit", params.limit.toString());
}
if (params?.offset !== undefined) {
queryParams.append("offset", params.offset.toString());
}

const url = `/api/experiments${
queryParams.toString() ? `?${queryParams.toString()}` : ""
}`;
const response = await this.client.get(url);

return this.validateResponse(response, ListExperimentsResponseSchema);
}

async getExperiment(
params: GetExperimentParams
): Promise<ExperimentDetails> {
const url = `/api/experiments/${params.experimentId}`;
const response = await this.client.get(url);

return this.validateResponse(response, ExperimentDetailsSchema);
}

async getExperimentVariants(
params: GetExperimentVariantsParams
): Promise<GetExperimentVariantsResponse> {
const url = `/api/experiments/${params.experimentId}/variants`;
const response = await this.client.get(url);

return this.validateResponse(
response,
GetExperimentVariantsResponseSchema
);
}
};
}
138 changes: 138 additions & 0 deletions src/types/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,141 @@ export type ExperimentMetricsResponse = z.infer<
export type GetExperimentMetricsParams = z.infer<
typeof GetExperimentMetricsParamsSchema
>;

/**
* Experiment list schemas and types
*/

export const ExperimentStatusSchema = z.enum(["draft", "running", "finished"]);

export const ExperimentListItemSchema = z.object({
id: z.number().describe("Experiment ID"),
name: z.string().describe("Experiment name"),
status: ExperimentStatusSchema.describe("Experiment status"),
startDate: z.string().optional().describe("Start date (ISO 8601)"),
channelType: z.string().describe("Channel type (e.g., email, push)"),
author: z.string().describe("Author email or name"),
});

export const ListExperimentsParamsSchema = z
.object({
campaignId: z.number().optional().describe("Filter by campaign ID"),
status: ExperimentStatusSchema.optional().describe(
"Filter by status (draft, running, finished)"
),
startDate: IterableDateTimeSchema.optional().describe(
"Filter experiments starting from this date (ISO 8601 format)"
),
endDate: IterableDateTimeSchema.optional().describe(
"Filter experiments ending before this date (ISO 8601 format)"
),
limit: z
.number()
.int()
.min(1)
.max(1000)
.optional()
.describe("Number of results to return (max 1000, default 20)"),
offset: z
.number()
.int()
.min(0)
.optional()
.describe("Number of results to skip (default 0)"),
})
.describe("Parameters for listing experiments");

export const ListExperimentsResponseSchema = z.object({
experiments: z.array(ExperimentListItemSchema),
totalCount: z.number().optional().describe("Total number of experiments"),
});

export type ExperimentStatus = z.infer<typeof ExperimentStatusSchema>;
export type ExperimentListItem = z.infer<typeof ExperimentListItemSchema>;
export type ListExperimentsParams = z.infer<typeof ListExperimentsParamsSchema>;
export type ListExperimentsResponse = z.infer<
typeof ListExperimentsResponseSchema
>;

/**
* Get experiment schemas and types
*/

export const ExperimentVariantSummarySchema = z.object({
id: z.number().describe("Variant ID"),
name: z.string().describe("Variant name"),
percentage: z.number().describe("Traffic percentage"),
});

export const ExperimentConstraintsSchema = z
.object({
startDate: z.string().optional().describe("Experiment start date"),
endDate: z.string().optional().describe("Experiment end date"),
timezone: z.string().optional().describe("Timezone"),
})
.passthrough();

export const ExperimentDetailsSchema = z.object({
id: z.number().describe("Experiment ID"),
name: z.string().describe("Experiment name"),
status: ExperimentStatusSchema.describe("Experiment status"),
campaignId: z.number().optional().describe("Associated campaign ID"),
channelType: z.string().describe("Channel type"),
author: z.string().describe("Author"),
createdAt: z.string().optional().describe("Creation timestamp"),
updatedAt: z.string().optional().describe("Last update timestamp"),
variants: z
.array(ExperimentVariantSummarySchema)
.optional()
.describe("Experiment variants"),
constraints: ExperimentConstraintsSchema.optional().describe(
"Experiment constraints and settings"
),
});

export const GetExperimentParamsSchema = z
.object({
experimentId: z.number().describe("Experiment ID"),
})
.describe("Parameters for getting experiment details");

export type ExperimentVariantSummary = z.infer<
typeof ExperimentVariantSummarySchema
>;
export type ExperimentConstraints = z.infer<typeof ExperimentConstraintsSchema>;
export type ExperimentDetails = z.infer<typeof ExperimentDetailsSchema>;
export type GetExperimentParams = z.infer<typeof GetExperimentParamsSchema>;

/**
* Get experiment variants schemas and types
*/

export const ExperimentVariantContentSchema = z.object({
id: z.number().describe("Variant ID"),
name: z.string().describe("Variant name"),
percentage: z.number().describe("Traffic percentage"),
subject: z.string().optional().describe("Email subject line"),
preheader: z.string().optional().describe("Email preheader"),
htmlSource: z.string().optional().describe("HTML email content"),
plainText: z.string().optional().describe("Plain text email content"),
});

export const GetExperimentVariantsParamsSchema = z
.object({
experimentId: z.number().describe("Experiment ID"),
})
.describe("Parameters for getting experiment variants");

export const GetExperimentVariantsResponseSchema = z.object({
variants: z.array(ExperimentVariantContentSchema),
});

export type ExperimentVariantContent = z.infer<
typeof ExperimentVariantContentSchema
>;
export type GetExperimentVariantsParams = z.infer<
typeof GetExperimentVariantsParamsSchema
>;
export type GetExperimentVariantsResponse = z.infer<
typeof GetExperimentVariantsResponseSchema
>;
Loading
Loading