1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 05:13:29 +00:00

[PM-14417] Create admin TaskService (#12891)

* [PM-14416] Add initial SecurityTask models and enums

* [PM-14416] Add support for PATCH request method and 204 No Content response

* [PM-14416] Add initial task service abstraction

* [PM-14416] Add SecurityTask state/key definitions

* [PM-14416] Add DefaultTaskService implementation

* [PM-14416] Add DefaultTaskService tests

* [PM-14416] Add better null checking to new models

* [PM-14416] Improve null value filtering for task service

* initial commit, added absract file and implementation file

* Added abstract method and implemented bulk create method

* Implemented get all api

* created spec file

* Fixed references

* Added exports

* Added tests

* fixed suggestions

* fixed test

---------

Co-authored-by: Shane Melton <smelton@bitwarden.com>
This commit is contained in:
SmithThe4th
2025-02-03 16:20:48 -05:00
committed by GitHub
parent 3c01abcdfd
commit 101cd940e9
4 changed files with 149 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
import { CipherId, OrganizationId } from "@bitwarden/common/types/guid";
import { SecurityTask, SecurityTaskStatus, SecurityTaskType } from "@bitwarden/vault";
/**
* Request type for creating tasks.
* @property cipherId - Optional. The ID of the cipher to create the task for.
* @property type - The type of task to create. Currently defined as "updateAtRiskCredential".
*/
export type CreateTasksRequest = Readonly<{
cipherId?: CipherId;
type: SecurityTaskType.UpdateAtRiskCredential;
}>;
export abstract class AdminTaskService {
/**
* Retrieves all tasks for a given organization.
* @param organizationId - The ID of the organization to retrieve tasks for.
* @param status - Optional. The status of the tasks to retrieve.
*/
abstract getAllTasks(
organizationId: OrganizationId,
status?: SecurityTaskStatus | undefined,
): Promise<SecurityTask[]>;
/**
* Creates multiple tasks for a given organization and sends out notifications to applicable users.
* @param organizationId - The ID of the organization to create tasks for.
* @param tasks - The tasks to create.
*/
abstract bulkCreateTasks(
organizationId: OrganizationId,
tasks: CreateTasksRequest[],
): Promise<void>;
}

View File

@@ -0,0 +1,65 @@
import { MockProxy, mock } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CipherId, OrganizationId } from "@bitwarden/common/types/guid";
import { SecurityTaskStatus, SecurityTaskType } from "@bitwarden/vault";
import { CreateTasksRequest } from "./abstractions/admin-task.abstraction";
import { DefaultAdminTaskService } from "./default-admin-task.service";
describe("DefaultAdminTaskService", () => {
let defaultAdminTaskService: DefaultAdminTaskService;
let apiService: MockProxy<ApiService>;
beforeEach(() => {
apiService = mock<ApiService>();
defaultAdminTaskService = new DefaultAdminTaskService(apiService);
});
describe("getAllTasks", () => {
it("should call the api service with the correct parameters with status", async () => {
const organizationId = "orgId" as OrganizationId;
const status = SecurityTaskStatus.Pending;
const expectedUrl = `/tasks/organization?organizationId=${organizationId}&status=0`;
await defaultAdminTaskService.getAllTasks(organizationId, status);
expect(apiService.send).toHaveBeenCalledWith("GET", expectedUrl, null, true, true);
});
it("should call the api service with the correct parameters without status", async () => {
const organizationId = "orgId" as OrganizationId;
const expectedUrl = `/tasks/organization?organizationId=${organizationId}`;
await defaultAdminTaskService.getAllTasks(organizationId);
expect(apiService.send).toHaveBeenCalledWith("GET", expectedUrl, null, true, true);
});
});
describe("bulkCreateTasks", () => {
it("should call the api service with the correct parameters", async () => {
const organizationId = "orgId" as OrganizationId;
const tasks: CreateTasksRequest[] = [
{
cipherId: "cipherId-1" as CipherId,
type: SecurityTaskType.UpdateAtRiskCredential,
},
{
cipherId: "cipherId-2" as CipherId,
type: SecurityTaskType.UpdateAtRiskCredential,
},
];
await defaultAdminTaskService.bulkCreateTasks(organizationId, tasks);
expect(apiService.send).toHaveBeenCalledWith(
"POST",
`/tasks/${organizationId}/bulk-create`,
tasks,
true,
true,
);
});
});
});

View File

@@ -0,0 +1,48 @@
import { Injectable } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { OrganizationId } from "@bitwarden/common/types/guid";
import {
SecurityTask,
SecurityTaskData,
SecurityTaskResponse,
SecurityTaskStatus,
} from "@bitwarden/vault";
import { AdminTaskService, CreateTasksRequest } from "./abstractions/admin-task.abstraction";
@Injectable()
export class DefaultAdminTaskService implements AdminTaskService {
constructor(private apiService: ApiService) {}
async getAllTasks(
organizationId: OrganizationId,
status?: SecurityTaskStatus | undefined,
): Promise<SecurityTask[]> {
const queryParams = new URLSearchParams();
queryParams.append("organizationId", organizationId);
if (status !== undefined) {
queryParams.append("status", status.toString());
}
const r = await this.apiService.send(
"GET",
`/tasks/organization?${queryParams.toString()}`,
null,
true,
true,
);
const response = new ListResponse(r, SecurityTaskResponse);
return response.data.map((d) => new SecurityTask(new SecurityTaskData(d)));
}
async bulkCreateTasks(
organizationId: OrganizationId,
tasks: CreateTasksRequest[],
): Promise<void> {
await this.apiService.send("POST", `/tasks/${organizationId}/bulk-create`, tasks, true, true);
}
}

View File

@@ -1 +1,3 @@
export * from "./security-task"; export * from "./security-task";
export * from "./security-task.data";
export * from "./security-task.response";