зеркало из https://github.com/Azure/git-rest-api.git
Add tree route and recursive option (#52)
* Add tree route and recursive option * Add recursive to swagger * Fix lint * Address feedback * Fix lint * Bump package * Rename variable * Fix missing path in spec * Fix logic * Add content test * Add test for tree controller * Update branches snap * Fix snap * Small fixes * Fix lint
This commit is contained in:
Родитель
638ea5b524
Коммит
c4a58efd9d
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "git-rest-api",
|
"name": "git-rest-api",
|
||||||
"version": "0.3.2",
|
"version": "0.3.3",
|
||||||
"description": "This project welcomes contributions and suggestions. Most contributions require you to agree to a\r Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\r the rights to use your contribution. For details, visit https://cla.microsoft.com.",
|
"description": "This project welcomes contributions and suggestions. Most contributions require you to agree to a\r Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\r the rights to use your contribution. For details, visit https://cla.microsoft.com.",
|
||||||
"main": "bin/main.js",
|
"main": "bin/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
ContentController,
|
ContentController,
|
||||||
HealthCheckController,
|
HealthCheckController,
|
||||||
} from "./controllers";
|
} from "./controllers";
|
||||||
|
import { TreeController } from "./controllers/tree/tree.controller";
|
||||||
import { Telemetry, createTelemetry } from "./core";
|
import { Telemetry, createTelemetry } from "./core";
|
||||||
import { ContextMiddleware, LoggingInterceptor } from "./middlewares";
|
import { ContextMiddleware, LoggingInterceptor } from "./middlewares";
|
||||||
import {
|
import {
|
||||||
|
@ -31,7 +32,14 @@ import { RepoIndexService } from "./services/repo-index";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [],
|
||||||
controllers: [HealthCheckController, BranchesController, CommitsController, CompareController, ContentController],
|
controllers: [
|
||||||
|
HealthCheckController,
|
||||||
|
BranchesController,
|
||||||
|
CommitsController,
|
||||||
|
CompareController,
|
||||||
|
ContentController,
|
||||||
|
TreeController,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AppService,
|
AppService,
|
||||||
CompareService,
|
CompareService,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Controller, Get, HttpException, Param } from "@nestjs/common";
|
||||||
import { ApiNotFoundResponse, ApiOkResponse, ApiOperation } from "@nestjs/swagger";
|
import { ApiNotFoundResponse, ApiOkResponse, ApiOperation } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { ApiHasPassThruAuth, Auth, RepoAuth } from "../../core";
|
import { ApiHasPassThruAuth, Auth, RepoAuth } from "../../core";
|
||||||
import { GitDiff } from "../../dtos/git-diff";
|
import { GitDiff } from "../../dtos";
|
||||||
import { CompareService } from "../../services";
|
import { CompareService } from "../../services";
|
||||||
|
|
||||||
@Controller("/repos/:remote/compare")
|
@Controller("/repos/:remote/compare")
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/contents' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir1",
|
||||||
|
"path": "dir1",
|
||||||
|
"sha": "b638a8a4a9f44184a3a430988a9c5ef383bad364",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"content": "IyBMb2dzCmxvZ3MKKi5sb2cKbnBtLWRlYnVnLmxvZyoKeWFybi1kZWJ1Zy5sb2cqCnlhcm4tZXJyb3IubG9nKgoKIyBSdW50aW1lIGRhdGEKcGlkcwoqLnBpZAoqLnNlZWQKKi5waWQubG9jawoKIyBEaXJlY3RvcnkgZm9yIGluc3RydW1lbnRlZCBsaWJzIGdlbmVyYXRlZCBieSBqc2NvdmVyYWdlL0pTQ292ZXIKbGliLWNvdgoKIyBDb3ZlcmFnZSBkaXJlY3RvcnkgdXNlZCBieSB0b29scyBsaWtlIGlzdGFuYnVsCmNvdmVyYWdlCgojIG55YyB0ZXN0IGNvdmVyYWdlCi5ueWNfb3V0cHV0CgojIEdydW50IGludGVybWVkaWF0ZSBzdG9yYWdlIChodHRwOi8vZ3J1bnRqcy5jb20vY3JlYXRpbmctcGx1Z2lucyNzdG9yaW5nLXRhc2stZmlsZXMpCi5ncnVudAoKIyBCb3dlciBkZXBlbmRlbmN5IGRpcmVjdG9yeSAoaHR0cHM6Ly9ib3dlci5pby8pCmJvd2VyX2NvbXBvbmVudHMKCiMgbm9kZS13YWYgY29uZmlndXJhdGlvbgoubG9jay13c2NyaXB0CgojIENvbXBpbGVkIGJpbmFyeSBhZGRvbnMgKGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvYWRkb25zLmh0bWwpCmJ1aWxkL1JlbGVhc2UKCiMgRGVwZW5kZW5jeSBkaXJlY3Rvcmllcwpub2RlX21vZHVsZXMvCmpzcG1fcGFja2FnZXMvCgojIFR5cGVTY3JpcHQgdjEgZGVjbGFyYXRpb24gZmlsZXMKdHlwaW5ncy8KCiMgT3B0aW9uYWwgbnBtIGNhY2hlIGRpcmVjdG9yeQoubnBtCgojIE9wdGlvbmFsIGVzbGludCBjYWNoZQouZXNsaW50Y2FjaGUKCiMgT3B0aW9uYWwgUkVQTCBoaXN0b3J5Ci5ub2RlX3JlcGxfaGlzdG9yeQoKIyBPdXRwdXQgb2YgJ25wbSBwYWNrJwoqLnRnegoKIyBZYXJuIEludGVncml0eSBmaWxlCi55YXJuLWludGVncml0eQoKIyBkb3RlbnYgZW52aXJvbm1lbnQgdmFyaWFibGVzIGZpbGUKLmVudgoKIyBuZXh0LmpzIGJ1aWxkIG91dHB1dAoubmV4dAo=",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": ".gitignore",
|
||||||
|
"path": ".gitignore",
|
||||||
|
"sha": "ad46b30886fa350c1f59761b100e5e4b01f9a7ec",
|
||||||
|
"size": 914,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgMjAxOSB0ZXN0LXJlcG8tYmlsbHkKClBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkKb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgIlNvZnR3YXJlIiksIHRvIGRlYWwKaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cwp0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsCmNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcwpmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOgoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW4gYWxsCmNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "LICENSE",
|
||||||
|
"path": "LICENSE",
|
||||||
|
"sha": "98023418cdb98210a5f71ea74ec557dbbd8f0e83",
|
||||||
|
"size": 1072,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "IyBnaXQtYXBpLXRlc3RzClJlcG8gdXNlZCBmb3IgaW50ZWdyYXRpb24gdGVzdGluZyBvZiB0aGUgZ2l0LXRlc3QtYXBpIHByb2plY3QK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/contents/' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir1",
|
||||||
|
"path": "dir1",
|
||||||
|
"sha": "b638a8a4a9f44184a3a430988a9c5ef383bad364",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"content": "IyBMb2dzCmxvZ3MKKi5sb2cKbnBtLWRlYnVnLmxvZyoKeWFybi1kZWJ1Zy5sb2cqCnlhcm4tZXJyb3IubG9nKgoKIyBSdW50aW1lIGRhdGEKcGlkcwoqLnBpZAoqLnNlZWQKKi5waWQubG9jawoKIyBEaXJlY3RvcnkgZm9yIGluc3RydW1lbnRlZCBsaWJzIGdlbmVyYXRlZCBieSBqc2NvdmVyYWdlL0pTQ292ZXIKbGliLWNvdgoKIyBDb3ZlcmFnZSBkaXJlY3RvcnkgdXNlZCBieSB0b29scyBsaWtlIGlzdGFuYnVsCmNvdmVyYWdlCgojIG55YyB0ZXN0IGNvdmVyYWdlCi5ueWNfb3V0cHV0CgojIEdydW50IGludGVybWVkaWF0ZSBzdG9yYWdlIChodHRwOi8vZ3J1bnRqcy5jb20vY3JlYXRpbmctcGx1Z2lucyNzdG9yaW5nLXRhc2stZmlsZXMpCi5ncnVudAoKIyBCb3dlciBkZXBlbmRlbmN5IGRpcmVjdG9yeSAoaHR0cHM6Ly9ib3dlci5pby8pCmJvd2VyX2NvbXBvbmVudHMKCiMgbm9kZS13YWYgY29uZmlndXJhdGlvbgoubG9jay13c2NyaXB0CgojIENvbXBpbGVkIGJpbmFyeSBhZGRvbnMgKGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvYWRkb25zLmh0bWwpCmJ1aWxkL1JlbGVhc2UKCiMgRGVwZW5kZW5jeSBkaXJlY3Rvcmllcwpub2RlX21vZHVsZXMvCmpzcG1fcGFja2FnZXMvCgojIFR5cGVTY3JpcHQgdjEgZGVjbGFyYXRpb24gZmlsZXMKdHlwaW5ncy8KCiMgT3B0aW9uYWwgbnBtIGNhY2hlIGRpcmVjdG9yeQoubnBtCgojIE9wdGlvbmFsIGVzbGludCBjYWNoZQouZXNsaW50Y2FjaGUKCiMgT3B0aW9uYWwgUkVQTCBoaXN0b3J5Ci5ub2RlX3JlcGxfaGlzdG9yeQoKIyBPdXRwdXQgb2YgJ25wbSBwYWNrJwoqLnRnegoKIyBZYXJuIEludGVncml0eSBmaWxlCi55YXJuLWludGVncml0eQoKIyBkb3RlbnYgZW52aXJvbm1lbnQgdmFyaWFibGVzIGZpbGUKLmVudgoKIyBuZXh0LmpzIGJ1aWxkIG91dHB1dAoubmV4dAo=",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": ".gitignore",
|
||||||
|
"path": ".gitignore",
|
||||||
|
"sha": "ad46b30886fa350c1f59761b100e5e4b01f9a7ec",
|
||||||
|
"size": 914,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgMjAxOSB0ZXN0LXJlcG8tYmlsbHkKClBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkKb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgIlNvZnR3YXJlIiksIHRvIGRlYWwKaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cwp0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsCmNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcwpmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOgoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW4gYWxsCmNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "LICENSE",
|
||||||
|
"path": "LICENSE",
|
||||||
|
"sha": "98023418cdb98210a5f71ea74ec557dbbd8f0e83",
|
||||||
|
"size": 1072,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "IyBnaXQtYXBpLXRlc3RzClJlcG8gdXNlZCBmb3IgaW50ZWdyYXRpb24gdGVzdGluZyBvZiB0aGUgZ2l0LXRlc3QtYXBpIHByb2plY3QK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/contents/README.md' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"content": "IyBnaXQtYXBpLXRlc3RzClJlcG8gdXNlZCBmb3IgaW50ZWdyYXRpb24gdGVzdGluZyBvZiB0aGUgZ2l0LXRlc3QtYXBpIHByb2plY3QK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/contents/dir1' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir2",
|
||||||
|
"path": "dir1/dir2",
|
||||||
|
"sha": "483221c9d8371862bdb2c5d452130ab5ca0534a3",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"content": "ZmlsZUEK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileA.txt",
|
||||||
|
"path": "dir1/fileA.txt",
|
||||||
|
"sha": "ab47708c98ac88bbdf3ca75f4730d86a84f702a2",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/contents/dir1?recursive=true' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir2",
|
||||||
|
"path": "dir1/dir2",
|
||||||
|
"sha": "483221c9d8371862bdb2c5d452130ab5ca0534a3",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"content": "ZmlsZUEK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileA.txt",
|
||||||
|
"path": "dir1/fileA.txt",
|
||||||
|
"sha": "ab47708c98ac88bbdf3ca75f4730d86a84f702a2",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "ZmlsZUIK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileB.txt",
|
||||||
|
"path": "dir1/dir2/fileB.txt",
|
||||||
|
"sha": "78ed112c991c8abeba325c039a398ba626c425ab",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/contents?recursive=true' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir1",
|
||||||
|
"path": "dir1",
|
||||||
|
"sha": "b638a8a4a9f44184a3a430988a9c5ef383bad364",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "dir2",
|
||||||
|
"path": "dir1/dir2",
|
||||||
|
"sha": "483221c9d8371862bdb2c5d452130ab5ca0534a3",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"content": "IyBMb2dzCmxvZ3MKKi5sb2cKbnBtLWRlYnVnLmxvZyoKeWFybi1kZWJ1Zy5sb2cqCnlhcm4tZXJyb3IubG9nKgoKIyBSdW50aW1lIGRhdGEKcGlkcwoqLnBpZAoqLnNlZWQKKi5waWQubG9jawoKIyBEaXJlY3RvcnkgZm9yIGluc3RydW1lbnRlZCBsaWJzIGdlbmVyYXRlZCBieSBqc2NvdmVyYWdlL0pTQ292ZXIKbGliLWNvdgoKIyBDb3ZlcmFnZSBkaXJlY3RvcnkgdXNlZCBieSB0b29scyBsaWtlIGlzdGFuYnVsCmNvdmVyYWdlCgojIG55YyB0ZXN0IGNvdmVyYWdlCi5ueWNfb3V0cHV0CgojIEdydW50IGludGVybWVkaWF0ZSBzdG9yYWdlIChodHRwOi8vZ3J1bnRqcy5jb20vY3JlYXRpbmctcGx1Z2lucyNzdG9yaW5nLXRhc2stZmlsZXMpCi5ncnVudAoKIyBCb3dlciBkZXBlbmRlbmN5IGRpcmVjdG9yeSAoaHR0cHM6Ly9ib3dlci5pby8pCmJvd2VyX2NvbXBvbmVudHMKCiMgbm9kZS13YWYgY29uZmlndXJhdGlvbgoubG9jay13c2NyaXB0CgojIENvbXBpbGVkIGJpbmFyeSBhZGRvbnMgKGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvYWRkb25zLmh0bWwpCmJ1aWxkL1JlbGVhc2UKCiMgRGVwZW5kZW5jeSBkaXJlY3Rvcmllcwpub2RlX21vZHVsZXMvCmpzcG1fcGFja2FnZXMvCgojIFR5cGVTY3JpcHQgdjEgZGVjbGFyYXRpb24gZmlsZXMKdHlwaW5ncy8KCiMgT3B0aW9uYWwgbnBtIGNhY2hlIGRpcmVjdG9yeQoubnBtCgojIE9wdGlvbmFsIGVzbGludCBjYWNoZQouZXNsaW50Y2FjaGUKCiMgT3B0aW9uYWwgUkVQTCBoaXN0b3J5Ci5ub2RlX3JlcGxfaGlzdG9yeQoKIyBPdXRwdXQgb2YgJ25wbSBwYWNrJwoqLnRnegoKIyBZYXJuIEludGVncml0eSBmaWxlCi55YXJuLWludGVncml0eQoKIyBkb3RlbnYgZW52aXJvbm1lbnQgdmFyaWFibGVzIGZpbGUKLmVudgoKIyBuZXh0LmpzIGJ1aWxkIG91dHB1dAoubmV4dAo=",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": ".gitignore",
|
||||||
|
"path": ".gitignore",
|
||||||
|
"sha": "ad46b30886fa350c1f59761b100e5e4b01f9a7ec",
|
||||||
|
"size": 914,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgMjAxOSB0ZXN0LXJlcG8tYmlsbHkKClBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkKb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgIlNvZnR3YXJlIiksIHRvIGRlYWwKaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cwp0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsCmNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcwpmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOgoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW4gYWxsCmNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "LICENSE",
|
||||||
|
"path": "LICENSE",
|
||||||
|
"sha": "98023418cdb98210a5f71ea74ec557dbbd8f0e83",
|
||||||
|
"size": 1072,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "IyBnaXQtYXBpLXRlc3RzClJlcG8gdXNlZCBmb3IgaW50ZWdyYXRpb24gdGVzdGluZyBvZiB0aGUgZ2l0LXRlc3QtYXBpIHByb2plY3QK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "ZmlsZUEK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileA.txt",
|
||||||
|
"path": "dir1/fileA.txt",
|
||||||
|
"sha": "ab47708c98ac88bbdf3ca75f4730d86a84f702a2",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"content": "ZmlsZUIK",
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileB.txt",
|
||||||
|
"path": "dir1/dir2/fileB.txt",
|
||||||
|
"sha": "78ed112c991c8abeba325c039a398ba626c425ab",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { TEST_REPO, e2eClient } from "../../../test/e2e";
|
||||||
|
|
||||||
|
describe("Test content controller", () => {
|
||||||
|
const base = `/repos/${TEST_REPO}/contents`;
|
||||||
|
test.each(["", "/", "/README.md", "/dir1", "/dir1?recursive=true", "?recursive=true"])(
|
||||||
|
`for path '${base}%s'`,
|
||||||
|
async tail => {
|
||||||
|
const response = await e2eClient.fetch(`${base}${tail}`);
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body).toMatchSnapshot();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,27 +1,40 @@
|
||||||
import { Controller, Get, HttpException, Param, Query } from "@nestjs/common";
|
import { Controller, Get, HttpException, Param, Query } from "@nestjs/common";
|
||||||
import { ApiImplicitQuery, ApiNotFoundResponse, ApiOkResponse, ApiOperation } from "@nestjs/swagger";
|
import { ApiImplicitParam, ApiImplicitQuery, ApiNotFoundResponse, ApiOkResponse, ApiOperation } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { ApiHasPassThruAuth, Auth, RepoAuth } from "../../core";
|
import { ApiHasPassThruAuth, Auth, RepoAuth } from "../../core";
|
||||||
import { GitContents } from "../../dtos/git-contents";
|
import { GitContents } from "../../dtos";
|
||||||
import { ContentService } from "../../services/content";
|
import { ContentService } from "../../services/content";
|
||||||
|
import { parseBooleanFromURLParam } from "../../utils";
|
||||||
|
|
||||||
@Controller("/repos/:remote/contents")
|
@Controller("/repos/:remote/contents")
|
||||||
export class ContentController {
|
export class ContentController {
|
||||||
constructor(private contentService: ContentService) {}
|
constructor(private contentService: ContentService) {}
|
||||||
|
|
||||||
@Get([":path([^/]*)", "*"])
|
@Get([":path([^/]*)", ""])
|
||||||
@ApiHasPassThruAuth()
|
@ApiHasPassThruAuth()
|
||||||
@ApiOkResponse({ type: GitContents })
|
@ApiOkResponse({ type: GitContents })
|
||||||
@ApiImplicitQuery({ name: "ref", required: false, type: "string" })
|
@ApiImplicitQuery({ name: "ref", required: false, type: "string" })
|
||||||
|
@ApiImplicitQuery({ name: "recursive", required: false, type: "string" })
|
||||||
|
@ApiImplicitParam({ name: "path", type: "string" })
|
||||||
@ApiOperation({ title: "Get content", operationId: "contents_get" })
|
@ApiOperation({ title: "Get content", operationId: "contents_get" })
|
||||||
@ApiNotFoundResponse({})
|
@ApiNotFoundResponse({})
|
||||||
public async getContents(
|
public async getContents(
|
||||||
@Param("remote") remote: string,
|
@Param("remote") remote: string,
|
||||||
@Param("path") path: string | undefined,
|
@Param("path") path: string | undefined,
|
||||||
@Query("ref") ref: string | undefined,
|
@Query("ref") ref: string | undefined,
|
||||||
|
@Query("recursive") recursive: string | undefined,
|
||||||
@Auth() auth: RepoAuth,
|
@Auth() auth: RepoAuth,
|
||||||
) {
|
) {
|
||||||
const content = await this.contentService.getContents(remote, path, ref, { auth });
|
const content = await this.contentService.getContents(
|
||||||
|
remote,
|
||||||
|
path,
|
||||||
|
ref,
|
||||||
|
parseBooleanFromURLParam(recursive),
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
auth,
|
||||||
|
},
|
||||||
|
);
|
||||||
if (content instanceof HttpException) {
|
if (content instanceof HttpException) {
|
||||||
throw content;
|
throw content;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/tree' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir1",
|
||||||
|
"path": "dir1",
|
||||||
|
"sha": "b638a8a4a9f44184a3a430988a9c5ef383bad364",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "dir2",
|
||||||
|
"path": "dir1/dir2",
|
||||||
|
"sha": "483221c9d8371862bdb2c5d452130ab5ca0534a3",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": ".gitignore",
|
||||||
|
"path": ".gitignore",
|
||||||
|
"sha": "ad46b30886fa350c1f59761b100e5e4b01f9a7ec",
|
||||||
|
"size": 914,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "LICENSE",
|
||||||
|
"path": "LICENSE",
|
||||||
|
"sha": "98023418cdb98210a5f71ea74ec557dbbd8f0e83",
|
||||||
|
"size": 1072,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileA.txt",
|
||||||
|
"path": "dir1/fileA.txt",
|
||||||
|
"sha": "ab47708c98ac88bbdf3ca75f4730d86a84f702a2",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileB.txt",
|
||||||
|
"path": "dir1/dir2/fileB.txt",
|
||||||
|
"sha": "78ed112c991c8abeba325c039a398ba626c425ab",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/tree/' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir1",
|
||||||
|
"path": "dir1",
|
||||||
|
"sha": "b638a8a4a9f44184a3a430988a9c5ef383bad364",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"name": "dir2",
|
||||||
|
"path": "dir1/dir2",
|
||||||
|
"sha": "483221c9d8371862bdb2c5d452130ab5ca0534a3",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": ".gitignore",
|
||||||
|
"path": ".gitignore",
|
||||||
|
"sha": "ad46b30886fa350c1f59761b100e5e4b01f9a7ec",
|
||||||
|
"size": 914,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "LICENSE",
|
||||||
|
"path": "LICENSE",
|
||||||
|
"sha": "98023418cdb98210a5f71ea74ec557dbbd8f0e83",
|
||||||
|
"size": 1072,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileA.txt",
|
||||||
|
"path": "dir1/fileA.txt",
|
||||||
|
"sha": "ab47708c98ac88bbdf3ca75f4730d86a84f702a2",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileB.txt",
|
||||||
|
"path": "dir1/dir2/fileB.txt",
|
||||||
|
"sha": "78ed112c991c8abeba325c039a398ba626c425ab",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/tree/README.md' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "README.md",
|
||||||
|
"path": "README.md",
|
||||||
|
"sha": "b5fd37e731f1e7931da42484ae0290554cb42c0f",
|
||||||
|
"size": 78,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Test content controller for path '/repos/github.com%2Ftest-repo-billy%2Fgit-api-tests/tree/dir1' 1`] = `
|
||||||
|
Object {
|
||||||
|
"dirs": Array [
|
||||||
|
Object {
|
||||||
|
"name": "dir2",
|
||||||
|
"path": "dir1/dir2",
|
||||||
|
"sha": "483221c9d8371862bdb2c5d452130ab5ca0534a3",
|
||||||
|
"size": 0,
|
||||||
|
"type": "dir",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"files": Array [
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileA.txt",
|
||||||
|
"path": "dir1/fileA.txt",
|
||||||
|
"sha": "ab47708c98ac88bbdf3ca75f4730d86a84f702a2",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"encoding": "base64",
|
||||||
|
"name": "fileB.txt",
|
||||||
|
"path": "dir1/dir2/fileB.txt",
|
||||||
|
"sha": "78ed112c991c8abeba325c039a398ba626c425ab",
|
||||||
|
"size": 6,
|
||||||
|
"type": "file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"submodules": Array [],
|
||||||
|
}
|
||||||
|
`;
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TEST_REPO, e2eClient } from "../../../test/e2e";
|
||||||
|
|
||||||
|
describe("Test content controller", () => {
|
||||||
|
const base = `/repos/${TEST_REPO}/tree`;
|
||||||
|
test.each(["", "/", "/README.md", "/dir1"])(`for path '${base}%s'`, async tail => {
|
||||||
|
const response = await e2eClient.fetch(`${base}${tail}`);
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Controller, Get, HttpException, Param, Query } from "@nestjs/common";
|
||||||
|
import { ApiImplicitParam, ApiImplicitQuery, ApiNotFoundResponse, ApiOkResponse, ApiOperation } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
import { ApiHasPassThruAuth, Auth, RepoAuth } from "../../core";
|
||||||
|
import { GitTree } from "../../dtos";
|
||||||
|
import { ContentService } from "../../services/content";
|
||||||
|
|
||||||
|
@Controller("/repos/:remote/tree")
|
||||||
|
export class TreeController {
|
||||||
|
constructor(private contentService: ContentService) {}
|
||||||
|
|
||||||
|
@Get([":path([^/]*)", ""])
|
||||||
|
@ApiHasPassThruAuth()
|
||||||
|
@ApiOkResponse({ type: GitTree })
|
||||||
|
@ApiImplicitQuery({ name: "ref", required: false, type: "string" })
|
||||||
|
@ApiOperation({ title: "Get tree", operationId: "tree_get" })
|
||||||
|
@ApiImplicitParam({ name: "path", type: "string" })
|
||||||
|
@ApiNotFoundResponse({})
|
||||||
|
public async getTree(
|
||||||
|
@Param("remote") remote: string,
|
||||||
|
@Param("path") path: string | undefined,
|
||||||
|
@Query("ref") ref: string | undefined,
|
||||||
|
@Auth() auth: RepoAuth,
|
||||||
|
) {
|
||||||
|
const tree = await this.contentService.getContents(remote, path, ref, true, false, { auth });
|
||||||
|
if (tree instanceof HttpException) {
|
||||||
|
throw tree;
|
||||||
|
}
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
import { ApiModelProperty } from "@nestjs/swagger";
|
import { ApiModelProperty } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { GitDirObjectContent } from "./git-dir-object-content";
|
import { GitDirObjectContent } from "./git-dir-object-content";
|
||||||
import { GitFileObjectContent } from "./git-file-object-content";
|
import { GitFileObjectWithContent } from "./git-file-object-with-content";
|
||||||
import { GitSubmoduleObjectContent } from "./git-submodule-object-content";
|
import { GitSubmoduleObjectContent } from "./git-submodule-object-content";
|
||||||
|
|
||||||
export class GitContents {
|
export class GitTree {
|
||||||
@ApiModelProperty({ type: GitDirObjectContent, isArray: true })
|
@ApiModelProperty({ type: GitDirObjectContent, isArray: true })
|
||||||
public dirs: GitDirObjectContent[];
|
public dirs: GitDirObjectContent[];
|
||||||
@ApiModelProperty({ type: GitFileObjectContent, isArray: true })
|
@ApiModelProperty({ type: GitFileObjectWithContent, isArray: true })
|
||||||
public files: GitFileObjectContent[];
|
public files: GitFileObjectWithContent[];
|
||||||
@ApiModelProperty({ type: GitSubmoduleObjectContent, isArray: true })
|
@ApiModelProperty({ type: GitSubmoduleObjectContent, isArray: true })
|
||||||
public submodules: GitSubmoduleObjectContent[];
|
public submodules: GitSubmoduleObjectContent[];
|
||||||
|
|
||||||
constructor(gitObjectContent: GitContents) {
|
constructor(gitObjectContent: GitTree) {
|
||||||
this.dirs = gitObjectContent.dirs;
|
this.dirs = gitObjectContent.dirs;
|
||||||
this.files = gitObjectContent.files;
|
this.files = gitObjectContent.files;
|
||||||
this.submodules = gitObjectContent.submodules;
|
this.submodules = gitObjectContent.submodules;
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { ApiModelProperty } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
import { GitFileObjectWithoutContent } from "./git-file-object-without-content";
|
||||||
|
|
||||||
|
export class GitFileObjectWithContent extends GitFileObjectWithoutContent {
|
||||||
|
@ApiModelProperty({ type: String })
|
||||||
|
public content: string;
|
||||||
|
|
||||||
|
constructor(gitObjectContent: GitFileObjectWithContent) {
|
||||||
|
super(gitObjectContent);
|
||||||
|
this.content = gitObjectContent.content;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,12 @@ import { ApiModelProperty } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { GitObjectContent } from "./git-object-content";
|
import { GitObjectContent } from "./git-object-content";
|
||||||
|
|
||||||
export class GitFileObjectContent extends GitObjectContent {
|
export class GitFileObjectWithoutContent extends GitObjectContent {
|
||||||
@ApiModelProperty({ type: String })
|
|
||||||
public content: string;
|
|
||||||
@ApiModelProperty({ type: String })
|
@ApiModelProperty({ type: String })
|
||||||
public encoding: string;
|
public encoding: string;
|
||||||
|
|
||||||
constructor(gitObjectContent: GitFileObjectContent) {
|
constructor(gitObjectContent: GitFileObjectWithoutContent) {
|
||||||
super(gitObjectContent);
|
super(gitObjectContent);
|
||||||
this.content = gitObjectContent.content;
|
|
||||||
this.encoding = gitObjectContent.encoding;
|
this.encoding = gitObjectContent.encoding;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { ApiModelProperty } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
import { GitDirObjectContent } from "./git-dir-object-content";
|
||||||
|
import { GitFileObjectWithoutContent } from "./git-file-object-without-content";
|
||||||
|
import { GitSubmoduleObjectContent } from "./git-submodule-object-content";
|
||||||
|
|
||||||
|
export class GitContents {
|
||||||
|
@ApiModelProperty({ type: GitDirObjectContent, isArray: true })
|
||||||
|
public dirs: GitDirObjectContent[];
|
||||||
|
@ApiModelProperty({ type: GitFileObjectWithoutContent, isArray: true })
|
||||||
|
public files: GitFileObjectWithoutContent[];
|
||||||
|
@ApiModelProperty({ type: GitSubmoduleObjectContent, isArray: true })
|
||||||
|
public submodules: GitSubmoduleObjectContent[];
|
||||||
|
|
||||||
|
constructor(gitObjectContent: GitContents) {
|
||||||
|
this.dirs = gitObjectContent.dirs;
|
||||||
|
this.files = gitObjectContent.files;
|
||||||
|
this.submodules = gitObjectContent.submodules;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,13 @@
|
||||||
export * from "./git-branch";
|
export * from "./git-branch";
|
||||||
export * from "./git-commit";
|
|
||||||
export * from "./git-commit-ref";
|
export * from "./git-commit-ref";
|
||||||
export * from "./git-signature";
|
export * from "./git-commit";
|
||||||
|
export * from "./git-contents";
|
||||||
|
export * from "./git-diff";
|
||||||
|
export * from "./git-dir-object-content";
|
||||||
export * from "./git-file-diff";
|
export * from "./git-file-diff";
|
||||||
|
export * from "./git-file-object-with-content";
|
||||||
|
export * from "./git-file-object-without-content";
|
||||||
|
export * from "./git-object-content";
|
||||||
|
export * from "./git-signature";
|
||||||
|
export * from "./git-submodule-object-content";
|
||||||
|
export * from "./git-tree";
|
||||||
|
|
|
@ -2,8 +2,7 @@ import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Commit, Oid, Repository, Revwalk, Signature, Time } from "nodegit";
|
import { Commit, Oid, Repository, Revwalk, Signature, Time } from "nodegit";
|
||||||
|
|
||||||
import { PaginatedList, Pagination, getPage, getPaginationSkip } from "../../core";
|
import { PaginatedList, Pagination, getPage, getPaginationSkip } from "../../core";
|
||||||
import { GitCommit, GitCommitRef } from "../../dtos";
|
import { GitCommit, GitCommitRef, GitSignature } from "../../dtos";
|
||||||
import { GitSignature } from "../../dtos/git-signature";
|
|
||||||
import { GitBaseOptions, RepoService } from "../repo";
|
import { GitBaseOptions, RepoService } from "../repo";
|
||||||
|
|
||||||
const LIST_COMMIT_PAGE_SIZE = 100;
|
const LIST_COMMIT_PAGE_SIZE = 100;
|
||||||
|
|
|
@ -2,8 +2,7 @@ import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Commit, ConvenientPatch, Diff, Merge, Oid, Repository } from "nodegit";
|
import { Commit, ConvenientPatch, Diff, Merge, Oid, Repository } from "nodegit";
|
||||||
|
|
||||||
import { Logger } from "../../core";
|
import { Logger } from "../../core";
|
||||||
import { GitFileDiff, PatchStatus } from "../../dtos";
|
import { GitDiff, GitFileDiff, PatchStatus } from "../../dtos";
|
||||||
import { GitDiff } from "../../dtos/git-diff";
|
|
||||||
import { GitUtils, notUndefined } from "../../utils";
|
import { GitUtils, notUndefined } from "../../utils";
|
||||||
import { CommitService, toGitCommit } from "../commit";
|
import { CommitService, toGitCommit } from "../commit";
|
||||||
import { GitBaseOptions, RepoService } from "../repo";
|
import { GitBaseOptions, RepoService } from "../repo";
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Repository, TreeEntry } from "nodegit";
|
import { Repository, Tree, TreeEntry } from "nodegit";
|
||||||
|
|
||||||
import { GitContents } from "../../dtos/git-contents";
|
import {
|
||||||
import { GitDirObjectContent } from "../../dtos/git-dir-object-content";
|
GitContents,
|
||||||
import { GitFileObjectContent } from "../../dtos/git-file-object-content";
|
GitDirObjectContent,
|
||||||
import { GitSubmoduleObjectContent } from "../../dtos/git-submodule-object-content";
|
GitFileObjectWithContent,
|
||||||
|
GitFileObjectWithoutContent,
|
||||||
|
GitSubmoduleObjectContent,
|
||||||
|
GitTree,
|
||||||
|
} from "../../dtos";
|
||||||
import { CommitService } from "../commit";
|
import { CommitService } from "../commit";
|
||||||
import { GitBaseOptions, RepoService } from "../repo";
|
import { GitBaseOptions, RepoService } from "../repo";
|
||||||
|
|
||||||
|
@ -16,14 +20,22 @@ export class ContentService {
|
||||||
remote: string,
|
remote: string,
|
||||||
path: string | undefined,
|
path: string | undefined,
|
||||||
ref: string | undefined = "master",
|
ref: string | undefined = "master",
|
||||||
|
recursive: boolean = false,
|
||||||
|
includeContents: boolean = true,
|
||||||
options: GitBaseOptions = {},
|
options: GitBaseOptions = {},
|
||||||
): Promise<GitContents | NotFoundException> {
|
): Promise<GitContents | GitTree | NotFoundException> {
|
||||||
return this.repoService.use(remote, options, async repo => {
|
return this.repoService.use(remote, options, async repo => {
|
||||||
return this.getGitContents(repo, path, ref);
|
return this.getGitContents(repo, path, recursive, includeContents, ref);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getGitContents(repo: Repository, path: string | undefined, ref: string | undefined = "master") {
|
public async getGitContents(
|
||||||
|
repo: Repository,
|
||||||
|
path: string | undefined,
|
||||||
|
recursive: boolean,
|
||||||
|
includeContents: boolean,
|
||||||
|
ref: string | undefined = "master",
|
||||||
|
) {
|
||||||
const commit = await this.commitService.getCommit(repo, ref);
|
const commit = await this.commitService.getCommit(repo, ref);
|
||||||
if (!commit) {
|
if (!commit) {
|
||||||
return new NotFoundException(`Ref '${ref}' not found.`);
|
return new NotFoundException(`Ref '${ref}' not found.`);
|
||||||
|
@ -33,30 +45,56 @@ export class ContentService {
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
try {
|
try {
|
||||||
entries = [await commit.getEntry(path)];
|
const pathEntry = await commit.getEntry(path);
|
||||||
|
|
||||||
|
if (pathEntry.isTree()) {
|
||||||
|
const tree = await pathEntry.getTree();
|
||||||
|
|
||||||
|
// for directories, either
|
||||||
|
if (recursive) {
|
||||||
|
// recursively get children
|
||||||
|
entries = await this.getAllChildEntries(tree);
|
||||||
|
} else {
|
||||||
|
// get children immediate children
|
||||||
|
entries = tree.entries();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for files get array of size 1
|
||||||
|
entries = [await commit.getEntry(path)];
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new NotFoundException(`${path} not found.`);
|
return new NotFoundException(`Path '${path}' not found.`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const tree = await commit.getTree();
|
const tree = await commit.getTree();
|
||||||
entries = await tree.entries();
|
entries = recursive ? await this.getAllChildEntries(tree) : tree.entries();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getEntries(entries);
|
return this.getEntries(entries, includeContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getFileEntryAsObject(entry: TreeEntry): Promise<GitFileObjectContent> {
|
private async getFileEntryAsObject(
|
||||||
|
entry: TreeEntry,
|
||||||
|
includeContents: boolean,
|
||||||
|
): Promise<GitFileObjectWithContent | GitFileObjectWithoutContent> {
|
||||||
const blob = await entry.getBlob();
|
const blob = await entry.getBlob();
|
||||||
|
const file = {
|
||||||
return new GitFileObjectContent({
|
|
||||||
type: "file",
|
type: "file",
|
||||||
encoding: "base64",
|
encoding: "base64",
|
||||||
size: blob.rawsize(),
|
size: blob.rawsize(),
|
||||||
name: entry.name(),
|
name: entry.name(),
|
||||||
path: entry.path(),
|
path: entry.path(),
|
||||||
content: blob.content().toString("base64"),
|
|
||||||
sha: entry.sha(),
|
sha: entry.sha(),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (includeContents) {
|
||||||
|
return new GitFileObjectWithContent({
|
||||||
|
...file,
|
||||||
|
content: blob.content().toString("base64"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GitFileObjectWithoutContent(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getDirEntryAsObject(entry: TreeEntry): Promise<GitDirObjectContent> {
|
private async getDirEntryAsObject(entry: TreeEntry): Promise<GitDirObjectContent> {
|
||||||
|
@ -78,15 +116,37 @@ export class ContentService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getEntries(entries: TreeEntry[]): Promise<GitContents> {
|
private async getEntries(entries: TreeEntry[], includeContents: boolean): Promise<GitContents | GitTree> {
|
||||||
const [files, dirs, submodules] = await Promise.all([
|
const [files, dirs, submodules] = await Promise.all([
|
||||||
Promise.all(entries.filter(entry => entry.isFile()).map(async entry => this.getFileEntryAsObject(entry))),
|
Promise.all(
|
||||||
|
entries.filter(entry => entry.isFile()).map(async entry => this.getFileEntryAsObject(entry, includeContents)),
|
||||||
|
),
|
||||||
Promise.all(entries.filter(entry => entry.isDirectory()).map(async entry => this.getDirEntryAsObject(entry))),
|
Promise.all(entries.filter(entry => entry.isDirectory()).map(async entry => this.getDirEntryAsObject(entry))),
|
||||||
Promise.all(
|
Promise.all(
|
||||||
entries.filter(entry => entry.isSubmodule()).map(async entry => this.getSubmoduleEntryAsObject(entry)),
|
entries.filter(entry => entry.isSubmodule()).map(async entry => this.getSubmoduleEntryAsObject(entry)),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return new GitContents({ files, dirs, submodules });
|
if (includeContents) {
|
||||||
|
return new GitContents({ files, dirs, submodules });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GitTree({ files: files as GitFileObjectWithContent[], dirs, submodules });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getAllChildEntries(tree: Tree): Promise<TreeEntry[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const eventEmitter = tree.walk(false);
|
||||||
|
|
||||||
|
eventEmitter.on("end", (trees: TreeEntry[]) => {
|
||||||
|
resolve(trees);
|
||||||
|
});
|
||||||
|
|
||||||
|
eventEmitter.on("error", error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
eventEmitter.start();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export const notUndefined = <T>(x: T | undefined): x is T => x !== undefined;
|
export const notUndefined = <T>(x: T | undefined): x is T => x !== undefined;
|
||||||
export const delay = (timeout?: number) => new Promise(r => setTimeout(r, timeout));
|
export const delay = (timeout?: number) => new Promise(r => setTimeout(r, timeout));
|
||||||
|
export const parseBooleanFromURLParam = (bool: string | undefined) => bool === "" || bool === "true";
|
||||||
|
|
||||||
export class Deferred<T = void> {
|
export class Deferred<T = void> {
|
||||||
public promise: Promise<T>;
|
public promise: Promise<T>;
|
||||||
|
|
|
@ -271,6 +271,18 @@
|
||||||
"required": true,
|
"required": true,
|
||||||
"in": "path"
|
"in": "path"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"required": true,
|
||||||
|
"in": "path",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "recursive",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "ref",
|
"name": "ref",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@ -311,6 +323,64 @@
|
||||||
"application/json"
|
"application/json"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/repos/{remote}/tree/{path}": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Get tree",
|
||||||
|
"operationId": "tree_get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "remote",
|
||||||
|
"required": true,
|
||||||
|
"in": "path"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"required": true,
|
||||||
|
"in": "path",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ref",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-authorization",
|
||||||
|
"required": false,
|
||||||
|
"in": "header",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x-github-token",
|
||||||
|
"required": false,
|
||||||
|
"in": "header",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/GitTree"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "When the x-authorization header is malformed"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
@ -498,7 +568,7 @@
|
||||||
"sha"
|
"sha"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"GitFileObjectContent": {
|
"GitFileObjectWithoutContent": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
|
@ -516,9 +586,6 @@
|
||||||
"sha": {
|
"sha": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"content": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
@ -529,7 +596,6 @@
|
||||||
"name",
|
"name",
|
||||||
"path",
|
"path",
|
||||||
"sha",
|
"sha",
|
||||||
"content",
|
|
||||||
"encoding"
|
"encoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -572,7 +638,70 @@
|
||||||
"files": {
|
"files": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/GitFileObjectContent"
|
"$ref": "#/definitions/GitFileObjectWithoutContent"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submodules": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/GitSubmoduleObjectContent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"dirs",
|
||||||
|
"files",
|
||||||
|
"submodules"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"GitFileObjectWithContent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sha": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"size",
|
||||||
|
"name",
|
||||||
|
"path",
|
||||||
|
"sha",
|
||||||
|
"encoding",
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"GitTree": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"dirs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/GitDirObjectContent"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/GitFileObjectWithContent"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"submodules": {
|
"submodules": {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче