node-red-contrib-rollun-aws-sp-api
Version:
Node-RED nodes to access AWS Selling Partner API
248 lines (217 loc) • 8.69 kB
HTML
<script type="text/javascript">
function getMethodName(method, path) {
return `${method} ${path}`;
}
async function fetchModelsList() {
if (window.modelsListCache) {
return window.modelsListCache;
}
const result = await fetch('/aws-sp-api/models');
const json = await result.json();
if (result.ok) {
return window.modelsListCache = json.models;
} else {
throw new Error('Failed to fetch models list: ' + JSON.stringify(json));
}
}
async function fetchVersion(id, version) {
if (!window.versionCache) {
window.versionCache = {};
}
if (window.versionCache[id + version]) {
return window.versionCache[id + version];
}
const result = await fetch(`/aws-sp-api/models/${id}/versions/${version}`);
const json = await result.json();
if (result.ok) {
return window.versionCache[id + version] = json;
} else {
throw new Error('Failed to fetch schema: ' + JSON.stringify(json));
}
}
RED.nodes.registerType('rollun-aws-sp-api', {
category: 'Rollun AWS SP API',
inputs: 1,
outputs: 2,
color: "#bce4d0",
icon: "aws-sp-api.png",
paletteLabel: 'AWS SP API',
defaults: {
name: {
value: '',
required: false,
},
config: {
value: '',
required: true,
type: 'rollun-aws-sp-api-credentials',
},
schema: {
value: '',
required: true,
},
operation: {
value: '',
required: true,
},
server: {
value: 0,
},
debug: {
value: false,
},
body: {
value: 'msg|body',
},
pathParams: {
value: 'msg|pathParams',
},
query: {
value: 'msg|query',
},
},
label: function () {
return this.name || this.operation || 'AWS SP API Request';
},
labelStyle: function () {
return this.name ? 'node_label_italic' : '';
},
outputLabels: function (index) {
return [
'error',
'success',
][index]
},
oneditsave: function() {
const node = this;
const existingConfig = node._config;
const current = node;
// for some reason node red does not detect changes in
// - schema
// - operation
// because of this, we need to manually detect changes
// and mark node as dirty
const somethingChanged = current.schema !== existingConfig.schema
|| current.operation !== existingConfig.operation;
console.log('somethingChanged', somethingChanged);
if (somethingChanged) {
node.changed = true;
RED.events.emit('workspace:dirty', { dirty: true })
}
},
oneditprepare: async function () {
try {
const node = this;
window.utils.makeTypedInput('body', [{ value: "msg", label: "msg." }], undefined, this.body);
window.utils.makeTypedInput('pathParams', [{ value: "msg", label: "msg." }], undefined, this.pathParams);
window.utils.makeTypedInput('query', [{ value: "msg", label: "msg." }], undefined, this.query);
function setSchemaDescription(id, version, title, description) {
$('#schema-description').html(description || title || '');
const openapiLink = `${window.location.origin}/aws-sp-api/models/${id}/versions/${version}`;
const swaggerEditorLink = `https://editor.swagger.io/?url=${openapiLink}`;
$('#schema-link').attr('href', swaggerEditorLink);
}
async function renderInputSchema(schemas, selectFirst = false) {
$('#node-input-schema')
.html(schemas.map(({ id, name, version, title, description }, schemaIndex) => {
const key = `${id}|${version}`;
let isSelected = false;
if (selectFirst && schemaIndex === 0) {
setSchemaDescription(id, version, title, description);
isSelected = true;
node.schema = key;
} else if (key === node.schema){
setSchemaDescription(id, version, title, description);
isSelected = true;
}
return `<option value="${key}" ${isSelected ? 'selected' : ''}>${name} | ${version}</option>`;
}).join('\n'));
}
async function renderInputOperation(selectFirst = false) {
if (node.schema) {
let firstSelected = false;
const [schemaId, schemaVersion] = node.schema.split('|');
const version = await fetchVersion(schemaId, schemaVersion);
$('#node-input-operation')
.html(Object.entries(version.paths).map(([path, methods], pathIndex) => {
return Object.entries(methods).map(([method, { summary, description }]) => {
const operation = getMethodName(method, path);
let isSelected = false;
if (selectFirst && pathIndex === 0 && firstSelected === false) {
$('#operation-description').html(summary || description || '');
isSelected = true;
firstSelected = true;
node.operation = operation;
} else if (operation === node.operation){
$('#operation-description').html(summary || description || '');
isSelected = true;
}
return `<option value="${operation}" ${isSelected ? 'selected' : ''}>${method.toUpperCase()} ${path}</option>`;
})
}).join('\n'));
}
}
async function render() {
const models = await fetchModelsList();
const flatten = models.map(({id, name, versions}) =>
versions.map((version) => ({ id, name, ...version })
)).flat();
$('#node-input-schema').on('change', async (e) => {
node.schema = e.target.value;
await renderInputSchema(flatten);
await renderInputOperation(true);
});
$('#node-input-operation').on('change', async (e) => {
node.operation = e.target.value;
await renderInputOperation();
});
await renderInputSchema(flatten, !node.schema);
await renderInputOperation(!node.operation);
}
await render();
} catch (err) {
console.error(err);
$('#aws-sp-api-errors').html(`
<div class="ui-state-error">
<p>
<span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
<strong>ERROR:</strong>
<pre>${err.stack}</pre>
</p>
</div>
`);
}
},
});
</script>
<script type="text/html" data-template-name="rollun-aws-sp-api">
<div id="aws-sp-api-errors"></div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span>Name:</span></label>
<input type="text" id="node-input-name" />
</div>
<div class="form-row" style>
<label for="node-input-config"><i class="fa fa-book"></i> <span>Credentials:</span></label>
<input type="text" id="node-input-config">
</div>
<div class="form-tips"><b>Tip:</b> Rest API Always return errors & payload. This node in case of success sets payload to <strong>msg.payload</strong> and errors to <strong>msg.payload.error</strong></div>
<div class="form-row">
<label for="node-input-schema"><i class="fa fa-tasks"></i> <span>Schema:</span></label>
<select style="width: 100%" id="node-input-schema"></select>
<pre id="schema-description"></pre>
<a id="schema-link" style="margin-bottom: 10px; color: cornflowerblue; text-decoration: underline;" target="_blank">Open Swagger UI in new tab</a>
</div>
<div class="form-row">
<label for="node-input-operation"><i class="fa fa-tasks"></i> <span>Operation:</span></label>
<select style="width: 100%" id="node-input-operation"></select>
<pre id="operation-description"></pre>
<div class="form-row">
<span> Debug: add request to output</span>
<input style="margin-left: 5px; margin-bottom: 6px" type="checkbox" id="node-input-debug">
</div>
<h3>Inputs:</h3>
<div class="form-row" id="body"></div>
<div class="form-row" id="pathParams"></div>
<div class="form-row" id="query"></div>
<div class="form-tips"><b>Tip:</b> If you do not have some of the inputs, leave them empty.</div>
</script>