Skip to content

Commit

Permalink
Merge branch 'main' into BC-5526-fix-printing-qr-code
Browse files Browse the repository at this point in the history
  • Loading branch information
wiaderwek authored Oct 23, 2023
2 parents 1fdfb3a + 69eb8c0 commit 2c99098
Show file tree
Hide file tree
Showing 41 changed files with 1,943 additions and 33 deletions.
22 changes: 22 additions & 0 deletions src/components/data-group/GroupApi.composable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { $axios } from "@/utils/api";
import { GroupApiFactory, GroupResponse } from "@/serverApi/v3";
import { AxiosResponse } from "axios";
import { GroupMapper } from "./GroupMapper";
import { Group } from "@data-group";

export const useGroupApi = () => {
const groupApi = GroupApiFactory(undefined, "/v3", $axios);

const getGroup = async (groupId: string): Promise<Group> => {
const response: AxiosResponse<GroupResponse> =
await groupApi.groupControllerGetGroup(groupId);

const group: Group = GroupMapper.mapToGroup(response.data);

return group;
};

return {
getGroup,
};
};
64 changes: 64 additions & 0 deletions src/components/data-group/GroupApi.composable.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as serverApi from "@/serverApi/v3/api";
import { GroupResponse } from "@/serverApi/v3/api";
import { mockApiResponse } from "@@/tests/test-utils";
import { createMock, DeepMocked } from "@golevelup/ts-jest";
import { groupResponseFactory } from "@@/tests/test-utils/factory/groupResponseFactory";
import { Group, GroupType, GroupUserRole, useGroupApi } from "@data-group";

describe("GroupApi.composable", () => {
let groupApi: DeepMocked<serverApi.GroupApiInterface>;

beforeEach(() => {
groupApi = createMock<serverApi.GroupApiInterface>();

jest.spyOn(serverApi, "GroupApiFactory").mockReturnValue(groupApi);
});

afterEach(() => {
jest.clearAllMocks();
});

describe("getGroup", () => {
const setup = () => {
const group: GroupResponse = groupResponseFactory.build();

groupApi.groupControllerGetGroup.mockResolvedValue(
mockApiResponse({ data: group })
);

return {
group,
};
};

it("should call the api for groups", async () => {
setup();

await useGroupApi().getGroup("groupId");

expect(groupApi.groupControllerGetGroup).toHaveBeenCalledWith("groupId");
});

it("should return a group", async () => {
const { group } = setup();

const result: Group = await useGroupApi().getGroup("groupId");

expect(result).toEqual<Group>({
id: group.id,
name: group.name,
type: GroupType.Class,
organizationId: group.organizationId,
users: [
{
id: group.users[0].id,
firstName: group.users[0].firstName,
lastName: group.users[0].lastName,
role: GroupUserRole.Student,
},
],
externalSource: group.externalSource,
});
});
});
});
62 changes: 62 additions & 0 deletions src/components/data-group/GroupMapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
GroupResponse,
GroupResponseTypeEnum,
GroupUserResponse,
GroupUserResponseRoleEnum,
} from "@/serverApi/v3";
import { Group, GroupType, GroupUser, GroupUserRole } from "./types";

export const GroupTypeMapping: Record<GroupResponseTypeEnum, GroupType> = {
[GroupResponseTypeEnum.Class]: GroupType.Class,
};

export const GroupUserRoleMapping: Partial<
Record<GroupUserResponseRoleEnum, GroupUserRole>
> = {
[GroupUserResponseRoleEnum.Administrator]: GroupUserRole.Administrator,
[GroupUserResponseRoleEnum.Student]: GroupUserRole.Student,
[GroupUserResponseRoleEnum.Teacher]: GroupUserRole.Teacher,
};

export const GroupUserRoleNameTranslationMapping: Record<
GroupUserRole,
string
> = {
[GroupUserRole.Administrator]: "common.roleName.administrator",
[GroupUserRole.Student]: "common.roleName.student",
[GroupUserRole.Teacher]: "common.roleName.teacher",
[GroupUserRole.Unknown]: "common.labels.unknown",
};

export class GroupMapper {
static mapToGroup(groupResponse: GroupResponse): Group {
return {
id: groupResponse.id,
name: groupResponse.name,
type: GroupTypeMapping[groupResponse.type],
users: groupResponse.users.map((user) =>
GroupMapper.mapToGroupUser(user)
),
externalSource: groupResponse.externalSource,
organizationId: groupResponse.organizationId,
};
}

private static mapToGroupUser(
groupUserResponse: GroupUserResponse
): GroupUser {
return {
id: groupUserResponse.id,
firstName: groupUserResponse.firstName,
lastName: groupUserResponse.lastName,
role:
GroupUserRoleMapping[groupUserResponse.role] ?? GroupUserRole.Unknown,
};
}

static getTranslationKey(role: GroupUserRole): string {
const translationKey: string = GroupUserRoleNameTranslationMapping[role];

return translationKey;
}
}
34 changes: 34 additions & 0 deletions src/components/data-group/GroupState.composable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useErrorHandler } from "@/components/error-handling/ErrorHandler.composable";
import { ref, Ref } from "vue";
import { Group, useGroupApi } from "@data-group";

