Skip to content

Commit e56210d

Browse files
feat: add baseURL option for dynamic http question (#1541)
1 parent 75133db commit e56210d

19 files changed

Lines changed: 376 additions & 33 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
DO
2+
$$
3+
BEGIN
4+
IF register_patch('addBaseulrtodynamiccs.sql', 'Zachary Hankin', 'Adds an option to the dynamic HTTP question type allowing use of base domain', '2026-5-21') THEN
5+
UPDATE questions
6+
SET default_config = jsonb_set(
7+
default_config::jsonb,
8+
'{useBaseDomain}',
9+
'false'::jsonb,
10+
true
11+
)
12+
WHERE data_type = 'DYNAMIC_MULTIPLE_CHOICE';
13+
END IF;
14+
END;
15+
$$
16+
LANGUAGE plpgsql;

apps/backend/db_patches/db_seeds/0005_ProposalQuestions.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ VALUES
376376
(
377377
'dynamic_multiple_choice_question',
378378
'DYNAMIC_MULTIPLE_CHOICE', 'Dynamic multiple choice question from seeds',
379-
'{"variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[], "readPermissions":[]}',
379+
'{"variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[], "readPermissions":[], "useBaseDomain":false}',
380380
'2023-02-08 10:23:10.285415+00',
381381
'2023-02-08 10:23:10.285415+00',
382382
'dynamic_multiple_choice_question',
@@ -389,7 +389,7 @@ INSERT INTO templates_has_questions(
389389
VALUES
390390
(
391391
'dynamic_multiple_choice_question',
392-
proposal_template_id_var, proposal_topic_id_var, 10, '{"variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[],"readPermissions":[]}'
392+
proposal_template_id_var, proposal_topic_id_var, 10, '{"useBaseDomain":false, "variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[],"readPermissions":[]}'
393393
);
394394
INSERT INTO answers(
395395
questionary_id, question_id, answer

apps/backend/example.development.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ NODE_ENV=development
4444
# DEPENDENCY_CONFIG=<e2e|ess|stfc|test>
4545
PING_PUBLIC_CRT=dummypingsecret
4646
DATABASE_URL=postgres://duouser:duopassword@127.0.0.1:5432/duo
47-
BASE_URL=localhost:3000
47+
BASE_URL=http://localhost:3000
4848
JWT_TOKEN_LIFE=7d
4949
JWT_SECRET=qMyLZALzs229ybdQXNyzYRdju7X784TH
5050
# SPARKPOST_TOKEN=insertokenhere

apps/backend/src/datasources/mockups/TemplateDataSource.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,20 @@ const dummyTemplateStepsFactory = () => {
126126
config: {
127127
url: '',
128128
jsonPath: '',
129+
useBaseDomain: false,
130+
} as DynamicMultipleChoiceConfig,
131+
}),
132+
});
133+
134+
const dmcQuestionWithBaseDomain = dummyQuestionTemplateRelationFactory({
135+
question: dummyQuestionFactory({
136+
id: 'dmcQuestionWithBaseDomain',
137+
naturalKey: 'dmcQuestionWithBaseDomain',
138+
dataType: DataType.DYNAMIC_MULTIPLE_CHOICE,
139+
config: {
140+
url: 'getListOfCountries',
141+
useBaseDomain: true,
142+
jsonPath: '',
129143
} as DynamicMultipleChoiceConfig,
130144
}),
131145
});
@@ -138,6 +152,7 @@ const dummyTemplateStepsFactory = () => {
138152
config: {
139153
url: 'api-url',
140154
jsonPath: '',
155+
useBaseDomain: false,
141156
apiCallRequestHeaders: [
142157
{
143158
name: 'header1',
@@ -160,6 +175,7 @@ const dummyTemplateStepsFactory = () => {
160175
config: {
161176
url: 'api-url',
162177
jsonPath: '$..option',
178+
useBaseDomain: false,
163179
} as DynamicMultipleChoiceConfig,
164180
}),
165181
});
@@ -175,6 +191,7 @@ const dummyTemplateStepsFactory = () => {
175191
dmcQuestionEmptyUrl,
176192
dmcQuestionEmptyJsonPath,
177193
dmcQuestionWithUrlAndJsonPath,
194+
dmcQuestionWithBaseDomain,
178195
]),
179196
];
180197
};

