Skip to content

Commit

Permalink
add select/unselect all functionality to captured apis table
Browse files Browse the repository at this point in the history
  • Loading branch information
Elliot Yibaebi committed Jan 25, 2025
1 parent 2e5b844 commit b23ca58
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 3 deletions.
31 changes: 28 additions & 3 deletions app/components/file-details/api-scan/captured-apis/index.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{#if this.fetchCapturedApis.isRunning}}
{{#if (and this.fetchCapturedApis.isRunning this.showAPIListLoadingState)}}
<AkStack @justifyContent='center' class='mt-7'>
<div local-class='captured-api-status-card'>
<FileDetails::ApiScan::CapturedApis::Loading />
Expand All @@ -25,13 +25,38 @@
>
<div class='mb-1'>
<AkTypography
data-test-fileDetails-apiScan-capturedApi-title
@color='textSecondary'
local-class='captured-api-title'
@variant='h6'
data-test-fileDetails-apiScan-capturedApi-title
>
{{t 'capturedApiListTitle'}}
</AkTypography>

<AkStack
@alignItems='center'
@spacing='1'
local-class='select-unselect-all-captured-api-cta'
class='w-full px-2 py-1'
>
<AkCheckbox
@disabled={{this.selectAllCapturedApis.isRunning}}
@checked={{this.allAPIsSelected}}
@onChange={{this.handleSelectAllCapturedApis}}
data-test-fileDetails-apiScan-selectAllCapturedApis-checkbox
/>

<AkTypography
@fontWeight='bold'
data-test-fileDetails-apiScan-selectAllCapturedApis-text
>
{{t 'selectOrUnSelectAll'}}
</AkTypography>

{{#if this.selectAllCapturedApis.isRunning}}
<AkLoader @size={{13}} class='ml-1' />
{{/if}}
</AkStack>

<AkStack @direction='column' @spacing='1.5'>
{{#each pgc.currentPageResults as |ca|}}
<AkDivider />
Expand Down
3 changes: 3 additions & 0 deletions app/components/file-details/api-scan/captured-apis/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

.captured-api-title {
padding: 1em;
}

.select-unselect-all-captured-api-cta {
background-color: var(
--file-details-api-scan-captured-apis-title-background
);
Expand Down
56 changes: 56 additions & 0 deletions app/components/file-details/api-scan/captured-apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type IntlService from 'ember-intl/services/intl';
import type { DS } from 'ember-data';

import ENV from 'irene/config/environment';
import parseError from 'irene/utils/parse-error';
import type { PaginationProviderActionsArgs } from 'irene/components/ak-pagination-provider';
import type FileModel from 'irene/models/file';
import type CapturedApiModel from 'irene/models/capturedapi';
Expand Down Expand Up @@ -41,6 +42,8 @@ export default class FileDetailsApiScanCapturedApisComponent extends Component<F
@service('notifications') declare notify: NotificationService;

@tracked selectedCount = 0;
@tracked allAPIsSelected = false;
@tracked showAPIListLoadingState = true;
@tracked capturedApiResponse: CapturedApiQueryResponse | null = null;
@tracked limit = 10;
@tracked offset = 0;
Expand All @@ -51,6 +54,7 @@ export default class FileDetailsApiScanCapturedApisComponent extends Component<F
) {
super(owner, args);

this.getAllAPIsSelectedStatus.perform();
this.setSelectedApiCount.perform();
this.fetchCapturedApis.perform(this.limit, this.offset);
}
Expand All @@ -67,6 +71,14 @@ export default class FileDetailsApiScanCapturedApisComponent extends Component<F
return this.totalCapturedApiCount === 0;
}

get modAllSelectedAPIsEndpoint() {
return [
ENV.endpoints['files'],
this.args.file.id,
'toggle_captured_apis',
].join('/');
}

setFooterComponentDetails() {
if (this.hasNoCapturedApi) {
return;
Expand Down Expand Up @@ -96,6 +108,11 @@ export default class FileDetailsApiScanCapturedApisComponent extends Component<F
this.fetchCapturedApis.perform(limit, this.offset);
}

@action
handleSelectAllCapturedApis(_: Event, checked: boolean) {
this.selectAllCapturedApis.perform(checked);
}

getSelectedApis = task(async () => {
const url = [
ENV.endpoints['files'],
Expand Down Expand Up @@ -151,6 +168,45 @@ export default class FileDetailsApiScanCapturedApisComponent extends Component<F
}
});

selectAllCapturedApis = task(async (is_active: boolean) => {
this.showAPIListLoadingState = false;

try {
await this.ajax.put(this.modAllSelectedAPIsEndpoint, {
data: { is_active: is_active },
namespace: ENV.namespace_v2,
});

await this.fetchCapturedApis.perform(this.limit, this.offset);
await this.getAllAPIsSelectedStatus.perform();

this.showAPIListLoadingState = true;
} catch (err) {
const error = err as AdapterError;
const errMsg = this.intl.t('tPleaseTryAgain');

this.notify.error(parseError(error, errMsg));
}
});

getAllAPIsSelectedStatus = task(async () => {
try {
const allAPIsSelected = await this.ajax.request<{ is_active: boolean }>(
this.modAllSelectedAPIsEndpoint,
{
namespace: ENV.namespace_v2,
}
);

this.allAPIsSelected = allAPIsSelected.is_active;
} catch (err) {
const error = err as AdapterError;
const errMsg = this.intl.t('tPleaseTryAgain');

this.notify.error(parseError(error, errMsg));
}
});

fetchCapturedApis = task(async (limit: number, offset: number) => {
try {
this.capturedApiResponse = (await this.store.query('capturedapi', {
Expand Down
88 changes: 88 additions & 0 deletions tests/acceptance/file-details/api-scan-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,94 @@ module('Acceptance | file-details/api-scan', function (hooks) {
.hasText(t('apiScan'));
});

test.each(
'it selects and unselects all captured APIs',
[true, false],
async function (assert, is_active) {
// Return opposite toggle states for all API endpoints
this.server.db.capturedapis.forEach((api) => {
this.server.db.capturedapis.update(api.id, { is_active });
});

this.server.get('/v2/files/:id/capturedapis', (schema) => {
const results = schema.capturedapis.all().models;

return { count: results.length, previous: null, next: null, results };
});

this.server.put('/v2/files/:id/toggle_captured_apis', (schema, req) => {
const { is_active } = JSON.parse(req.requestBody);

const results = schema.capturedapis.all().models;

results.forEach((api) => api.update({ is_active }));

return { count: results.length, previous: null, next: null, results };
});

this.server.get('/v2/files/:id/toggle_captured_apis', () => {
return { is_active };
});

await visit(`/dashboard/file/${this.file.id}/api-scan`);

assert
.dom('[data-test-fileDetails-apiScan-breadcrumbContainer]')
.exists();
assert.dom('[data-test-fileDetailsSummary-root]').exists();

assert
.dom('[data-test-fileDetails-apiScan-tabs="api-scan-tab"]')
.hasText(t('apiScan'));

assert
.dom('[data-test-fileDetails-apiScan-capturedApi-title]')
.hasText(t('capturedApiListTitle'));

assert
.dom('[data-test-fileDetails-apiScan-selectAllCapturedApis-text]')
.hasText(t('selectOrUnSelectAll'));

let apiEndpoints = findAll(
'[data-test-fileDetails-apiScan-capturedApi-endpointContainer]'
);

assert.strictEqual(apiEndpoints.length, 10);

// All APIs should reflect the correct states
apiEndpoints.forEach((endpoint) => {
const endpointSelector =
'[data-test-fileDetails-apiScan-capturedApi-endpointSelectCheckbox]';

if (is_active) {
assert.dom(endpointSelector, endpoint).isNotDisabled().isChecked();
} else {
assert.dom(endpointSelector, endpoint).isNotDisabled().isNotChecked();
}
});

await click(
'[data-test-fileDetails-apiScan-selectAllCapturedApis-checkbox]'
);

apiEndpoints = findAll(
'[data-test-fileDetails-apiScan-capturedApi-endpointContainer]'
);

// All APIs should reflect the correct states
apiEndpoints.forEach((endpoint) => {
const endpointSelector =
'[data-test-fileDetails-apiScan-capturedApi-endpointSelectCheckbox]';

if (is_active) {
assert.dom(endpointSelector, endpoint).isNotDisabled().isNotChecked();
} else {
assert.dom(endpointSelector, endpoint).isNotDisabled().isChecked();
}
});
}
);

test('test toggle api endpoint selection', async function (assert) {
this.server.get('/v2/files/:id/capturedapis', (schema, req) => {
const results = req.queryParams.is_active
Expand Down
1 change: 1 addition & 0 deletions translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,7 @@
"selectedProject": "Selected Project",
"selectedScope": "Selected Scope",
"selectedVersion": "Selected OS Version",
"selectOrUnSelectAll": "Select/UnSelect All",
"sendInvitation": "Send Invitation",
"sendPasswordResetMail": "Send password reset email",
"sentInvitationTo": "Sent invitation to",
Expand Down
1 change: 1 addition & 0 deletions translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,7 @@
"selectedProject": "Selected Project",
"selectedScope": "Selected Scope",
"selectedVersion": "選択したOSのバージョン",
"selectOrUnSelectAll": "Select/UnSelect All",
"sendInvitation": "Send Invitation",
"sendPasswordResetMail": "Send password reset email",
"sentInvitationTo": "Sent invitation to",
Expand Down

0 comments on commit b23ca58

Please sign in to comment.