Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
API_URL=URL
API_BASE_URL=https://cdn.rs.school
YOUTUBE_API_KEY=YOUTUBE_API_KEY
NEXT_PUBLIC_API_BASE_URL=https://cdn.rs.school
LOG_QUERY=true

# YouTube
NEXT_PUBLIC_YOUTUBE_API_BASE_URL=https://www.googleapis.com/youtube/v3
NEXT_PUBLIC_YOUTUBE_API_KEY=YOUTUBE_API_KEY

# Contentful
CONTENTFUL_SPACE_ID=CONTENTFUL_SPACE_ID
CONTENTFUL_MANAGEMENT_TOKEN=CONTENTFUL_MANAGEMENT_TOKEN
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ on:
env:
NODE_VERSION: 20.x
API_URL: ${{ secrets.API_URL }}
API_BASE_URL: ${{ secrets.API_BASE_URL }}
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}
NEXT_PUBLIC_API_BASE_URL: ${{ secrets.API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_BASE_URL: ${{ secrets.YOUTUBE_API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}

jobs:
ci:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/preview-create.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOY_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: 'eu-central-1'
API_URL: ${{ secrets.API_URL }}
API_BASE_URL: ${{ secrets.API_BASE_URL }}
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}
NEXT_PUBLIC_API_BASE_URL: ${{ secrets.API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_BASE_URL: ${{ secrets.YOUTUBE_API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}

jobs:
build-rs-school:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ env:
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOY_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: 'eu-central-1'
API_URL: ${{ secrets.API_URL }}
API_BASE_URL: ${{ secrets.API_BASE_URL }}
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_PRODUCTION }}
NEXT_PUBLIC_API_BASE_URL: ${{ secrets.API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_BASE_URL: ${{ secrets.YOUTUBE_API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_PRODUCTION }}

jobs:
rs-school:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/visual-testing-on-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ on:
env:
NODE_VERSION: 20.x
API_URL: ${{ secrets.API_URL }}
API_BASE_URL: ${{ secrets.API_BASE_URL }}
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}
NEXT_PUBLIC_API_BASE_URL: ${{ secrets.API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_BASE_URL: ${{ secrets.YOUTUBE_API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}

jobs:
run-visial-testing:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/visual-testing-on-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ on:
env:
NODE_VERSION: 20.x
API_URL: ${{ secrets.API_URL }}
API_BASE_URL: ${{ secrets.API_BASE_URL }}
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}
NEXT_PUBLIC_API_BASE_URL: ${{ secrets.API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_BASE_URL: ${{ secrets.YOUTUBE_API_BASE_URL }}
NEXT_PUBLIC_YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY_DEVELOPMENT }}

jobs:
visial-testing:
Expand Down
5 changes: 3 additions & 2 deletions env.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
declare namespace NodeJS {
interface ProcessEnv {
API_URL: string;
YOUTUBE_API_KEY: string;
API_BASE_URL: string;
NEXT_PUBLIC_API_BASE_URL: string;
NEXT_PUBLIC_YOUTUBE_API_BASE_URL: string;
NEXT_PUBLIC_YOUTUBE_API_KEY: string;
LOG_QUERY: string;
}
}
25 changes: 24 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"remark-rehype": "^11.1.2",
"remark-remove-comments": "^1.1.1",
"remark-toc": "^9.0.0",
"swiper": "^11.2.6"
"swiper": "^11.2.6",
"swr": "^2.3.3"
},
"devDependencies": {
"@eslint/js": "^9.25.1",
Expand Down
13 changes: 11 additions & 2 deletions src/core/api/app-api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MentorApi } from '@/entities/mentor/api/mentor-api';
import { TrainerApi } from '@/entities/trainer/api/trainer-api';
import { ApiBaseClass } from '@/shared/api/api-base-class';
import { ApiServices } from '@/shared/types';
Expand All @@ -6,10 +7,18 @@ export class Api {
public readonly services: ApiServices;

public readonly trainer: TrainerApi;
public readonly mentor: MentorApi;

constructor(private readonly baseURI: string) {
this.services = { rest: new ApiBaseClass(this.baseURI) };
constructor(
private readonly baseURI: string,
private readonly youtubeBaseURI: string,
) {
this.services = {
rest: new ApiBaseClass(this.baseURI),
youtube: new ApiBaseClass(this.youtubeBaseURI),
};

this.trainer = new TrainerApi(this.services);
this.mentor = new MentorApi(this.services);
}
}
19 changes: 19 additions & 0 deletions src/entities/mentor/api/mentor-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MENTORSHIP_YOUTUBE_PLAYLIST_ID } from '../constants';
import { MentorPlaylistResponse } from '../types';
import { YOUTUBE_API_MAX_RESULTS_PER_PAGE } from '@/shared/constants';
import { ApiServices } from '@/shared/types/';

export class MentorApi {
constructor(private readonly services: ApiServices) {}

public queryMentorPlaylist() {
return this.services.youtube.get<MentorPlaylistResponse>(`/playlistItems`, {
params: {
part: ['snippet', 'status'],
maxResults: YOUTUBE_API_MAX_RESULTS_PER_PAGE,
key: process.env.NEXT_PUBLIC_YOUTUBE_API_KEY,
playlistId: MENTORSHIP_YOUTUBE_PLAYLIST_ID,
},
});
}
}
1 change: 1 addition & 0 deletions src/entities/mentor/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MENTORSHIP_YOUTUBE_PLAYLIST_ID = 'PLzLiprpVuH8f7Jg8pgZUCeTN-Q6uVZNhg';
9 changes: 9 additions & 0 deletions src/entities/mentor/helpers/transform-mentor-videos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Video } from '@/shared/types';

export function transformMentorVideos(videos: GoogleApiYouTubePlaylistItemResource[]): Video[] {
return videos.map((item) => ({
id: item.snippet.resourceId.videoId,
title: item.snippet.title,
thumbnail: item.snippet.thumbnails.medium.url,
}));
}
1 change: 1 addition & 0 deletions src/entities/mentor/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type { MentorFeedback } from './types';
export { MentorFeedbackCard } from './ui/mentor-feedback-card/mentor-feedback-card';
export { mentorStore } from './model/store';
20 changes: 20 additions & 0 deletions src/entities/mentor/model/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { transformMentorVideos } from '@/entities/mentor/helpers/transform-mentor-videos';
import { api } from '@/shared/api/api';
import { filterYoutubePrivateVideos } from '@/shared/helpers/filter-youtube-private-videos';

export class MentorStore {
public loadYoutubePlaylist = async () => {
const res = await api.mentor.queryMentorPlaylist();

if (res.isSuccess) {
const publicVideos = filterYoutubePrivateVideos(res.result);
const videoItems = transformMentorVideos(publicVideos);

return videoItems;
}

throw new Error('Error while loading mentor playlist.');
};
}

export const mentorStore = new MentorStore();
4 changes: 4 additions & 0 deletions src/entities/mentor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ export type MentorFeedback = {
review: string;
photo: StaticImageData;
};

export type MentorPlaylistResponse = {
items: GoogleApiYouTubePlaylistItemResource[];
};
4 changes: 2 additions & 2 deletions src/shared/api/api-base-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class ApiBaseClass {

private async query<TResponse>(
url: string,
{ method = HTTP_METHOD.GET, body, headers, params, shouldLog }: RequestOptions = {},
{ method = HTTP_METHOD.GET, body, headers, params, shouldLog = true }: RequestOptions = {},
): Promise<QueryResult<TResponse>> {
const compiledUrl = this.compileUrl(url, params);
const queryHeaders = {
Expand Down Expand Up @@ -122,6 +122,6 @@ export class ApiBaseClass {
const urlWithQueryString = `${url}?${queryString}`;
const baseUrl = !isValidUrl(url) ? this.baseUrl : '';

return new URL(urlWithQueryString, baseUrl).toString();
return `${baseUrl}${urlWithQueryString}`;
}
}
5 changes: 4 additions & 1 deletion src/shared/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Api } from '@/core/api/app-api';

export const api = new Api(process.env.API_BASE_URL);
export const api = new Api(
process.env.NEXT_PUBLIC_API_BASE_URL,
process.env.NEXT_PUBLIC_YOUTUBE_API_BASE_URL,
);
3 changes: 3 additions & 0 deletions src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const TO_BE_DETERMINED = 'TBD';
export const REGISTRATION_WILL_OPEN_SOON = 'Registration will open soon!';
export const REGISTRATION_WILL_OPEN_SOON_RU = 'РСгистрация откроСтся скоро!';
export const UNKNOWN_API_ERROR = 'Unknown error, API request failed.';
export const YOUTUBE_API_MAX_RESULTS_PER_PAGE = 50;

/**
* https://www.contentful.com/developers/docs/references/content-preview-api/#/reference/links
Expand Down Expand Up @@ -103,3 +104,5 @@ export const ROUTES = {
DOCS_RU: 'docs/ru',
NOT_FOUND: '*',
} as const;

export const SWR_CACHE_KEY = { MENTORS_PLAYLIST: 'MENTORS_PLAYLIST' };
6 changes: 6 additions & 0 deletions src/shared/helpers/filter-youtube-private-videos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { MentorPlaylistResponse } from '@/entities/mentor/types';
import { videoPrivacyStatus } from '@/shared/ui/video-playlist-with-player/constants';

export function filterYoutubePrivateVideos(videos: MentorPlaylistResponse) {
return videos.items.filter((item) => item.status.privacyStatus === videoPrivacyStatus.public);
}
8 changes: 4 additions & 4 deletions src/shared/helpers/log-request.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import util from 'node:util';
import { inspect } from 'util';

import { stringifyJSONSafe } from '@/shared/helpers/stringify-json-safe';
import { HttpMethod } from '@/shared/types';
Expand Down Expand Up @@ -26,7 +26,7 @@ export function logRequest({
body,
error,
}: LogRequestParams) {
if (process.env.LOG_QUERY === 'false') {
if (process.env.NODE_ENV === 'production' || process.env.LOG_QUERY === 'false') {
return;
}

Expand Down Expand Up @@ -57,9 +57,9 @@ export function logRequest({
}

console.log(
util.inspect(logObject, {
inspect(logObject, {
depth: null,
colors: process.env.NODE_ENV !== 'production',
colors: true,
}),
);
}
1 change: 1 addition & 0 deletions src/shared/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type Video = {

export type ApiServices = {
rest: ApiBaseClass;
youtube: ApiBaseClass;
};

export type HttpMethod = (typeof HTTP_METHOD)[keyof typeof HTTP_METHOD];
Expand Down
Loading
Loading