Skip to content

Commit

Permalink
Merge pull request #1 from felix-reck/dev
Browse files Browse the repository at this point in the history
New Minor Version
  • Loading branch information
felix-reck authored Oct 3, 2024
2 parents 82c16a6 + b08506a commit 597cc96
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 107 deletions.
47 changes: 47 additions & 0 deletions lib/body-creator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
function workplaceBody(workplace,state,timestamp){

let ts = timestamp ?? new Date(); //either provide date or current date is being used

const body = {
"params": [
{
"acronym": "service.logging_ts",
"value": ts,
"operator": "EQUAL"
},
{
"acronym": "workplace.id",
"value": workplace,
"operator": "EQUAL"
},
{
"acronym": "workplace.status.id",
"value": state,
"operator": "EQUAL"
}
],
"columns": [],
"returnAsObject": false
}

return body;
}

function customBody(body){

if (typeof body == 'string') {
try {
body = JSON.parse(body)
} catch (error) {

}
}
return body;
}



module.exports = {
workplaceBody,
customBody
}
93 changes: 93 additions & 0 deletions lib/mip-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const cookieJar = {}; // Shared Cookie Storage
const axios = require('axios');
const https = require('https');
const responseHandler = require('./response-handler.js');

module.exports = {
// Cookie Management
getCookie: function (accessId) {
return cookieJar[accessId];
},
setCookie: function (accessId, cookie) {
cookieJar[accessId] = cookie;
},
updateSessionCookie: function (responseHeaders, accessId) {
const setCookieHeader = responseHeaders['set-cookie'];
if (setCookieHeader) {
const sessionCookie = setCookieHeader.find(cookie => cookie.startsWith('JSESSIONID'));
if (sessionCookie) {
this.setCookie(accessId, sessionCookie.split(';')[0]);
}
}
},

// Helper Functions
createOptions: function (config, requestBody) {
const agent = new https.Agent({ rejectUnauthorized: config.sslVerify === 'true' });
const endpoint = new URL(config.servicetype + '/' + config.service, config.url);

const optionsBase = {
method: config.method,
url: endpoint.href,
data: requestBody,
httpsAgent: agent
}

const optionsCookie = {
headers: {
'Cookie': this.getCookie(config.accessId),
'Content-Type': 'application/json'
}
};

const optionsAuth = {
auth: {
username: config.user,
password: config.password
},
headers: {
'X-Access-Id': config.accessId,
'Content-Type': 'application/json'
}
};
return { optionsCookie: { ...optionsBase, ...optionsCookie }, optionsAuth: { ...optionsBase, ...optionsAuth } };
},

performRequest: async function (node, requestOptions) {
let info = requestOptions.auth ? 'Requesting with credentials' : 'Requesting with cookie';
node.status({ fill: 'blue', shape: 'dot', text: info });
try {
return await axios(requestOptions);
} catch (error) {
if (error.response) {
// Der Request wurde ausgeführt und der Server hat eine Antwort gegeben
const status = error.response.status;
const errorMessage = `Error: Received ${status} - ${error.response.statusText}`;
//node.error(errorMessage, { payload: error.response.data });
return { success: false, status, errorMessage }; // Rückgabe eines Fehlerobjekts
} else {
// Fehler, der nicht durch den Server zurückgegeben wurde
node.error(`Error: ${error.message}`, { payload: error.message });
return { success: false, errorMessage: error.message }; // Rückgabe eines Fehlerobjekts
}
}
},

// Response Handling
handleResponse: function (response, format, servicetype, accessId) {
let payload;
if (format === 'modified' && servicetype === 'data') {
payload = responseHandler.formatData(response.data);
} else {
payload = response.data;
}

this.updateSessionCookie(response.headers, accessId); // Update the session cookie

return {
payload,
request: response.request._header,
status: response.status
};
}
};
34 changes: 32 additions & 2 deletions lib/mip-node.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
outputs: 1, // Number of outputs from the node
icon: 'font-awesome/fa-exchange', // Icon for the node
label: function () {
return this.name || this.url || 'MIP';
return this.name || this.url || 'mip node';
}
});
</script>
Expand Down Expand Up @@ -60,4 +60,34 @@
</select>
</div>

</script>
</script>

<script type="text/markdown" data-help-name="mip">
Connects to the workplace and changes its status.

### Inputs

: **msg.payload** json : contains request body. For example:
{
"params": [
{
"acronym": "order.id",
"value": "000000000815",
"operator": "EQUAL"
}
],
"columns": [],
"returnAsObject": false
}

for the request body beeing accepted, POST has to be used

### Outputs

: **msg.payload** (any) : Contains the output of the request, either in its original format or better formatted, based on the configuration.

### Configuration

