Skip to content

Commit c850d03

Browse files
committed
feat: ✨ Add basic GitHub auth API
The api can now get user data and primary email.
1 parent 21c6fa8 commit c850d03

15 files changed

+133
-40
lines changed

_module/DefaultFallback/default-fallback.controller.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { All, Controller, HttpCode } from "@nestjs/common";
22

3-
import { HttpStatus } from "../../_StatusCode/HTTP";
3+
import { HttpStatus } from "../../_status-code/HTTP";
44

55

66
@Controller("*")
File renamed without changes.
File renamed without changes.

_types/GitHub/email-api-response.dto.ts _types/Github/email-api-response.dto.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export class GitHubEmailApiResponseDto {
1+
export class GithubEmailApiResponseDto {
22
email: string;
33
verified: boolean;
44
primary: boolean;

_types/GitHub/user-data-api-response.dto.ts _types/Github/user-data-api-response.dto.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export class GitHubUserDataApiResponseDto {
1+
export class GithubUserDataApiResponseDto {
22
login: string;
33
id: number;
44
node_id: string;

_types/github-account-data.dto.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { GithubEmailApiResponseDto } from "./Github/email-api-response.dto";
2+
import { GithubUserDataApiResponseDto } from "./Github/user-data-api-response.dto";
3+
4+
5+
export class GithubAccountDataDto extends GithubUserDataApiResponseDto {
6+
primary_email: GithubEmailApiResponseDto;
7+
}

_types/github-auth-code.dto.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export class GithubAuthCodeDto {
2+
code: string;
3+
}

_types/github-code.dto.ts

-3
This file was deleted.

src/auth/auth.module.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { Module } from '@nestjs/common';
1+
import { Module } from "@nestjs/common";
22

3-
@Module({})
3+
import { GithubModule } from "./github/github.module";
4+
5+
6+
@Module({
7+
imports: [GithubModule]
8+
})
49
export class AuthModule {}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Test, TestingModule } from "@nestjs/testing";
2+
3+
import { GithubController } from "./github.controller";
4+
5+
6+
describe("GithubController", () => {
7+
let controller: GithubController;
8+
9+
beforeEach(async () => {
10+
const module: TestingModule = await Test.createTestingModule({
11+
controllers: [GithubController]
12+
}).compile();
13+
14+
controller = module.get<GithubController>(GithubController);
15+
});
16+
17+
it("should be defined", () => {
18+
expect(controller).toBeDefined();
19+
});
20+
});

src/auth/github/github.controller.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Body, Controller, Post, Res } from "@nestjs/common";
2+
import { Response } from "express";
3+
4+
import { HttpStatus } from "_status-code/HTTP";
5+
import { GithubAuthCodeDto } from "_types/github-auth-code.dto";
6+
import { GithubService } from "./github.service";
7+
8+
9+
@Controller("auth/github")
10+
export class GithubController {
11+
constructor(private githubService: GithubService) {}
12+
13+
@Post()
14+
async getGitHubAccountData(
15+
@Body() data: GithubAuthCodeDto,
16+
@Res() res: Response,
17+
) {
18+
try {
19+
const accountData = await this.githubService.getGitHubAccountData(
20+
data.code,
21+
);
22+
23+
return res.status(HttpStatus.OK).json({
24+
account_data: accountData
25+
});
26+
}
27+
catch (error) {
28+
return res.status(HttpStatus.UNAUTHORIZED).json({
29+
error: "Please provide a valid authentication code"
30+
});
31+
}
32+
}
33+
}

src/auth/github/github.module.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Module } from "@nestjs/common";
2+
3+
import { GithubController } from "./github.controller";
4+
import { GithubService } from "./github.service";
5+
6+
7+
@Module({
8+
controllers: [GithubController],
9+
providers: [GithubService]
10+
})
11+
export class GithubModule {}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Test, TestingModule } from "@nestjs/testing";
2+
import { GithubService } from "./github.service";
3+
4+
5+
describe("GithubService", () => {
6+
let service: GithubService;
7+
8+
beforeEach(async () => {
9+
const module: TestingModule = await Test.createTestingModule({
10+
providers: [GithubService]
11+
}).compile();
12+
13+
service = module.get<GithubService>(GithubService);
14+
});
15+
16+
it("should be defined", () => {
17+
expect(service).toBeDefined();
18+
});
19+
});