apps/backend/src/models/questionTypes/DynamicMultipleChoice.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const dynamicMultipleChoiceDefinition: Question<DataType.DYNAMIC_MULTIPLE
2929
config.variant = 'radio';
3030
config.url = '';
3131
config.jsonPath = '';
32+
config.useBaseDomain = false;
3233
config.isMultipleSelect = false;
3334
config.apiCallRequestHeaders = [];
3435
config.readPermissions = [];

apps/backend/src/queries/TemplateQueries.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,34 @@ describe('getDynamicMultipleChoiceOptions', () => {
153153
expect(options).toEqual(['option1', 'option2']);
154154
});
155155

156+
it('Should return options if selected useBaseDomain', async () => {
157+
process.env = {
158+
...process.env,
159+
BASE_URL: 'http://mocked.example.com',
160+
};
161+
162+
jest
163+
.spyOn(global, 'fetch')
164+
.mockImplementation((input: RequestInfo | URL) => {
165+
const url = typeof input === 'string' ? input : input.toString();
166+
167+
if (url === 'http://mocked.example.com/getListOfCountries') {
168+
return Promise.resolve({
169+
json: () => Promise.resolve(['option1', 'option2']),
170+
ok: true,
171+
} as Response);
172+
} else {
173+
return Promise.reject(new Error('Unknown URL'));
174+
}
175+
});
176+
177+
const options = await templateQueries.getDynamicMultipleChoiceOptions(
178+
dummyUserWithRole,
179+
'dmcQuestionWithBaseDomain'
180+
);
181+
182+
expect(options).toEqual(['option1', 'option2']);
183+
});
156184
it('should use the question template relation if a template ID is supplied', async () => {
157185
const templateDataSource = container.resolve<TemplateDataSourceMock>(
158186
Tokens.TemplateDataSource

apps/backend/src/queries/TemplateQueries.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,11 @@ export default class TemplateQueries {
104104
if (!question) return [];
105105

106106
const config = question.config as DynamicMultipleChoiceConfig;
107-
if (config.url === '') return [];
107+
const dynamicURL = constructDynamicURL(config.url, config.useBaseDomain);
108+
if (dynamicURL === '') return [];
108109

109110
try {
110-
const response = await fetch(config.url, {
111+
const response = await fetch(dynamicURL, {
111112
headers: config.apiCallRequestHeaders?.reduce(
112113
(acc, header) => ({
113114
...acc,
@@ -166,3 +167,11 @@ export default class TemplateQueries {
166167
return this.dataSource.getQuestion(questionId);
167168
}
168169
}
170+
171+
function constructDynamicURL(url: string, useBaseURL: boolean): string {
172+
if (useBaseURL) {
173+
return `${process.env.BASE_URL}/${url}`;
174+
}
175+
176+
return url;
177+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Query, Resolver } from 'type-graphql';
2+
3+
import { ServerConfig } from '../types/ServerConfig';
4+
5+
@Resolver()
6+
export class ServerConfigQuery {
7+
@Query(() => ServerConfig, { nullable: false })
8+
ServerConfig() {
9+
return { baseURL: process.env.BASE_URL };
10+
}
11+
}

apps/backend/src/resolvers/types/FieldConfig.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ export class DynamicMultipleChoiceConfig extends ConfigBase {
156156
@Field(() => String)
157157
jsonPath: string;
158158

159+
@Field(() => Boolean)
160+
useBaseDomain: boolean;
161+
159162
@Field(() => Boolean)
160163
isMultipleSelect: boolean;
161164

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ObjectType, Field } from 'type-graphql';
2+
3+
@ObjectType()
4+
export class ServerConfig {
5+
@Field(() => String)
6+
public baseURL: string;
7+
}

0 commit comments

Comments
 (0)