зеркало из https://github.com/Azure/git-rest-api.git
Get a commit for a repo (#10)
This commit is contained in:
Родитель
ca4516703b
Коммит
63f7effed9
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "bin", "test", "src/**/*.test.ts"]
|
||||
"exclude": ["node_modules", "bin", "test", "../src/**/*.test.ts"]
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { Configuration } from "./config";
|
||||
import { AppController, BranchesController, HealthCheckController } from "./controllers";
|
||||
import { AppController, BranchesController, CommitsController, HealthCheckController } from "./controllers";
|
||||
import {
|
||||
AppService,
|
||||
BranchService,
|
||||
CommitService,
|
||||
FSService,
|
||||
GitFetchService,
|
||||
HttpService,
|
||||
|
@ -15,7 +16,7 @@ import {
|
|||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController, HealthCheckController, BranchesController],
|
||||
controllers: [AppController, HealthCheckController, BranchesController, CommitsController],
|
||||
providers: [
|
||||
AppService,
|
||||
RepoService,
|
||||
|
@ -26,6 +27,7 @@ import {
|
|||
PermissionCacheService,
|
||||
HttpService,
|
||||
Configuration,
|
||||
CommitService,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
@ -15,7 +15,7 @@ const b2 = {
|
|||
},
|
||||
};
|
||||
|
||||
describe("BranchController", () => {
|
||||
describe("BranchesController", () => {
|
||||
let controller: BranchesController;
|
||||
const branchServiceSpy = {
|
||||
list: jest.fn(() => [b1, b2]),
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { NotFoundException } from "@nestjs/common";
|
||||
|
||||
import { RepoAuth } from "../../core";
|
||||
import { CommitsController } from "./commits.controller";
|
||||
|
||||
const c1 = {
|
||||
sha: "sha1",
|
||||
};
|
||||
|
||||
describe("CommitsController", () => {
|
||||
let controller: CommitsController;
|
||||
const commitServiceSpy = {
|
||||
get: jest.fn((_, sha) => (sha === "sha1" ? c1 : undefined)),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
controller = new CommitsController(commitServiceSpy as any);
|
||||
});
|
||||
|
||||
it("get a commit", async () => {
|
||||
const auth = new RepoAuth();
|
||||
const commit = await controller.get("github.com/Azure/git-rest-api", "sha1", auth);
|
||||
expect(commitServiceSpy.get).toHaveBeenCalledTimes(1);
|
||||
expect(commitServiceSpy.get).toHaveBeenCalledWith("github.com/Azure/git-rest-api", "sha1", { auth });
|
||||
expect(commit).toEqual(c1);
|
||||
});
|
||||
|
||||
it("throw a NotFoundException if commit doesn't exists", async () => {
|
||||
const auth = new RepoAuth();
|
||||
await expect(controller.get("github.com/Azure/git-rest-api", "sha-not-found", auth)).rejects.toThrow(
|
||||
NotFoundException,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import { Controller, Get, NotFoundException, Param } from "@nestjs/common";
|
||||
import { ApiNotFoundResponse, ApiOkResponse } from "@nestjs/swagger";
|
||||
|
||||
import { ApiHasPassThruAuth, Auth, RepoAuth } from "../../core";
|
||||
import { GitCommit } from "../../dtos";
|
||||
import { CommitService } from "../../services";
|
||||
|
||||
@Controller("/repos/:remote/commits")
|
||||
export class CommitsController {
|
||||
constructor(private commitService: CommitService) {}
|
||||
|
||||
@Get(":commitSha")
|
||||
@ApiHasPassThruAuth()
|
||||
@ApiOkResponse({ type: GitCommit, isArray: true })
|
||||
@ApiNotFoundResponse({})
|
||||
public async get(@Param("remote") remote: string, @Param("commitSha") commitSha: string, @Auth() auth: RepoAuth) {
|
||||
const commit = await this.commitService.get(remote, commitSha, { auth });
|
||||
|
||||
if (!commit) {
|
||||
throw new NotFoundException(`Commit with sha ${commitSha} was not found`);
|
||||
}
|
||||
return commit;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./app.controller";
|
||||
export * from "./commits/commits.controller";
|
||||
export * from "./health-check/health-check.controller";
|
||||
export * from "./branches/branches.controller";
|
||||
|
|
|
@ -30,7 +30,7 @@ export function ApiHasPassThruAuth(): MethodDecorator {
|
|||
}),
|
||||
);
|
||||
const badRequestResponse = ApiBadRequestResponse({
|
||||
description: "When the api request header is malformed",
|
||||
description: "When the x-authorization header is malformed",
|
||||
});
|
||||
|
||||
return (...args) => {
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import { ApiModelProperty } from "@nestjs/swagger";
|
||||
|
||||
import { GitCommit, IGitCommit } from "./git-commit";
|
||||
import { GitCommitRef, IGitCommitRef } from "./git-commit-ref";
|
||||
|
||||
export interface IGitBranch {
|
||||
name: string;
|
||||
commit: IGitCommit;
|
||||
commit: IGitCommitRef;
|
||||
}
|
||||
|
||||
export class GitBranch implements IGitBranch {
|
||||
@ApiModelProperty({ type: String })
|
||||
public name: string;
|
||||
|
||||
@ApiModelProperty({ type: GitCommit })
|
||||
public commit: GitCommit;
|
||||
@ApiModelProperty({ type: GitCommitRef })
|
||||
public commit: GitCommitRef;
|
||||
|
||||
constructor(branch: IGitBranch) {
|
||||
this.name = branch.name;
|
||||
this.commit = new GitCommit(branch.commit);
|
||||
this.commit = new GitCommitRef(branch.commit);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { ApiModelProperty } from "@nestjs/swagger";
|
||||
|
||||
export interface IGitCommitRef {
|
||||
sha: string;
|
||||
}
|
||||
|
||||
export class GitCommitRef implements IGitCommitRef {
|
||||
@ApiModelProperty({ type: String })
|
||||
public sha: string;
|
||||
|
||||
constructor(commit: IGitCommitRef) {
|
||||
this.sha = commit.sha;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,34 @@
|
|||
import { ApiModelProperty } from "@nestjs/swagger";
|
||||
|
||||
export interface IGitCommit {
|
||||
import { GitCommitRef, IGitCommitRef } from "./git-commit-ref";
|
||||
import { GitSignature, IGitSignature } from "./git-signature";
|
||||
|
||||
export interface IGitCommit extends IGitCommitRef {
|
||||
sha: string;
|
||||
message: string;
|
||||
author: IGitSignature;
|
||||
committer: IGitSignature;
|
||||
parents: IGitCommitRef[];
|
||||
}
|
||||
|
||||
export class GitCommit implements IGitCommit {
|
||||
export class GitCommit extends GitCommitRef implements IGitCommit {
|
||||
@ApiModelProperty({ type: String })
|
||||
public sha: string;
|
||||
public message: string;
|
||||
|
||||
constructor(commit: { sha: string }) {
|
||||
this.sha = commit.sha;
|
||||
@ApiModelProperty({ type: GitSignature })
|
||||
public author: GitSignature;
|
||||
|
||||
@ApiModelProperty({ type: GitSignature })
|
||||
public committer: GitSignature;
|
||||
|
||||
@ApiModelProperty({ type: GitCommitRef, isArray: true })
|
||||
public parents: GitCommitRef[];
|
||||
|
||||
constructor(commit: IGitCommit) {
|
||||
super(commit);
|
||||
this.message = commit.message;
|
||||
this.author = commit.author;
|
||||
this.committer = commit.committer;
|
||||
this.parents = commit.parents.map(x => new GitCommitRef(x));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { ApiModelProperty } from "@nestjs/swagger";
|
||||
|
||||
export interface IGitSignature {
|
||||
name: string;
|
||||
email: string;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
export class GitSignature implements IGitSignature {
|
||||
@ApiModelProperty({ type: String })
|
||||
public name: string;
|
||||
@ApiModelProperty({ type: String })
|
||||
public email: string;
|
||||
@ApiModelProperty({ type: String, format: "date-time" })
|
||||
public date: Date;
|
||||
|
||||
constructor(sig: IGitSignature) {
|
||||
this.name = sig.name;
|
||||
this.email = sig.email;
|
||||
this.date = sig.date;
|
||||
}
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
export * from "./git-branch";
|
||||
export * from "./git-commit";
|
||||
export * from "./git-commit-ref";
|
||||
export * from "./git-signature";
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { Commit, Repository, Signature, Time } from "nodegit";
|
||||
|
||||
import { GitCommit, GitCommitRef } from "../../dtos";
|
||||
import { GitSignature } from "../../dtos/git-signature";
|
||||
import { GitBaseOptions, RepoService } from "../repo";
|
||||
|
||||
@Injectable()
|
||||
export class CommitService {
|
||||
constructor(private repoService: RepoService) {}
|
||||
|
||||
public async get(remote: string, commitSha: string, options: GitBaseOptions = {}): Promise<GitCommit | undefined> {
|
||||
const repo = await this.repoService.get(remote, options);
|
||||
const commit = await this.getCommit(repo, commitSha);
|
||||
if (!commit) {
|
||||
return undefined;
|
||||
}
|
||||
const [author, committer, parents] = await Promise.all([
|
||||
getAuthor(commit),
|
||||
getCommitter(commit),
|
||||
getParents(commit),
|
||||
]);
|
||||
return new GitCommit({
|
||||
sha: commit.sha(),
|
||||
message: commit.message(),
|
||||
author,
|
||||
committer,
|
||||
parents,
|
||||
});
|
||||
}
|
||||
|
||||
public async getCommit(repo: Repository, commitSha: string): Promise<Commit | undefined> {
|
||||
try {
|
||||
return await repo.getCommit(commitSha);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the parents of the commit
|
||||
*/
|
||||
export async function getParents(commit: Commit): Promise<GitCommitRef[]> {
|
||||
const parents = await commit.getParents(10);
|
||||
return parents.map(parent => {
|
||||
return new GitCommitRef({ sha: parent.sha() });
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAuthor(commit: Commit): Promise<GitSignature> {
|
||||
const author = await commit.author();
|
||||
return getSignature(author);
|
||||
}
|
||||
|
||||
export async function getCommitter(commit: Commit): Promise<GitSignature> {
|
||||
const committer = await commit.committer();
|
||||
return getSignature(committer);
|
||||
}
|
||||
|
||||
export function getSignature(sig: Signature): GitSignature {
|
||||
return new GitSignature({ email: sig.email(), name: sig.name(), date: getDateFromTime(sig.when()) });
|
||||
}
|
||||
|
||||
export function getDateFromTime(time: Time): Date {
|
||||
return new Date(time.time() * 1000);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from "./commit.service";
|
|
@ -1,5 +1,6 @@
|
|||
export * from "./app.service";
|
||||
export * from "./branch";
|
||||
export * from "./commit";
|
||||
export * from "./fs";
|
||||
export * from "./repo";
|
||||
export * from "./permission";
|
||||
|
|
|
@ -75,7 +75,60 @@
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "When the api request header is malformed"
|
||||
"description": "When the x-authorization header is malformed"
|
||||
},
|
||||
"404": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/repos/{remote}/commits/{commitSha}": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "commitSha",
|
||||
"required": true,
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "remote",
|
||||
"required": true,
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "x-authorization",
|
||||
"required": false,
|
||||
"in": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "x-github-token",
|
||||
"required": false,
|
||||
"in": "header",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GitCommit"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "When the x-authorization header is malformed"
|
||||
},
|
||||
"404": {
|
||||
"description": ""
|
||||
|
@ -91,7 +144,7 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"GitCommit": {
|
||||
"GitCommitRef": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sha": {
|
||||
|
@ -109,13 +162,63 @@
|
|||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"$ref": "#/definitions/GitCommit"
|
||||
"$ref": "#/definitions/GitCommitRef"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"commit"
|
||||
]
|
||||
},
|
||||
"GitSignature": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"date": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"email",
|
||||
"date"
|
||||
]
|
||||
},
|
||||
"GitCommit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sha": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/GitSignature"
|
||||
},
|
||||
"committer": {
|
||||
"$ref": "#/definitions/GitSignature"
|
||||
},
|
||||
"parents": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GitCommitRef"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sha",
|
||||
"message",
|
||||
"author",
|
||||
"committer",
|
||||
"parents"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче