Skip to content

Commit

Permalink
Update responseHandler and add getGitHubInfo, postIssue and getCollab…
Browse files Browse the repository at this point in the history
…oratorsPerRepo calls
  • Loading branch information
Mouhajer-CO committed Feb 28, 2024
1 parent 6e34e9a commit d1b0c27
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 21 deletions.
66 changes: 52 additions & 14 deletions src/api-sdk/github/github.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,75 @@
import { GitHubRepos, GitHubMembers, GitHubTeams, GitHubMembersPerTeam, GitHubReposPerTeam } from './type';
import {
GitHubRepos,
GitHubMembers,
GitHubTeams,
GitHubMembersPerTeam,
GitHubReposPerTeam,
GitHubCollaboratorsPerRepo,
GitHubIssueRequest
} from './type';
import {
reposMapping,
membersMapping,
teamsMapping,
membersPerTeamMapping,
reposPerTeamMapping,
collaboratorsPerRepoMapping
} from './mapping';

import { ApiResponse, ApiErrorResponse } from '../response';
import { reposMapping, membersMapping, teamsMapping, membersPerTeamMapping, reposPerTeamMapping } from './mapping';
import { HttpRequest } from '../../http-request';

export class Github {
constructor(private readonly request: HttpRequest) {
/**/

constructor(private readonly request: HttpRequest) { /**/ }

// Get info data. Could be repos/members/collaborator/teams ...
public async getGitHubInfo(url: string): Promise<ApiResponse<any[]> | ApiErrorResponse> {
const response = await this.request.httpGet(url);
return this.responseHandler(response);
}

// Create Issue on defined repo
public async postIssue (url: string, body: GitHubIssueRequest): Promise<ApiResponse<any> | ApiErrorResponse> {
const response = await this.request.httpPost(url, body);
return this.responseHandler(response);
}

// Github data Mapped for IDC project
public async getRepos(url: string): Promise<ApiResponse<GitHubRepos[]> | ApiErrorResponse> {
return await this.fetchData<GitHubRepos[]>(url, reposMapping);
const response = await this.request.httpGet(url);
return this.responseHandler<GitHubRepos[]>(response, reposMapping);
}

public async getMembers(url: string): Promise<ApiResponse<GitHubMembers[]> | ApiErrorResponse> {
return await this.fetchData<GitHubMembers[]>(url, membersMapping);
const response = await this.request.httpGet(url);
return this.responseHandler<GitHubMembers[]>(response, membersMapping);
}

public async getTeams(url: string): Promise<ApiResponse<GitHubTeams[]> | ApiErrorResponse> {
return await this.fetchData<GitHubTeams[]>(url, teamsMapping);
const response = await this.request.httpGet(url);
return this.responseHandler<GitHubTeams[]>(response, teamsMapping);
}

public async getMembersPerTeam(url: string): Promise<ApiResponse<GitHubMembersPerTeam[]> | ApiErrorResponse> {
return await this.fetchData<GitHubMembersPerTeam[]>(url, membersPerTeamMapping);
const response = await this.request.httpGet(url);
return this.responseHandler<GitHubMembersPerTeam[]>(response, membersPerTeamMapping);
}

public async getReposPerTeam(url: string): Promise<ApiResponse<GitHubReposPerTeam[]> | ApiErrorResponse> {
return await this.fetchData<GitHubReposPerTeam[]>(url, reposPerTeamMapping);
const response = await this.request.httpGet(url);
return this.responseHandler<GitHubReposPerTeam[]>(response, reposPerTeamMapping);
}

private async fetchData<T>(
url: string,
mappingFunction: (body: any) => T
): Promise<ApiResponse<T> | ApiErrorResponse> {
public async getCollaboratorsPerRepo(url: string): Promise<ApiResponse<GitHubCollaboratorsPerRepo[]> | ApiErrorResponse> {
const response = await this.request.httpGet(url);
return this.responseHandler<GitHubCollaboratorsPerRepo[]>(response, collaboratorsPerRepoMapping);
}

private responseHandler<T>(
response: any,
responseMap?: (body: any) => T
): ApiResponse<T> | ApiErrorResponse {
const resource: ApiResponse<T> & ApiErrorResponse = {
httpStatusCode: response.status
};
Expand All @@ -41,7 +79,7 @@ export class Github {
} else if (response.status >= 400) {
resource.errors = [response.body];
} else {
resource.resource = mappingFunction(response.body);
resource.resource = (responseMap) ? responseMap(response.body) : response.body;
}

return resource;
Expand Down
16 changes: 15 additions & 1 deletion src/api-sdk/github/mapping.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { GitHubMembers, GitHubRepos, GitHubTeams, GitHubMembersPerTeam, GitHubReposPerTeam } from './type';
import {
GitHubMembers,
GitHubRepos,
GitHubTeams,
GitHubMembersPerTeam,
GitHubReposPerTeam,
GitHubCollaboratorsPerRepo
} from './type';

export const reposMapping = (body: any[]): GitHubRepos[] => {
return body.map((obj) => ({
Expand Down Expand Up @@ -41,3 +48,10 @@ export const reposPerTeamMapping = (body: any[]): GitHubReposPerTeam[] => {
name: obj.name,
}));
};

export const collaboratorsPerRepoMapping = (body: any[]): GitHubCollaboratorsPerRepo[] => {
return body.map((obj) => ({
login: obj.login,
permissions: { ...obj.permissions }
}));
};
19 changes: 19 additions & 0 deletions src/api-sdk/github/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,22 @@ export interface GitHubMembersPerTeam {
export interface GitHubReposPerTeam {
name: string;
}

export interface GitHubCollaboratorsPerRepo {
login: string;
permissions: {
pull: boolean;
triage: boolean;
push: boolean;
maintain: boolean;
admin: boolean;
}
}

export interface GitHubIssueRequest {
title: string,
body: string,
milestone: number,
assignees: string[],
labels: string[]
}
40 changes: 39 additions & 1 deletion test/mock/data.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const MOCK_REPOS = [
}
];

export const MOCK_UNMAPPED_ENTREE_REPO = [
export const MOCK_UNMAPPED_RESPONSE_REPO = [
{
name: "repo1",
archived: false,
Expand All @@ -54,6 +54,8 @@ export const MOCK_UNMAPPED_ENTREE_REPO = [
}
];

export const MOCK_FULL_RESPONSE_REPO = [ ...MOCK_UNMAPPED_RESPONSE_REPO ];

export const MOCK_REPO_FETCH_RESPONSE = {
httpStatusCode: 200,
resource: MOCK_REPOS
Expand All @@ -74,6 +76,8 @@ export const MOCK_MEMBERS = [
}
];

export const MOCK_FULL_RESPONSE_MEMBERS = [ ...MOCK_MEMBERS ];

export const MOCK_MEMBER_FETCH_RESPONSE = {
httpStatusCode: 200,
resource: MOCK_MEMBERS.map(({ repos_url, ...rest }) => rest)
Expand Down Expand Up @@ -131,6 +135,40 @@ export const MOCK_REPOS_PER_TEAM_RESPONSE = {
resource: MOCK_REPOS_PER_TEAM
};

export const MOCK_COLLABORATORS_PER_REPO = [
{
other: 'other',
login: 'name',
permissions: { pull: true, triage: true, push: true, maintain: true, admin: true }
},
{
other: 'other2',
login: 'name2',
permissions: { pull: true, triage: false, push: false, maintain: false, admin: false }
},
];

export const MOCK_COLLABORATORS_PER_REPO_RESPONSE = {
httpStatusCode: 200,
resource: MOCK_COLLABORATORS_PER_REPO.map(({ other, ...rest }) => rest)
};

export const MOCK_ISSUE_BODY = {
title: "Add member to the team",
body: "Add member Bob to the team Alice.",
assignees: ["IDC TEAM"],
milestone: 1,
labels: ["GIT"]
};

export const MOCK_ISSUE_RESPONSE = {
id: 1,
number: 1347,
state: "open",
title: "Add member to the team",
body: "Add member Bob to the team Alice."
};

export const MOCK_ERROR = {
error: 'Error: test error'
};
Expand Down
58 changes: 53 additions & 5 deletions test/unit/api-sdk/github/github.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,27 @@ import {
MOCK_TEAMS,
MOCK_ERROR_RESPONSE,
MOCK_ERROR,
MOCK_UNMAPPED_ENTREE_REPO,
MOCK_UNMAPPED_RESPONSE_REPO,
MOCK_MEMBERS_PER_TEAM,
MOCK_MEMBERS_PER_TEAM_RESPONSE,
MOCK_REPOS_PER_TEAM,
MOCK_REPOS_PER_TEAM_RESPONSE,
MOCK_403_ERROR_MSG,
MOCK_403_ERROR_RESPONSE
MOCK_403_ERROR_RESPONSE,
MOCK_COLLABORATORS_PER_REPO,
MOCK_COLLABORATORS_PER_REPO_RESPONSE,
MOCK_FULL_RESPONSE_REPO,
MOCK_FULL_RESPONSE_MEMBERS,
MOCK_ISSUE_RESPONSE,
MOCK_ISSUE_BODY
} from '../../../mock/data.mock';
import { HttpResponse } from '../../../../src/http-request/type';

jest.mock('../../../../src/http-request/index', () => {
return {
HttpRequest: jest.fn().mockImplementation(() => ({
httpGet: jest.fn()
httpGet: jest.fn(),
httpPost: jest.fn()
}))
};
});
Expand Down Expand Up @@ -57,6 +64,39 @@ describe('Github sdk module test suites', () => {
expect(result).toEqual(MOCK_REPO_FETCH_RESPONSE);
});

test('Should return full repo data as an object', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_FULL_RESPONSE_REPO));

const url = 'https://api.github.com/org/test/repos';
const result = await github.getGitHubInfo(url);
expect(result).toEqual({
...MOCK_REPO_FETCH_RESPONSE,
resource: MOCK_FULL_RESPONSE_REPO
});
});

test('Should return full members data as an object', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_FULL_RESPONSE_MEMBERS));

const url = 'https://api.github.com/org/test/members';
const result = await github.getGitHubInfo(url);
expect(result).toEqual({
httpStatusCode: 200,
resource: MOCK_FULL_RESPONSE_MEMBERS
});
});

test('Should post an issue and return the object data', async () => {
httpRequestMock.httpPost.mockResolvedValue(createMockHttpResponse(MOCK_ISSUE_RESPONSE));

const url = 'https://api.github.com/repos/org1/repo1/issues';
const result = await github.postIssue(url, MOCK_ISSUE_BODY);
expect(result).toEqual({
httpStatusCode: 200,
resource: MOCK_ISSUE_RESPONSE
});
});

test('Should return member data as an object', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_MEMBERS));

Expand Down Expand Up @@ -89,6 +129,14 @@ describe('Github sdk module test suites', () => {
expect(result).toEqual(MOCK_REPOS_PER_TEAM_RESPONSE);
});

test('Should return collaborators per repo data as an object', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_COLLABORATORS_PER_REPO));

const url = 'https://api.github.com/repos/organizations/repo/collaborators';
const result = await github.getCollaboratorsPerRepo(url);
expect(result).toEqual(MOCK_COLLABORATORS_PER_REPO_RESPONSE);
});

test('Should return an object with an error property', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_TEAMS, 500, MOCK_ERROR));

Expand All @@ -97,8 +145,8 @@ describe('Github sdk module test suites', () => {
expect(result).toEqual(MOCK_ERROR_RESPONSE);
});

test('Should ignore an unexpected entree in the object ', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_UNMAPPED_ENTREE_REPO));
test('Should ignore an unexpected response fields in the object ', async () => {
httpRequestMock.httpGet.mockResolvedValue(createMockHttpResponse(MOCK_UNMAPPED_RESPONSE_REPO));

const url = 'https://api.github.com/users/test/repos';
const result = await github.getRepos(url);
Expand Down

0 comments on commit d1b0c27

Please sign in to comment.