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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

Adjust Custom HTTPS Destination types ([#13274](https://github.com/linode/manager/pull/13274))
22 changes: 14 additions & 8 deletions packages/api-v4/src/delivery/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface Destination extends DestinationCore, AuditData {

export type DestinationDetails =
| AkamaiObjectStorageDetails
| CustomHTTPsDetails;
| CustomHTTPSDetails;

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

export interface CustomHTTPsDetails {
export interface CustomHTTPSDetails {
authentication: Authentication;
client_certificate_details?: ClientCertificateDetails;
content_type: ContentType;
Expand All @@ -84,13 +84,19 @@ export interface CustomHTTPsDetails {
}

interface ClientCertificateDetails {
client_ca_certificate: string;
client_certificate: string;
client_private_key: string;
tls_hostname: string;
client_ca_certificate?: string;
client_certificate?: string;
client_private_key?: string;
tls_hostname?: string;
}

type AuthenticationType = 'basic' | 'none';
export const authenticationType = {
Basic: 'basic',
None: 'none',
} as const;

export type AuthenticationType =
(typeof authenticationType)[keyof typeof authenticationType];

interface Authentication {
details?: AuthenticationDetails;
Expand Down Expand Up @@ -133,7 +139,7 @@ export interface AkamaiObjectStorageDetailsPayload

export type DestinationDetailsPayload =
| AkamaiObjectStorageDetailsPayload
| CustomHTTPsDetails;
| CustomHTTPSDetails;

export interface CreateDestinationPayload {
details: DestinationDetailsPayload;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add Custom HTTPS destination type with proper fields to Create Destination forms ([#13274](https://github.com/linode/manager/pull/13274))
4 changes: 4 additions & 0 deletions packages/manager/src/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ interface AclpLogsFlag extends BetaFeatureFlag {
* This property indicates whether to bypass account capabilities check or not
*/
bypassAccountCapabilities?: boolean;
/**
* This property indicates whether to show Custom HTTPS destination type
*/
customHttpsEnabled?: boolean;
}

interface LkeEnterpriseFlag extends BaseFeatureFlag {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import { DestinationCreate } from './DestinationCreate';

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

describe('DestinationCreate', () => {
const renderDestinationCreate = (
flags: Partial<Flags>,
defaultValues?: Partial<CreateDestinationPayload>
) => {
renderWithThemeAndHookFormContext({
Expand All @@ -25,88 +27,183 @@
...defaultValues,
},
},
options: {
flags,
},
});
};

it('should render disabled Destination Type input with proper selection', async () => {
renderDestinationCreate();
describe('when customHttpsEnabled feature flag is set to false', () => {
const flags = {
aclpLogs: {
enabled: true,
beta: false,
customHttpsEnabled: false,
},
};

const destinationTypeAutocomplete =
screen.getByLabelText('Destination Type');
it('should render disabled Destination Type input with proper selection', async () => {
renderDestinationCreate(flags);

expect(destinationTypeAutocomplete).toBeDisabled();
expect(destinationTypeAutocomplete).toHaveValue('Akamai Object Storage');
});
const destinationTypeAutocomplete =
screen.getByLabelText('Destination Type');

Check warning on line 49 in packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationCreate.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":49,"column":31,"nodeType":"Literal","endLine":49,"endColumn":49}

it(
'should render all inputs for Akamai Object Storage type and allow to fill out them',
{ timeout: 10000 },
async () => {
renderDestinationCreate({ label: '' });
expect(destinationTypeAutocomplete).toBeDisabled();
expect(destinationTypeAutocomplete).toHaveValue('Akamai Object Storage');
});

const destinationNameInput = screen.getByLabelText('Destination Name');
await userEvent.type(destinationNameInput, 'Test');
const hostInput = screen.getByLabelText('Host');
await userEvent.type(hostInput, 'test');
const bucketInput = screen.getByLabelText('Bucket');
await userEvent.type(bucketInput, 'test');
const accessKeyIDInput = screen.getByLabelText('Access Key ID');
await userEvent.type(accessKeyIDInput, 'Test');
const secretAccessKeyInput = screen.getByLabelText('Secret Access Key');
await userEvent.type(secretAccessKeyInput, 'Test');
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');
await userEvent.type(logPathPrefixInput, 'Test');
it(
'should render all inputs for Akamai Object Storage type and allow to fill out them',
{ timeout: 10000 },
async () => {
renderDestinationCreate(flags, { label: '' });

const destinationNameInput = screen.getByLabelText('Destination Name');

Check warning on line 61 in packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationCreate.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":61,"column":60,"nodeType":"Literal","endLine":61,"endColumn":78}
await userEvent.type(destinationNameInput, 'Test');
const hostInput = screen.getByLabelText('Host');
await userEvent.type(hostInput, 'test');
const bucketInput = screen.getByLabelText('Bucket');
await userEvent.type(bucketInput, 'test');
const accessKeyIDInput = screen.getByLabelText('Access Key ID');
await userEvent.type(accessKeyIDInput, 'Test');
const secretAccessKeyInput = screen.getByLabelText('Secret Access Key');
await userEvent.type(secretAccessKeyInput, 'Test');
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');

Check warning on line 71 in packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationCreate.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 3 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":71,"column":58,"nodeType":"Literal","endLine":71,"endColumn":75}
await userEvent.type(logPathPrefixInput, 'Test');

expect(destinationNameInput).toHaveValue('Test');
expect(hostInput).toHaveValue('test');
expect(bucketInput).toHaveValue('test');
expect(accessKeyIDInput).toHaveValue('Test');
expect(secretAccessKeyInput).toHaveValue('Test');
expect(logPathPrefixInput).toHaveValue('Test');
}
);

it('should render Sample Destination Object Name and change its value according to Log Path Prefix input', async () => {
const accountEuuid = 'XYZ-123';
const [month, day, year] = new Date().toLocaleDateString().split('/');
server.use(
http.get('*/account', () => {
return HttpResponse.json(accountFactory.build({ euuid: accountEuuid }));
})
expect(destinationNameInput).toHaveValue('Test');
expect(hostInput).toHaveValue('test');
expect(bucketInput).toHaveValue('test');
expect(accessKeyIDInput).toHaveValue('Test');
expect(secretAccessKeyInput).toHaveValue('Test');
expect(logPathPrefixInput).toHaveValue('Test');
}
);

renderDestinationCreate();
it('should render Sample Destination Object Name and change its value according to Log Path Prefix input', async () => {
const accountEuuid = 'XYZ-123';
const [month, day, year] = new Date().toLocaleDateString().split('/');
server.use(
http.get('*/account', () => {
return HttpResponse.json(
accountFactory.build({ euuid: accountEuuid })
);
})
);

renderDestinationCreate(flags);

let samplePath;
await waitFor(() => {
samplePath = screen.getByText(
`/audit_logs/com.akamai.audit/${accountEuuid}/${year}/${month}/${day}/akamai_log-000166-1756015362-319597-login.gz`
);
expect(samplePath).toBeInTheDocument();
});
// Type the test value inside the input
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');

await userEvent.type(logPathPrefixInput, 'test');
// sample path should be created based on *log path* value
expect(samplePath!.textContent).toEqual(
'/test/akamai_log-000166-1756015362-319597-login.gz'
);

await userEvent.clear(logPathPrefixInput);
await userEvent.type(logPathPrefixInput, '/test');
expect(samplePath!.textContent).toEqual(
'/test/akamai_log-000166-1756015362-319597-login.gz'
);

let samplePath;
await waitFor(() => {
samplePath = screen.getByText(
`/audit_logs/com.akamai.audit/${accountEuuid}/${year}/${month}/${day}/akamai_log-000166-1756015362-319597-login.gz`
await userEvent.clear(logPathPrefixInput);
await userEvent.type(logPathPrefixInput, '/');
expect(samplePath!.textContent).toEqual(
'/akamai_log-000166-1756015362-319597-login.gz'
);
expect(samplePath).toBeInTheDocument();
});
// Type the test value inside the input
const logPathPrefixInput = screen.getByLabelText('Log Path Prefix');
});

await userEvent.type(logPathPrefixInput, 'test');
// sample path should be created based on *log path* value
expect(samplePath!.textContent).toEqual(
'/test/akamai_log-000166-1756015362-319597-login.gz'
);
describe('when customHttpsEnabled feature flag is set to true', () => {
const flags = {
aclpLogs: {
enabled: true,
beta: false,
customHttpsEnabled: true,
},
};

await userEvent.clear(logPathPrefixInput);
await userEvent.type(logPathPrefixInput, '/test');
expect(samplePath!.textContent).toEqual(
'/test/akamai_log-000166-1756015362-319597-login.gz'
);
it('should render enabled Destination Type input with Akamai Object Storage selected and allow to select Custom HTTPS', async () => {
renderDestinationCreate(flags);

const destinationTypeAutocomplete =
screen.getByLabelText('Destination Type');

await userEvent.clear(logPathPrefixInput);
await userEvent.type(logPathPrefixInput, '/');
expect(samplePath!.textContent).toEqual(
'/akamai_log-000166-1756015362-319597-login.gz'
expect(destinationTypeAutocomplete).toBeEnabled();
expect(destinationTypeAutocomplete).toHaveValue('Akamai Object Storage');
await userEvent.click(destinationTypeAutocomplete);
const customHttpsOption = await screen.findByText('Custom HTTPS');

Check warning on line 144 in packages/manager/src/features/Delivery/Destinations/DestinationForm/DestinationCreate.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Define a constant instead of duplicating this literal 4 times. Raw Output: {"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 4 times.","line":144,"column":57,"nodeType":"Literal","endLine":144,"endColumn":71}
await userEvent.click(customHttpsOption);
expect(destinationTypeAutocomplete).toHaveValue('Custom HTTPS');
});

it(
'should render all inputs for Custom HTTPS type and allow to fill them out',
{ timeout: 10000 },
async () => {
renderDestinationCreate(flags, { label: '' });

const destinationTypeAutocomplete =
screen.getByLabelText('Destination Type');
await userEvent.click(destinationTypeAutocomplete);
const customHttpsOption = await screen.findByText('Custom HTTPS');
await userEvent.click(customHttpsOption);
expect(destinationTypeAutocomplete).toHaveValue('Custom HTTPS');

const destinationNameInput = screen.getByLabelText('Destination Name');
await userEvent.type(destinationNameInput, 'Test');

// With None Authentication type selected, the Username and Password inputs should not be rendered
const notYetExistingUsernameInput = screen.queryByLabelText('Username');
expect(notYetExistingUsernameInput).not.toBeInTheDocument();
const notYetExistingPasswordInput = screen.queryByLabelText('Password');
expect(notYetExistingPasswordInput).not.toBeInTheDocument();

// Open Authentication select and choose Basic option
const authenticationAutocomplete =
screen.getByLabelText('Authentication');
expect(authenticationAutocomplete).toHaveValue('None');
await userEvent.click(authenticationAutocomplete);
const basicAuthentication = await screen.findByText('Basic');
await userEvent.click(basicAuthentication);
expect(authenticationAutocomplete).toHaveValue('Basic');

// With Authentication type set to Basic, the Username and Password inputs should be rendered
const usernameInput = screen.getByLabelText('Username');
await userEvent.type(usernameInput, 'Username test');
expect(usernameInput.getAttribute('value')).toEqual('Username test');

const passwordInput = screen.getByLabelText('Password');
await userEvent.type(passwordInput, 'Password test');
expect(passwordInput.getAttribute('value')).toEqual('Password test');

// Endpoint URL
const endpointUrlInput = screen.getByLabelText('Endpoint URL');
await userEvent.type(endpointUrlInput, 'Endpoint URL test');
expect(endpointUrlInput.getAttribute('value')).toEqual(
'Endpoint URL test'
);
}
);
});

describe('given Test Connection and Create Destination buttons', () => {
const flags = {
aclpLogs: {
enabled: true,
beta: false,
customHttpsEnabled: false,
},
};
const testConnectionButtonText = 'Test Connection';
const createDestinationButtonText = 'Create Destination';

Expand Down Expand Up @@ -144,7 +241,7 @@
})
);

renderDestinationCreate();
renderDestinationCreate(flags);

const testConnectionButton = screen.getByRole('button', {
name: testConnectionButtonText,
Expand Down Expand Up @@ -181,7 +278,7 @@
})
);

renderDestinationCreate();
renderDestinationCreate(flags);

const testConnectionButton = screen.getByRole('button', {
name: testConnectionButtonText,
Expand Down
Loading
Loading