export const useGroupState = () => {
const { handleError } = useErrorHandler();
const { getGroup } = useGroupApi();

const isLoading: Ref<boolean> = ref(false);
const group: Ref<Group | undefined> = ref();

const fetchGroup = async (groupId: string): Promise<void> => {
isLoading.value = true;

try {
const fetchedGroup: Group = await getGroup(groupId);
group.value = fetchedGroup;
} catch (error) {
// TODO: fix this
handleError(error, {
404: undefined,
500: undefined,
});
}

isLoading.value = false;
};

return {
isLoading,
group,
fetchGroup,
};
};
85 changes: 85 additions & 0 deletions src/components/data-group/GroupState.composable.unit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { createMock, DeepMocked } from "@golevelup/ts-jest";
import { useErrorHandler } from "../error-handling/ErrorHandler.composable";
import { Group, useGroupApi, useGroupState } from "./index";
import { groupFactory } from "@@/tests/test-utils/factory/groupFactory";

jest.mock("@data-group/GroupApi.composable");
jest.mock("@/components/error-handling/ErrorHandler.composable");

describe("GroupState.composable", () => {
let useGroupApiMock: DeepMocked<ReturnType<typeof useGroupApi>>;
let useErrorHandlerMock: DeepMocked<ReturnType<typeof useErrorHandler>>;

beforeEach(() => {
useGroupApiMock = createMock<ReturnType<typeof useGroupApi>>();
useErrorHandlerMock = createMock<ReturnType<typeof useErrorHandler>>();

jest.mocked(useGroupApi).mockReturnValue(useGroupApiMock);
jest.mocked(useErrorHandler).mockReturnValue(useErrorHandlerMock);
});

afterEach(() => {
jest.clearAllMocks();
});

describe("when no data is loaded", () => {
it("should not have data", async () => {
const { group } = useGroupState();

expect(group.value).toBeUndefined();
});
});

describe("when data is loaded", () => {
const setup = () => {
const groupMock: Group = groupFactory.build();

useGroupApiMock.getGroup.mockResolvedValue(groupMock);

return {
groupMock,
...useGroupState(),
};
};

it("should call the api to get a group", async () => {
const { fetchGroup } = setup();

await fetchGroup("groupId");

expect(useGroupApiMock.getGroup).toHaveBeenCalledWith("groupId");
});

it("should set the group in the state", async () => {
const { fetchGroup, group, groupMock } = setup();

await fetchGroup("groupId");

expect(group.value).toEqual(groupMock);
});
});

describe("when an error occurs during loading", () => {
const setup = () => {
const error = new Error("unable to load");

useGroupApiMock.getGroup.mockRejectedValue(error);

return {
error,
...useGroupState(),
};
};

it("should handle the error", async () => {
const { fetchGroup, error } = setup();

await fetchGroup("groupId");

expect(useErrorHandlerMock.handleError).toHaveBeenCalledWith(error, {
404: undefined,
500: undefined,
});
});
});
});
4 changes: 4 additions & 0 deletions src/components/data-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./types";
export * from "./GroupState.composable";
export * from "./GroupApi.composable";
export * from "./GroupMapper";
5 changes: 5 additions & 0 deletions src/components/data-group/types/ExternalSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type ExternalSource = {
externalId: string;

systemId: string;
};
17 changes: 17 additions & 0 deletions src/components/data-group/types/Group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ExternalSourceResponse } from "@/serverApi/v3";
import { GroupType } from "./GroupType";
import { GroupUser } from "./GroupUser";

export type Group = {
id: string;

name: string;

type: GroupType;

users: GroupUser[];

externalSource?: ExternalSourceResponse;

organizationId?: string;
};
3 changes: 3 additions & 0 deletions src/components/data-group/types/GroupType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum GroupType {
Class = "class",
}
11 changes: 11 additions & 0 deletions src/components/data-group/types/GroupUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GroupUserRole } from "./GroupUserRole";

export type GroupUser = {
id: string;

firstName: string;

lastName: string;

role: GroupUserRole;
};
6 changes: 6 additions & 0 deletions src/components/data-group/types/GroupUserRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum GroupUserRole {
Administrator = "administrator",
Student = "student",
Teacher = "teacher",
Unknown = "unknown",
}
5 changes: 5 additions & 0 deletions src/components/data-group/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./Group";
export * from "./GroupType";
export * from "./GroupUser";
export * from "./GroupUserRole";
export * from "./ExternalSource";
33 changes: 33 additions & 0 deletions src/components/data-system/SystemApi.composable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { PublicSystemResponse, SystemsApiFactory } from "@/serverApi/v3";
import { $axios } from "@/utils/api";
import { AxiosResponse } from "axios";
import { System } from "./type";
import { useErrorHandler } from "@/components/error-handling/ErrorHandler.composable";

export const useSystemApi = () => {
const { handleError } = useErrorHandler();
const systemApi = SystemsApiFactory(undefined, "/v3", $axios);

const getSystem = async (systemId: string): Promise<System | undefined> => {
try {
const response: AxiosResponse<PublicSystemResponse> =
await systemApi.systemControllerGetSystem(systemId);

const system: System = {
id: response.data.id,
displayName: response.data.displayName ?? "",
};

return system;
} catch (error) {
handleError(error, {
404: undefined,
500: undefined,
});
}
};

return {
getSystem,
};
};
Loading

0 comments on commit 2c99098

Please sign in to comment.