Skip to content
Open
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
145 changes: 145 additions & 0 deletions spec/src/modules/pia.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,62 @@ describe(`ConstructorIO - Pia${bundledDescriptionSuffix}`, () => {
});
});

it('Should pass features as query parameters when provided', () => {
const features = { pia_v2: true };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getSuggestedQuestions(validItemId, { features }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('features').to.deep.equal({ pia_v2: 'true' });
});
});

it('Should pass feature_variants as query parameters when provided', () => {
const featureVariants = { pia_v2: 'control' };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getSuggestedQuestions(validItemId, { featureVariants }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('feature_variants').to.deep.equal({ pia_v2: 'control' });
});
});

it('Should pass pre_filter_expression as a query parameter when provided', () => {
const preFilterExpression = { brand: 'apple' };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getSuggestedQuestions(validItemId, { preFilterExpression }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('pre_filter_expression').to.equal(JSON.stringify(preFilterExpression));
});
});

it('Should pass qs as a query parameter when qsParam is provided', () => {
const qsParam = { new_qs_param: 'test_value' };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getSuggestedQuestions(validItemId, { qsParam }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('qs').to.equal(JSON.stringify(qsParam));
});
});

it('Should be rejected if response is malformed', () => {
const malformedFetch = () => Promise.resolve({
ok: true,
Expand Down Expand Up @@ -283,6 +339,95 @@ describe(`ConstructorIO - Pia${bundledDescriptionSuffix}`, () => {
});
});

it('Should pass features as query parameters when provided', function () {
this.timeout(10000);
const features = { pia_v2: true };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getAnswerResults(validItemId, validQuestion, { features }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('features').to.deep.equal({ pia_v2: 'true' });
});
});

it('Should pass feature_variants as query parameters when provided', function () {
this.timeout(10000);
const featureVariants = { pia_v2: 'control' };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getAnswerResults(validItemId, validQuestion, { featureVariants }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('feature_variants').to.deep.equal({ pia_v2: 'control' });
});
});

it('Should pass pre_filter_expression as a query parameter when provided', function () {
this.timeout(10000);
const preFilterExpression = { brand: 'apple' };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getAnswerResults(validItemId, validQuestion, { preFilterExpression }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('pre_filter_expression').to.equal(JSON.stringify(preFilterExpression));
});
});

it('Should pass guard as a query parameter when provided', function () {
this.timeout(10000);
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getAnswerResults(validItemId, validQuestion, { guard: true }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('guard').to.equal('true');
});
});

it('Should pass fmt_options as query parameters when provided', function () {
this.timeout(10000);
const fmtOptions = { groups_max_depth: 2 };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getAnswerResults(validItemId, validQuestion, { fmtOptions }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('fmt_options').to.deep.equal({ groups_max_depth: '2' });
});
});

it('Should pass qs as a query parameter when qsParam is provided', function () {
this.timeout(10000);
const qsParam = { new_qs_param: 'test_value' };
const { agent: { pia } } = new ConstructorIO({
apiKey: piaApiKey,
fetch: fetchSpy,
});

return pia.getAnswerResults(validItemId, validQuestion, { qsParam }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);

expect(requestedUrlParams).to.have.property('qs').to.equal(JSON.stringify(qsParam));
});
});

it('Should be rejected if response is malformed', () => {
const malformedFetch = () => Promise.resolve({
ok: true,
Expand Down
45 changes: 44 additions & 1 deletion src/modules/pia.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,17 @@ function createPiaUrl(itemId, parameters, options, questionPath) {
}

if (parameters) {
const { threadId, variationId, numResults } = parameters;
const {
threadId,
variationId,
numResults,
features,
featureVariants,
preFilterExpression,
qsParam,
guard,
fmtOptions,
} = parameters;

if (threadId) {
queryParams.thread_id = threadId;
Expand All @@ -52,6 +62,30 @@ function createPiaUrl(itemId, parameters, options, questionPath) {
if (!helpers.isNil(numResults)) {
queryParams.num_results = numResults;
}

if (features) {
queryParams.features = features;
}

if (featureVariants) {
queryParams.feature_variants = featureVariants;
}

if (preFilterExpression) {
queryParams.pre_filter_expression = JSON.stringify(preFilterExpression);
}

if (qsParam) {
queryParams.qs = JSON.stringify(qsParam);
}

if (!helpers.isNil(guard)) {
queryParams.guard = guard;
}

if (fmtOptions) {
queryParams.fmt_options = fmtOptions;
}
}

queryParams._dt = Date.now();
Expand Down Expand Up @@ -85,13 +119,17 @@ class Pia {
* @param {string} [parameters.threadId] - Thread ID for conversation context (UUID)
* @param {string} [parameters.variationId] - Variation ID of the item
* @param {number} [parameters.numResults] - Number of suggested questions to return
* @param {object} [parameters.features] - Feature toggles for A/B testing
* @param {object} [parameters.featureVariants] - Feature variant overrides
* @param {object} [parameters.preFilterExpression] - Faceting expression to scope results
* @param {object} [networkParameters] - Parameters relevant to the network request
* @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
* @returns {Promise}
* @example
* constructorio.agent.pia.getSuggestedQuestions('item-123', {
* variationId: 'variation-456',
* numResults: 3,
* features: { my_feature: true },
* });
*/
getSuggestedQuestions(itemId, parameters, networkParameters = {}) {
Expand Down Expand Up @@ -136,6 +174,11 @@ class Pia {
* @param {object} [parameters] - Additional parameters to refine result set
* @param {string} [parameters.threadId] - Thread ID for conversation context (UUID)
* @param {string} [parameters.variationId] - Variation ID of the item
* @param {object} [parameters.features] - Feature toggles for A/B testing
* @param {object} [parameters.featureVariants] - Feature variant overrides
* @param {object} [parameters.preFilterExpression] - Faceting expression to scope results
* @param {boolean} [parameters.guard] - Enable or disable moderation check
* @param {object} [parameters.fmtOptions] - Response format options
* @param {object} [networkParameters] - Parameters relevant to the network request
* @param {number} [networkParameters.timeout] - Request timeout (in milliseconds)
* @returns {Promise}
Expand Down
17 changes: 13 additions & 4 deletions src/types/pia.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {
ConstructorClientOptions,
FilterExpression,
FmtOptions,
NetworkParameters,
Item,
} from '.';
Expand All @@ -10,15 +12,22 @@ export interface PiaQuestion {
value: string;
}

export interface PiaSuggestedQuestionsParameters {
export interface PiaBaseParameters {
threadId?: string;
variationId?: string;
features?: Record<string, boolean>;
featureVariants?: Record<string, string>;
preFilterExpression?: FilterExpression;
qsParam?: Record<string, any>;
}

export interface PiaSuggestedQuestionsParameters extends PiaBaseParameters {
numResults?: number;
}

export interface PiaAnswerResultsParameters {
threadId?: string;
variationId?: string;
export interface PiaAnswerResultsParameters extends PiaBaseParameters {
guard?: boolean;
fmtOptions?: FmtOptions;
}

export interface PiaSuggestedQuestionsResponse {
Expand Down
Loading