Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
49c1da3
add: baseURL option for dynamic http question
zacharyjhankin May 27, 2026
e18d453
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin May 27, 2026
c63a5df
add better title for covnersion
zacharyjhankin May 27, 2026
3f7ec07
do not change existing rows
zacharyjhankin Jun 1, 2026
51ec9ff
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 1, 2026
c821594
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 2, 2026
0d00405
fix syntax
zacharyjhankin Jun 2, 2026
3ad0476
fix syntax error
zacharyjhankin Jun 4, 2026
bb9e017
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 4, 2026
f567da0
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 4, 2026
1afff76
remove UOS
zacharyjhankin Jun 4, 2026
80f560c
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 4, 2026
95ff712
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 4, 2026
f827702
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 5, 2026
2ae00c9
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 5, 2026
c3d420b
fix seed
zacharyjhankin Jun 5, 2026
bf37efe
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 8, 2026
1c39381
fix tests
zacharyjhankin Jun 8, 2026
afd026b
use graphQL for be to fe talk
zacharyjhankin Jun 8, 2026
2b4eed9
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 8, 2026
681242e
fix edge cases
zacharyjhankin Jun 8, 2026
d8bd693
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 8, 2026
eb41c39
fix: test
zacharyjhankin Jun 9, 2026
cec4024
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 9, 2026
267aae4
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 15, 2026
ebd72e9
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 16, 2026
0991b18
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 19, 2026
76599b2
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 22, 2026
0f01deb
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 23, 2026
c9bfafc
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 23, 2026
2c96ed7
remove http from name
zacharyjhankin Jun 29, 2026
221bd42
update variable examples
zacharyjhankin Jun 29, 2026
f995fe5
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jun 29, 2026
e725846
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jul 2, 2026
3e827be
fix: tests/default
zacharyjhankin Jul 2, 2026
6a150b4
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jul 2, 2026
203724c
fix merge conflict
zacharyjhankin Jul 2, 2026
379601b
simplify regex
zacharyjhankin Jul 6, 2026
c5c712d
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jul 6, 2026
02a9771
Merge branch 'develop' into feat-dynamic-http-option3
zacharyjhankin Jul 6, 2026
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
16 changes: 16 additions & 0 deletions apps/backend/db_patches/0209_addBaseulrtodynamiccs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
DO
$$
BEGIN
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
UPDATE questions
SET default_config = jsonb_set(
default_config::jsonb,
'{useBaseDomain}',
'false'::jsonb,
true
)
WHERE data_type = 'DYNAMIC_MULTIPLE_CHOICE';
END IF;
END;
$$
LANGUAGE plpgsql;
4 changes: 2 additions & 2 deletions apps/backend/db_patches/db_seeds/0005_ProposalQuestions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ VALUES
(
'dynamic_multiple_choice_question',
'DYNAMIC_MULTIPLE_CHOICE', 'Dynamic multiple choice question from seeds',
'{"variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[], "readPermissions":[]}',
'{"variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[], "readPermissions":[], "useBaseDomain":false}',
'2023-02-08 10:23:10.285415+00',
'2023-02-08 10:23:10.285415+00',
'dynamic_multiple_choice_question',
Expand All @@ -389,7 +389,7 @@ INSERT INTO templates_has_questions(
VALUES
(
'dynamic_multiple_choice_question',
proposal_template_id_var, proposal_topic_id_var, 10, '{"variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[],"readPermissions":[]}'
proposal_template_id_var, proposal_topic_id_var, 10, '{"useBaseDomain":false, "variant":"dropdown", "url":"", "jsonPath":"","isMultipleSelect":true, "apiCallRequestHeaders":[],"readPermissions":[]}'
);
INSERT INTO answers(
questionary_id, question_id, answer
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/example.development.env
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ NODE_ENV=development
# DEPENDENCY_CONFIG=<e2e|ess|stfc|test>
PING_PUBLIC_CRT=dummypingsecret
DATABASE_URL=postgres://duouser:duopassword@127.0.0.1:5432/duo
BASE_URL=localhost:3000
BASE_URL=http://localhost:3000
JWT_TOKEN_LIFE=7d
JWT_SECRET=qMyLZALzs229ybdQXNyzYRdju7X784TH
# SPARKPOST_TOKEN=insertokenhere
Expand Down
17 changes: 17 additions & 0 deletions apps/backend/src/datasources/mockups/TemplateDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,20 @@ const dummyTemplateStepsFactory = () => {
config: {
url: '',
jsonPath: '',
useBaseDomain: false,
} as DynamicMultipleChoiceConfig,
}),
});