_module/GetGitHubAccountData/index.ts src/auth/github/github.service.ts

+29-22
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1+
import { Injectable } from "@nestjs/common";
12
import axios from "axios";
23

3-
import { GitHubEmailApiResponseDto } from "_types/GitHub/email-api-response.dto";
4-
import { GitHubUserDataApiResponseDto } from "_types/GitHub/user-data-api-response.dto";
4+
import { GithubEmailApiResponseDto } from "_types/Github/email-api-response.dto";
5+
import { GithubUserDataApiResponseDto } from "_types/Github/user-data-api-response.dto";
6+
import { GithubAccountDataDto } from "_types/github-account-data.dto";
57

68

79
const GITHUB_ACCESS_TOKEN_API = "https://github.com/login/oauth/access_token";
810
const GITHUB_USER_DATA_API = "https://api.github.com/user";
911
const GITHUB_EMAIL_DATA_API = "https://api.github.com/user/emails";
1012

11-
export async function getGitHubAccountData(code: string) {
12-
try {
13-
const accessToken = await getAccessToken(code);
14-
if (!accessToken) {
15-
throw new Error("Error while getting access token");
16-
}
13+
@Injectable()
14+
export class GithubService {
15+
async getGitHubAccountData(
16+
code: string,
17+
): Promise<GithubAccountDataDto | null> {
18+
try {
19+
const accessToken = await getAccessToken(code);
20+
const accountData = await getAccountData(accessToken);
1721

18-
const accountData = await getAccountData(accessToken);
19-
return accountData;
20-
}
21-
catch (error) {
22-
console.error(error.message);
23-
return null;
22+
return accountData;
23+
}
24+
catch (error) {
25+
throw new Error("Error while getting GitHub account data");
26+
}
2427
}
2528
}
2629

@@ -48,19 +51,20 @@ async function getAccessToken(code: string): Promise<string | null> {
4851
return accessToken;
4952
}
5053
catch (error) {
51-
console.error(error.message);
52-
return null;
54+
throw new Error("Error while getting access token");
5355
}
5456
}
5557

56-
async function getAccountData(accessToken: string): Promise<any | null> {
58+
async function getAccountData(
59+
accessToken: string,
60+
): Promise<GithubAccountDataDto | null> {
5761
try {
5862
const userDataResponse = await axios.get(GITHUB_USER_DATA_API, {
5963
headers: {
6064
Authorization: `Bearer ${accessToken}`
6165
}
6266
});
63-
const userData: GitHubUserDataApiResponseDto | null =
67+
const userData: GithubUserDataApiResponseDto | null =
6468
await userDataResponse.data;
6569
if (!userData) {
6670
throw new Error("User data not found");
@@ -71,17 +75,20 @@ async function getAccountData(accessToken: string): Promise<any | null> {
7175
Authorization: `Bearer ${accessToken}`
7276
}
7377
});
74-
const userEmails: [GitHubEmailApiResponseDto] | null =
78+
const userEmails: [GithubEmailApiResponseDto] | null =
7579
await userEmailsResponse.data;
7680
if (!userEmails) {
7781
throw new Error("User emails not found");
7882
}
79-
const primaryEmail = userEmails.filter((email) => email.primary)[0];
83+
84+
const primaryEmail = userEmails.find((email) => email.primary);
85+
if (!primaryEmail) {
86+
throw new Error("Primary email not found");
87+
}
8088

8189
return { ...userData, primary_email: primaryEmail };
8290
}
8391
catch (error) {
84-
console.error(error.message);
85-
return null;
92+
throw new Error("Error while getting account data");
8693
}
8794
}

src/test/test.controller.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import { Body, Controller, Get, Post } from "@nestjs/common";
2-
3-
import { getGitHubAccountData } from "_module/GetGitHubAccountData";
4-
import { GithubCodeDto } from "_types/github-code.dto";
1+
import { Controller, Get } from "@nestjs/common";
52

63

74
@Controller("test")
@@ -12,10 +9,4 @@ export class TestController {
129
message: "test route"
1310
};
1411
}
15-
16-
@Post()
17-
async postTest(@Body() GithubCodeDto: GithubCodeDto) {
18-
const accountData = await getGitHubAccountData(GithubCodeDto.code);
19-
return { data: accountData };
20-
}
2112
}

0 commit comments

Comments
 (0)