node-red-contrib-prib-functions 0.23.2 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/copilot-instructions.md +36 -0
- package/README.md +153 -140
- package/columnar/columnar.html +258 -0
- package/columnar/columnar.js +1055 -0
- package/columnar/icons/columnar.svg +38 -0
- package/fileSystem/filesystem.html +299 -0
- package/fileSystem/filesystem.js +170 -0
- package/gitlab/gitlab.html +191 -0
- package/gitlab/gitlab.js +248 -0
- package/gitlab/icons/gitlab.svg +17 -0
- package/lib/AlphaBeta.js +32 -0
- package/lib/GraphDB.js +40 -9
- package/lib/MinMax.js +17 -0
- package/lib/Tree.js +64 -0
- package/lib/objectExtensions.js +28 -5
- package/lib/timeDimension.js +36 -0
- package/lib/typedInput.js +18 -2
- package/logisticRegression/icons/logisticregression.svg +22 -0
- package/logisticRegression/logisticRegression.html +136 -0
- package/logisticRegression/logisticRegression.js +83 -0
- package/package.json +21 -9
- package/test/02-graphdb.js +46 -0
- package/test/columnar.js +509 -0
- package/test/data/.config.nodes.json +114 -70
- package/test/data/.config.nodes.json.backup +104 -71
- package/test/data/.config.runtime.json +2 -1
- package/test/data/.config.runtime.json.backup +2 -1
- package/test/data/.config.users.json +3 -2
- package/test/data/.config.users.json.backup +3 -2
- package/test/data/.flow.json.backup +1545 -369
- package/test/data/flow.json +1457 -270
- package/test/data/package-lock.json +11 -11
- package/test/data/shares/.config.nodes.json +611 -0
- package/test/data/shares/.config.nodes.json.backup +589 -0
- package/test/data/shares/.config.runtime.json +5 -0
- package/test/data/shares/.config.runtime.json.backup +4 -0
- package/test/data/shares/.config.users.json +33 -0
- package/test/data/shares/.config.users.json.backup +33 -0
- package/test/data/shares/.flow.json.backup +230 -0
- package/test/data/shares/.flow_cred.json.backup +3 -0
- package/test/data/shares/flow.json +267 -0
- package/test/data/shares/flow_cred.json +3 -0
- package/test/data/shares/package.json +6 -0
- package/test/data/shares/settings.js +544 -0
- package/test/dataAnalysisExtensions.js +93 -93
- package/test/logisticRegression.js +379 -0
- package/test/transform.js +11 -11
- package/test/transformConfluence.js +4 -2
- package/test/transformNumPy.js +3 -1
- package/test/transformXLSX.js +4 -2
- package/test/transformXML.js +4 -2
- package/test-runner.js +400 -0
- package/test.parq +0 -0
- package/test_select.js +37 -0
- package/testing/test.js +8 -7
- package/transform/transform.html +23 -2
- package/transform/transform.js +239 -283
- package/transform/xlsx2.js +74 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('gitlab', {
|
|
3
|
+
category: 'function',
|
|
4
|
+
color: '#FC6D26',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: {value:"", required:false},
|
|
7
|
+
action: {value:"getRepo", required:true},
|
|
8
|
+
gitlabUrl: {value:"https://gitlab.com", required:true},
|
|
9
|
+
accessToken: {value:"", required:true},
|
|
10
|
+
projectId: {value:"", required:true},
|
|
11
|
+
filePath: {value:"", required:false}
|
|
12
|
+
},
|
|
13
|
+
inputs: 1,
|
|
14
|
+
inputLabels: "in",
|
|
15
|
+
outputs:1,
|
|
16
|
+
outputLabels: ["Result"],
|
|
17
|
+
paletteLabel: "GitLab",
|
|
18
|
+
icon: "gitlab.svg",
|
|
19
|
+
label: function() {
|
|
20
|
+
return this.name || (this.action + " GitLab");
|
|
21
|
+
},
|
|
22
|
+
labelStyle: function() {
|
|
23
|
+
return "node_label_italic";
|
|
24
|
+
},
|
|
25
|
+
oneditprepare: function() {
|
|
26
|
+
const node = this;
|
|
27
|
+
$("#node-input-action").change(function() {
|
|
28
|
+
const action = $(this).val();
|
|
29
|
+
if (action === 'getFile') {
|
|
30
|
+
$("#file-path-row").show();
|
|
31
|
+
} else {
|
|
32
|
+
$("#file-path-row").hide();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
$("#node-input-action").change(); // trigger on load
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<script type="text/x-red" data-template-name="gitlab">
|
|
41
|
+
<div class="form-row">
|
|
42
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
43
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
44
|
+
</div>
|
|
45
|
+
<div class="form-row">
|
|
46
|
+
<label for="node-input-action"><i class="fa fa-cogs"></i> Action</label>
|
|
47
|
+
<select type="text" id="node-input-action">
|
|
48
|
+
<option value="getRepo">Get Repository Info</option>
|
|
49
|
+
<option value="listIssues">List Issues</option>
|
|
50
|
+
<option value="createIssue">Create Issue</option>
|
|
51
|
+
<option value="getMergeRequests">Get Merge Requests</option>
|
|
52
|
+
<option value="createMergeRequest">Create Merge Request</option>
|
|
53
|
+
<option value="getFile">Get File</option>
|
|
54
|
+
<option value="runPipeline">Run Pipeline</option>
|
|
55
|
+
<option value="createCommit">Create Commit</option>
|
|
56
|
+
<option value="getCommits">Get Commits</option>
|
|
57
|
+
<option value="getCommit">Get Commit</option>
|
|
58
|
+
</select>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="form-row">
|
|
61
|
+
<label for="node-input-gitlabUrl">GitLab URL</label>
|
|
62
|
+
<input type="text" id="node-input-gitlabUrl" placeholder="https://gitlab.com">
|
|
63
|
+
</div>
|
|
64
|
+
<div class="form-row">
|
|
65
|
+
<label for="node-input-accessToken">Access Token</label>
|
|
66
|
+
<input type="password" id="node-input-accessToken" placeholder="GitLab personal access token">
|
|
67
|
+
</div>
|
|
68
|
+
<div class="form-row">
|
|
69
|
+
<label for="node-input-projectId">Project ID</label>
|
|
70
|
+
<input type="text" id="node-input-projectId" placeholder="project-id or namespace/project-name">
|
|
71
|
+
</div>
|
|
72
|
+
<div class="form-row" id="file-path-row" style="display:none;">
|
|
73
|
+
<label for="node-input-filePath">File Path</label>
|
|
74
|
+
<input type="text" id="node-input-filePath" placeholder="path/to/file.txt">
|
|
75
|
+
</div>
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<script type="text/x-red" data-help-name="gitlab">
|
|
79
|
+
<p>GitLab API access node for performing various operations on GitLab repositories.</p>
|
|
80
|
+
|
|
81
|
+
<h3>Configuration</h3>
|
|
82
|
+
<ul>
|
|
83
|
+
<li><b>GitLab URL:</b> The GitLab instance URL (default: https://gitlab.com)</li>
|
|
84
|
+
<li><b>Access Token:</b> GitLab personal access token with appropriate permissions</li>
|
|
85
|
+
<li><b>Project ID:</b> The project identifier (numeric ID or namespace/project-name)</li>
|
|
86
|
+
<li><b>File Path:</b> Path to file in repository (only shown for Get File action)</li>
|
|
87
|
+
</ul>
|
|
88
|
+
|
|
89
|
+
<h3>Actions</h3>
|
|
90
|
+
<ul>
|
|
91
|
+
<li><b>Get Repository Info:</b> Retrieves basic information about the repository</li>
|
|
92
|
+
<li><b>List Issues:</b> Lists issues in the repository. Optional filters in msg.payload: state, labels, assignee_id</li>
|
|
93
|
+
<li><b>Create Issue:</b> Creates a new issue. Required in msg.payload: title. Optional: description, labels, assignee_ids</li>
|
|
94
|
+
<li><b>Get Merge Requests:</b> Lists merge requests. Optional filters in msg.payload: state, source_branch, target_branch</li>
|
|
95
|
+
<li><b>Create Merge Request:</b> Creates a new merge request. Required in msg.payload: title, source_branch, target_branch. Optional: description, assignee_id, labels</li>
|
|
96
|
+
<li><b>Get File:</b> Retrieves file content from repository. File path can be in msg.payload.file_path or configured in node. Optional: ref (branch/tag/commit)</li>
|
|
97
|
+
<li><b>Run Pipeline:</b> Triggers a CI/CD pipeline. Optional in msg.payload: ref (default: main), variables (array of {key, value})</li>
|
|
98
|
+
<li><b>Create Commit:</b> Creates a new commit with file changes. Required in msg.payload: branch, commit_message, actions (array of file operations)</li>
|
|
99
|
+
<li><b>Get Commits:</b> Lists commits in repository. Optional filters in msg.payload: ref_name, since, until, path, author, per_page</li>
|
|
100
|
+
<li><b>Get Commit:</b> Retrieves details of a specific commit. Required in msg.payload: commit_sha</li>
|
|
101
|
+
</ul>
|
|
102
|
+
|
|
103
|
+
<h3>Authentication</h3>
|
|
104
|
+
<p>You need a GitLab personal access token with appropriate scopes:
|
|
105
|
+
<ul>
|
|
106
|
+
<li>api - for full API access</li>
|
|
107
|
+
<li>read_repository - for read operations</li>
|
|
108
|
+
<li>write_repository - for write operations</li>
|
|
109
|
+
</ul>
|
|
110
|
+
Create tokens at: GitLab Profile → Access Tokens</p>
|
|
111
|
+
|
|
112
|
+
<h3>Project ID</h3>
|
|
113
|
+
<p>The project ID can be found in the repository URL or project settings. It can be:
|
|
114
|
+
<ul>
|
|
115
|
+
<li>Numeric ID (e.g., 12345)</li>
|
|
116
|
+
<li>URL-encoded path (e.g., namespace%2Fproject-name)</li>
|
|
117
|
+
</ul></p>
|
|
118
|
+
|
|
119
|
+
<h3>Examples</h3>
|
|
120
|
+
|
|
121
|
+
<h4>Get Repository Info</h4>
|
|
122
|
+
<pre>Input: (any)
|
|
123
|
+
Output: Repository object with id, name, description, etc.</pre>
|
|
124
|
+
|
|
125
|
+
<h4>Create Issue</h4>
|
|
126
|
+
<pre>Input:
|
|
127
|
+
{
|
|
128
|
+
"title": "Bug report",
|
|
129
|
+
"description": "Found a critical bug",
|
|
130
|
+
"labels": ["bug", "critical"],
|
|
131
|
+
"assignee_ids": [123]
|
|
132
|
+
}
|
|
133
|
+
Output: Created issue object</pre>
|
|
134
|
+
|
|
135
|
+
<h4>Get File</h4>
|
|
136
|
+
<pre>Input:
|
|
137
|
+
{
|
|
138
|
+
"file_path": "README.md",
|
|
139
|
+
"ref": "main"
|
|
140
|
+
}
|
|
141
|
+
Output: File object with content, encoding, size, etc.</pre>
|
|
142
|
+
|
|
143
|
+
<h4>Run Pipeline</h4>
|
|
144
|
+
<pre>Input:
|
|
145
|
+
{
|
|
146
|
+
"ref": "develop",
|
|
147
|
+
"variables": [
|
|
148
|
+
{"key": "ENV", "value": "staging"}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
Output: Pipeline object with id, status, etc.</pre>
|
|
152
|
+
|
|
153
|
+
<h4>Create Commit</h4>
|
|
154
|
+
<pre>Input:
|
|
155
|
+
{
|
|
156
|
+
"branch": "main",
|
|
157
|
+
"commit_message": "Update README.md",
|
|
158
|
+
"actions": [
|
|
159
|
+
{
|
|
160
|
+
"action": "update",
|
|
161
|
+
"file_path": "README.md",
|
|
162
|
+
"content": "Updated content here..."
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
"author_email": "user@example.com",
|
|
166
|
+
"author_name": "User Name"
|
|
167
|
+
}
|
|
168
|
+
Output: Commit object with id, short_id, title, etc.</pre>
|
|
169
|
+
|
|
170
|
+
<h4>Get Commits</h4>
|
|
171
|
+
<pre>Input:
|
|
172
|
+
{
|
|
173
|
+
"ref_name": "main",
|
|
174
|
+
"per_page": 10,
|
|
175
|
+
"author": "user@example.com"
|
|
176
|
+
}
|
|
177
|
+
Output: Array of commit objects</pre>
|
|
178
|
+
|
|
179
|
+
<h4>Get Commit</h4>
|
|
180
|
+
<pre>Input:
|
|
181
|
+
{
|
|
182
|
+
"commit_sha": "abc123..."
|
|
183
|
+
}
|
|
184
|
+
Output: Detailed commit object with stats, files, etc.</pre>
|
|
185
|
+
|
|
186
|
+
<h3>Output</h3>
|
|
187
|
+
<p>Results are returned in msg.result. The structure depends on the action performed.</p>
|
|
188
|
+
|
|
189
|
+
<h3>Error Handling</h3>
|
|
190
|
+
<p>Errors from the GitLab API are caught and reported. Check msg.error for details.</p>
|
|
191
|
+
</script>
|
package/gitlab/gitlab.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
const logger = new (require("node-red-contrib-logger"))("GitLab Access");
|
|
2
|
+
logger.sendInfo("Copyright 2025 Jaroslav Peter Prib");
|
|
3
|
+
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
|
|
6
|
+
const actions = {
|
|
7
|
+
getRepo: async (RED, node, msg) => {
|
|
8
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}`;
|
|
9
|
+
const response = await axios.get(url, {
|
|
10
|
+
headers: {
|
|
11
|
+
'Private-Token': node.accessToken,
|
|
12
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return response.data;
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
listIssues: async (RED, node, msg) => {
|
|
19
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/issues`;
|
|
20
|
+
const params = {};
|
|
21
|
+
if (msg.payload && msg.payload.state) params.state = msg.payload.state;
|
|
22
|
+
if (msg.payload && msg.payload.labels) params.labels = msg.payload.labels;
|
|
23
|
+
if (msg.payload && msg.payload.assignee_id) params.assignee_id = msg.payload.assignee_id;
|
|
24
|
+
|
|
25
|
+
const response = await axios.get(url, {
|
|
26
|
+
headers: {
|
|
27
|
+
'Private-Token': node.accessToken,
|
|
28
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
29
|
+
},
|
|
30
|
+
params
|
|
31
|
+
});
|
|
32
|
+
return response.data;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
createIssue: async (RED, node, msg) => {
|
|
36
|
+
if (!msg.payload || !msg.payload.title) {
|
|
37
|
+
throw new Error("msg.payload must contain title for issue creation");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/issues`;
|
|
41
|
+
const data = {
|
|
42
|
+
title: msg.payload.title,
|
|
43
|
+
description: msg.payload.description || '',
|
|
44
|
+
labels: msg.payload.labels || [],
|
|
45
|
+
assignee_ids: msg.payload.assignee_ids || []
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const response = await axios.post(url, data, {
|
|
49
|
+
headers: {
|
|
50
|
+
'Private-Token': node.accessToken,
|
|
51
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return response.data;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
getMergeRequests: async (RED, node, msg) => {
|
|
58
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/merge_requests`;
|
|
59
|
+
const params = {};
|
|
60
|
+
if (msg.payload && msg.payload.state) params.state = msg.payload.state;
|
|
61
|
+
if (msg.payload && msg.payload.source_branch) params.source_branch = msg.payload.source_branch;
|
|
62
|
+
if (msg.payload && msg.payload.target_branch) params.target_branch = msg.payload.target_branch;
|
|
63
|
+
|
|
64
|
+
const response = await axios.get(url, {
|
|
65
|
+
headers: {
|
|
66
|
+
'Private-Token': node.accessToken,
|
|
67
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
68
|
+
},
|
|
69
|
+
params
|
|
70
|
+
});
|
|
71
|
+
return response.data;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
createMergeRequest: async (RED, node, msg) => {
|
|
75
|
+
if (!msg.payload || !msg.payload.title || !msg.payload.source_branch || !msg.payload.target_branch) {
|
|
76
|
+
throw new Error("msg.payload must contain title, source_branch, and target_branch for MR creation");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/merge_requests`;
|
|
80
|
+
const data = {
|
|
81
|
+
title: msg.payload.title,
|
|
82
|
+
description: msg.payload.description || '',
|
|
83
|
+
source_branch: msg.payload.source_branch,
|
|
84
|
+
target_branch: msg.payload.target_branch,
|
|
85
|
+
assignee_id: msg.payload.assignee_id,
|
|
86
|
+
labels: msg.payload.labels || []
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const response = await axios.post(url, data, {
|
|
90
|
+
headers: {
|
|
91
|
+
'Private-Token': node.accessToken,
|
|
92
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return response.data;
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
getFile: async (RED, node, msg) => {
|
|
99
|
+
const filePath = msg.payload && msg.payload.file_path ? msg.payload.file_path : node.filePath;
|
|
100
|
+
if (!filePath) {
|
|
101
|
+
throw new Error("file_path must be provided in msg.payload or configured in node");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/repository/files/${encodeURIComponent(filePath)}`;
|
|
105
|
+
const params = {};
|
|
106
|
+
if (msg.payload && msg.payload.ref) params.ref = msg.payload.ref;
|
|
107
|
+
else params.ref = 'main';
|
|
108
|
+
|
|
109
|
+
const response = await axios.get(url, {
|
|
110
|
+
headers: {
|
|
111
|
+
'Private-Token': node.accessToken,
|
|
112
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
113
|
+
},
|
|
114
|
+
params
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
file_path: response.data.file_path,
|
|
118
|
+
file_name: response.data.file_name,
|
|
119
|
+
size: response.data.size,
|
|
120
|
+
encoding: response.data.encoding,
|
|
121
|
+
content: Buffer.from(response.data.content, response.data.encoding).toString('utf8'),
|
|
122
|
+
ref: response.data.ref,
|
|
123
|
+
blob_id: response.data.blob_id,
|
|
124
|
+
commit_id: response.data.commit_id,
|
|
125
|
+
last_commit_id: response.data.last_commit_id
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
runPipeline: async (RED, node, msg) => {
|
|
130
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/pipeline`;
|
|
131
|
+
const data = {
|
|
132
|
+
ref: msg.payload && msg.payload.ref ? msg.payload.ref : 'main',
|
|
133
|
+
variables: msg.payload && msg.payload.variables ? msg.payload.variables : []
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const response = await axios.post(url, data, {
|
|
137
|
+
headers: {
|
|
138
|
+
'Private-Token': node.accessToken,
|
|
139
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return response.data;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
createCommit: async (RED, node, msg) => {
|
|
146
|
+
if (!msg.payload || !msg.payload.branch || !msg.payload.commit_message || !msg.payload.actions) {
|
|
147
|
+
throw new Error("msg.payload must contain branch, commit_message, and actions for commit creation");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/repository/commits`;
|
|
151
|
+
const data = {
|
|
152
|
+
branch: msg.payload.branch,
|
|
153
|
+
commit_message: msg.payload.commit_message,
|
|
154
|
+
actions: msg.payload.actions,
|
|
155
|
+
author_email: msg.payload.author_email,
|
|
156
|
+
author_name: msg.payload.author_name
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const response = await axios.post(url, data, {
|
|
160
|
+
headers: {
|
|
161
|
+
'Private-Token': node.accessToken,
|
|
162
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
return response.data;
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
getCommits: async (RED, node, msg) => {
|
|
169
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/repository/commits`;
|
|
170
|
+
const params = {};
|
|
171
|
+
if (msg.payload && msg.payload.ref_name) params.ref_name = msg.payload.ref_name;
|
|
172
|
+
if (msg.payload && msg.payload.since) params.since = msg.payload.since;
|
|
173
|
+
if (msg.payload && msg.payload.until) params.until = msg.payload.until;
|
|
174
|
+
if (msg.payload && msg.payload.path) params.path = msg.payload.path;
|
|
175
|
+
if (msg.payload && msg.payload.author) params.author = msg.payload.author;
|
|
176
|
+
params.per_page = msg.payload && msg.payload.per_page ? msg.payload.per_page : 20;
|
|
177
|
+
|
|
178
|
+
const response = await axios.get(url, {
|
|
179
|
+
headers: {
|
|
180
|
+
'Private-Token': node.accessToken,
|
|
181
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
182
|
+
},
|
|
183
|
+
params
|
|
184
|
+
});
|
|
185
|
+
return response.data;
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
getCommit: async (RED, node, msg) => {
|
|
189
|
+
const commitSha = msg.payload && msg.payload.commit_sha ? msg.payload.commit_sha : '';
|
|
190
|
+
if (!commitSha) {
|
|
191
|
+
throw new Error("commit_sha must be provided in msg.payload");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const url = `${node.gitlabUrl}/api/v4/projects/${encodeURIComponent(node.projectId)}/repository/commits/${commitSha}`;
|
|
195
|
+
|
|
196
|
+
const response = await axios.get(url, {
|
|
197
|
+
headers: {
|
|
198
|
+
'Private-Token': node.accessToken,
|
|
199
|
+
'User-Agent': 'Node-RED-GitLab-Node'
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return response.data;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = function (RED) {
|
|
206
|
+
function GitLabNode(config) {
|
|
207
|
+
RED.nodes.createNode(this, config);
|
|
208
|
+
const node = Object.assign(this, config, {
|
|
209
|
+
gitlabUrl: config.gitlabUrl || 'https://gitlab.com',
|
|
210
|
+
accessToken: config.accessToken || '',
|
|
211
|
+
projectId: config.projectId || '',
|
|
212
|
+
filePath: config.filePath || ''
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
node.callFunction = actions[config.action];
|
|
216
|
+
if (!node.callFunction) {
|
|
217
|
+
node.error("Unknown action: " + config.action);
|
|
218
|
+
node.status({ fill: "red", shape: "ring", text: "Unknown action: " + config.action });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!node.accessToken) {
|
|
223
|
+
node.error("GitLab access token is required");
|
|
224
|
+
node.status({ fill: "red", shape: "ring", text: "Access token required" });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!node.projectId) {
|
|
229
|
+
node.error("GitLab project ID is required");
|
|
230
|
+
node.status({ fill: "red", shape: "ring", text: "Project ID required" });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
node.status({ fill: "green", shape: "dot", text: "Ready" });
|
|
235
|
+
|
|
236
|
+
node.on('input', async function (msg) {
|
|
237
|
+
try {
|
|
238
|
+
msg.result = await node.callFunction(RED, node, msg);
|
|
239
|
+
node.send(msg);
|
|
240
|
+
node.status({ fill: "green", shape: "dot", text: "Success" });
|
|
241
|
+
} catch (error) {
|
|
242
|
+
node.error(error.message, msg);
|
|
243
|
+
node.status({ fill: "red", shape: "ring", text: error.message.substring(0, 20) });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
RED.nodes.registerType("gitlab", GitLabNode);
|
|
248
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="gitlabGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" style="stop-color:#FC6D26;stop-opacity:1" />
|
|
5
|
+
<stop offset="100%" style="stop-color:#E24329;stop-opacity:1" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
|
|
9
|
+
<!-- GitLab fox-like logo simplified -->
|
|
10
|
+
<path d="M16 2C8.3 2 2 8.3 2 16s6.3 14 14 14 14-6.3 14-14S23.7 2 16 2zm6.5 20.5c-.3.3-.7.3-1 0L16 17l-5.5 5.5c-.3.3-.7.3-1 0s-.3-.7 0-1L15 16l-5.5-5.5c-.3-.3-.3-.7 0-1s.7-.3 1 0L16 15l5.5-5.5c.3-.3.7-.3 1 0s.3.7 0 1L17 16l5.5 5.5c.3.3.3.7 0 1z" fill="url(#gitlabGradient)"/>
|
|
11
|
+
|
|
12
|
+
<!-- Simplified fox face -->
|
|
13
|
+
<circle cx="16" cy="12" r="2" fill="#FFFFFF"/>
|
|
14
|
+
<circle cx="13" cy="10" r="1" fill="#FFFFFF"/>
|
|
15
|
+
<circle cx="19" cy="10" r="1" fill="#FFFFFF"/>
|
|
16
|
+
<path d="M14 14c1-1 2-1 3 0" stroke="#FFFFFF" stroke-width="1" fill="none" stroke-linecap="round"/>
|
|
17
|
+
</svg>
|
package/lib/AlphaBeta.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function alphabeta(node, depth,
|
|
2
|
+
isTerminalNode=node=>node.children.lenght==0 ,
|
|
3
|
+
forEachChild=call=>this.children.forEach(c=>call(c)),
|
|
4
|
+
getHeuristicValue=node=>node.value,
|
|
5
|
+
alpha=-Infinity, beta=Infinity,
|
|
6
|
+
maximizingPlayer=true
|
|
7
|
+
){
|
|
8
|
+
if( depth == 0 || isTerminalNode(node)) return getHeuristicValue (node)
|
|
9
|
+
const notMaximizingPlayer=!maximizingPlayer
|
|
10
|
+
const minMaxFunction=maximizingPlayer?Math.max:Math.min
|
|
11
|
+
let value=Infinity*(maximizingPlayer?-1:1)
|
|
12
|
+
let loopFunction=maximizingPlayer?
|
|
13
|
+
child=>{
|
|
14
|
+
value = minMaxFunction(value,
|
|
15
|
+
minimax(child,depth-1,isTerminalNode,forEachChild,getHeuristicValue ,alpha, beta, notMaximizingPlayer))
|
|
16
|
+
if(value >= beta ) throw Error("break")
|
|
17
|
+
else alpha = minMaxFunction(alpha, value)
|
|
18
|
+
}:child=>{
|
|
19
|
+
value = minMaxFunction(value,
|
|
20
|
+
minimax(child,depth-1,isTerminalNode,forEachChild,getHeuristicValue ,alpha, beta, notMaximizingPlayer))
|
|
21
|
+
if(value <= alpha) throw Error("break")
|
|
22
|
+
beta = minMaxFunction(beta, value)
|
|
23
|
+
}
|
|
24
|
+
try{
|
|
25
|
+
forEachChild(loopFunction)
|
|
26
|
+
} catch(ex){
|
|
27
|
+
if(ex.message!=="break") throw ex
|
|
28
|
+
}
|
|
29
|
+
return value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports=alphabeta
|
package/lib/GraphDB.js
CHANGED
|
@@ -1,14 +1,45 @@
|
|
|
1
1
|
require("../lib/objectExtensions")
|
|
2
|
+
function Edge(from,to,properties){
|
|
3
|
+
if(!from) throw Error("from vertix is not defined")
|
|
4
|
+
if(!to) throw Error("to vertix is not defined")
|
|
5
|
+
if(!from instanceof Vertex) throw Error("from not type Vertex")
|
|
6
|
+
if(!to instanceof Vertex) throw Error("to not type Vertex")
|
|
7
|
+
Object.assign(this,{_from:from,_to:to},properties)
|
|
8
|
+
from.addTo(this)
|
|
9
|
+
to.addFrom(this)
|
|
10
|
+
}
|
|
11
|
+
function Vertex(properties){
|
|
12
|
+
Object.assign(this,{_to:[],_from:[]},properties)
|
|
13
|
+
}
|
|
14
|
+
Vertex.prototype.addTo=function(r){
|
|
15
|
+
this._to.push(r)
|
|
16
|
+
}
|
|
17
|
+
Vertex.prototype.addFrom=function(r){
|
|
18
|
+
this._from.push(r)
|
|
19
|
+
}
|
|
2
20
|
function GraphDB(properties){
|
|
3
|
-
|
|
4
|
-
|
|
21
|
+
Object.assign(this,{vertices:[],edges:[]},properties)
|
|
22
|
+
}
|
|
23
|
+
GraphDB.prototype.addEdge=function(from,to,properties) {
|
|
24
|
+
const edge=new Edge(from,to,properties)
|
|
25
|
+
this.edges.push(edge)
|
|
26
|
+
return edge
|
|
27
|
+
}
|
|
28
|
+
GraphDB.prototype.addVertex=function(vertexDetails) {
|
|
29
|
+
const vertex=new Vertex(vertexDetails)
|
|
30
|
+
this.vertices.push(vertex)
|
|
31
|
+
return vertex
|
|
32
|
+
}
|
|
33
|
+
GraphDB.prototype.getEdgesCount=function() {
|
|
34
|
+
return this.edges.length
|
|
35
|
+
}
|
|
36
|
+
GraphDB.prototype.getVerticesCount=function() {
|
|
37
|
+
return this.vertices.length
|
|
5
38
|
}
|
|
6
|
-
GraphDB.prototype.
|
|
7
|
-
|
|
39
|
+
GraphDB.prototype.getVertices=function(filterFunction) {
|
|
40
|
+
return this.vertices.filter(filterFunction)
|
|
8
41
|
}
|
|
9
|
-
GraphDB.prototype.
|
|
10
|
-
|
|
42
|
+
GraphDB.prototype.getEdges=function(filterFunction) {
|
|
43
|
+
return this.Edges.filter(filterFunction)
|
|
11
44
|
}
|
|
12
|
-
|
|
13
|
-
this.addErrorFunctions()
|
|
14
|
-
module.exports=GraphDB
|
|
45
|
+
module.exports=GraphDB
|
package/lib/MinMax.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function minimax(node, depth,
|
|
2
|
+
isTerminalNode=node=>node.children.lenght==0 ,
|
|
3
|
+
forEachChild=call=>this.children.forEach(c=>call(c)),
|
|
4
|
+
getHeuristicValue =node=>node.value,
|
|
5
|
+
maximizingPlayer=true
|
|
6
|
+
){
|
|
7
|
+
if( depth == 0 || isTerminalNode(node)) return getHeuristicValue (node)
|
|
8
|
+
const notMaximizingPlayer=!maximizingPlayer
|
|
9
|
+
const minMaxFunction=maximizingPlayer?Math.max:Math.min
|
|
10
|
+
let value=Infinity*(maximizingPlayer?-1:1)
|
|
11
|
+
forEachChild(child=> value = minMaxFunction(value,
|
|
12
|
+
minimax(child,depth-1,isTerminalNode,forEachChild,getHeuristicValue ,notMaximizingPlayer))
|
|
13
|
+
)
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports=minimax
|
package/lib/Tree.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
function Level(parent,...children){
|
|
2
|
+
this.children=children
|
|
3
|
+
this.setBTree(10)
|
|
4
|
+
}
|
|
5
|
+
function Leaf(parent,value){
|
|
6
|
+
this.values=[]
|
|
7
|
+
}
|
|
8
|
+
Leaf.prototype.find=function(value,foundCall,notFoundCall=foundCall,notFoundAllCall=v=>notFoundCall(0,v)){
|
|
9
|
+
let i=this.keys.length
|
|
10
|
+
const key=this.getKey(value)
|
|
11
|
+
while (i >= 0){
|
|
12
|
+
const currentKey=this.getKey(this.values[i])
|
|
13
|
+
if(key>currentKey) return notFoundCall(i,key)
|
|
14
|
+
if(key==currentKey) return foundCall(i,key)
|
|
15
|
+
i--
|
|
16
|
+
}
|
|
17
|
+
notFoundAllCall(key)
|
|
18
|
+
}
|
|
19
|
+
Leaf.prototype.getKey=function(node){return node}
|
|
20
|
+
Leaf.prototype.insert=function(value){
|
|
21
|
+
const _this=this
|
|
22
|
+
this.find(value,i=>_this.values.splice(i, 0, value))
|
|
23
|
+
}
|
|
24
|
+
function Tree (root=new Level(null)){
|
|
25
|
+
this.root=root
|
|
26
|
+
}
|
|
27
|
+
Tree.prototype.setDegree=function(v){this.degree=v}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Tree.prototype.insert(key,value) {
|
|
32
|
+
const root = this.root;
|
|
33
|
+
if(root.keys.length === (2 * this.degree - 1)) { // Root is full
|
|
34
|
+
const newRoot = new BTreeNode(false);
|
|
35
|
+
newRoot.children.push(root);
|
|
36
|
+
this.splitChild(newRoot, 0, root);
|
|
37
|
+
this.root = newRoot;
|
|
38
|
+
this.insertNonFull(newRoot, key);
|
|
39
|
+
} else {
|
|
40
|
+
this.insertNonFull(root, key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Tree.prototype.insertNonFull(node, key) {
|
|
45
|
+
let i = node.keys.length - 1;
|
|
46
|
+
if (node.isLeaf) {
|
|
47
|
+
|
|
48
|
+
} else {
|
|
49
|
+
while (i >= 0 && key < node.keys[i]) {
|
|
50
|
+
i--;
|
|
51
|
+
}
|
|
52
|
+
i++;
|
|
53
|
+
if (node.children[i].keys.length === (2 * this.degree - 1)) {
|
|
54
|
+
this.splitChild(node, i, node.children[i]);
|
|
55
|
+
if (key > node.keys[i]) {
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.insertNonFull(node.children[i], key);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
module.exports=Tree
|
package/lib/objectExtensions.js
CHANGED
|
@@ -440,8 +440,26 @@ const nullif=(a,b)=>{return a==b?null:a}
|
|
|
440
440
|
defineMethod(type,"deepClone",function(){ return new type(this)})
|
|
441
441
|
})
|
|
442
442
|
|
|
443
|
+
const getWord=(value,wordPosition)=>value.split(/\s+/g,wordPosition+1)[wordPosition-1]
|
|
444
|
+
|
|
445
|
+
const deepClone=(deepObject) =>{
|
|
446
|
+
if(deepObject==null) return null;
|
|
447
|
+
if(deepObject instanceof Array)
|
|
448
|
+
var newObj=[];
|
|
449
|
+
else if(deepObject instanceof String)
|
|
450
|
+
return new String(deepObject);
|
|
451
|
+
else if(deepObject instanceof Number)
|
|
452
|
+
return new Number(deepObject);
|
|
453
|
+
else if(deepObject instanceof Date)
|
|
454
|
+
return new Date(deepObject);
|
|
455
|
+
else if(typeof deepObject == "object")
|
|
456
|
+
var newObj={};
|
|
457
|
+
else return deepObject;
|
|
458
|
+
for (const i in deepObject)
|
|
459
|
+
newObj[i]=deepClone(deepObject[i]);
|
|
460
|
+
return newObj;
|
|
461
|
+
}
|
|
443
462
|
|
|
444
|
-
//defineMethod(TypedArray,"deepClone",function(){ return new type(this)})
|
|
445
463
|
defineMethod(Array,"deepClone",function(){
|
|
446
464
|
const clone=[]
|
|
447
465
|
for (let i in this)
|
|
@@ -449,11 +467,13 @@ defineMethod(Array,"deepClone",function(){
|
|
|
449
467
|
return clone
|
|
450
468
|
})
|
|
451
469
|
defineMethod(Object,"deepClone",function(){
|
|
452
|
-
const clone={}
|
|
453
|
-
for (let i in this)
|
|
454
|
-
clone[i]=this[i].deepClone()
|
|
455
|
-
return clone
|
|
470
|
+
// const clone={}
|
|
471
|
+
// for (let i in this)
|
|
472
|
+
// clone[i]=this[i].deepClone()
|
|
473
|
+
// return clone
|
|
474
|
+
return deepClone(this)
|
|
456
475
|
})
|
|
476
|
+
const rangeLimit=(value,min=value,max=value)=>value<min?min:value>max?max:value
|
|
457
477
|
defineMethod(Number, "rangeLimit",function(min,max) {return (min!=null && this<min) ? min : ((max!=null && this>max) ? max : this)})
|
|
458
478
|
defineMethod(BigInt, "rangeLimit",function(min,max) {return (min!=null && this<min) ? min : ((max!=null && this>max) ? max : this)})
|
|
459
479
|
defineMethod(Date, "rangeLimit",function(min,max) {return (min!=null && this<min) ? min : ((max!=null && this>max) ? max : this)})
|
|
@@ -465,7 +485,10 @@ module.exports={
|
|
|
465
485
|
coalesce:coalesce,
|
|
466
486
|
colours:colours,
|
|
467
487
|
colourSmallList:colourSmallList,
|
|
488
|
+
deepClone,deepClone,
|
|
489
|
+
getWord,getWord,
|
|
468
490
|
nullif:nullif,
|
|
491
|
+
rangeLimit,rangeLimit,
|
|
469
492
|
toDateType,
|
|
470
493
|
toDateTypeZulu,
|
|
471
494
|
}
|