Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 42 API로부터 사용자의 과제 정보 받아오는 기능 구현 #690

57 changes: 57 additions & 0 deletions backend/src/v1/DTO/cursus.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export type RawProject = {
id: number;
occurrence: number;
final_mark: number;
status: string;
'validated?': boolean;
current_team_id: number;
project: {
id: number;
name: string;
slug: string;
parent_id: number;
};
cursus_ids: number[];
marked_at: string;
marked: boolean;
retriable_at: string;
created_at: string;
updated_at: string;
user: {
id: number;
email: string;
login: string;
first_name: string;
last_name: string;
usual_full_name: string;
usual_first_name: string;
url: string;
phone: string;
displayname: string;
kind: string;
image: object;
'staff?': boolean;
correction_point: number;
pool_month: string;
pool_year: string;
location: string;
wallet: number;
anonymize_date: string;
data_erasure_date: string;
created_at: string;
updated_at: string;
alumnized_at: string;
'alumni?': boolean;
'active?': boolean;
};
teams: object[];
}

export type Project = {
id: RawProject['id'];
status: RawProject['status'];
validated: RawProject['validated?'];
project: RawProject['project'];
cursus_ids: RawProject['cursus_ids'];
marked: RawProject['marked'];
}
22 changes: 19 additions & 3 deletions backend/src/v1/books/books.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import { logger } from '~/logger';
import * as errorCode from '~/v1/utils/error/errorCode';
import ErrorResponse from '~/v1/utils/error/errorResponse';
import isNullish from '~/v1/utils/isNullish';
import * as parseCheck from '~/v1/utils/parseCheck';
import { fetchApi } from '@ts-rest/core';
import { JwtPayload, verify } from 'jsonwebtoken';
import { jwtOption } from '~/config';
import axios from 'axios';
import * as BooksService from './books.service';
import * as types from './books.type';
import LikesService from './likes.service';
import { searchSchema } from '../users/users.types';
import { User } from '../DTO/users.model';
import UsersService from '../users/users.service';
import * as parseCheck from '~/v1/utils/parseCheck';

const likesService = new LikesService();
const usersService = new UsersService();
Expand Down Expand Up @@ -187,7 +191,7 @@ export const getInfoId: RequestHandler = async (
) => {
const id = parseInt(String(req.params.id), 10);
if (Number.isNaN(id)) {
return next(new ErrorResponse(errorCode.INVALID_INPUT, status.BAD_REQUEST));
// return next(new ErrorResponse(errorCode.INVALID_INPUT, status.BAD_REQUEST));
nyj001012 marked this conversation as resolved.
Show resolved Hide resolved
}
try {
const bookInfo = await BooksService.getInfo(req.params.id);
Expand Down Expand Up @@ -443,7 +447,7 @@ export const updateBookDonator = async (
donatorId: user.id,
donator: user.nickname,
};

if (bookDonator.id <= 0 || Number.isNaN(bookDonator.id)) {
return next(new ErrorResponse(errorCode.INVALID_INPUT, status.BAD_REQUEST));
}
Expand All @@ -462,3 +466,15 @@ export const updateBookDonator = async (
}
return 0;
};

export const recommandBook = async (
req: Request,
res: Response,
) => {
const { nickname: login } = req.user as any;
const accessToken: string = await BooksService.getAccessToken();
// TODO => accessToken이 없을 경우를 분리해야 함
const userId: string = await BooksService.getUserIdFrom42API(accessToken, login);
const userProject = await BooksService.getUserProjectFrom42API(accessToken, userId);
res.status(status.OK).send();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

send가 비어있는 거면 응답으로 아무것도 보내주지 않는 건가요?

Copy link
Contributor Author

@nyj001012 nyj001012 Aug 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 지금은 그냥 상태값만 보내주고 있어요.
추후 도서 목록을 보내줄 예정입니다 :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 그렇군요! 잘 보았습니다.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러면 헤더만 전송되는건가요?

};
79 changes: 77 additions & 2 deletions backend/src/v1/books/books.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { logger } from '~/logger';
import { executeQuery } from '~/mysql';
import * as errorCode from '~/v1/utils/error/errorCode';
import { StringRows } from '~/v1/utils/types';
import { VSearchBookByTag } from '~/entity/entities';
import * as models from './books.model';
import BooksRepository from './books.repository';
import {
CreateBookInfo, LendingBookList, UpdateBook, UpdateBookInfo,
categoryIds, UpdateBookDonator,
} from './books.type';
import { categoryWithBookCount } from '../DTO/common.interface';
import { VSearchBookByTag } from '~/entity/entities';
import { Project, RawProject } from '../DTO/cursus.model';

const getInfoInNationalLibrary = async (isbn: string) => {
let book;
Expand Down Expand Up @@ -425,4 +426,78 @@ export const updateBook = async (book: UpdateBook) => {
export const updateBookDonator = async (bookDonator: UpdateBookDonator) => {
const booksRepository = new BooksRepository();
await booksRepository.updateBookDonator(bookDonator);
}
};

export const getAccessToken = async (): Promise<string> => {
const tokenURL = 'https://api.intra.42.fr/oauth/token';
const queryString = {
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
redirect_uri: process.env.REDIRECT_URL,
};
let accessToken: string = '';
await axios(tokenURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: queryString,
}).then((response) => {
accessToken = response.data.access_token;
}).catch((error) => {
console.log(error.message);
});
return accessToken;
};

export const getUserIdFrom42API = async (
accessToken: string,
login: string,
): Promise<string> => {
const userURL = 'https://api.intra.42.fr/v2/users';
const queryString = `filter[login]=${login}`;
let userId: string = '';
await axios(`${userURL}?${queryString}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
}).then((response) => {
userId = response.data[0].id;
}).catch((error) => {
console.log(error.message);
});
return userId;
};

export const getUserProjectFrom42API = async (
accessToken: string,
userId: string,
): Promise<Project[]> => {
const projectURL = `https://api.intra.42.fr/v2/users/${userId}/projects_users`;
const userProject: Array<Project> = [];
await axios(projectURL, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
}).then((response) => {
const rawData: RawProject[] = response.data;
rawData.forEach((data: RawProject) => {
userProject.push({
id: data.id,
status: data.status,
validated: data['validated?'],
project: data.project,
cursus_ids: data.cursus_ids,
marked: data.marked,
});
});
}).catch((error) => {
console.log(error.message);
});
return userProject;
};
Loading