const dmcQuestionWithBaseDomain = dummyQuestionTemplateRelationFactory({
question: dummyQuestionFactory({
id: 'dmcQuestionWithBaseDomain',
naturalKey: 'dmcQuestionWithBaseDomain',
dataType: DataType.DYNAMIC_MULTIPLE_CHOICE,
config: {
url: 'getListOfCountries',
useBaseDomain: true,
jsonPath: '',
} as DynamicMultipleChoiceConfig,
}),
});
Expand All @@ -138,6 +152,7 @@ const dummyTemplateStepsFactory = () => {
config: {
url: 'api-url',
jsonPath: '',
useBaseDomain: false,
apiCallRequestHeaders: [
{
name: 'header1',
Expand All @@ -160,6 +175,7 @@ const dummyTemplateStepsFactory = () => {
config: {
url: 'api-url',
jsonPath: '$..option',
useBaseDomain: false,
} as DynamicMultipleChoiceConfig,
}),
});
Expand All @@ -175,6 +191,7 @@ const dummyTemplateStepsFactory = () => {
dmcQuestionEmptyUrl,
dmcQuestionEmptyJsonPath,
dmcQuestionWithUrlAndJsonPath,
dmcQuestionWithBaseDomain,
]),
];
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const dynamicMultipleChoiceDefinition: Question<DataType.DYNAMIC_MULTIPLE
config.variant = 'radio';
config.url = '';
config.jsonPath = '';
config.useBaseDomain = false;
config.isMultipleSelect = false;
config.apiCallRequestHeaders = [];
config.readPermissions = [];
Expand Down
28 changes: 28 additions & 0 deletions apps/backend/src/queries/TemplateQueries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,34 @@ describe('getDynamicMultipleChoiceOptions', () => {
expect(options).toEqual(['option1', 'option2']);
});

