UNPKG

4.45 kBJavaScriptView Raw
1/*
2 * Copyright 2018 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13'use strict';
14
15const { join: joinPaths } = require('path');
16const { debug } = require('@adobe/helix-log');
17const { resolveTree, getObject } = require('./git');
18const { resolveRepositoryPath } = require('./utils');
19
20/**
21 * Export the api handler (express middleware) through a parameterizable function
22 *
23 * @param {object} options configuration hash
24 * @returns {function(*, *, *)} handler function
25 */
26function createMiddleware(options) {
27 /**
28 * Express middleware handling Git API Blob requests
29 *
30 * Only a small subset will be implemented
31 *
32 * @param {Request} req Request object
33 * @param {Response} res Response object
34 * @param {callback} next next middleware in chain
35 *
36 * @see https://developer.github.com/v3/git/trees/#get-a-tree-recursively
37 */
38 return (req, res, next) => {
39 // GET /repos/:owner/:repo/git/trees/:ref_or_sha?recursive
40 const { owner } = req.params;
41 const repoName = req.params.repo;
42 const refOrSha = req.params.ref_or_sha;
43 const recursive = typeof req.query.recursive !== 'undefined' && req.query.recursive !== '';
44 const host = req.mappedSubDomain ? `localhost:${options.listen[req.protocol].port}` : req.headers.host;
45
46 const repPath = resolveRepositoryPath(options, owner, repoName);
47
48 async function dirEntryToJson({
49 oid: sha, type, path, mode,
50 }) {
51 return {
52 path,
53 mode,
54 type,
55 sha,
56 url: `${req.protocol}://${host}/api/repos/${owner}/${repoName}/git/trees/${sha}`,
57 };
58 }
59
60 async function fileEntryToJson({
61 oid: sha, type, path, mode,
62 }) {
63 const { object: content } = await getObject(repPath, sha);
64
65 return {
66 path,
67 mode,
68 type,
69 sha,
70 size: content.length,
71 url: `${req.protocol}://${host}/api/repos/${owner}/${repoName}/git/blobs/${sha}`,
72 };
73 }
74
75 async function collectTreeEntries(tree, result, treePath, deep) {
76 const entries = tree.entries.map(({
77 oid, type, mode, path,
78 }) => {
79 return {
80 oid, type, mode, path: joinPaths(treePath, path),
81 };
82 });
83 result.push(...entries);
84 if (deep) {
85 const treeEntries = entries.filter((entry) => entry.type === 'tree');
86 for (let i = 0; i < treeEntries.length; i += 1) {
87 const { oid, path } = treeEntries[i];
88 /* eslint-disable no-await-in-loop */
89 const { object: subTree } = await getObject(repPath, oid);
90 await collectTreeEntries(subTree, result, path, deep);
91 }
92 }
93 return result;
94 }
95
96 async function treeEntriesToJson(tree, deep) {
97 const result = [];
98 await collectTreeEntries(tree, result, '', deep);
99 return Promise.all(result.map(async (entry) => {
100 /* eslint arrow-body-style: "off" */
101 return entry.type === 'blob'
102 ? fileEntryToJson(entry) : dirEntryToJson(entry);
103 }));
104 }
105
106 resolveTree(repPath, refOrSha)
107 .then(async ({ oid: sha, object: tree }) => {
108 res.json({
109 sha,
110 url: `${req.protocol}://${host}/api/repos/${owner}/${repoName}/git/trees/${sha}`,
111 tree: await treeEntriesToJson(tree, recursive),
112 truncated: false,
113 });
114 })
115 .catch((err) => {
116 // TODO: use generic errors
117 if (err.code === 'ReadObjectFail' || err.code === 'ResolveRefError') {
118 debug(`[treeHandler] resource not found: ${err.message}`);
119 res.status(404).json({
120 message: 'Not Found',
121 documentation_url: 'https://developer.github.com/v3/git/trees/#get-a-tree',
122 });
123 } else {
124 debug(`[treeHandler] code: ${err.code} message: ${err.message} stack: ${err.stack}`);
125 next(err);
126 }
127 });
128 };
129}
130module.exports = createMiddleware;