Skip to content

Commit

Permalink
Merge pull request #5 from toufiq-austcse/feature/github
Browse files Browse the repository at this point in the history
Feature: Add Github Service
  • Loading branch information
toufiq-austcse committed Mar 2, 2023
2 parents dc5779d + a82b749 commit 3aac3d9
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 25 deletions.
3 changes: 2 additions & 1 deletion src/api/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { AuthModule } from './auth/auth.module';
import { DeploymentsModule } from './deployments/deployments.module';
import { RabbitMQModule } from '@common/rabbit-mq/rabbit-mq.module';
import { ProxyMiddleware } from '../common/middleware/proxy.middleware';
import { HttpClientsModule } from '@common/http-clients/http-clients.module';


@Module({
imports: [DatabaseModule, RabbitMQModule, AppConfigModule, IndexModule, AuthModule, DeploymentsModule],
imports: [DatabaseModule, RabbitMQModule, AppConfigModule, HttpClientsModule, IndexModule, AuthModule, DeploymentsModule],
controllers: [],
providers: []
})
Expand Down
4 changes: 2 additions & 2 deletions src/api/deployments/controllers/repository.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export class RepositoryController {
@ApiOkResponse({ type: SwaggerBaseApiResponse(ValidateRepositoryResDto, HttpStatus.OK) })
@UseGuards(JwtAuthGuard)
@ApiSecurity('auth')
async validateRepository(@Query('url') url: string): Promise<BaseApiResponse<ValidateRepositoryResDto>> {
let data = await this.repositoryService.validateRepository(url);
async validateRepository(@Query('repo_url') repoUrl: string): Promise<BaseApiResponse<ValidateRepositoryResDto>> {
let data = await this.repositoryService.validateRepository(repoUrl);
return {
message: null,
data
Expand Down
3 changes: 1 addition & 2 deletions src/api/deployments/deployments.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import { DockerService } from './services/docker.service';
import { ProxyService } from './services/proxy.service';
import { RepositoryController } from './controllers/repository.controller';
import { RepositoryService } from './services/repository.service';
import { HttpModule } from '@nestjs/axios';

@Module({
imports: [HttpModule, TypeOrmModule.forFeature([Deployment, DeploymentType, EnvironmentVariable])],
imports: [TypeOrmModule.forFeature([Deployment, DeploymentType, EnvironmentVariable])],
providers: [DeploymentRepository, DeploymentTypeRepository, EnvironmentVariableRepository, DeploymentService,
EnvironmentVariableService, DeploymentEntitySubscriber, EnvironmentVariableEntitySubscriber, DeploymentJobHandler,
DockerService, ProxyService, RepositoryService],
Expand Down
2 changes: 1 addition & 1 deletion src/api/deployments/dto/res/repository-res.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export class ValidateRepositoryResDto {

@ApiProperty()
@Expose()
repo_name_with_owner: string;
repo_full_name: string;

}
5 changes: 5 additions & 0 deletions src/api/deployments/entities/deployment.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export class Deployment extends AppBaseEntity {
})
last_deployed_at: Date;

@Column({
nullable: true
})
repository_full_name: string;

@Column({
type: 'text'
})
Expand Down
38 changes: 23 additions & 15 deletions src/api/deployments/services/repository.service.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import { HttpStatus, Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { ValidateRepositoryResDto } from '../dto/res/repository-res.dto';
import { firstValueFrom } from 'rxjs';
import { GithubService } from '@common/http-clients/github/services/github.service';

@Injectable()
export class RepositoryService {
constructor(private httpService: HttpService) {
constructor(private githubService: GithubService) {
}

async validateRepository(url: string): Promise<ValidateRepositoryResDto> {
let isValidRepo = false;
let repoNameWithOwner = null;
parseRepoUrl(repoUrl: string): { repoName: string, repoOwner: string } {
return {
repoName: repoUrl.split('/').pop().replace('.git', ''),
repoOwner: repoUrl.split('/').slice(-2)[0]
};
}

async validateRepository(repoUrl: string): Promise<ValidateRepositoryResDto> {
let isValid = false;
let repoFullName = null;
if (!repoUrl.endsWith('.git')) {
throw new BadRequestException('Invalid repository url');
}
let parsedRepoUrl = this.parseRepoUrl(repoUrl);
try {
let res = await firstValueFrom(this.httpService.get(url));
if (res.status === HttpStatus.OK) {
isValidRepo = true;
repoNameWithOwner = url.split('/').slice(-2).join('/');
}
let repoDetails = await this.githubService.getRepository(parsedRepoUrl.repoOwner, parsedRepoUrl.repoName);
isValid = true;
repoFullName = repoDetails.full_name;
} catch (e) {
console.log(e);
Logger.log(e, 'RepositoryService.validateRepository');
}
return {
is_valid: isValidRepo,
repo_name_with_owner: repoNameWithOwner
is_valid: isValid,
repo_full_name: repoFullName
};
}
}
3 changes: 3 additions & 0 deletions src/common/app-config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ export interface EnvironmentVariables {
DB_PORT: number;
DB_MIGRATE: string;
JWT_SECRET: string;

GITHUB_BASE_URL: string;
GITHUB_API_TOKEN: string;
}
6 changes: 4 additions & 2 deletions src/common/app-config/service/app-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class AppConfigService {
let port = this.configService.get('PORT', 3000, { infer: true });
AppConfigService.appConfig = {
PORT: port,
DOMAIN_NAME: this.configService.get('DOMAIN_NAME','toufiq.live'),
DOMAIN_NAME: this.configService.get('DOMAIN_NAME', 'toufiq.live'),
RABBIT_MQ_DEPLOY_IT_EXCHANGE: this.configService.get('RABBIT_MQ_DEPLOY_IT_EXCHANGE'),
REPOSITORIES_LOCAL_DIR_PATH: this.configService.get('REPOSITORIES_LOCAL_DIR_PATH'),
RABBIT_MQ_URL: this.configService.get('RABBIT_MQ_URL'),
Expand All @@ -30,7 +30,9 @@ export class AppConfigService {
DB_PORT: this.configService.get('DB_PORT', { infer: true }),
DB_USER: this.configService.get('DB_USER', { infer: true }),
SWAGGER_USERNAME: this.configService.get('SWAGGER_USERNAME'),
SWAGGER_PASSWORD: this.configService.get('SWAGGER_PASSWORD')
SWAGGER_PASSWORD: this.configService.get('SWAGGER_PASSWORD'),
GITHUB_API_TOKEN: this.configService.getOrThrow('GITHUB_API_TOKEN'),
GITHUB_BASE_URL: this.configService.get('GITHUB_BASE_URL', 'https://api.github.com')

};
}
Expand Down
27 changes: 27 additions & 0 deletions src/common/exceptions/axios-error.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { HttpException, HttpStatus } from '@nestjs/common';
import { AxiosError } from 'axios';

export class AxiosErrorException extends HttpException {
constructor(error: AxiosError, contextName: string) {
let msg = `error in internal server communication, ${contextName}`;
if (error.response) {
super(
{
message: msg,
error: JSON.stringify(error.response.data),
statusCode: error.response.status
},
error.response.status
);
} else {
super(
{
message: msg,
error: error.toJSON(),
statusCode: HttpStatus.INTERNAL_SERVER_ERROR
},
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
}
114 changes: 114 additions & 0 deletions src/common/http-clients/github/dto/res.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
export interface GithubGetRepositoryRes {
id: number;
node_id: string;
name: string;
full_name: string;
private: boolean;
owner: Owner;
html_url: string;
description: null;
fork: boolean;
url: string;
forks_url: string;
keys_url: string;
collaborators_url: string;
teams_url: string;
hooks_url: string;
issue_events_url: string;
events_url: string;
assignees_url: string;
branches_url: string;
tags_url: string;
blobs_url: string;
git_tags_url: string;
git_refs_url: string;
trees_url: string;
statuses_url: string;
languages_url: string;
stargazers_url: string;
contributors_url: string;
subscribers_url: string;
subscription_url: string;
commits_url: string;
git_commits_url: string;
comments_url: string;
issue_comment_url: string;
contents_url: string;
compare_url: string;
merges_url: string;
archive_url: string;
downloads_url: string;
issues_url: string;
pulls_url: string;
milestones_url: string;
notifications_url: string;
labels_url: string;
releases_url: string;
deployments_url: string;
created_at: Date;
updated_at: Date;
pushed_at: Date;
git_url: string;
ssh_url: string;
clone_url: string;
svn_url: string;
homepage: null;
size: number;
stargazers_count: number;
watchers_count: number;
language: string;
has_issues: boolean;
has_projects: boolean;
has_downloads: boolean;
has_wiki: boolean;
has_pages: boolean;
has_discussions: boolean;
forks_count: number;
mirror_url: null;
archived: boolean;
disabled: boolean;
open_issues_count: number;
license: null;
allow_forking: boolean;
is_template: boolean;
web_commit_signoff_required: boolean;
topics: any[];
visibility: string;
forks: number;
open_issues: number;
watchers: number;
default_branch: string;
permissions: Permissions;
template_repository?: GithubGetRepositoryRes;
network_count?: number;
subscribers_count?: number;
}

export interface Owner {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}

export interface Permissions {
admin: boolean;
maintain: boolean;
push: boolean;
triage: boolean;
pull: boolean;
}
30 changes: 30 additions & 0 deletions src/common/http-clients/github/services/github.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { GithubGetRepositoryRes } from '@common/http-clients/github/dto/res.dto';
import { AxiosError } from 'axios';
import { AxiosErrorException } from '@common/exceptions/axios-error.exception';
import { AppConfigService } from '@common/app-config/service/app-config.service';

@Injectable()
export class GithubService {
constructor(private httpService: HttpService) {
}

async getRepository(ownerName: string, name: string): Promise<GithubGetRepositoryRes> {
try {

let res = await firstValueFrom(this.httpService.get(`${AppConfigService.appConfig.GITHUB_BASE_URL}/repos/${ownerName}/${name}`, {
headers: {
Authorization: `Bearer ${AppConfigService.appConfig.GITHUB_API_TOKEN}`
}
}));
return res.data;
} catch (error) {
if (error instanceof AxiosError) {
throw new AxiosErrorException(error, GithubService.name);
}
throw new Error(error as any);
}
}
}
11 changes: 9 additions & 2 deletions src/common/http-clients/http-clients.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Module } from '@nestjs/common';
import { Global, Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { GithubService } from '@common/http-clients/github/services/github.service';

@Module({})
@Global()
@Module({
imports: [HttpModule],
providers: [GithubService],
exports: [GithubService]
})
export class HttpClientsModule {

}

0 comments on commit 3aac3d9

Please sign in to comment.