it('Should return options if selected useBaseDomain', async () => {
process.env = {
...process.env,
BASE_URL: 'http://mocked.example.com',
};

jest
.spyOn(global, 'fetch')
.mockImplementation((input: RequestInfo | URL) => {
const url = typeof input === 'string' ? input : input.toString();

if (url === 'http://mocked.example.com/getListOfCountries') {
return Promise.resolve({
json: () => Promise.resolve(['option1', 'option2']),
ok: true,
} as Response);
} else {
return Promise.reject(new Error('Unknown URL'));
}
});

const options = await templateQueries.getDynamicMultipleChoiceOptions(
dummyUserWithRole,
'dmcQuestionWithBaseDomain'
);

expect(options).toEqual(['option1', 'option2']);
});
it('should use the question template relation if a template ID is supplied', async () => {
const templateDataSource = container.resolve<TemplateDataSourceMock>(
Tokens.TemplateDataSource
Expand Down
13 changes: 11 additions & 2 deletions apps/backend/src/queries/TemplateQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,11 @@ export default class TemplateQueries {
if (!question) return [];

const config = question.config as DynamicMultipleChoiceConfig;
if (config.url === '') return [];
const dynamicURL = constructDynamicURL(config.url, config.useBaseDomain);
if (dynamicURL === '') return [];

try {
const response = await fetch(config.url, {
const response = await fetch(dynamicURL, {
headers: config.apiCallRequestHeaders?.reduce(
(acc, header) => ({
...acc,
Expand Down Expand Up @@ -166,3 +167,11 @@ export default class TemplateQueries {
return this.dataSource.getQuestion(questionId);
}
}

function constructDynamicURL(url: string, useBaseURL: boolean): string {
if (useBaseURL) {
return `${process.env.BASE_URL}/${url}`;
}

return url;
}
11 changes: 11 additions & 0 deletions apps/backend/src/resolvers/queries/ServerConfigQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Query, Resolver } from 'type-graphql';

import { ServerConfig } from '../types/ServerConfig';

@Resolver()
export class ServerConfigQuery {
@Query(() => ServerConfig, { nullable: false })
ServerConfig() {
return { baseURL: process.env.BASE_URL };
}
}
3 changes: 3 additions & 0 deletions apps/backend/src/resolvers/types/FieldConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ export class DynamicMultipleChoiceConfig extends ConfigBase {
@Field(() => String)
jsonPath: string;

@Field(() => Boolean)
useBaseDomain: boolean;

@Field(() => Boolean)
isMultipleSelect: boolean;

Expand Down
7 changes: 7 additions & 0 deletions apps/backend/src/resolvers/types/ServerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ObjectType, Field } from 'type-graphql';

@ObjectType()
export class ServerConfig {
@Field(() => String)
public baseURL: string;
}
1 change: 1 addition & 0 deletions apps/e2e/cypress/e2e/templateContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const dynamicMultipleChoiceQuestion = {
title: faker.lorem.words(2),
url: 'http://localhost:9000',
jsonPath: '$.*.item',
useBaseDomain: false,
answers: {
arrayString: [
faker.lorem.words(3),
Expand Down
5 changes: 5 additions & 0 deletions apps/e2e/cypress/e2e/templatesBasic.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ context('Template Basic tests', () => {
{
url: dynamicMultipleChoiceQuestion.url,
isMultipleSelect: true,
useBaseDomain: false,
}
);

Expand Down Expand Up @@ -1547,6 +1548,7 @@ context('Template Basic tests', () => {
jsonPath: '$.[*].item',
isMultipleSelect: true,
firstTopic: true,
useBaseDomain: false,
}
);

Expand All @@ -1567,6 +1569,7 @@ context('Template Basic tests', () => {
jsonPath: dynamicMultipleChoiceQuestion.jsonPath,
isMultipleSelect: true,
firstTopic: true,
useBaseDomain: false,
}
);
createProposalAndClickDropdownBehavior();
Expand All @@ -1589,6 +1592,7 @@ context('Template Basic tests', () => {
url: dynamicMultipleChoiceQuestion.url,
isMultipleSelect: true,
firstTopic: true,
useBaseDomain: false,
}
);
createProposalAndClickDropdownBehavior();
Expand All @@ -1614,6 +1618,7 @@ context('Template Basic tests', () => {
isMultipleSelect: true,
firstTopic: true,
headers: { Authorization: 'Bearer 1234', 'Content-Type': 'text/' },
useBaseDomain: false,
}
);

Expand Down
6 changes: 6 additions & 0 deletions apps/e2e/cypress/fixtures/template_export.json
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@
"variant": "dropdown",
"url": "",
"jsonPath": "",
"useBaseDomain": false,
"isMultipleSelect": true,
"apiCallRequestHeaders": [],
"readPermissions": []
Expand All @@ -473,6 +474,7 @@
"variant": "dropdown",
"url": "",
"jsonPath": "",
"useBaseDomain": false,
"isMultipleSelect": true,
"apiCallRequestHeaders": [],
"readPermissions": []
Expand Down Expand Up @@ -785,6 +787,7 @@
"variant": "dropdown",
"url": "",
"jsonPath": "",
"useBaseDomain": false,
"isMultipleSelect": true,
"apiCallRequestHeaders": [],
"readPermissions": []
Expand Down Expand Up @@ -1171,6 +1174,7 @@
"variant": "dropdown",
"url": "",
"jsonPath": "",
"useBaseDomain": false,
"isMultipleSelect": true,
"apiCallRequestHeaders": [],
"readPermissions": []
Expand All @@ -1186,6 +1190,7 @@
"variant": "dropdown",
"url": "",
"jsonPath": "",
"useBaseDomain": false,
"isMultipleSelect": true,
"apiCallRequestHeaders": [],
"readPermissions": []
Expand Down Expand Up @@ -1403,6 +1408,7 @@
"variant": "dropdown",
"url": "",
"jsonPath": "",
"useBaseDomain": false,
"isMultipleSelect": true,
"apiCallRequestHeaders": [],
"readPermissions": []
Expand Down
1 change: 1 addition & 0 deletions apps/e2e/cypress/types/template.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ declare global {
options?: {
url?: string;
jsonPath?: string;
useBaseDomain: boolean;
isMultipleSelect?: boolean;
type?: 'radio' | 'dropdown';
firstTopic?: boolean;
Expand Down
Loading
Loading