Skip to content

Commit 47a14c4

Browse files
committed
upcoming: [DPS-35924] Custom HTTPs form - first part
1 parent 64174db commit 47a14c4

File tree

13 files changed

+866
-209
lines changed

13 files changed

+866
-209
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/api-v4": Changed
3+
---
4+
5+
Adjust Custom HTTPS Destination types ([#13274](https://github.com/linode/manager/pull/13274))

packages/api-v4/src/delivery/types.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export interface Destination extends DestinationCore, AuditData {
5757

5858
export type DestinationDetails =
5959
| AkamaiObjectStorageDetails
60-
| CustomHTTPsDetails;
60+
| CustomHTTPSDetails;
6161

6262
export interface AkamaiObjectStorageDetails {
6363
access_key_id: string;
@@ -74,7 +74,7 @@ export interface AkamaiObjectStorageDetailsExtended
7474
type ContentType = 'application/json' | 'application/json; charset=utf-8';
7575
type DataCompressionType = 'gzip' | 'None';
7676

77-
export interface CustomHTTPsDetails {
77+
export interface CustomHTTPSDetails {
7878
authentication: Authentication;
7979
client_certificate_details?: ClientCertificateDetails;
8080
content_type: ContentType;
@@ -84,13 +84,19 @@ export interface CustomHTTPsDetails {
8484
}
8585

8686
interface ClientCertificateDetails {
87-
client_ca_certificate: string;
88-
client_certificate: string;
89-
client_private_key: string;
90-
tls_hostname: string;
87+
client_ca_certificate?: string;
88+
client_certificate?: string;
89+
client_private_key?: string;
90+
tls_hostname?: string;
9191
}
9292

93-
type AuthenticationType = 'basic' | 'none';
93+
export const authenticationType = {
94+
Basic: 'basic',
95+
None: 'none',
96+
} as const;
97+
98+
export type AuthenticationType =
99+
(typeof authenticationType)[keyof typeof authenticationType];
94100

95101
interface Authentication {
96102
details?: AuthenticationDetails;
@@ -133,7 +139,7 @@ export interface AkamaiObjectStorageDetailsPayload
133139

134140
export type DestinationDetailsPayload =
135141
| AkamaiObjectStorageDetailsPayload
136-
| CustomHTTPsDetails;
142+
| CustomHTTPSDetails;
137143

138144
export interface CreateDestinationPayload {
139145
details: DestinationDetailsPayload;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Add Custom HTTPS destination type with proper fields to Create Destination forms ([#13274](https://github.com/linode/manager/pull/13274))

packages/manager/src/featureFlags.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ interface AclpLogsFlag extends BetaFeatureFlag {
120120
* This property indicates whether to bypass account capabilities check or not
121121
*/
122122
bypassAccountCapabilities?: boolean;
123+
/**
124+
* This property indicates whether to show Custom HTTPS destination type
125+
*/
126+
customHttpsEnabled?: boolean;
123127
}
124128

125129
interface LkeEnterpriseFlag extends BaseFeatureFlag {

packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationCreate.test.tsx

Lines changed: 161 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';
1212
import { DestinationCreate } from './DestinationCreate';
1313

1414
import type { CreateDestinationPayload } from '@linode/api-v4';
15+
import type { Flags } from 'src/featureFlags';
1516

1617
describe('DestinationCreate', () => {
1718
const renderDestinationCreate = (
19+
flags: Partial<Flags>,
1820
defaultValues?: Partial<CreateDestinationPayload>
1921
) => {
2022
renderWithThemeAndHookFormContext({
@@ -25,88 +27,183 @@ describe('DestinationCreate', () => {
2527
...defaultValues,
2628
},
2729
},
30+
options: {
31+
flags,
32+
},
2833
});
2934
};
3035

31-
it('should render disabled Destination Type input with proper selection', async () => {
32-
renderDestinationCreate();
36+
describe('when customHttpsEnabled feature flag is set to false', () => {
37+
const flags = {
38+
aclpLogs: {
39+
enabled: true,
40+
beta: false,
41+
customHttpsEnabled: false,
42+
},
43+
};
3344

34-
const destinationTypeAutocomplete =
35-
screen.getByLabelText('Destination Type');
45+
it('should render disabled Destination Type input with proper selection', async () => {
46+
renderDestinationCreate(flags);
3647

37-
expect(destinationTypeAutocomplete).toBeDisabled();
38-
expect(destinationTypeAutocomplete).toHaveValue('Akamai Object Storage');
39-
});
48+
const destinationTypeAutocomplete =
49+
screen.getByLabelText('Destination Type');
4050

41-
it(
42-
'should render all inputs for Akamai Object Storage type and allow to fill out them',
43-
{ timeout: 10000 },
44-
async () => {
45-
renderDestinationCreate({ label: '' });
51+
expect(destinationTypeAutocomplete).toBeDisabled();
52+
expect(destinationTypeAutocomplete).toHaveValue('Akamai Object Storage');
53+
});
4654

47-
const destinationNameInput = screen.getByLabelText('Destination Name');
48-
await userEvent.type(destinationNameInput, 'Test');
49-
const hostInput = screen.getByLabelText('Host');
50-
await userEvent.type(hostInput, 'test');
51-
const bucketInput = screen.getByLabelText('Bucket');
52-
await userEvent.type(bucketInput, 'test');
53-
const accessKeyIDInput = screen.getByLabelText('Access Key ID');
54-
await userEvent.type(accessKeyIDInput, 'Test');
55-
const secretAccessKeyInput = screen.getByLabelText('Secret Access Key');
56-
await userEvent.type(secretAccessKeyInput, 'Test');
57-
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');
58-
await userEvent.type(logPathPrefixInput, 'Test');
55+
it(
56+
'should render all inputs for Akamai Object Storage type and allow to fill out them',
57+
{ timeout: 10000 },
58+
async () => {
59+
renderDestinationCreate(flags, { label: '' });
60+
61+
const destinationNameInput = screen.getByLabelText('Destination Name');
62+
await userEvent.type(destinationNameInput, 'Test');
63+
const hostInput = screen.getByLabelText('Host');
64+
await userEvent.type(hostInput, 'test');
65+
const bucketInput = screen.getByLabelText('Bucket');
66+
await userEvent.type(bucketInput, 'test');
67+
const accessKeyIDInput = screen.getByLabelText('Access Key ID');
68+
await userEvent.type(accessKeyIDInput, 'Test');
69+
const secretAccessKeyInput = screen.getByLabelText('Secret Access Key');
70+
await userEvent.type(secretAccessKeyInput, 'Test');
71+
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');
72+
await userEvent.type(logPathPrefixInput, 'Test');
5973

60-
expect(destinationNameInput).toHaveValue('Test');
61-
expect(hostInput).toHaveValue('test');
62-
expect(bucketInput).toHaveValue('test');
63-
expect(accessKeyIDInput).toHaveValue('Test');
64-
expect(secretAccessKeyInput).toHaveValue('Test');
65-
expect(logPathPrefixInput).toHaveValue('Test');
66-
}
67-
);
68-
69-
it('should render Sample Destination Object Name and change its value according to Log Path Prefix input', async () => {
70-
const accountEuuid = 'XYZ-123';
71-
const [month, day, year] = new Date().toLocaleDateString().split('/');
72-
server.use(
73-
http.get('*/account', () => {
74-
return HttpResponse.json(accountFactory.build({ euuid: accountEuuid }));
75-
})
74+
expect(destinationNameInput).toHaveValue('Test');
75+
expect(hostInput).toHaveValue('test');
76+
expect(bucketInput).toHaveValue('test');
77+
expect(accessKeyIDInput).toHaveValue('Test');
78+
expect(secretAccessKeyInput).toHaveValue('Test');
79+
expect(logPathPrefixInput).toHaveValue('Test');
80+
}
7681
);
7782

78-
renderDestinationCreate();
83+
it('should render Sample Destination Object Name and change its value according to Log Path Prefix input', async () => {
84+
const accountEuuid = 'XYZ-123';
85+
const [month, day, year] = new Date().toLocaleDateString().split('/');
86+
server.use(
87+
http.get('*/account', () => {
88+
return HttpResponse.json(
89+
accountFactory.build({ euuid: accountEuuid })
90+
);
91+
})
92+
);
93+
94+
renderDestinationCreate(flags);
95+
96+
let samplePath;
97+
await waitFor(() => {
98+
samplePath = screen.getByText(
99+
`/audit_logs/com.akamai.audit/${accountEuuid}/${year}/${month}/${day}/akamai_log-000166-1756015362-319597-login.gz`
100+
);
101+
expect(samplePath).toBeInTheDocument();
102+
});
103+
// Type the test value inside the input
104+
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');
105+
106+
await userEvent.type(logPathPrefixInput, 'test');
107+
// sample path should be created based on *log path* value
108+
expect(samplePath!.textContent).toEqual(
109+
'/test/akamai_log-000166-1756015362-319597-login.gz'
110+
);
111+
112+
await userEvent.clear(logPathPrefixInput);
113+
await userEvent.type(logPathPrefixInput, '/test');
114+
expect(samplePath!.textContent).toEqual(
115+
'/test/akamai_log-000166-1756015362-319597-login.gz'
116+
);
79117

80-
let samplePath;
81-
await waitFor(() => {
82-
samplePath = screen.getByText(
83-
`/audit_logs/com.akamai.audit/${accountEuuid}/${year}/${month}/${day}/akamai_log-000166-1756015362-319597-login.gz`
118+
await userEvent.clear(logPathPrefixInput);
119+
await userEvent.type(logPathPrefixInput, '/');
120+
expect(samplePath!.textContent).toEqual(
121+
'/akamai_log-000166-1756015362-319597-login.gz'
84122
);
85-
expect(samplePath).toBeInTheDocument();
86123
});
87-
// Type the test value inside the input
88-
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');
124+
});
89125

90-
await userEvent.type(logPathPrefixInput, 'test');
91-
// sample path should be created based on *log path* value
92-
expect(samplePath!.textContent).toEqual(
93-
'/test/akamai_log-000166-1756015362-319597-login.gz'
94-
);
126+
describe('when customHttpsEnabled feature flag is set to true', () => {
127+
const flags = {
128+
aclpLogs: {
129+
enabled: true,
130+
beta: false,
131+
customHttpsEnabled: true,
132+
},
133+
};
95134

96-
await userEvent.clear(logPathPrefixInput);
97-
await userEvent.type(logPathPrefixInput, '/test');
98-
expect(samplePath!.textContent).toEqual(
99-
'/test/akamai_log-000166-1756015362-319597-login.gz'
100-
);
135+
it('should render enabled Destination Type input with Akamai Object Storage selected and allow to select Custom HTTPS', async () => {
136+
renderDestinationCreate(flags);
137+
138+
const destinationTypeAutocomplete =
139+
screen.getByLabelText('Destination Type');
101140

102-
await userEvent.clear(logPathPrefixInput);
103-
await userEvent.type(logPathPrefixInput, '/');
104-
expect(samplePath!.textContent).toEqual(
105-
'/akamai_log-000166-1756015362-319597-login.gz'
141+
expect(destinationTypeAutocomplete).toBeEnabled();
142+
expect(destinationTypeAutocomplete).toHaveValue('Akamai Object Storage');
143+
await userEvent.click(destinationTypeAutocomplete);
144+
const customHttpsOption = await screen.findByText('Custom HTTPS');
145+
await userEvent.click(customHttpsOption);
146+
expect(destinationTypeAutocomplete).toHaveValue('Custom HTTPS');
147+
});
148+
149+
it(
150+
'should render all inputs for Custom HTTPS type and allow to fill them out',
151+
{ timeout: 10000 },
152+
async () => {
153+
renderDestinationCreate(flags, { label: '' });
154+
155+
const destinationTypeAutocomplete =
156+
screen.getByLabelText('Destination Type');
157+
await userEvent.click(destinationTypeAutocomplete);
158+
const customHttpsOption = await screen.findByText('Custom HTTPS');
159+
await userEvent.click(customHttpsOption);
160+
expect(destinationTypeAutocomplete).toHaveValue('Custom HTTPS');
161+
162+
const destinationNameInput = screen.getByLabelText('Destination Name');
163+
await userEvent.type(destinationNameInput, 'Test');
164+
165+
// With None Authentication type selected, the Username and Password inputs should not be rendered
166+
const notYetExistingUsernameInput = screen.queryByLabelText('Username');
167+
expect(notYetExistingUsernameInput).not.toBeInTheDocument();
168+
const notYetExistingPasswordInput = screen.queryByLabelText('Password');
169+
expect(notYetExistingPasswordInput).not.toBeInTheDocument();
170+
171+
// Open Authentication select and choose Basic option
172+
const authenticationAutocomplete =
173+
screen.getByLabelText('Authentication');
174+
expect(authenticationAutocomplete).toHaveValue('None');
175+
await userEvent.click(authenticationAutocomplete);
176+
const basicAuthentication = await screen.findByText('Basic');
177+
await userEvent.click(basicAuthentication);
178+
expect(authenticationAutocomplete).toHaveValue('Basic');
179+
180+
// With Authentication type set to Basic, the Username and Password inputs should be rendered
181+
const usernameInput = screen.getByLabelText('Username');
182+
await userEvent.type(usernameInput, 'Username test');
183+
expect(usernameInput.getAttribute('value')).toEqual('Username test');
184+
185+
const passwordInput = screen.getByLabelText('Password');
186+
await userEvent.type(passwordInput, 'Password test');
187+
expect(passwordInput.getAttribute('value')).toEqual('Password test');
188+
189+
// Endpoint URL
190+
const endpointUrlInput = screen.getByLabelText('Endpoint URL');
191+
await userEvent.type(endpointUrlInput, 'Endpoint URL test');
192+
expect(endpointUrlInput.getAttribute('value')).toEqual(
193+
'Endpoint URL test'
194+
);
195+
}
106196
);
107197
});
108198

109199
describe('given Test Connection and Create Destination buttons', () => {
200+
const flags = {
201+
aclpLogs: {
202+
enabled: true,
203+
beta: false,
204+
customHttpsEnabled: false,
205+
},
206+
};
110207
const testConnectionButtonText = 'Test Connection';
111208
const createDestinationButtonText = 'Create Destination';
112209

@@ -144,7 +241,7 @@ describe('DestinationCreate', () => {
144241
})
145242
);
146243

147-
renderDestinationCreate();
244+
renderDestinationCreate(flags);
148245

149246
const testConnectionButton = screen.getByRole('button', {
150247
name: testConnectionButtonText,
@@ -181,7 +278,7 @@ describe('DestinationCreate', () => {
181278
})
182279
);
183280

184-
renderDestinationCreate();
281+
renderDestinationCreate(flags);
185282

186283
const testConnectionButton = screen.getByRole('button', {
187284
name: testConnectionButtonText,

0 commit comments

Comments
 (0)