mockttp
Version:
Mock HTTP server for testing HTTP clients and stubbing webservices
159 lines • 7.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAdminServerModel = buildAdminServerModel;
const _ = require("lodash");
const graphql_subscriptions_1 = require("graphql-subscriptions");
const util_1 = require("@httptoolkit/util");
const rule_deserialization_1 = require("../rules/rule-deserialization");
const request_utils_1 = require("../util/request-utils");
const graphqlSubscriptionPairs = Object.entries({
'requestInitiated': 'request-initiated',
'requestReceived': 'request',
'responseCompleted': 'response',
'webSocketRequest': 'websocket-request',
'webSocketAccepted': 'websocket-accepted',
'webSocketMessageReceived': 'websocket-message-received',
'webSocketMessageSent': 'websocket-message-sent',
'webSocketClose': 'websocket-close',
'requestAborted': 'abort',
'tlsPassthroughOpened': 'tls-passthrough-opened',
'tlsPassthroughClosed': 'tls-passthrough-closed',
'failedTlsRequest': 'tls-client-error',
'failedClientRequest': 'client-error',
'rawPassthroughOpened': 'raw-passthrough-opened',
'rawPassthroughClosed': 'raw-passthrough-closed',
'rawPassthroughData': 'raw-passthrough-data',
'ruleEvent': 'rule-event'
});
async function buildMockedEndpointData(endpoint) {
return {
id: endpoint.id,
explanation: endpoint.toString(true),
seenRequests: await endpoint.getSeenRequests(),
isPending: await endpoint.isPending()
};
}
const decodeAndSerializeBody = async (body, headers) => {
try {
const decoded = await (0, request_utils_1.decodeBodyBuffer)(body.buffer, headers);
if (decoded === body.buffer)
return false; // No decoding required - no-op.
else
return { decoded }; // Successful decoding result
}
catch (e) {
return {
decodingError: e?.message ?? 'Failed to decode message body'
};
}
};
const serverSideRuleBodySerializer = async (body, headers) => {
const encoded = body.buffer.toString('base64');
const result = await decodeAndSerializeBody(body, headers);
if (result === false) { // No decoding required - no-op.
return { encoded };
}
else if (result.decodingError !== undefined) { // Failed decoding - we just return the error message.
return { encoded, decodingError: result.decodingError };
}
else if (result.decoded) { // Success - we return both formats to the client
return { encoded, decoded: result.decoded.toString('base64') };
}
else {
throw new util_1.UnreachableCheck(result);
}
};
// messageBodyDecoding === 'None' => Just send encoded body as base64
const noopRuleBodySerializer = (body) => body.buffer.toString('base64');
function buildAdminServerModel(mockServer, stream, ruleParams, options = {}) {
const pubsub = new graphql_subscriptions_1.PubSub();
const messageBodyDecoding = options.messageBodyDecoding || 'server-side';
const ruleDeserializationOptions = {
bodySerializer: messageBodyDecoding === 'server-side'
? serverSideRuleBodySerializer
: noopRuleBodySerializer,
ruleParams
};
for (let [gqlName, eventName] of graphqlSubscriptionPairs) {
mockServer.on(eventName, (evt) => {
pubsub.publish(eventName, { [gqlName]: evt });
});
}
const subscriptionResolvers = Object.fromEntries(graphqlSubscriptionPairs.map(([gqlName, eventName]) => ([
gqlName, {
subscribe: () => pubsub.asyncIterator(eventName)
}
])));
return {
Query: {
mockedEndpoints: async () => {
return Promise.all((await mockServer.getMockedEndpoints()).map(buildMockedEndpointData));
},
pendingEndpoints: async () => {
return Promise.all((await mockServer.getPendingEndpoints()).map(buildMockedEndpointData));
},
mockedEndpoint: async (__, { id }) => {
let endpoint = _.find(await mockServer.getMockedEndpoints(), (endpoint) => {
return endpoint.id === id;
});
if (!endpoint)
return null;
return buildMockedEndpointData(endpoint);
}
},
Mutation: {
addRule: async (__, { input }) => {
return mockServer.addRequestRule((0, rule_deserialization_1.deserializeRuleData)(input, stream, ruleDeserializationOptions));
},
addRules: async (__, { input }) => {
return mockServer.addRequestRules(...input.map((rule) => (0, rule_deserialization_1.deserializeRuleData)(rule, stream, ruleDeserializationOptions)));
},
setRules: async (__, { input }) => {
return mockServer.setRequestRules(...input.map((rule) => (0, rule_deserialization_1.deserializeRuleData)(rule, stream, ruleDeserializationOptions)));
},
addWebSocketRule: async (__, { input }) => {
return mockServer.addWebSocketRule((0, rule_deserialization_1.deserializeWebSocketRuleData)(input, stream, ruleDeserializationOptions));
},
addWebSocketRules: async (__, { input }) => {
return mockServer.addWebSocketRules(...input.map((rule) => (0, rule_deserialization_1.deserializeWebSocketRuleData)(rule, stream, ruleDeserializationOptions)));
},
setWebSocketRules: async (__, { input }) => {
return mockServer.setWebSocketRules(...input.map((rule) => (0, rule_deserialization_1.deserializeWebSocketRuleData)(rule, stream, ruleDeserializationOptions)));
}
},
Subscription: subscriptionResolvers,
Request: {
body: (request) => {
return request.body.buffer;
},
decodedBody: async (request) => {
if (messageBodyDecoding === 'none') {
throw new Error('Decoded body requested, but messageBodyDecoding is set to "none"');
}
return (await decodeAndSerializeBody(request.body, request.headers))
|| {}; // No decoding required
}
},
Response: {
body: (response) => {
return response.body.buffer;
},
decodedBody: async (response) => {
if (messageBodyDecoding === 'none') {
throw new Error('Decoded body requested, but messageBodyDecoding is set to "none"');
}
return (await decodeAndSerializeBody(response.body, response.headers))
|| {}; // No decoding required
}
},
ClientError: {
response: (error) => {
if (error.response === 'aborted')
return undefined;
else
return error.response;
}
}
};
}
//# sourceMappingURL=mockttp-admin-model.js.map