: **Output** : Either the response as received from the web server or a more readable formatted output.
</script>

141 changes: 38 additions & 103 deletions lib/mip-node.js
Original file line number Diff line number Diff line change
@@ -1,125 +1,60 @@
const mipclient = require('./mip-client.js');
const bodyCreator = require('./body-creator.js');
const responseHandler = require('./response-handler.js');

module.exports = function (RED) {
function HttpNode(config) {
function httpNode(config) {
RED.nodes.createNode(this, config);
let node = this;
let cookieJar = {}; // Cookie Storage

node.on('input', async function (msg) {
const axios = require('axios');
const https = require('https');
const responseHandler = require('./responseHandler.js');
node.name = config.name || 'mip-node';

node.name = config.name || 'HX';
// Input parameters
const server = RED.nodes.getNode(config.server);
const service = (config.service || msg.service || '').replace(/\./g, '/');
const method = config.method || 'GET';
const format = config.format; // || 'original';
const servicetype = config.servicetype; //meta oder data

//Input parameters
const server = RED.nodes.getNode(config.server);
const url = server.url;
const user = server.user;
const password = server.password;
const accessId = server.accessId;
const sslVerify = server.sslVerify == 'true';

const agent = new https.Agent({ rejectUnauthorized: sslVerify });
const endpoint = new URL(servicetype + '/' + service, url);



let payload = msg.payload;

if (typeof payload == 'string') {
try {
payload = JSON.parse(payload)
} catch (error) {

}
}

payload = format == 'original' ? payload : { ...payload, ...{ returnAsObject: false } };

const optionsCookie = {
const format = config.format;
const servicetype = config.servicetype;

// Create request body
let requestBody = bodyCreator.customBody(msg.payload);
requestBody = format === 'original' ? requestBody : { ...requestBody, returnAsObject: false };

// Create options for the request using the mip-client helper
const options = mipclient.createOptions({
url: server.url,
user: server.user,
password: server.password,
accessId: server.accessId,
sslVerify: server.sslVerify,
method: method,
url: endpoint.href,
headers: {
'Cookie': cookieJar[accessId],
'Content-Type': 'application/json'
},
data: payload,
httpsAgent: agent
};

const optionsAuth = {
auth: {
username: user,
password: password
},
headers: {
'X-Access-Id': accessId,
'Content-Type': 'application/json'
}
};

const options = cookieJar[accessId] ? optionsCookie : { ...optionsCookie, ...optionsAuth };

try {
const response = await performRequest(options);
handleResponse(response, msg);
} catch (error) {
await handleRetry(error, optionsCookie, optionsAuth, msg);
}
service: service,
servicetype: servicetype
}, requestBody);


async function performRequest(requestOptions) {
let info = requestOptions.auth ? 'Requesting with credentials' : 'Requesting with cookie';
node.status({ fill: 'blue', shape: 'dot', text: info });
return await axios(requestOptions);
let response = await mipclient.performRequest(node, options.optionsCookie);
if (response.status === 401) {
response = await mipclient.performRequest(node, options.optionsAuth);
}
if (response.status === 200) {
const result = mipclient.handleResponse(response, format, servicetype, server.accessId);
msg.payload = result.payload;
msg.request = result.request;

function handleResponse(response, msg) {

if (format == 'modified' && servicetype == 'data') {
msg.payload = responseHandler.formatData(response.data)
} else {
msg.payload = response.data

}

msg.request = response.request._header;
const setCookieHeader = response.headers['set-cookie'];
if (setCookieHeader) {
const sessionCookie = setCookieHeader.find(cookie => cookie.startsWith('JSESSIONID'));
if (sessionCookie) {
cookieJar[accessId] = sessionCookie.split(';')[0];
}
}
node.status({ fill: 'green', shape: 'dot', text: 'Success, HTTP ' + response.status });
node.send(msg);
}
} else {
node.status({ fill: 'red', shape: 'dot', text: 'Fail' });
node.error('FAIL')

async function handleRetry(error, optionsCookie, optionsAuth, msg) {
if (error.response && error.response.status === 401) {
node.warn('Retry with Auth');
try {
const response = await performRequest({ ...optionsCookie, ...optionsAuth });
handleResponse(response, msg);
} catch (retryError) {
node.status({ fill: 'red', shape: 'dot', text: 'Fail HTTP:' + retryError.status });
node.error(retryError.message, msg);
node.send(retryError);
}
} else {
node.status({ fill: 'red', shape: 'dot', text: 'Fail' });
node.error(error.message, msg);
node.send(error);
}
}
});



});
}

RED.nodes.registerType('mip', HttpNode);
RED.nodes.registerType('mip', httpNode);
};
Loading

0 comments on commit 597cc96

Please sign in to comment.