diff --git a/packages/contentstack-export/src/config/index.ts b/packages/contentstack-export/src/config/index.ts index 3d2f8cc42a..73e9149160 100644 --- a/packages/contentstack-export/src/config/index.ts +++ b/packages/contentstack-export/src/config/index.ts @@ -36,7 +36,7 @@ const config: DefaultConfig = { 'entries', 'labels', 'marketplace-apps', - 'eclipse', + 'personalization', ], locales: { dirName: 'locales', @@ -144,8 +144,8 @@ const config: DefaultConfig = { dependencies: ['locales', 'content-types'], exportVersions: false, }, - eclipse: { - dirName: 'eclipse', + personalization: { + dirName: 'personalization', baseURL: 'https://personalization-api.contentstack.com' }, variantEntry: { diff --git a/packages/contentstack-export/src/export/modules/eclipse.ts b/packages/contentstack-export/src/export/modules/personalization.ts similarity index 70% rename from packages/contentstack-export/src/export/modules/eclipse.ts rename to packages/contentstack-export/src/export/modules/personalization.ts index 240826dd1e..92b9028b34 100644 --- a/packages/contentstack-export/src/export/modules/eclipse.ts +++ b/packages/contentstack-export/src/export/modules/personalization.ts @@ -6,15 +6,13 @@ import { ExportAudiences, } from '@contentstack/cli-variants'; -import BaseClass from './base-class'; -import { log, formatError, fsUtil } from '../../utils'; -import { LabelConfig, ModuleClassParams, ExportConfig } from '../../types'; -import { ContentstackClient } from '@contentstack/cli-utilities'; +import { log, formatError } from '../../utils'; +import { ModuleClassParams, ExportConfig } from '../../types'; -export default class ExportEclipse { +export default class ExportPersonalization { public exportConfig: ExportConfig; - constructor({ exportConfig, stackAPIClient }: ModuleClassParams) { + constructor({ exportConfig }: ModuleClassParams) { this.exportConfig = exportConfig; } @@ -27,10 +25,7 @@ export default class ExportEclipse { const projectHandler = new ExportProjects(this.exportConfig); await projectHandler.start(); if (this.exportConfig.personalizationEnabled) { - // export events, attributes & audiences // export experiences, along with this export content type details as well - - log(this.exportConfig, 'Starting personalization project export', 'info'); await new ExportExperiences(this.exportConfig).start(); await new ExportEvents(this.exportConfig).start(); await new ExportAudiences(this.exportConfig).start(); diff --git a/packages/contentstack-export/src/types/default-config.ts b/packages/contentstack-export/src/types/default-config.ts index eeb8169255..c9f1e1e70d 100644 --- a/packages/contentstack-export/src/types/default-config.ts +++ b/packages/contentstack-export/src/types/default-config.ts @@ -122,7 +122,7 @@ export default interface DefaultConfig { dependencies?: Modules[]; exportVersions: boolean; }; - eclipse: { + personalization: { dirName: string, baseURL: string, } & AnyProperty; diff --git a/packages/contentstack-export/src/types/index.ts b/packages/contentstack-export/src/types/index.ts index 9c776df579..7549353ff5 100644 --- a/packages/contentstack-export/src/types/index.ts +++ b/packages/contentstack-export/src/types/index.ts @@ -42,7 +42,7 @@ export type Modules = | 'labels' | 'marketplace-apps' | 'taxonomies' - | 'eclipse'; + | 'personalization'; export type ModuleClassParams = { stackAPIClient: ReturnType; diff --git a/packages/contentstack-import/src/config/index.ts b/packages/contentstack-import/src/config/index.ts index c4ef8c9127..bd2c6d4036 100644 --- a/packages/contentstack-import/src/config/index.ts +++ b/packages/contentstack-import/src/config/index.ts @@ -34,6 +34,7 @@ const config: DefaultConfig = { 'marketplace-apps', 'global-fields', 'content-types', + 'personalization', 'custom-roles', 'workflows', 'entries', @@ -153,7 +154,7 @@ const config: DefaultConfig = { personalization: { importData: true, dirName: 'personalization', - importOrder: ['projects', 'attributes'], + importOrder: ['projects','attributes', 'audiences'], projects: { dirName: 'projects', fileName: 'projects.json', @@ -162,6 +163,10 @@ const config: DefaultConfig = { dirName: 'attributes', fileName: 'attributes.json', }, + audiences: { + dirName: 'audiences', + fileName: 'audiences.json', + }, }, }, languagesCode: [ diff --git a/packages/contentstack-import/src/import/modules/personalization.ts b/packages/contentstack-import/src/import/modules/personalization.ts index de20a8c508..a3e9e23ddf 100644 --- a/packages/contentstack-import/src/import/modules/personalization.ts +++ b/packages/contentstack-import/src/import/modules/personalization.ts @@ -1,13 +1,15 @@ import { Import } from '@contentstack/cli-variants'; -import { log } from '../..//utils'; -import { ImportConfig } from '../../types'; +import { log } from '../../utils'; +import { ImportConfig, ModuleClassParams } from '../../types'; export default class ImportPersonalization { - public readonly personalization: ImportConfig['modules']['personalization']; + private config: ImportConfig; + public personalization: ImportConfig['modules']['personalization']; - constructor(public readonly config: ImportConfig) { - this.personalization = this.config.modules.personalization; + constructor({ importConfig }: ModuleClassParams) { + this.personalization = importConfig.modules.personalization; + this.config = importConfig; } /** @@ -20,6 +22,7 @@ export default class ImportPersonalization { const moduleMapper = { projects: Import.Project, attributes: Import.Attribute, + audiences: Import.Audiences }; const order: (keyof typeof moduleMapper)[] = this.personalization.importOrder as (keyof typeof moduleMapper)[]; diff --git a/packages/contentstack-import/src/types/default-config.ts b/packages/contentstack-import/src/types/default-config.ts index 73c4791ccf..b2ba1f3e99 100644 --- a/packages/contentstack-import/src/types/default-config.ts +++ b/packages/contentstack-import/src/types/default-config.ts @@ -135,6 +135,10 @@ export default interface DefaultConfig { dirName: string; fileName: string; }; + audiences: { + dirName: string; + fileName: string; + }; }; }; languagesCode: string[]; diff --git a/packages/contentstack-import/src/types/index.ts b/packages/contentstack-import/src/types/index.ts index fb9c041d05..8a67585112 100644 --- a/packages/contentstack-import/src/types/index.ts +++ b/packages/contentstack-import/src/types/index.ts @@ -41,7 +41,8 @@ export type Modules = | 'workflows' | 'labels' | 'marketplace-apps' - | 'taxonomies'; + | 'taxonomies' + | 'personalization'; export type ModuleClassParams = { stackAPIClient: ReturnType; diff --git a/packages/contentstack-variants/src/export/attributes.ts b/packages/contentstack-variants/src/export/attributes.ts index 1220bfbfe1..d220ae5e15 100644 --- a/packages/contentstack-variants/src/export/attributes.ts +++ b/packages/contentstack-variants/src/export/attributes.ts @@ -1,10 +1,10 @@ import omit from 'lodash/omit'; import { resolve as pResolve } from 'node:path'; +import { formatError, fsUtil, PersonalizationAdapter, log } from '../utils'; import { EclipseConfig, ExportConfig, AttributesConfig, AttributeStruct } from '../types'; -import { formatError, fsUtil, PersonalizationAdapter, log, VariantHttpClient } from '../utils'; -export default class ExportAttributes extends PersonalizationAdapter> { +export default class ExportAttributes extends PersonalizationAdapter { private attributesConfig: AttributesConfig; private attributesFolderPath: string; private attributes: Record[]; @@ -12,15 +12,11 @@ export default class ExportAttributes extends PersonalizationAdapter> { +export default class ExportAudiences extends PersonalizationAdapter { private audiencesConfig: AudiencesConfig; private audiencesFolderPath: string; private audiences: Record[]; @@ -12,15 +12,11 @@ export default class ExportAudiences extends PersonalizationAdapter> { +export default class ExportEvents extends PersonalizationAdapter { private eventsConfig: EventsConfig; private eventsFolderPath: string; private events: Record[]; @@ -12,15 +12,11 @@ export default class ExportEvents extends PersonalizationAdapter public eclipseConfig: EclipseConfig; constructor(exportConfig: ExportConfig) { super({ - config: {...exportConfig}, - baseURL: exportConfig.modules.eclipse.baseURL, - headers: { organization_uid: exportConfig.org_uid, authtoken: exportConfig.auth_token }, + config: exportConfig, + baseURL: exportConfig.modules.personalization.baseURL, + headers: { authtoken: exportConfig.auth_token, organization_uid: exportConfig.org_uid, }, }); this.exportConfig = exportConfig; - this.eclipseConfig = exportConfig.modules.eclipse; + this.eclipseConfig = exportConfig.modules.personalization; this.projectFolderPath = path.resolve( exportConfig.data, exportConfig.branchName || '', diff --git a/packages/contentstack-variants/src/import/attribute.ts b/packages/contentstack-variants/src/import/attribute.ts index bc24d6f9d8..46cf0bb574 100644 --- a/packages/contentstack-variants/src/import/attribute.ts +++ b/packages/contentstack-variants/src/import/attribute.ts @@ -1,40 +1,65 @@ -import { join } from 'path'; -import { existsSync, readFileSync } from 'fs'; +import { resolve } from 'path'; +import { existsSync } from 'fs'; -import { PersonalizationAdapter } from '../utils'; -import { APIConfig, AttributeStruct, ImportConfig, LogType } from '../types'; +import { PersonalizationAdapter, fsUtil, log } from '../utils'; +import { APIConfig, AttributeStruct, ImportConfig } from '../types'; export default class Attribute extends PersonalizationAdapter { - constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) { + private mapperDirPath: string; + private attributesUidMapperPath: string; + private attributesUidMapper: Record; + private personalizationConfig: ImportConfig['modules']['personalization']; + private attributeConfig: ImportConfig['modules']['personalization']['attributes']; + + constructor(public readonly config: ImportConfig) { const conf: APIConfig = { config, baseURL: config.personalizationHost, - headers: { 'x-project-uid': config.project_uid, authtoken: config.auth_token }, + headers: { 'X-Project-Uid': config.project_id, authtoken: config.auth_token }, }; super(Object.assign(config, conf)); + this.personalizationConfig = this.config.modules.personalization; + this.attributeConfig = this.personalizationConfig.attributes; + this.mapperDirPath = resolve( + this.config.backupDir, + 'mapper', + this.personalizationConfig.dirName, + this.attributeConfig.dirName, + ); + this.attributesUidMapperPath = resolve(this.mapperDirPath, 'uid-mapping.json'); + this.attributesUidMapper = {}; } /** * The function asynchronously imports attributes from a JSON file and creates them in the system. */ async import() { - const personalization = this.config.modules.personalization; - const { dirName, fileName } = personalization.attributes; - const attributesPath = join(this.config.data, personalization.dirName, dirName, fileName); + log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Attributes' }), 'info'); + + await fsUtil.makeDirectory(this.mapperDirPath); + const { dirName, fileName } = this.attributeConfig; + const attributesPath = resolve(this.config.data, this.personalizationConfig.dirName, dirName, fileName); if (existsSync(attributesPath)) { try { - const attributes = JSON.parse(readFileSync(attributesPath, 'utf8')) as AttributeStruct[]; + const attributes = fsUtil.readFile(attributesPath, true) as AttributeStruct[]; for (const attribute of attributes) { - const { key, name, description } = attribute - await this.createAttribute({ key, name, description }) + const { key, name, description, uid } = attribute; + const attributeRes = await this.createAttribute({ key, name, description }); + //map old attribute uid to new attribute uid + //mapper file is used to check whether attribute created or not before creating audience + this.attributesUidMapper[uid] = attributeRes?.uid ?? ''; } - this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Attributes' }), 'info'); - } catch (error) { - this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Attributes' }), 'error'); - this.log(this.config, error, 'error'); + fsUtil.writeFile(this.attributesUidMapperPath, this.attributesUidMapper); + log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Attributes' }), 'info'); + } catch (error: any) { + if (error?.errorMessage || error?.message || error?.error_message) { + log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Attributes' }), 'error'); + } else { + log(this.config, error, 'error'); + } } } } diff --git a/packages/contentstack-variants/src/import/audiences.ts b/packages/contentstack-variants/src/import/audiences.ts new file mode 100644 index 0000000000..98729dc16d --- /dev/null +++ b/packages/contentstack-variants/src/import/audiences.ts @@ -0,0 +1,80 @@ +import { resolve } from 'path'; +import { existsSync } from 'fs'; + +import { APIConfig, AudienceStruct, ImportConfig, LogType } from '../types'; +import { PersonalizationAdapter, fsUtil, log, lookUpAttributes } from '../utils'; + +export default class Audiences extends PersonalizationAdapter { + private mapperDirPath: string; + private attributesMapperPath: string; + private audiencesUidMapperPath: string; + private audiencesUidMapper: Record; + private personalizationConfig: ImportConfig['modules']['personalization']; + private audienceConfig: ImportConfig['modules']['personalization']['audiences']; + + constructor(public readonly config: ImportConfig) { + const conf: APIConfig = { + config, + baseURL: config.personalizationHost, + headers: { authtoken: config.auth_token, 'X-Project-Uid': config.project_id}, + }; + super(Object.assign(config, conf)); + this.personalizationConfig = this.config.modules.personalization; + this.audienceConfig = this.personalizationConfig.audiences; + this.mapperDirPath = resolve( + this.config.backupDir, + 'mapper', + this.personalizationConfig.dirName, + this.audienceConfig.dirName, + ); + this.audiencesUidMapperPath = resolve(this.mapperDirPath, 'uid-mapping.json'); + this.attributesMapperPath = resolve( + this.config.backupDir, + 'mapper', + this.personalizationConfig.dirName, + 'attributes', + 'uid-mapping.json', + ); + this.audiencesUidMapper = {}; + } + + /** + * The function asynchronously imports audiences from a JSON file and creates them in the system. + */ + async import() { + log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Audiences' }), 'info'); + + await fsUtil.makeDirectory(this.mapperDirPath); + const { dirName, fileName } = this.audienceConfig; + const audiencesPath = resolve(this.config.data, this.personalizationConfig.dirName, dirName, fileName); + + if (existsSync(audiencesPath)) { + try { + const audiences = fsUtil.readFile(audiencesPath, true) as AudienceStruct[]; + const attributesUid = fsUtil.readFile(this.attributesMapperPath, true) as Record; + + for (const audience of audiences) { + let { name, definition, description, uid } = audience; + //check whether reference attributes exists or not + if(definition.rules?.length){ + const updatedDefRules = lookUpAttributes(definition.rules, attributesUid); + definition.rules = updatedDefRules; + } + const audienceRes = await this.createAudience({ definition, name, description }); + //map old audience uid to new audience uid + //mapper file is used to check whether audience created or not before creating experience + this.audiencesUidMapper[uid] = audienceRes?.uid ?? ''; + } + + fsUtil.writeFile(this.audiencesUidMapperPath, this.audiencesUidMapper); + log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Audiences' }), 'info'); + } catch (error: any) { + if (error?.errorMessage || error?.message || error?.error_message) { + log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Audiences' }), 'error'); + } else { + log(this.config, error, 'error'); + } + } + } + } +} diff --git a/packages/contentstack-variants/src/import/index.ts b/packages/contentstack-variants/src/import/index.ts index 79f3063d6a..063e5585fa 100644 --- a/packages/contentstack-variants/src/import/index.ts +++ b/packages/contentstack-variants/src/import/index.ts @@ -1,8 +1,10 @@ import Project from './project'; import Attribute from './attribute'; +import Audiences from './audiences'; // NOTE Acting as namespace to avoid the same class name conflicts in other modules export const Import = { Project, Attribute, + Audiences }; diff --git a/packages/contentstack-variants/src/import/project.ts b/packages/contentstack-variants/src/import/project.ts index 6a5f3dd694..9959638c5c 100644 --- a/packages/contentstack-variants/src/import/project.ts +++ b/packages/contentstack-variants/src/import/project.ts @@ -30,6 +30,7 @@ export default class Project extends PersonalizationAdapter { for (const project of projects) { const { name, description, connectedStackApiKey } = project; await this.createProject({ name, description, connectedStackApiKey }); + // store created project id -> this.config.project_id = ''; } this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Projects' }), 'info'); diff --git a/packages/contentstack-variants/src/messages/index.ts b/packages/contentstack-variants/src/messages/index.ts index c5bd649bf9..67ad650216 100644 --- a/packages/contentstack-variants/src/messages/index.ts +++ b/packages/contentstack-variants/src/messages/index.ts @@ -8,9 +8,14 @@ const commonMsg = { CREATE_SUCCESS: '${module} created successfully!', }; -const messages: typeof errors & typeof commonMsg = { +const migrationMsg = { + IMPORT_MSG: 'Migrating ${module}...', +}; + +const messages: typeof errors & typeof commonMsg & typeof migrationMsg = { ...errors, ...commonMsg, + ...migrationMsg }; /** diff --git a/packages/contentstack-variants/src/types/export-config.ts b/packages/contentstack-variants/src/types/export-config.ts index bf8be80f4e..c4110bbca9 100644 --- a/packages/contentstack-variants/src/types/export-config.ts +++ b/packages/contentstack-variants/src/types/export-config.ts @@ -20,7 +20,7 @@ export type Modules = | 'labels' | 'marketplace-apps' | 'taxonomies' - | 'eclipse'; + | 'personalization'; export type branch = { uid: string; @@ -154,7 +154,7 @@ export interface DefaultConfig { include_variant: boolean; } & AnyProperty; } & AnyProperty; - eclipse: { + personalization: { dirName: string; baseURL: string; } & AnyProperty; @@ -256,7 +256,7 @@ export interface ExportConfig extends DefaultConfig { authtoken?: string; 'X-User-Agent': string; organization_uid?: string; - project_id?: string; + 'X-Project-Uid'?: string; }; project_id?: string; // To fetch events, audiences & attributes access_token?: string; diff --git a/packages/contentstack-variants/src/types/import-config.ts b/packages/contentstack-variants/src/types/import-config.ts index 47b8113387..1ebd2055d0 100644 --- a/packages/contentstack-variants/src/types/import-config.ts +++ b/packages/contentstack-variants/src/types/import-config.ts @@ -1,5 +1,4 @@ -import { AnyProperty } from "./utils"; - +import { AnyProperty } from './utils'; export interface ImportDefaultConfig extends AnyProperty { personalizationHost: string; @@ -16,7 +15,11 @@ export interface ImportDefaultConfig extends AnyProperty { dirName: string; fileName: string; }; - } + audiences: { + dirName: string; + fileName: string; + }; + }; }; } @@ -36,6 +39,7 @@ export interface ImportConfig extends ImportDefaultConfig, AnyProperty { access_token?: string; authtoken?: string; 'X-User-Agent': string; + 'X-Project-Uid'?: string; }; access_token?: string; isAuthenticated?: boolean; @@ -50,6 +54,7 @@ export interface ImportConfig extends ImportDefaultConfig, AnyProperty { replaceExisting?: boolean; skipExisting?: boolean; stackName?: string; + project_id?: string; } type branch = { diff --git a/packages/contentstack-variants/src/types/personalization-api-adapter.ts b/packages/contentstack-variants/src/types/personalization-api-adapter.ts index aa34b7924e..f39b254c98 100644 --- a/packages/contentstack-variants/src/types/personalization-api-adapter.ts +++ b/packages/contentstack-variants/src/types/personalization-api-adapter.ts @@ -41,7 +41,11 @@ export type EventStruct = { export type AudienceStruct = { _id: string; uid: string; - definition: object; + definition: { + _type: string; + combinationType: string; + rules: Record[]; + }; name: string; description: string; project: string; @@ -62,6 +66,12 @@ export interface CreateAttributeInput { description: string; } +export interface CreateAudienceInput { + name: string; + definition: object; + description: string; +} + export interface Personalization extends AdapterHelperInterface { projects(options: GetProjectsParams): Promise; @@ -74,4 +84,6 @@ export interface Personalization extends AdapterHelperInterface; createAttribute(attribute: CreateAttributeInput): Promise; + + createAudience(attribute: CreateAudienceInput): Promise; } diff --git a/packages/contentstack-variants/src/utils/attributes-helper.ts b/packages/contentstack-variants/src/utils/attributes-helper.ts new file mode 100644 index 0000000000..49f0c358a3 --- /dev/null +++ b/packages/contentstack-variants/src/utils/attributes-helper.ts @@ -0,0 +1,38 @@ +interface AttributeRule { + _type: string; + attribute?: { + ref: string; + }; + rules?: AttributeRule[]; +} + +/** + * + * @param attributeRules + * @param attributesUid + */ +export const lookUpAttributes = (attributeRules: Record[], attributesUid: Record) => { + for (let index =0; index< attributeRules?.length; index++) { + const rule = attributeRules[index]; + + if (rule['__type'] === 'Rule') { + // Check if attribute reference exists in attributesUid + const attributeRef = rule.attribute?.ref; + const attributeType = rule.attribute['__type']; + // check if type is UserAttributeReference + if (attributeType === 'UserAttributeReference') { + if (attributeRef && attributesUid.hasOwnProperty(attributeRef) && attributesUid[attributeRef]) { + rule.attribute.ref = attributesUid[attributeRef]; + } else { + // Remove the rule if the attribute reference is not found + attributeRules.splice(index, 1); + --index; + } + } + } else if (rule['__type'] === 'RuleCombination' && Array.isArray(rule.rules)) { + // Recursively look up attributes in nested rule combinations + lookUpAttributes(rule.rules, attributesUid); + } + } + return attributeRules; +}; diff --git a/packages/contentstack-variants/src/utils/index.ts b/packages/contentstack-variants/src/utils/index.ts index 6930872eed..b70609f8ba 100644 --- a/packages/contentstack-variants/src/utils/index.ts +++ b/packages/contentstack-variants/src/utils/index.ts @@ -3,3 +3,4 @@ export * from './variant-api-adapter'; export * from './personalization-api-adapter'; export * from './logger'; export * from './helper'; +export * from './attributes-helper'; diff --git a/packages/contentstack-variants/src/utils/logger.ts b/packages/contentstack-variants/src/utils/logger.ts index 77849ed079..8fae72a458 100644 --- a/packages/contentstack-variants/src/utils/logger.ts +++ b/packages/contentstack-variants/src/utils/logger.ts @@ -1,7 +1,7 @@ import * as winston from 'winston'; import * as path from 'path'; import mkdirp from 'mkdirp'; -import { ExportConfig } from '../types'; +import { ExportConfig, ImportConfig } from '../types'; const slice = Array.prototype.slice; @@ -127,7 +127,7 @@ function init(_logPath: string) { }; } -export const log = (config: ExportConfig, message: any, type: 'info' | 'error' | 'success') => { +export const log = (config: ExportConfig | ImportConfig, message: any, type: 'info' | 'error' | 'success') => { const logsPath = config.data; // ignoring the type argument, as we are not using it to create a logfile anymore if (type !== 'error') { diff --git a/packages/contentstack-variants/src/utils/personalization-api-adapter.ts b/packages/contentstack-variants/src/utils/personalization-api-adapter.ts index ad2ddbdc62..8ca67f5d4c 100644 --- a/packages/contentstack-variants/src/utils/personalization-api-adapter.ts +++ b/packages/contentstack-variants/src/utils/personalization-api-adapter.ts @@ -12,6 +12,7 @@ import { EventStruct, AudienceStruct, AttributeStruct, + CreateAudienceInput } from '../types'; export class PersonalizationAdapter extends AdapterHelper implements Personalization { constructor(options: APIConfig) { @@ -47,7 +48,7 @@ export class PersonalizationAdapter extends AdapterHelper impl * `ProjectStruct`. */ async createAttribute(attribute: CreateAttributeInput): Promise { - return (await this.apiClient.post('/attributes', attribute)).data; + return (await this.apiClient.post('/attributes', attribute)).data; } async getExperiences(): Promise { @@ -72,4 +73,16 @@ export class PersonalizationAdapter extends AdapterHelper impl async getAttributes(): Promise { return (await this.apiClient.get('/attributes')).data; } + + /** + * @param {CreateAudienceInput} audience - The `audience` parameter in the `createAudience` function is + * of type `CreateAudienceInput`. This parameter likely contains the necessary data or information + * needed to create a new audience. + * @returns The `createAudience` function is returning the data obtained from a GET request to the + * `/audiences` endpoint using the `apiClient` with the input provided. The data returned is of type + * `AudienceStruct`. + */ + async createAudience(audience: CreateAudienceInput): Promise { + return (await this.apiClient.post('/audiences', audience)).data; + } }