Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "polyapi",
"version": "0.23.13",
"version": "0.23.14",
"description": "Poly is a CLI tool to help create and manage your Poly definitions.",
"license": "MIT",
"repository": {
Expand Down
11 changes: 11 additions & 0 deletions src/commands/generate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ const prepareDir = async (polyPath: string) => {
}
};

const getExecutionConfig = () => ({
directExecute: process.env.API_FUNCTION_DIRECT_EXECUTE === 'true',
mtls: {
certPath: process.env.MTLS_CERT_PATH,
keyPath: process.env.MTLS_KEY_PATH,
caPath: process.env.MTLS_CA_PATH,
rejectUnauthorized: process.env.NODE_ENV !== 'development',
},
});

const generateRedirectIndexFiles = async (polyPath: string) => {
const defaultPolyLib = getPolyLibPath(DEFAULT_POLY_PATH);

Expand Down Expand Up @@ -156,6 +166,7 @@ const generateApiFunctionJSFiles = async (libPath: string, specifications: ApiFu
`${libPath}/api/index.js`,
template({
specifications,
executionConfig: getExecutionConfig(),
}),
);
};
Expand Down
97 changes: 95 additions & 2 deletions templates/api-index.js.hbs
Original file line number Diff line number Diff line change
@@ -1,22 +1,115 @@
const axios = require('../axios');
const set = require('lodash/set');
const merge = require('lodash/merge');
const https = require('https');
const fs = require('fs');

// Environment variables injected during generation
const env = {
directExecute: {{executionConfig.directExecute}},
mtls: {
certPath: '{{executionConfig.mtls.certPath}}',
keyPath: '{{executionConfig.mtls.keyPath}}',
caPath: '{{executionConfig.mtls.caPath}}',
rejectUnauthorized: {{executionConfig.mtls.rejectUnauthorized}},
}
};

const functions = [
{{#each specifications}}
['{{#if context}}{{context}}.{{/if}}{{name}}', '{{id}}',{{#each function.arguments}}'{{name}}',{{/each}}],
{{/each}}
];

// Create MTLS agent if paths are provided
let httpsAgent = undefined;
const getHttpsAgent = () => {
if (httpsAgent) {
return httpsAgent;
}

const { mtls } = env;
if (!mtls.certPath || !mtls.keyPath || !mtls.caPath) {
return undefined;
}

httpsAgent = new https.Agent({
cert: fs.readFileSync(mtls.certPath),
key: fs.readFileSync(mtls.keyPath),
ca: fs.readFileSync(mtls.caPath),
rejectUnauthorized: mtls.rejectUnauthorized,
});

return httpsAgent;
};

module.exports = (clientID, polyCustom) => merge(
{},
functions.reduce(
(acc, [path, id, ...argKeys]) => set(
acc,
path,
(...args) => {
const requestStartTime = Date.now();
const requestServerStartTime = Date.now();
const requestArgs = argKeys.reduce((acc, key, index) => ({ ...acc, [key]: args[index] }), {});

// Check if direct execution is enabled
const { directExecute } = env;

if (directExecute === true) {
// Make direct API call

let polyHeaders;
let serverPreperationTimeMs;
let roundTripServerNetworkLatencyMs;
let requestApiStartTime;

return axios.post(
`/functions/api/${id}/direct-execute?clientId=${clientID}`,
requestArgs,
{
headers: {
'x-poly-execution-id': polyCustom.executionId,
}
}
).then(({ headers, data }) => {
polyHeaders = headers;
if (data && (data.status < 200 || data.status >= 300)) {
console.error('Error getting direct execution data for api function with id:', id, 'Status code:', data.status, 'Request data:', requestArgs, 'Response data:', data.data);
}

serverPreperationTimeMs = Number(polyHeaders['x-poly-execution-duration']);
roundTripServerNetworkLatencyMs = Date.now() - requestServerStartTime - serverPreperationTimeMs;

requestApiStartTime = Date.now();
const httpsAgent = getHttpsAgent();

return axios({
...data,
headers: {
...data.headers,
},
httpsAgent,
})
}).then(({ headers, data, status, ...args }) => {
if (status && (status < 200 || status >= 300)) {
console.error('Error direct executing api function with id:', id, 'Status code:', status, 'Request data:', requestArgs, 'Response data:', data.data);
}
const apiExecutionTimeMs = Date.now() - requestApiStartTime;
return {
data: data,
status: status,
headers: { ...headers },
metrics: {
roundTripServerNetworkLatencyMs,
serverPreperationTimeMs,
apiExecutionTimeMs,
}
};
});
}

// default indirect execution
return axios.post(
`/functions/api/${id}/execute?clientId=${clientID}`,
{
Expand All @@ -36,7 +129,7 @@ module.exports = (clientID, polyCustom) => merge(
console.error('Error executing api function with id:', id, 'Status code:', data.status, 'Request data:', requestArgs, 'Response data:', responseData);
}
const serverExecutionTimeMs = Number(headers['x-poly-execution-duration']);
const roundTripNetworkLatencyMs = Date.now() - requestStartTime - serverExecutionTimeMs;
const roundTripNetworkLatencyMs = Date.now() - requestServerStartTime - serverExecutionTimeMs;
return {
...data,
metrics: {
Expand Down