http-api-proxy-server
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -1,2 +0,1 @@ | ||
import { IncomingMessage as Req } from "http"; | ||
export type HttpApiProxyServerSettings = { | ||
@@ -16,4 +15,11 @@ find?: string; | ||
}; | ||
type ResponseBody = (Record<string, unknown> & GraphQLCompatibleResponse) | any; | ||
type ResponseBody = Record<string, unknown> & GraphQLCompatibleResponse; | ||
type RequestId = `responseFor${string}`; | ||
export type Request = { | ||
requestId: RequestId; | ||
url?: string; | ||
method?: string; | ||
headers: Record<string, string | string[] | undefined>; | ||
body: string | undefined; | ||
}; | ||
type ProxyResponse = { | ||
@@ -29,12 +35,15 @@ body: ResponseBody; | ||
type ProxyBehavior = "SAVE_RESPONSES_FOR_NEW_QUERIES" | "RELOAD_RESPONSES_WITH_ERRORS" | "NO_REQUEST_FORWARDING" | "FORCE_UPDATE_ALL"; | ||
export declare const generateResponseOverwriteCode: (req: Req, potentialPathsForChanges: MatchPaths[], searchValue: string, filePath: string) => string; | ||
export declare const generateResponseOverwriteCode: (request: Request, potentialPathsForChanges: MatchPaths[], searchValue: string, filePath: string) => string; | ||
export declare const hasError: (resp: ProxyResponse) => boolean; | ||
export declare const printErrors: (response: ProxyResponse, settings: HttpApiProxyServerSettings | undefined, filePath: string) => void; | ||
export declare const requestToId: (req: Req) => RequestId; | ||
export declare const createRequestId: ({ url, body, }: { | ||
url: string | undefined; | ||
body: string | undefined; | ||
}) => RequestId; | ||
export declare const isNumberString: (str: string) => boolean; | ||
export declare const findObjectPaths: (obj: ResponseBody, search: string) => MatchPaths[]; | ||
export declare const printFindDeveloperHelp: (req: Req, response: ProxyResponse, filePath: string, find: string | undefined) => void; | ||
export declare const printFindDeveloperHelp: (request: Request, response: ProxyResponse, filePath: string, find: string | undefined) => void; | ||
export declare const printResponseLogs: (settings: { | ||
responsesToLog?: RequestId[]; | ||
}, req: Req, response: ProxyResponse) => void; | ||
}, request: Request, response: ProxyResponse) => void; | ||
/** @description Snapshots responses for request and provide them as stubs. */ | ||
@@ -41,0 +50,0 @@ export declare class HttpApiProxyServer { |
@@ -53,3 +53,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.HttpApiProxyServer = exports.printResponseLogs = exports.printFindDeveloperHelp = exports.findObjectPaths = exports.isNumberString = exports.requestToId = exports.printErrors = exports.hasError = exports.generateResponseOverwriteCode = void 0; | ||
exports.HttpApiProxyServer = exports.printResponseLogs = exports.printFindDeveloperHelp = exports.findObjectPaths = exports.isNumberString = exports.createRequestId = exports.printErrors = exports.hasError = exports.generateResponseOverwriteCode = void 0; | ||
var axios_1 = __importDefault(require("axios")); | ||
@@ -62,14 +62,12 @@ var http_1 = require("http"); | ||
var defaultProxyBehavior = "SAVE_RESPONSES_FOR_NEW_QUERIES"; | ||
// TODO THINK ABOUT: putting the Options above (along with find from Settings) into separate yarn commands (and maybe own files) | ||
// TODO move into class | ||
var generateResponseOverwriteCode = function (req, potentialPathsForChanges, searchValue, filePath) { | ||
var cacheDirPath = ["put", "your", "path", "here"]; | ||
var requestId = (0, exports.requestToId)(req); | ||
var generateResponseOverwriteCode = function (request, potentialPathsForChanges, searchValue, filePath) { | ||
var cacheDirPathExample = ["put", "your", "path", "here"]; | ||
// TODO update code | ||
return "You can change values containing \"".concat(searchValue, "\" as follows:\n\n import ").concat(requestId, " from '").concat(filePath, "'\n\n ").concat(potentialPathsForChanges | ||
return "You can change values containing \"".concat(searchValue, "\" as follows:\n\n import ").concat(request.requestId, " from '").concat(filePath, "'\n\n ").concat(potentialPathsForChanges | ||
.map(function (_a) { | ||
var path = _a.path, value = _a.value; | ||
return "".concat(requestId).concat(path, " = ").concat(value, "\n"); | ||
return "".concat(request.requestId).concat(path, " = ").concat(value, "\n"); | ||
}) | ||
.join(" "), "\n\n // Use the custom values like this:\n const proxyServer = new HttpApiProxyServer({\n cacheDirPath: ").concat(JSON.stringify(cacheDirPath), ",\n overwrites: {").concat(requestId, "},\n })\n\n // Do not forget to run proxyServer.start() and proxyServer.stop() to use the proxy\n "); | ||
.join(" "), "\n\n // Use the custom values like this:\n const proxyServer = new HttpApiProxyServer({\n cacheDirPath: ").concat(JSON.stringify(cacheDirPathExample), ",\n overwrites: {").concat(request.requestId, "},\n })\n\n // Do not forget to run proxyServer.start() and proxyServer.stop() to use the proxy\n "); | ||
}; | ||
@@ -99,8 +97,9 @@ exports.generateResponseOverwriteCode = generateResponseOverwriteCode; | ||
// TODO move into class | ||
var requestToId = function (req) { | ||
if (!req.url) { | ||
var createRequestId = function (_a) { | ||
var url = _a.url, body = _a.body; | ||
if (!url) { | ||
throw new Error("[requestToId] Cannot handle a request with missing URL"); | ||
} | ||
return (requestIdPrefix + | ||
Array.from(req.url) | ||
Array.from(JSON.stringify({ url: url, body: body })) | ||
.reduce(function (hash, char) { return 0 | (31 * hash + char.charCodeAt(0)); }, 0) | ||
@@ -110,3 +109,3 @@ .toString() | ||
}; | ||
exports.requestToId = requestToId; | ||
exports.createRequestId = createRequestId; | ||
var isNumberString = function (str) { | ||
@@ -136,3 +135,3 @@ return !isNaN(str) && !isNaN(parseFloat(str)); | ||
// TODO move into class | ||
var printFindDeveloperHelp = function (req, response, filePath, find) { | ||
var printFindDeveloperHelp = function (request, response, filePath, find) { | ||
// TODO exclude if (!find) | ||
@@ -148,10 +147,10 @@ if (!find) | ||
return (0, print_1.printLimit)(responseValuePaths.length, find, matchMax, filePath); | ||
return (0, print_1.print)((0, exports.generateResponseOverwriteCode)(req, responseValuePaths, find, filePath)); | ||
return (0, print_1.print)((0, exports.generateResponseOverwriteCode)(request, responseValuePaths, find, filePath)); | ||
}; | ||
exports.printFindDeveloperHelp = printFindDeveloperHelp; | ||
// TODO move into class | ||
var printResponseLogs = function (settings, req, response) { | ||
var printResponseLogs = function (settings, request, response) { | ||
var _a; | ||
if ((_a = settings.responsesToLog) === null || _a === void 0 ? void 0 : _a.includes((0, exports.requestToId)(req))) | ||
(0, print_1.print)("".concat((0, exports.requestToId)(req), ": ").concat(JSON.stringify(response))); | ||
if ((_a = settings.responsesToLog) === null || _a === void 0 ? void 0 : _a.includes(request.requestId)) | ||
(0, print_1.print)("".concat(request.requestId, ": ").concat(JSON.stringify(response))); | ||
}; | ||
@@ -161,7 +160,9 @@ exports.printResponseLogs = printResponseLogs; | ||
/** This insures there will be no type errors when using headers in axios. It also replaces accept-encoding to make sure axios can handle.*/ | ||
var convertHeaders = function (headers) { return (__assign(__assign({}, Object.keys(headers).reduce(function (obj, key) { | ||
var convertHeaders = function (headers, host, port) { return (__assign(__assign({}, Object.keys(headers).reduce(function (obj, key) { | ||
var _a; | ||
var _b, _c; | ||
return (__assign(__assign({}, obj), (_a = {}, _a[key] = (_c = (_b = headers[key]) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : "", _a))); | ||
}, {})), { "accept-encoding": "gzip" })); }; | ||
}, {})), { | ||
// host and port need to be in the headers in order for the TLS handshake to work | ||
host: host, port: port, "accept-encoding": "gzip" })); }; | ||
var getRequestBody = function (req) { | ||
@@ -171,3 +172,5 @@ return new Promise(function (resolve, reject) { | ||
req.on("data", function (chunk) { | ||
body += chunk.toString().split("\\n").join("\r\n"); | ||
body += chunk.toString(); | ||
// We used to include this "fix" which broke things later. | ||
// body += chunk.toString().split("\\n").join("\r\n"); | ||
}); | ||
@@ -178,20 +181,10 @@ req.on("end", function () { return resolve(body); }); | ||
}; | ||
var convertToAxiosRequestConfig = function (req) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, _b; | ||
var _c; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
_a = [{ headers: convertHeaders(req.headers), url: "".concat(req.headers.port === "443" ? "https" : "http", "://").concat(req.headers.host).concat(req.url), method: req.method }]; | ||
if (!(req.method === "POST")) return [3 /*break*/, 2]; | ||
_c = {}; | ||
return [4 /*yield*/, getRequestBody(req)]; | ||
case 1: | ||
_b = (_c.data = _d.sent(), _c); | ||
return [3 /*break*/, 3]; | ||
case 2: | ||
_b = {}; | ||
_d.label = 3; | ||
case 3: return [2 /*return*/, (__assign.apply(void 0, _a.concat([(_b)])))]; | ||
} | ||
var convertToAxiosRequestConfig = function (request, host, port) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, ({ | ||
headers: convertHeaders(request.headers, host, port), | ||
url: "".concat(port === "443" ? "https" : "http", "://").concat(host).concat(request.url), | ||
method: request.method, | ||
data: request.body, | ||
})]; | ||
}); | ||
@@ -220,4 +213,4 @@ }); }; | ||
var _this = this; | ||
this.printReplacementCharAlert = function (stringValue, req) { | ||
if (_this.cache.getMetaInfo((0, exports.requestToId)(req))["ignoreBrockenChars"]) | ||
this.printReplacementCharAlert = function (stringValue, request) { | ||
if (_this.cache.getMetaInfo(request.requestId)["ignoreBrockenChars"]) | ||
return; | ||
@@ -231,3 +224,3 @@ var replacementChar = /\uFFFD/g; | ||
? "".concat(brockenChars.join(", ").slice(0, 99), "...") | ||
: brockenChars.join(", "), " in ").concat(_this.cache.filePathForRequest(req))); | ||
: brockenChars.join(", "), " in ").concat(_this.cache.filePathForRequest(request))); | ||
} | ||
@@ -238,16 +231,15 @@ }; | ||
}; | ||
this.resolveRequest = function (req) { return __awaiter(_this, void 0, void 0, function () { | ||
var requestId, localResponse; | ||
this.resolveRequest = function (request) { return __awaiter(_this, void 0, void 0, function () { | ||
var localResponse; | ||
return __generator(this, function (_a) { | ||
requestId = (0, exports.requestToId)(req); | ||
localResponse = this.getLocalResponseIfExists(requestId); | ||
localResponse = this.getLocalResponseIfExists(request.requestId); | ||
switch (this.settings.proxyBehavior) { | ||
case "FORCE_UPDATE_ALL": | ||
return [2 /*return*/, this.getApiResponseAndSaveToLocal(req)]; | ||
return [2 /*return*/, this.getApiResponseAndSaveToLocal(request)]; | ||
case "SAVE_RESPONSES_FOR_NEW_QUERIES": | ||
return [2 /*return*/, localResponse || this.getApiResponseAndSaveToLocal(req)]; | ||
return [2 /*return*/, localResponse || this.getApiResponseAndSaveToLocal(request)]; | ||
case "RELOAD_RESPONSES_WITH_ERRORS": | ||
return [2 /*return*/, localResponse && !(0, exports.hasError)(localResponse) | ||
? localResponse | ||
: this.getApiResponseAndSaveToLocal(req)]; | ||
: this.getApiResponseAndSaveToLocal(request)]; | ||
case "NO_REQUEST_FORWARDING": | ||
@@ -257,3 +249,3 @@ if (localResponse) | ||
else | ||
throw Error("[HttpApiProxyServer proxyBehavior is set to ".concat(this.settings.proxyBehavior, "] No ").concat(requestId, " stored in the proxy cache")); | ||
throw Error("[HttpApiProxyServer proxyBehavior is set to ".concat(this.settings.proxyBehavior, "] No ").concat(request.requestId, " stored in the proxy cache")); | ||
} | ||
@@ -263,10 +255,10 @@ return [2 /*return*/, localResponse]; | ||
}); }; | ||
this.getApiResponseAndSaveToLocal = function (req) { return __awaiter(_this, void 0, void 0, function () { | ||
this.getApiResponseAndSaveToLocal = function (request) { return __awaiter(_this, void 0, void 0, function () { | ||
var apiResponse; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.realApiResponse(req)]; | ||
case 0: return [4 /*yield*/, this.realApiResponse(request)]; | ||
case 1: | ||
apiResponse = _a.sent(); | ||
this.cache.saveResponse(req, apiResponse); | ||
this.cache.saveResponse(request, apiResponse); | ||
return [2 /*return*/, apiResponse]; | ||
@@ -276,4 +268,4 @@ } | ||
}); }; | ||
this.realApiResponse = function (req) { return __awaiter(_this, void 0, void 0, function () { | ||
var requestConfig, _a, data, status, error_1, axiosError; | ||
this.realApiResponse = function (request) { return __awaiter(_this, void 0, void 0, function () { | ||
var host, port, requestConfig, _a, data, status, error_1, axiosError; | ||
var _b, _c; | ||
@@ -283,8 +275,8 @@ return __generator(this, function (_d) { | ||
case 0: | ||
req.headers.host = this.settings.sourceHost; | ||
req.headers.port = this.settings.sourcePort.toString(); | ||
host = this.settings.sourceHost; | ||
port = this.settings.sourcePort.toString(); | ||
_d.label = 1; | ||
case 1: | ||
_d.trys.push([1, 4, , 5]); | ||
return [4 /*yield*/, convertToAxiosRequestConfig(req)]; | ||
return [4 /*yield*/, convertToAxiosRequestConfig(request, host, port)]; | ||
case 2: | ||
@@ -304,3 +296,3 @@ requestConfig = _d.sent(); | ||
{ | ||
message: "[HttpApiProxyServer] No successful response from ".concat(req.headers.host), | ||
message: "[HttpApiProxyServer] No successful response from ".concat(host), | ||
}, | ||
@@ -317,9 +309,8 @@ axiosError.toJSON(), | ||
/** Prints console outputs depending on its inputs. No output is also possible */ | ||
this.printConsoleFeedback = function (req, response) { | ||
this.printConsoleFeedback = function (request, response) { | ||
var _a; | ||
var filePath = _this.cache.filePathForRequest(req); | ||
// TODO cleanup parameter order (once in class) | ||
(0, exports.printFindDeveloperHelp)(req, response, filePath, (_a = _this.settings) === null || _a === void 0 ? void 0 : _a.find); | ||
var filePath = _this.cache.filePathForRequest(request); | ||
(0, exports.printFindDeveloperHelp)(request, response, filePath, (_a = _this.settings) === null || _a === void 0 ? void 0 : _a.find); | ||
(0, exports.printErrors)(response, _this.settings, filePath); | ||
(0, exports.printResponseLogs)(_this.settings, req, response); | ||
(0, exports.printResponseLogs)(_this.settings, request, response); | ||
}; | ||
@@ -382,11 +373,37 @@ // TODO Test using to have been called with (put handles in other function) | ||
this.initialOverwrites = this.overwrites; | ||
// TODO do not pass requestToId but import inside ResponseCacheConnector | ||
this.cache = new response_cache_1.ResponseCacheConnector(cacheDirPath, exports.requestToId); | ||
this.httpServer = (0, http_1.createServer)(function (req, res) { | ||
_this.resolveRequest(req).then(function (response) { | ||
_this.printReplacementCharAlert(JSON.stringify(response.body), req); | ||
_this.printConsoleFeedback(req, response); | ||
_this.replyToClient(res, response); | ||
this.cache = new response_cache_1.ResponseCacheConnector(cacheDirPath); | ||
this.httpServer = (0, http_1.createServer)(function (req, res) { return __awaiter(_this, void 0, void 0, function () { | ||
var url, body, _a, requestId, request; | ||
var _this = this; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
url = req.url; | ||
if (!(req.method === "POST")) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, getRequestBody(req)]; | ||
case 1: | ||
_a = _b.sent(); | ||
return [3 /*break*/, 3]; | ||
case 2: | ||
_a = undefined; | ||
_b.label = 3; | ||
case 3: | ||
body = _a; | ||
requestId = (0, exports.createRequestId)({ url: url, body: body }); | ||
request = { | ||
requestId: requestId, | ||
url: url, | ||
body: body, | ||
method: req.method, | ||
headers: req.headers, | ||
}; | ||
this.resolveRequest(request).then(function (response) { | ||
_this.printReplacementCharAlert(JSON.stringify(response.body), request); | ||
_this.printConsoleFeedback(request, response); | ||
_this.replyToClient(res, response); | ||
}); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}); }); | ||
} | ||
@@ -393,0 +410,0 @@ return HttpApiProxyServer; |
@@ -6,6 +6,17 @@ "use strict"; | ||
}; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var proxy_server_1 = require("./proxy-server"); | ||
var print_1 = require("./print"); | ||
jest.mock('./print', function () { return ({ | ||
jest.mock("./print", function () { return ({ | ||
print: jest.fn(function () { return null; }), | ||
@@ -16,13 +27,21 @@ printError: jest.fn(function () { return null; }), | ||
}); }); | ||
var req0415987281 = { url: '/graphql?mock' }; | ||
//const reqOther = { url: '/graphql?test' } as Req | ||
var createRequest = function (overwrites) { | ||
var _a, _b; | ||
return (__assign({ requestId: (0, proxy_server_1.createRequestId)({ | ||
url: (_a = overwrites.url) !== null && _a !== void 0 ? _a : "/", | ||
body: (_b = overwrites.body) !== null && _b !== void 0 ? _b : undefined, | ||
}), method: "GET", url: "/", headers: {}, body: undefined }, overwrites)); | ||
}; | ||
var req1927740808 = createRequest({ | ||
url: "/graphql?mock", | ||
}); | ||
var mockResponses = { | ||
responseFor123456: { status: 200, body: { msg: 'unknown response' } }, | ||
responseFor0415987281: { status: 200, body: { msg: 'req0415987281' } }, | ||
responseFor123456: { status: 200, body: { msg: "unknown response" } }, | ||
responseFor0415987281: { status: 200, body: { msg: "req0415987281" } }, | ||
}; | ||
var basePath = 'test/responses'; | ||
var path = basePath + '/test'; | ||
var existingPathMock = path + '/responseFor0415987281.json'; | ||
var basePath = "test/responses"; | ||
var path = basePath + "/test"; | ||
var existingPathMock = path + "/responseFor0415987281.json"; | ||
// TODO replace with mocks for wrapper functions | ||
jest.mock('fs', function () { return ({ | ||
jest.mock("fs", function () { return ({ | ||
existsSync: jest.fn(function (filepath) { | ||
@@ -37,19 +56,19 @@ return filepath === existingPathMock || | ||
}); }); | ||
describe('Proxy-Server', function () { | ||
describe("Proxy-Server", function () { | ||
beforeEach(function () { | ||
jest.clearAllMocks(); // resets call counts for the mocks | ||
}); | ||
it('generateMockCode will generate the correct mock code', function () { | ||
it("generateMockCode will generate the correct mock code", function () { | ||
var potentialMockPaths = [ | ||
{ path: '.this.is.a.path', value: 'example-value1' }, | ||
{ path: '.this.is.a.path.too', value: 'example-value2' }, | ||
{ path: ".this.is.a.path", value: "example-value1" }, | ||
{ path: ".this.is.a.path.too", value: "example-value2" }, | ||
]; | ||
var searchValue = 'example'; | ||
var filePath = './responses/name/responseFor0415987281.json'; | ||
var expectedCode = "You can change values containing \"example\" as follows:\n\n import responseFor0415987281 from './responses/name/responseFor0415987281.json'\n\n responseFor0415987281.this.is.a.path = example-value1\n responseFor0415987281.this.is.a.path.too = example-value2\n\n\n // Use the custom values like this:\n const proxyServer = new HttpApiProxyServer({\n cacheDirPath: [\"put\",\"your\",\"path\",\"here\"],\n overwrites: {responseFor0415987281},\n })\n\n // Do not forget to run proxyServer.start() and proxyServer.stop() to use the proxy\n "; | ||
var generatedCode = (0, proxy_server_1.generateResponseOverwriteCode)(req0415987281, potentialMockPaths, searchValue, filePath); | ||
var searchValue = "example"; | ||
var filePath = "./responses/name/responseFor1927740808.json"; | ||
var expectedCode = "You can change values containing \"example\" as follows:\n\n import responseFor1927740808 from './responses/name/responseFor1927740808.json'\n\n responseFor1927740808.this.is.a.path = example-value1\n responseFor1927740808.this.is.a.path.too = example-value2\n\n\n // Use the custom values like this:\n const proxyServer = new HttpApiProxyServer({\n cacheDirPath: [\"put\",\"your\",\"path\",\"here\"],\n overwrites: {responseFor1927740808},\n })\n\n // Do not forget to run proxyServer.start() and proxyServer.stop() to use the proxy\n "; | ||
var generatedCode = (0, proxy_server_1.generateResponseOverwriteCode)(req1927740808, potentialMockPaths, searchValue, filePath); | ||
expect(generatedCode).toEqual(expectedCode); | ||
}); | ||
describe('hasError', function () { | ||
it.each(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n resp | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "], ["\n resp | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "])), { body: { errors: [] }, status: 400 }, true, { body: { errors: [] }, status: 200 }, true, { body: {}, status: 400 }, true, { body: {}, status: 200 }, false)('returns $expected when response is $resp', function (_a) { | ||
describe("hasError", function () { | ||
it.each(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n resp | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "], ["\n resp | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "])), { body: { errors: [] }, status: 400 }, true, { body: { errors: [] }, status: 200 }, true, { body: {}, status: 400 }, true, { body: {}, status: 200 }, false)("returns $expected when response is $resp", function (_a) { | ||
var resp = _a.resp, expected = _a.expected; | ||
@@ -59,22 +78,39 @@ expect((0, proxy_server_1.hasError)(resp)).toBe(expected); | ||
}); | ||
describe('requestToId', function () { | ||
var prefix = 'responseFor'; | ||
describe("createRequestId", function () { | ||
var prefix = "responseFor"; | ||
it("returns a string that starts with ".concat(prefix), function () { | ||
var result1 = (0, proxy_server_1.requestToId)({ url: '/graphql?mock' }); | ||
var result2 = (0, proxy_server_1.requestToId)({ url: '/graphql?test' }); | ||
var result1 = (0, proxy_server_1.createRequestId)({ | ||
url: "/graphql?mock", | ||
body: undefined, | ||
}); | ||
var result2 = (0, proxy_server_1.createRequestId)({ | ||
url: "/graphql?test", | ||
body: undefined, | ||
}); | ||
expect(result1.startsWith(prefix)).toBe(true); | ||
expect(result2.startsWith(prefix)).toBe(true); | ||
}); | ||
it('returns a different value for different input reqests', function () { | ||
var result1 = (0, proxy_server_1.requestToId)({ url: '/graphql?mock' }); | ||
var result2 = (0, proxy_server_1.requestToId)({ url: '/graphql?test' }); | ||
it("returns a different value for different urls", function () { | ||
var result1 = (0, proxy_server_1.createRequestId)({ | ||
url: "/graphql?mock", | ||
body: undefined, | ||
}); | ||
var result2 = (0, proxy_server_1.createRequestId)({ | ||
url: "/graphql?test", | ||
body: undefined, | ||
}); | ||
expect(result1).not.toEqual(result2); | ||
}); | ||
it('returns a valide filename or variable name for Typescript', function () { | ||
var result = (0, proxy_server_1.requestToId)({ url: '/graphql?mock' }); | ||
it("returns a different value for different body data", function () { | ||
var result1 = (0, proxy_server_1.createRequestId)({ url: "/graphql", body: "data1" }); | ||
var result2 = (0, proxy_server_1.createRequestId)({ url: "/graphql", body: "data2" }); | ||
expect(result1).not.toEqual(result2); | ||
}); | ||
it("returns a valide filename or variable name for Typescript", function () { | ||
var result = (0, proxy_server_1.createRequestId)({ url: "/graphql?mock", body: undefined }); | ||
expect(/[^a-zA-Z0-9_]/.test(result)).toBe(false); | ||
}); | ||
}); | ||
describe('isNumberString', function () { | ||
it.each(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n value | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "], ["\n value | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "])), '123', true, '0', true, '-42', true, '', false, 'one', false, '1string', false, 'test2', false)('returns $expected when value is $value', function (_a) { | ||
describe("isNumberString", function () { | ||
it.each(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n value | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "], ["\n value | expected\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "])), "123", true, "0", true, "-42", true, "", false, "one", false, "1string", false, "test2", false)("returns $expected when value is $value", function (_a) { | ||
var value = _a.value, expected = _a.expected; | ||
@@ -84,13 +120,13 @@ expect((0, proxy_server_1.isNumberString)(value)).toBe(expected); | ||
}); | ||
describe('findObjectPaths', function () { | ||
it('will return the paths to a JSON field containg the search value', function () { | ||
describe("findObjectPaths", function () { | ||
it("will return the paths to a JSON field containg the search value", function () { | ||
expect((0, proxy_server_1.findObjectPaths)({ | ||
status: 200, | ||
body: { key1: { a: 'no', b: 'a test' }, key2: 'the test' }, | ||
}, 'test')).toEqual([ | ||
{ path: '.body.key1.b', value: '"a test"' }, | ||
{ path: '.body.key2', value: '"the test"' }, | ||
body: { key1: { a: "no", b: "a test" }, key2: "the test" }, | ||
}, "test")).toEqual([ | ||
{ path: ".body.key1.b", value: '"a test"' }, | ||
{ path: ".body.key2", value: '"the test"' }, | ||
]); | ||
}); | ||
it('will handle arrays', function () { | ||
it("will handle arrays", function () { | ||
expect((0, proxy_server_1.findObjectPaths)({ | ||
@@ -100,17 +136,17 @@ status: 200, | ||
key1: { | ||
a: 'no', | ||
a: "no", | ||
b: [ | ||
'the test', | ||
'or', | ||
'a test', | ||
['test', { something: ['x', 'testing'], value: 'test' }], | ||
"the test", | ||
"or", | ||
"a test", | ||
["test", { something: ["x", "testing"], value: "test" }], | ||
], | ||
}, | ||
}, | ||
}, 'test')).toEqual([ | ||
{ path: '.body.key1.b[0]', value: '"the test"' }, | ||
{ path: '.body.key1.b[2]', value: '"a test"' }, | ||
{ path: '.body.key1.b[3][0]', value: '"test"' }, | ||
{ path: '.body.key1.b[3][1].something[1]', value: '"testing"' }, | ||
{ path: '.body.key1.b[3][1].value', value: '"test"' }, | ||
}, "test")).toEqual([ | ||
{ path: ".body.key1.b[0]", value: '"the test"' }, | ||
{ path: ".body.key1.b[2]", value: '"a test"' }, | ||
{ path: ".body.key1.b[3][0]", value: '"test"' }, | ||
{ path: ".body.key1.b[3][1].something[1]", value: '"testing"' }, | ||
{ path: ".body.key1.b[3][1].value", value: '"test"' }, | ||
]); | ||
@@ -120,28 +156,28 @@ }); | ||
// TODO split into fn cases | ||
test.each(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n matchCount | fn\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "], ["\n matchCount | fn\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "])), 0, 'printNoMatch', 1, 'print', 42, 'print', 80, 'print', 81, 'printLimit', 123, 'printLimit')('printFindDeveloperHelp will call only $fn if there are $matchCount matches', function (_a) { | ||
test.each(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n matchCount | fn\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "], ["\n matchCount | fn\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n ", " | ", "\n "])), 0, "printNoMatch", 1, "print", 42, "print", 80, "print", 81, "printLimit", 123, "printLimit")("printFindDeveloperHelp will call only $fn if there are $matchCount matches", function (_a) { | ||
var matchCount = _a.matchCount, fn = _a.fn; | ||
var body = { a: Array.from({ length: matchCount }).fill('t') }; | ||
(0, proxy_server_1.printFindDeveloperHelp)(req0415987281, { status: 200, body: body }, '', 't'); | ||
expect(print_1.print).toHaveBeenCalledTimes(fn === 'print' ? 1 : 0); | ||
expect(print_1.printLimit).toHaveBeenCalledTimes(fn === 'printLimit' ? 1 : 0); | ||
expect(print_1.printNoMatch).toHaveBeenCalledTimes(fn === 'printNoMatch' ? 1 : 0); | ||
var body = { a: Array.from({ length: matchCount }).fill("t") }; | ||
(0, proxy_server_1.printFindDeveloperHelp)(req1927740808, { status: 200, body: body }, "", "t"); | ||
expect(print_1.print).toHaveBeenCalledTimes(fn === "print" ? 1 : 0); | ||
expect(print_1.printLimit).toHaveBeenCalledTimes(fn === "printLimit" ? 1 : 0); | ||
expect(print_1.printNoMatch).toHaveBeenCalledTimes(fn === "printNoMatch" ? 1 : 0); | ||
}); | ||
describe('handleResponseLogs', function () { | ||
test('will not log anyting if settings do not request any response logs', function () { | ||
(0, proxy_server_1.printResponseLogs)({}, req0415987281, { | ||
describe("handleResponseLogs", function () { | ||
test("will not log anyting if settings do not request any response logs", function () { | ||
(0, proxy_server_1.printResponseLogs)({}, req1927740808, { | ||
status: 200, | ||
body: { t: 'test' }, | ||
body: { t: "test" }, | ||
}); | ||
expect(print_1.print).not.toHaveBeenCalled(); | ||
}); | ||
test('will not log anyting if the response is not matching', function () { | ||
test("will not log anyting if the response is not matching", function () { | ||
(0, proxy_server_1.printResponseLogs)({ | ||
responsesToLog: ['responseFor123456', 'responseFor987654'], | ||
}, req0415987281, { status: 200, body: { t: 'test' } }); | ||
responsesToLog: ["responseFor123456", "responseFor987654"], | ||
}, req1927740808, { status: 200, body: { t: "test" } }); | ||
expect(print_1.print).not.toHaveBeenCalled(); | ||
}); | ||
test('will log matching responses', function () { | ||
test("will log matching responses", function () { | ||
(0, proxy_server_1.printResponseLogs)({ | ||
responsesToLog: ['responseFor0415987281', 'responseFor123456'], | ||
}, req0415987281, { status: 200, body: { t: 'test' } }); | ||
responsesToLog: [req1927740808.requestId, "responseFor123456"], | ||
}, req1927740808, { status: 200, body: { t: "test" } }); | ||
expect(print_1.print).toHaveBeenCalled(); | ||
@@ -148,0 +184,0 @@ }); |
@@ -1,3 +0,1 @@ | ||
/// <reference types="node" /> | ||
import { IncomingMessage as Req } from "http"; | ||
type ResponseData = Record<string, unknown> & { | ||
@@ -9,2 +7,9 @@ errors?: { | ||
type RequestId = `responseFor${string}`; | ||
type Request = { | ||
requestId: RequestId; | ||
url?: string; | ||
method?: string; | ||
headers: Record<string, string | string[] | undefined>; | ||
body: string | undefined; | ||
}; | ||
type ProxyResponse = { | ||
@@ -16,7 +21,6 @@ body: ResponseData; | ||
private cacheDirPath; | ||
private requestToId; | ||
constructor(cacheDirPath: string[] | undefined, requestIdFunction: (req: Req) => RequestId); | ||
constructor(cacheDirPath?: string[]); | ||
private requireDir; | ||
getResponse: (requestId: string) => any; | ||
saveResponse: (req: Req, response: ProxyResponse) => void; | ||
saveResponse: (request: Request, response: ProxyResponse) => void; | ||
getMetaInfo: (requestId: string) => any; | ||
@@ -26,4 +30,4 @@ saveMetaInfo: (requestId: string, metaInfo: Record<string, boolean>) => void; | ||
private metaInfoFilePathForRequestId; | ||
filePathForRequest: (req: Req) => string; | ||
filePathForRequest: (request: Request) => string; | ||
} | ||
export {}; |
@@ -25,2 +25,5 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -30,5 +33,5 @@ exports.ResponseCacheConnector = void 0; | ||
var print_1 = require("./print"); | ||
var path = __importStar(require("path")); | ||
var path_1 = __importDefault(require("path")); | ||
var ResponseCacheConnector = /** @class */ (function () { | ||
function ResponseCacheConnector(cacheDirPath, requestIdFunction) { | ||
function ResponseCacheConnector(cacheDirPath) { | ||
if (cacheDirPath === void 0) { cacheDirPath = ["responses"]; } | ||
@@ -42,13 +45,13 @@ var _this = this; | ||
}; | ||
this.saveResponse = function (req, response) { | ||
if (!req.url) { | ||
this.saveResponse = function (request, response) { | ||
if (!request.url) { | ||
throw new Error("[saveResponse] Cannot handle a request with missing URL"); | ||
} | ||
var responseDir = _this.requireDir(); | ||
var fileName = _this.filePathForRequest(req); | ||
var logLine = "".concat(fileName, ", ").concat(decodeURIComponent(req.url)); | ||
var fileName = _this.filePathForRequest(request); | ||
var logLine = "".concat(fileName, ", ").concat(decodeURIComponent(request.url)); | ||
try { | ||
fs.appendFileSync( | ||
// TODO create log name config | ||
path.join(responseDir, "apiQuery.log"), logLine + "\n\n"); | ||
path_1.default.join(responseDir, "apiQuery.log"), logLine + "\n\n"); | ||
} | ||
@@ -84,12 +87,11 @@ catch (_a) { | ||
this.filePathForRequestId = function (RequestId) { | ||
return path.join(path.join.apply(path, _this.cacheDirPath), "".concat(RequestId, ".json")); | ||
return path_1.default.join(path_1.default.join.apply(path_1.default, _this.cacheDirPath), "".concat(RequestId, ".json")); | ||
}; | ||
this.metaInfoFilePathForRequestId = function (RequestId) { | ||
return path.join(path.join.apply(path, _this.cacheDirPath), "".concat(RequestId, ".meta.json")); | ||
return path_1.default.join(path_1.default.join.apply(path_1.default, _this.cacheDirPath), "".concat(RequestId, ".meta.json")); | ||
}; | ||
this.filePathForRequest = function (req) { | ||
return _this.filePathForRequestId(_this.requestToId(req)); | ||
this.filePathForRequest = function (request) { | ||
return _this.filePathForRequestId(request.requestId); | ||
}; | ||
this.cacheDirPath = cacheDirPath; | ||
this.requestToId = requestIdFunction; | ||
} | ||
@@ -99,3 +101,3 @@ ResponseCacheConnector.prototype.requireDir = function () { | ||
this.cacheDirPath.forEach(function (part) { | ||
currentDir = path.join(currentDir, part); | ||
currentDir = path_1.default.join(currentDir, part); | ||
if (!fs.existsSync(currentDir)) | ||
@@ -102,0 +104,0 @@ fs.mkdirSync(currentDir); |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var proxy_server_1 = require("./proxy-server"); | ||
var response_cache_1 = require("./response-cache"); | ||
describe('ResponseCacheConnector', function () { | ||
var req0415987281 = { url: '/graphql?mock' }; | ||
//const reqOther = { url: '/graphql?test' } as Req | ||
var cache = new response_cache_1.ResponseCacheConnector(['test', 'responses', 'testName'], proxy_server_1.requestToId); | ||
test('filePathForHash will build valid path', function () { | ||
expect(cache.filePathForRequestId('hash')).toEqual('test/responses/testName/hash.json'); | ||
var createRequest = function (overwrites) { | ||
var _a, _b; | ||
return (__assign({ requestId: (0, proxy_server_1.createRequestId)({ | ||
url: (_a = overwrites.url) !== null && _a !== void 0 ? _a : "/", | ||
body: (_b = overwrites.body) !== null && _b !== void 0 ? _b : undefined, | ||
}), method: "GET", url: "/", headers: {}, body: undefined }, overwrites)); | ||
}; | ||
describe("ResponseCacheConnector", function () { | ||
var req1927740808 = createRequest({ url: "/graphql?mock" }); | ||
var cache = new response_cache_1.ResponseCacheConnector(["test", "responses", "testName"]); | ||
test("filePathForHash will build valid path", function () { | ||
expect(cache.filePathForRequestId("hash")).toEqual("test/responses/testName/hash.json"); | ||
}); | ||
test('filePathForRequest will build valid path', function () { | ||
expect(cache.filePathForRequest(req0415987281)).toEqual('test/responses/testName/responseFor0415987281.json'); | ||
test("filePathForRequest will build valid path", function () { | ||
expect(cache.filePathForRequest(req1927740808)).toEqual("test/responses/testName/responseFor1927740808.json"); | ||
}); | ||
}); |
{ | ||
"name": "http-api-proxy-server", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"license": "MIT", | ||
@@ -11,2 +11,3 @@ "main": "dist/index.js", | ||
"test": "yarn test:unit && yarn use-case:tests", | ||
"lint": "echo TODO", | ||
"test:unit": "jest --config=\"jest.config.ts\"", | ||
@@ -13,0 +14,0 @@ "test:unit:watch": "yarn run test:unit -- --watchAll", |
51623
865