@justbrunasso/n8n-nodes-glpi 1.0.2
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/README.md +109 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/Glpi/Glpi.descriptions.d.ts +3 -0
- package/dist/nodes/Glpi/Glpi.descriptions.js +132 -0
- package/dist/nodes/Glpi/Glpi.descriptions.js.map +1 -0
- package/dist/nodes/Glpi/Glpi.node.d.ts +8 -0
- package/dist/nodes/Glpi/Glpi.node.js +253 -0
- package/dist/nodes/Glpi/Glpi.node.js.map +1 -0
- package/dist/nodes/Glpi/GlpiApi.credentials.d.ts +7 -0
- package/dist/nodes/Glpi/GlpiApi.credentials.js +57 -0
- package/dist/nodes/Glpi/GlpiApi.credentials.js.map +1 -0
- package/index.ts +2 -0
- package/justbrunasso-n8n-nodes-glpi-1.0.2.tgz +0 -0
- package/nodes/Glpi/Glpi.descriptions.ts +131 -0
- package/nodes/Glpi/Glpi.node.ts +265 -0
- package/nodes/Glpi/GlpiApi.credentials.ts +52 -0
- package/package.json +33 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# n8n-nodes-glpi
|
|
2
|
+
|
|
3
|
+
GLPI REST API node for n8n — GLPI 10.x compatible — **Criteria Builder** + **automatic session** (no cache).
|
|
4
|
+
|
|
5
|
+
**Features**
|
|
6
|
+
- Automatic `initSession` on each execution (no cache) — você não precisa chamar Get Session Token manualmente.
|
|
7
|
+
- Criteria Builder: adicione múltiplos critérios e envie `criteria[n][...]` corretamente.
|
|
8
|
+
- Operações suportadas (v1):
|
|
9
|
+
- initSession
|
|
10
|
+
- killSession (force logout)
|
|
11
|
+
- search (generic) — Criteria Builder
|
|
12
|
+
- getTickets (convenience)
|
|
13
|
+
- listSearchOptions
|
|
14
|
+
- getItem (GET /:itemtype/:id)
|
|
15
|
+
- getTicketFollowup
|
|
16
|
+
- getTicketSolution
|
|
17
|
+
- getTicketTasks
|
|
18
|
+
- getUserById
|
|
19
|
+
- getUserByEmail (helper)
|
|
20
|
+
- createTicket (POST /Ticket/)
|
|
21
|
+
- updateTicket (PUT /Ticket/:id)
|
|
22
|
+
- addFollowup (POST /Ticket/:id/ITILFollowup)
|
|
23
|
+
- addSolution (POST /ITILSolution/)
|
|
24
|
+
|
|
25
|
+
> Implementação obtida e revisada com base na documentação oficial do GLPI (initSession é GET; listSearchOptions/search endpoints e uso de headers). :contentReference[oaicite:1]{index=1}
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Instalação (local / desenvolvimento)
|
|
30
|
+
|
|
31
|
+
1. Coloque a pasta `n8n-nodes-glpi` no seu workspace.
|
|
32
|
+
2. `npm install`
|
|
33
|
+
3. `npm run build`
|
|
34
|
+
4. No n8n (instância), instale o pacote (opções):
|
|
35
|
+
- Link local: `npm link` no pacote e `npm link n8n-nodes-glpi` na instância do n8n.
|
|
36
|
+
- Ou publique no npm e `npm install n8n-nodes-glpi` no n8n.
|
|
37
|
+
5. Reinicie o n8n.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Credenciais
|
|
42
|
+
|
|
43
|
+
Crie credenciais do tipo **GLPI API** no n8n com:
|
|
44
|
+
|
|
45
|
+
- Base URL: `https://seu-glpi.exemplo` (sem `/apirest.php`)
|
|
46
|
+
- App Token (obrigatório)
|
|
47
|
+
- User Token (opcional) — se presente, usará `Authorization: user_token <token>`
|
|
48
|
+
- Username/Password (opcional) — se não usar userToken, o node usa Basic Auth
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Como usar (exemplos)
|
|
53
|
+
|
|
54
|
+
### 1) Init Session (operação manual — raramente precisa)
|
|
55
|
+
- Operation: `Init Session`
|
|
56
|
+
- Executar → retorna objeto com `session_token`.
|
|
57
|
+
|
|
58
|
+
### 2) Search com Criteria Builder
|
|
59
|
+
- Operation: `Search`
|
|
60
|
+
- Entity: `Ticket`
|
|
61
|
+
- Criteria:
|
|
62
|
+
- Field: `19`
|
|
63
|
+
- Search type: `morethan`
|
|
64
|
+
- Value: `={{ (() => { const d = new Date(); d.setDate(d.getDate() - 1); return d.toISOString().split('T')[0] + ' 00:00:00'; })() }}`
|
|
65
|
+
- Glue: `AND`
|
|
66
|
+
- Range: `0-500`
|
|
67
|
+
|
|
68
|
+
> O node monta parâmetros do tipo `criteria[n][...]` e envia para `/apirest.php/search/Ticket`. Exemplo:
|
|
69
|
+
>
|
|
70
|
+
> `criteria[0][field]=...&criteria[0][searchtype]=...&criteria[0][value]=...&criteria[0][glue]=AND&range=0-50`
|
|
71
|
+
>
|
|
72
|
+
> (Nota: o operador lógico é enviado em `criteria[n][glue]` — ajuste apenas se sua versão da API exigir outra chave.)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Headers gerados (exemplo)
|
|
77
|
+
- Sempre: `App-Token: <appToken>`
|
|
78
|
+
- Se `userToken` presente: `Authorization: user_token <userToken>`
|
|
79
|
+
- Se usar username/password: Basic Auth via `auth` (não envia header Authorization direto, usa credenciais HTTP)
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Testar localmente (recomendações rápidas)
|
|
84
|
+
1. Build:
|
|
85
|
+
```bash
|
|
86
|
+
npm install
|
|
87
|
+
npm run build
|
|
88
|
+
```
|
|
89
|
+
2. Gerar tarball e instalar na instância do n8n:
|
|
90
|
+
```bash
|
|
91
|
+
npm pack
|
|
92
|
+
# copia o .tgz para o host do n8n e, no diretório do n8n:
|
|
93
|
+
npm install /caminho/para/n8n-nodes-glpi-1.0.0.tgz
|
|
94
|
+
# reinicie o n8n
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Publicação
|
|
100
|
+
- Adicione em package.json: `"files": ["dist"]`, `repository`, `bugs` e `homepage`.
|
|
101
|
+
- Mantenha `n8n-core` e `n8n-workflow` como peerDependencies e instale-os como devDependencies para desenvolvimento.
|
|
102
|
+
- Use `npm publish --access public` após verificar o tarball (`npm pack`) localmente.
|
|
103
|
+
|
|
104
|
+
# Troubleshooting rápido
|
|
105
|
+
- Erro TS no VS Code: instale devDependencies (`n8n-core`, `n8n-workflow`, `@types/node`, `typescript`) e reinicie o TS Server (Ctrl+Shift+P → TypeScript: Restart TS Server).
|
|
106
|
+
- Se o node não aparecer no n8n, verifique se `dist/index.js` existe e se instalou o pacote na instância do n8n; reinicie o serviço n8n.
|
|
107
|
+
|
|
108
|
+
# Licença / Contribuição
|
|
109
|
+
- Inclua LICENSE e um link para issues/PRs no package.json.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./nodes/Glpi/Glpi.node"), exports);
|
|
18
|
+
__exportStar(require("./nodes/Glpi/GlpiApi.credentials"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yDAAuC;AACvC,mEAAiD"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.glpiFields = exports.glpiOperations = void 0;
|
|
4
|
+
exports.glpiOperations = [
|
|
5
|
+
{
|
|
6
|
+
displayName: 'Operation',
|
|
7
|
+
name: 'operation',
|
|
8
|
+
type: 'options',
|
|
9
|
+
options: [
|
|
10
|
+
{ name: 'Init Session', value: 'initSession', description: 'GET /apirest.php/initSession' },
|
|
11
|
+
{ name: 'Kill Session (force logout)', value: 'killSession', description: 'GET /apirest.php/killSession' },
|
|
12
|
+
{ name: 'Search (generic) - Criteria Builder', value: 'search', description: 'GET /apirest.php/search/:entity' },
|
|
13
|
+
{ name: 'Get Tickets (convenience)', value: 'getTickets', description: 'Search Ticket (with criteria builder or searchText)' },
|
|
14
|
+
{ name: 'List Search Options', value: 'listSearchOptions', description: 'GET /apirest.php/listSearchOptions/:entity' },
|
|
15
|
+
{ name: 'Get Item', value: 'getItem', description: 'GET /apirest.php/:itemtype/:id' },
|
|
16
|
+
{ name: 'Get Ticket Followup', value: 'getTicketFollowup', description: 'GET /apirest.php/Ticket/:id/ITILFollowup' },
|
|
17
|
+
{ name: 'Get Ticket Solution', value: 'getTicketSolution', description: 'GET /apirest.php/Ticket/:id/ITILSolution' },
|
|
18
|
+
{ name: 'Get Ticket Tasks', value: 'getTicketTasks', description: 'GET /apirest.php/Ticket/:id/TicketTask' },
|
|
19
|
+
{ name: 'Get User by ID', value: 'getUserById', description: 'GET /apirest.php/User/:id' },
|
|
20
|
+
{ name: 'Get User by Email (helper)', value: 'getUserByEmail', description: 'Search User by email' },
|
|
21
|
+
{ name: 'Create Ticket', value: 'createTicket', description: 'POST /apirest.php/Ticket' },
|
|
22
|
+
{ name: 'Update Ticket', value: 'updateTicket', description: 'PUT /apirest.php/Ticket/:id' },
|
|
23
|
+
{ name: 'Add Followup', value: 'addFollowup', description: 'POST /apirest.php/Ticket/:id/ITILFollowup' },
|
|
24
|
+
{ name: 'Add Solution', value: 'addSolution', description: 'POST /apirest.php/ITILSolution' },
|
|
25
|
+
],
|
|
26
|
+
default: 'initSession',
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
exports.glpiFields = [
|
|
30
|
+
// entity for search and listSearchOptions
|
|
31
|
+
{
|
|
32
|
+
displayName: 'Entity',
|
|
33
|
+
name: 'entity',
|
|
34
|
+
type: 'string',
|
|
35
|
+
default: 'Ticket',
|
|
36
|
+
description: 'Entity name (Ticket, User, ITILCategory, ...).',
|
|
37
|
+
displayOptions: { show: { operation: ['search', 'listSearchOptions', 'getTickets'] } },
|
|
38
|
+
},
|
|
39
|
+
// Criteria builder as fixedCollection multiple
|
|
40
|
+
{
|
|
41
|
+
displayName: 'Criteria',
|
|
42
|
+
name: 'criteria',
|
|
43
|
+
type: 'fixedCollection',
|
|
44
|
+
typeOptions: { multipleValues: true },
|
|
45
|
+
placeholder: 'Add Criterion',
|
|
46
|
+
description: 'One or more criteria for the search',
|
|
47
|
+
displayOptions: {
|
|
48
|
+
show: {
|
|
49
|
+
operation: ['search', 'getTickets'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
options: [
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Criterion',
|
|
55
|
+
name: 'criterion',
|
|
56
|
+
values: [
|
|
57
|
+
{ displayName: 'Field', name: 'field', type: 'string', default: '' },
|
|
58
|
+
{ displayName: 'Search Type', name: 'searchtype', type: 'options', options: [{ name: 'contains', value: 'contains' }], default: 'contains' },
|
|
59
|
+
{ displayName: 'Value', name: 'value', type: 'string', default: '' },
|
|
60
|
+
{ displayName: 'Glue (AND/OR)', name: 'glue', type: 'options', options: [{ name: 'AND', value: 'AND' }, { name: 'OR', value: 'OR' }], default: 'AND' },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
default: [], // <-- obrigatório para fixedCollection com multipleValues: true
|
|
65
|
+
},
|
|
66
|
+
// range and searchText
|
|
67
|
+
{
|
|
68
|
+
displayName: 'Range',
|
|
69
|
+
name: 'range',
|
|
70
|
+
type: 'string',
|
|
71
|
+
default: '0-50',
|
|
72
|
+
displayOptions: { show: { operation: ['search', 'getTickets'] } },
|
|
73
|
+
description: 'Range parameter for GLPI (ex: 0-500).'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
displayName: 'Search Text (convenience)',
|
|
77
|
+
name: 'searchText',
|
|
78
|
+
type: 'string',
|
|
79
|
+
default: '',
|
|
80
|
+
displayOptions: { show: { operation: ['getTickets'] } },
|
|
81
|
+
description: 'searchText param for Ticket search.',
|
|
82
|
+
},
|
|
83
|
+
// generic itemId & ticketInput
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Item ID',
|
|
86
|
+
name: 'itemId',
|
|
87
|
+
type: 'number',
|
|
88
|
+
default: 0,
|
|
89
|
+
displayOptions: { show: { operation: ['getTicketFollowup', 'getTicketSolution', 'getTicketTasks', 'getItem', 'updateTicket', 'addFollowup'] } },
|
|
90
|
+
description: 'ID of the item.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
displayName: 'Ticket Input (JSON)',
|
|
94
|
+
name: 'ticketInput',
|
|
95
|
+
type: 'json',
|
|
96
|
+
default: { input: {} },
|
|
97
|
+
displayOptions: { show: { operation: ['createTicket', 'updateTicket'] } },
|
|
98
|
+
description: 'Provide {"input": { ... }} body for Ticket creation or update.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
displayName: 'Followup Input (JSON)',
|
|
102
|
+
name: 'followupInput',
|
|
103
|
+
type: 'json',
|
|
104
|
+
default: { input: {} },
|
|
105
|
+
displayOptions: { show: { operation: ['addFollowup'] } },
|
|
106
|
+
description: 'Provide {"input": {...}} for ITILFollowup.',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
displayName: 'Solution Input (JSON)',
|
|
110
|
+
name: 'solutionInput',
|
|
111
|
+
type: 'json',
|
|
112
|
+
default: { input: {} },
|
|
113
|
+
displayOptions: { show: { operation: ['addSolution'] } },
|
|
114
|
+
description: 'Provide {"input": {...}} for ITILSolution.',
|
|
115
|
+
},
|
|
116
|
+
// user helpers
|
|
117
|
+
{
|
|
118
|
+
displayName: 'User ID',
|
|
119
|
+
name: 'userId',
|
|
120
|
+
type: 'number',
|
|
121
|
+
default: 0,
|
|
122
|
+
displayOptions: { show: { operation: ['getUserById'] } },
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
displayName: 'User Email (helper)',
|
|
126
|
+
name: 'userEmail',
|
|
127
|
+
type: 'string',
|
|
128
|
+
default: '',
|
|
129
|
+
displayOptions: { show: { operation: ['getUserByEmail'] } },
|
|
130
|
+
}
|
|
131
|
+
];
|
|
132
|
+
//# sourceMappingURL=Glpi.descriptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Glpi.descriptions.js","sourceRoot":"","sources":["../../../nodes/Glpi/Glpi.descriptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class Glpi implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
private buildBaseAuth;
|
|
5
|
+
private getSessionToken;
|
|
6
|
+
private buildCriteriaQuery;
|
|
7
|
+
execute(this: any): Promise<INodeExecutionData[][]>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Glpi = void 0;
|
|
4
|
+
const Glpi_descriptions_1 = require("./Glpi.descriptions");
|
|
5
|
+
class Glpi {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'GLPI',
|
|
9
|
+
name: 'glpi',
|
|
10
|
+
icon: 'file:glpi.svg',
|
|
11
|
+
group: ['transform'],
|
|
12
|
+
version: 1,
|
|
13
|
+
description: 'GLPI REST API node (GLPI 10.x) with Criteria Builder and automatic session (no cache).',
|
|
14
|
+
defaults: { name: 'GLPI' },
|
|
15
|
+
inputs: ['main'],
|
|
16
|
+
outputs: ['main'],
|
|
17
|
+
credentials: [{ name: 'glpiApi', required: true }],
|
|
18
|
+
properties: [
|
|
19
|
+
...Glpi_descriptions_1.glpiOperations,
|
|
20
|
+
...Glpi_descriptions_1.glpiFields,
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// build base headers & possible basic auth from credentials
|
|
25
|
+
async buildBaseAuth() {
|
|
26
|
+
const credentials = await this.getCredentials('glpiApi');
|
|
27
|
+
if (!credentials)
|
|
28
|
+
throw new Error('Credenciais GLPI não configuradas.');
|
|
29
|
+
const baseUrl = credentials.baseUrl.replace(/\/+$/, '');
|
|
30
|
+
const appToken = credentials.appToken;
|
|
31
|
+
const userToken = credentials.userToken;
|
|
32
|
+
const username = credentials.username;
|
|
33
|
+
const password = credentials.password;
|
|
34
|
+
const headers = {
|
|
35
|
+
'App-Token': appToken,
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
};
|
|
38
|
+
let auth;
|
|
39
|
+
if (userToken && userToken.toString().trim() !== '') {
|
|
40
|
+
headers['Authorization'] = `user_token ${userToken}`;
|
|
41
|
+
}
|
|
42
|
+
else if (username && username.toString().trim() !== '') {
|
|
43
|
+
auth = { user: username, pass: password };
|
|
44
|
+
}
|
|
45
|
+
return { baseUrl, headers, auth };
|
|
46
|
+
}
|
|
47
|
+
// request initSession and return session token
|
|
48
|
+
async getSessionToken(baseUrl, headers, auth) {
|
|
49
|
+
// initSession is GET
|
|
50
|
+
const endpoint = `${baseUrl}/apirest.php/initSession`;
|
|
51
|
+
const response = await this.helpers.request({
|
|
52
|
+
method: 'GET',
|
|
53
|
+
uri: endpoint,
|
|
54
|
+
headers,
|
|
55
|
+
auth,
|
|
56
|
+
json: true,
|
|
57
|
+
resolveWithFullResponse: false,
|
|
58
|
+
});
|
|
59
|
+
// response usually contains session_token
|
|
60
|
+
// support different shapes: { session_token: '...' } or { session: { session_token: '...' } }
|
|
61
|
+
if (!response) {
|
|
62
|
+
throw new Error('No response from GLPI initSession');
|
|
63
|
+
}
|
|
64
|
+
const token = response.session_token || response.session?.session_token;
|
|
65
|
+
if (!token) {
|
|
66
|
+
throw new Error('No session token returned by GLPI initSession');
|
|
67
|
+
}
|
|
68
|
+
return token;
|
|
69
|
+
}
|
|
70
|
+
// monta query string para criteria[] usando índice sequencial
|
|
71
|
+
buildCriteriaQuery(criteriaInput) {
|
|
72
|
+
if (!criteriaInput || !Array.isArray(criteriaInput) || criteriaInput.length === 0)
|
|
73
|
+
return '';
|
|
74
|
+
const parts = [];
|
|
75
|
+
// Because fixedCollection returns array of {criterion: [ ... ]}, flatten
|
|
76
|
+
let flat = [];
|
|
77
|
+
for (const c of criteriaInput) {
|
|
78
|
+
if (c && c.criterion && Array.isArray(c.criterion))
|
|
79
|
+
flat = flat.concat(c.criterion);
|
|
80
|
+
else if (Array.isArray(c))
|
|
81
|
+
flat = flat.concat(c);
|
|
82
|
+
else
|
|
83
|
+
flat.push(c);
|
|
84
|
+
}
|
|
85
|
+
for (let i = 0; i < flat.length; i++) {
|
|
86
|
+
const item = flat[i];
|
|
87
|
+
parts.push(`criteria[${i}][field]=${encodeURIComponent(String(item.field))}`);
|
|
88
|
+
parts.push(`criteria[${i}][searchtype]=${encodeURIComponent(String(item.searchtype))}`);
|
|
89
|
+
parts.push(`criteria[${i}][value]=${encodeURIComponent(String(item.value))}`);
|
|
90
|
+
// send glue (logical operator) — use "glue" key (change to "email" only if required for compatibility)
|
|
91
|
+
parts.push(`criteria[${i}][glue]=${encodeURIComponent(String(item.glue || 'AND'))}`);
|
|
92
|
+
}
|
|
93
|
+
return parts.join('&');
|
|
94
|
+
}
|
|
95
|
+
async execute() {
|
|
96
|
+
const items = this.getInputData();
|
|
97
|
+
const returnData = [];
|
|
98
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
99
|
+
// build base auth & headers
|
|
100
|
+
const { baseUrl, headers: baseHeaders, auth } = await this.buildBaseAuth();
|
|
101
|
+
try {
|
|
102
|
+
// If operation is initSession itself, just call it and return
|
|
103
|
+
if (operation === 'initSession') {
|
|
104
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/initSession`, headers: baseHeaders, auth, json: true });
|
|
105
|
+
returnData.push({ json: response });
|
|
106
|
+
return [returnData];
|
|
107
|
+
}
|
|
108
|
+
// For all other operations, per your option C (no cache) -> request NEW session token now
|
|
109
|
+
const sessionToken = await this.getSessionToken(baseUrl, baseHeaders, auth);
|
|
110
|
+
// add Session-Token header for subsequent requests
|
|
111
|
+
const headers = { ...baseHeaders, 'Session-Token': sessionToken };
|
|
112
|
+
// handle operations
|
|
113
|
+
// killSession (force logout)
|
|
114
|
+
if (operation === 'killSession') {
|
|
115
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/killSession`, headers, json: true });
|
|
116
|
+
returnData.push({ json: response });
|
|
117
|
+
return [returnData];
|
|
118
|
+
}
|
|
119
|
+
// listSearchOptions
|
|
120
|
+
if (operation === 'listSearchOptions') {
|
|
121
|
+
const entity = this.getNodeParameter('entity', 0);
|
|
122
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/listSearchOptions/${entity}`, headers, json: true });
|
|
123
|
+
returnData.push({ json: response });
|
|
124
|
+
return [returnData];
|
|
125
|
+
}
|
|
126
|
+
// generic search (build criteria)
|
|
127
|
+
if (operation === 'search') {
|
|
128
|
+
const entity = this.getNodeParameter('entity', 0);
|
|
129
|
+
const criteriaInput = this.getNodeParameter('criteria', 0, []);
|
|
130
|
+
const range = this.getNodeParameter('range', 0, '0-50');
|
|
131
|
+
const qs = this.buildCriteriaQuery(criteriaInput);
|
|
132
|
+
const endpoint = `${baseUrl}/apirest.php/search/${entity}?${qs}&range=${encodeURIComponent(range)}`;
|
|
133
|
+
const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
|
|
134
|
+
returnData.push({ json: response });
|
|
135
|
+
return [returnData];
|
|
136
|
+
}
|
|
137
|
+
// getTickets convenience
|
|
138
|
+
if (operation === 'getTickets') {
|
|
139
|
+
const criteriaInput = this.getNodeParameter('criteria', 0, []);
|
|
140
|
+
const range = this.getNodeParameter('range', 0, '0-50');
|
|
141
|
+
const searchText = this.getNodeParameter('searchText', 0, '');
|
|
142
|
+
const qs = this.buildCriteriaQuery(criteriaInput);
|
|
143
|
+
let endpoint = `${baseUrl}/apirest.php/search/Ticket?${qs}&range=${encodeURIComponent(range)}`;
|
|
144
|
+
if (searchText && searchText.trim() !== '')
|
|
145
|
+
endpoint += `&searchText=${encodeURIComponent(searchText)}`;
|
|
146
|
+
const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
|
|
147
|
+
returnData.push({ json: response });
|
|
148
|
+
return [returnData];
|
|
149
|
+
}
|
|
150
|
+
// getItem generic
|
|
151
|
+
if (operation === 'getItem') {
|
|
152
|
+
const entity = this.getNodeParameter('entity', 0);
|
|
153
|
+
const itemId = this.getNodeParameter('itemId', 0);
|
|
154
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/${entity}/${itemId}`, headers, json: true });
|
|
155
|
+
returnData.push({ json: response });
|
|
156
|
+
return [returnData];
|
|
157
|
+
}
|
|
158
|
+
// getTicketFollowup
|
|
159
|
+
if (operation === 'getTicketFollowup') {
|
|
160
|
+
const itemId = this.getNodeParameter('itemId', 0);
|
|
161
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, json: true });
|
|
162
|
+
returnData.push({ json: response });
|
|
163
|
+
return [returnData];
|
|
164
|
+
}
|
|
165
|
+
// getTicketSolution
|
|
166
|
+
if (operation === 'getTicketSolution') {
|
|
167
|
+
const itemId = this.getNodeParameter('itemId', 0);
|
|
168
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILSolution`, headers, json: true });
|
|
169
|
+
returnData.push({ json: response });
|
|
170
|
+
return [returnData];
|
|
171
|
+
}
|
|
172
|
+
// getTicketTasks
|
|
173
|
+
if (operation === 'getTicketTasks') {
|
|
174
|
+
const itemId = this.getNodeParameter('itemId', 0);
|
|
175
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/TicketTask`, headers, json: true });
|
|
176
|
+
returnData.push({ json: response });
|
|
177
|
+
return [returnData];
|
|
178
|
+
}
|
|
179
|
+
// getUserById
|
|
180
|
+
if (operation === 'getUserById') {
|
|
181
|
+
const userId = this.getNodeParameter('userId', 0);
|
|
182
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/User/${userId}`, headers, json: true });
|
|
183
|
+
returnData.push({ json: response });
|
|
184
|
+
return [returnData];
|
|
185
|
+
}
|
|
186
|
+
// getUserByEmail helper
|
|
187
|
+
if (operation === 'getUserByEmail') {
|
|
188
|
+
const email = this.getNodeParameter('userEmail', 0);
|
|
189
|
+
if (!email)
|
|
190
|
+
throw new Error('E-mail obrigatório.');
|
|
191
|
+
const qs = `criteria[0][glue]=AND&criteria[0][field]=1&criteria[0][searchtype]=contains&criteria[0][value]=${encodeURIComponent(email)}&range=0-50`;
|
|
192
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/search/User?${qs}`, headers, json: true });
|
|
193
|
+
returnData.push({ json: response });
|
|
194
|
+
return [returnData];
|
|
195
|
+
}
|
|
196
|
+
// createTicket (POST /Ticket/)
|
|
197
|
+
if (operation === 'createTicket') {
|
|
198
|
+
const ticketInput = this.getNodeParameter('ticketInput', 0);
|
|
199
|
+
if (!ticketInput || typeof ticketInput !== 'object')
|
|
200
|
+
throw new Error('ticketInput inválido.');
|
|
201
|
+
const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/`, headers, body: ticketInput, json: true });
|
|
202
|
+
returnData.push({ json: response });
|
|
203
|
+
return [returnData];
|
|
204
|
+
}
|
|
205
|
+
// updateTicket (PUT /Ticket/:id)
|
|
206
|
+
if (operation === 'updateTicket') {
|
|
207
|
+
const itemId = this.getNodeParameter('itemId', 0);
|
|
208
|
+
const ticketInput = this.getNodeParameter('ticketInput', 0);
|
|
209
|
+
if (!itemId)
|
|
210
|
+
throw new Error('itemId obrigatório para updateTicket.');
|
|
211
|
+
if (!ticketInput || typeof ticketInput !== 'object')
|
|
212
|
+
throw new Error('ticketInput inválido.');
|
|
213
|
+
const response = await this.helpers.request({ method: 'PUT', uri: `${baseUrl}/apirest.php/Ticket/${itemId}`, headers, body: ticketInput, json: true });
|
|
214
|
+
returnData.push({ json: response });
|
|
215
|
+
return [returnData];
|
|
216
|
+
}
|
|
217
|
+
// addFollowup (POST /Ticket/:id/ITILFollowup)
|
|
218
|
+
if (operation === 'addFollowup') {
|
|
219
|
+
const itemId = this.getNodeParameter('itemId', 0);
|
|
220
|
+
const followupInput = this.getNodeParameter('followupInput', 0);
|
|
221
|
+
if (!itemId)
|
|
222
|
+
throw new Error('itemId obrigatório para addFollowup.');
|
|
223
|
+
if (!followupInput || typeof followupInput !== 'object')
|
|
224
|
+
throw new Error('followupInput inválido.');
|
|
225
|
+
const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, body: followupInput, json: true });
|
|
226
|
+
returnData.push({ json: response });
|
|
227
|
+
return [returnData];
|
|
228
|
+
}
|
|
229
|
+
// addSolution (POST /ITILSolution/)
|
|
230
|
+
if (operation === 'addSolution') {
|
|
231
|
+
const solutionInput = this.getNodeParameter('solutionInput', 0);
|
|
232
|
+
if (!solutionInput || typeof solutionInput !== 'object')
|
|
233
|
+
throw new Error('solutionInput inválido.');
|
|
234
|
+
const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/ITILSolution/`, headers, body: solutionInput, json: true });
|
|
235
|
+
returnData.push({ json: response });
|
|
236
|
+
return [returnData];
|
|
237
|
+
}
|
|
238
|
+
throw new Error(`Operação desconhecida: ${operation}`);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
returnData.push({
|
|
242
|
+
json: {
|
|
243
|
+
error: true,
|
|
244
|
+
message: err.message || String(err),
|
|
245
|
+
stack: err.stack,
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
return [returnData];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.Glpi = Glpi;
|
|
253
|
+
//# sourceMappingURL=Glpi.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Glpi.node.js","sourceRoot":"","sources":["../../../nodes/Glpi/Glpi.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GlpiApi = void 0;
|
|
4
|
+
class GlpiApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'glpiApi';
|
|
7
|
+
this.displayName = 'GLPI API';
|
|
8
|
+
this.documentationUrl = 'https://your-glpi-docs-or-repo.example.com';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Base URL',
|
|
12
|
+
name: 'baseUrl',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: '',
|
|
15
|
+
placeholder: 'https://atendimento.centrium.com.br',
|
|
16
|
+
required: true,
|
|
17
|
+
description: 'Base URL sem o sufixo /apirest.php',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'App Token',
|
|
21
|
+
name: 'appToken',
|
|
22
|
+
type: 'string',
|
|
23
|
+
typeOptions: { password: true },
|
|
24
|
+
default: '',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
displayName: 'User Token (optional)',
|
|
29
|
+
name: 'userToken',
|
|
30
|
+
type: 'string',
|
|
31
|
+
typeOptions: { password: true },
|
|
32
|
+
default: '',
|
|
33
|
+
required: false,
|
|
34
|
+
description: 'Se presente, envia header "Authorization: user_token <token>" e pode ser usado para initSession.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
displayName: 'Username (for Basic Auth, optional)',
|
|
38
|
+
name: 'username',
|
|
39
|
+
type: 'string',
|
|
40
|
+
default: '',
|
|
41
|
+
placeholder: 'usuário',
|
|
42
|
+
required: false,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
displayName: 'Password (for Basic Auth, optional)',
|
|
46
|
+
name: 'password',
|
|
47
|
+
type: 'string',
|
|
48
|
+
typeOptions: { password: true },
|
|
49
|
+
default: '',
|
|
50
|
+
placeholder: 'senha',
|
|
51
|
+
required: false,
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.GlpiApi = GlpiApi;
|
|
57
|
+
//# sourceMappingURL=GlpiApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GlpiApi.credentials.js","sourceRoot":"","sources":["../../../nodes/Glpi/GlpiApi.credentials.ts"],"names":[],"mappings":";;;AAEA,MAAa,OAAO;IAApB;QACC,SAAI,GAAG,SAAS,CAAC;QACjB,gBAAW,GAAG,UAAU,CAAC;QACzB,qBAAgB,GAAG,4CAA4C,CAAC;QAChE,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,qCAAqC;gBAClD,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,oCAAoC;aACjD;YACD;gBACC,WAAW,EAAE,WAAW;gBACxB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,uBAAuB;gBACpC,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,kGAAkG;aAC/G;YACD;gBACC,WAAW,EAAE,qCAAqC;gBAClD,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,SAAS;gBACtB,QAAQ,EAAE,KAAK;aACf;YACD;gBACC,WAAW,EAAE,qCAAqC;gBAClD,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,OAAO;gBACpB,QAAQ,EAAE,KAAK;aACf;SACD,CAAC;IACH,CAAC;CAAA;AAjDD,0BAiDC"}
|
package/index.ts
ADDED
|
Binary file
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
|
|
3
|
+
export const glpiOperations: INodeProperties[] = [
|
|
4
|
+
{
|
|
5
|
+
displayName: 'Operation',
|
|
6
|
+
name: 'operation',
|
|
7
|
+
type: 'options',
|
|
8
|
+
options: [
|
|
9
|
+
{ name: 'Init Session', value: 'initSession', description: 'GET /apirest.php/initSession' },
|
|
10
|
+
{ name: 'Kill Session (force logout)', value: 'killSession', description: 'GET /apirest.php/killSession' },
|
|
11
|
+
{ name: 'Search (generic) - Criteria Builder', value: 'search', description: 'GET /apirest.php/search/:entity' },
|
|
12
|
+
{ name: 'Get Tickets (convenience)', value: 'getTickets', description: 'Search Ticket (with criteria builder or searchText)' },
|
|
13
|
+
{ name: 'List Search Options', value: 'listSearchOptions', description: 'GET /apirest.php/listSearchOptions/:entity' },
|
|
14
|
+
{ name: 'Get Item', value: 'getItem', description: 'GET /apirest.php/:itemtype/:id' },
|
|
15
|
+
{ name: 'Get Ticket Followup', value: 'getTicketFollowup', description: 'GET /apirest.php/Ticket/:id/ITILFollowup' },
|
|
16
|
+
{ name: 'Get Ticket Solution', value: 'getTicketSolution', description: 'GET /apirest.php/Ticket/:id/ITILSolution' },
|
|
17
|
+
{ name: 'Get Ticket Tasks', value: 'getTicketTasks', description: 'GET /apirest.php/Ticket/:id/TicketTask' },
|
|
18
|
+
{ name: 'Get User by ID', value: 'getUserById', description: 'GET /apirest.php/User/:id' },
|
|
19
|
+
{ name: 'Get User by Email (helper)', value: 'getUserByEmail', description: 'Search User by email' },
|
|
20
|
+
{ name: 'Create Ticket', value: 'createTicket', description: 'POST /apirest.php/Ticket' },
|
|
21
|
+
{ name: 'Update Ticket', value: 'updateTicket', description: 'PUT /apirest.php/Ticket/:id' },
|
|
22
|
+
{ name: 'Add Followup', value: 'addFollowup', description: 'POST /apirest.php/Ticket/:id/ITILFollowup' },
|
|
23
|
+
{ name: 'Add Solution', value: 'addSolution', description: 'POST /apirest.php/ITILSolution' },
|
|
24
|
+
],
|
|
25
|
+
default: 'initSession',
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const glpiFields: INodeProperties[] = [
|
|
30
|
+
// entity for search and listSearchOptions
|
|
31
|
+
{
|
|
32
|
+
displayName: 'Entity',
|
|
33
|
+
name: 'entity',
|
|
34
|
+
type: 'string',
|
|
35
|
+
default: 'Ticket',
|
|
36
|
+
description: 'Entity name (Ticket, User, ITILCategory, ...).',
|
|
37
|
+
displayOptions: { show: { operation: ['search', 'listSearchOptions', 'getTickets'] } },
|
|
38
|
+
},
|
|
39
|
+
// Criteria builder as fixedCollection multiple
|
|
40
|
+
{
|
|
41
|
+
displayName: 'Criteria',
|
|
42
|
+
name: 'criteria',
|
|
43
|
+
type: 'fixedCollection',
|
|
44
|
+
typeOptions: { multipleValues: true },
|
|
45
|
+
placeholder: 'Add Criterion',
|
|
46
|
+
description: 'One or more criteria for the search',
|
|
47
|
+
displayOptions: {
|
|
48
|
+
show: {
|
|
49
|
+
operation: ['search', 'getTickets'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
options: [
|
|
53
|
+
{
|
|
54
|
+
displayName: 'Criterion',
|
|
55
|
+
name: 'criterion',
|
|
56
|
+
values: [
|
|
57
|
+
{ displayName: 'Field', name: 'field', type: 'string', default: '' },
|
|
58
|
+
{ displayName: 'Search Type', name: 'searchtype', type: 'options', options: [{ name: 'contains', value: 'contains' }], default: 'contains' },
|
|
59
|
+
{ displayName: 'Value', name: 'value', type: 'string', default: '' },
|
|
60
|
+
{ displayName: 'Glue (AND/OR)', name: 'glue', type: 'options', options: [{ name: 'AND', value: 'AND' }, { name: 'OR', value: 'OR' }], default: 'AND' },
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
default: [], // <-- obrigatório para fixedCollection com multipleValues: true
|
|
65
|
+
},
|
|
66
|
+
// range and searchText
|
|
67
|
+
{
|
|
68
|
+
displayName: 'Range',
|
|
69
|
+
name: 'range',
|
|
70
|
+
type: 'string',
|
|
71
|
+
default: '0-50',
|
|
72
|
+
displayOptions: { show: { operation: ['search', 'getTickets'] } },
|
|
73
|
+
description: 'Range parameter for GLPI (ex: 0-500).'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
displayName: 'Search Text (convenience)',
|
|
77
|
+
name: 'searchText',
|
|
78
|
+
type: 'string',
|
|
79
|
+
default: '',
|
|
80
|
+
displayOptions: { show: { operation: ['getTickets'] } },
|
|
81
|
+
description: 'searchText param for Ticket search.',
|
|
82
|
+
},
|
|
83
|
+
// generic itemId & ticketInput
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Item ID',
|
|
86
|
+
name: 'itemId',
|
|
87
|
+
type: 'number',
|
|
88
|
+
default: 0,
|
|
89
|
+
displayOptions: { show: { operation: ['getTicketFollowup','getTicketSolution','getTicketTasks','getItem','updateTicket','addFollowup'] } },
|
|
90
|
+
description: 'ID of the item.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
displayName: 'Ticket Input (JSON)',
|
|
94
|
+
name: 'ticketInput',
|
|
95
|
+
type: 'json',
|
|
96
|
+
default: { input: {} },
|
|
97
|
+
displayOptions: { show: { operation: ['createTicket','updateTicket'] } },
|
|
98
|
+
description: 'Provide {"input": { ... }} body for Ticket creation or update.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
displayName: 'Followup Input (JSON)',
|
|
102
|
+
name: 'followupInput',
|
|
103
|
+
type: 'json',
|
|
104
|
+
default: { input: {} },
|
|
105
|
+
displayOptions: { show: { operation: ['addFollowup'] } },
|
|
106
|
+
description: 'Provide {"input": {...}} for ITILFollowup.',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
displayName: 'Solution Input (JSON)',
|
|
110
|
+
name: 'solutionInput',
|
|
111
|
+
type: 'json',
|
|
112
|
+
default: { input: {} },
|
|
113
|
+
displayOptions: { show: { operation: ['addSolution'] } },
|
|
114
|
+
description: 'Provide {"input": {...}} for ITILSolution.',
|
|
115
|
+
},
|
|
116
|
+
// user helpers
|
|
117
|
+
{
|
|
118
|
+
displayName: 'User ID',
|
|
119
|
+
name: 'userId',
|
|
120
|
+
type: 'number',
|
|
121
|
+
default: 0,
|
|
122
|
+
displayOptions: { show: { operation: ['getUserById'] } },
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
displayName: 'User Email (helper)',
|
|
126
|
+
name: 'userEmail',
|
|
127
|
+
type: 'string',
|
|
128
|
+
default: '',
|
|
129
|
+
displayOptions: { show: { operation: ['getUserByEmail'] } },
|
|
130
|
+
}
|
|
131
|
+
];
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INodeExecutionData,
|
|
3
|
+
INodeType,
|
|
4
|
+
INodeTypeDescription,
|
|
5
|
+
} from 'n8n-workflow';
|
|
6
|
+
|
|
7
|
+
import { glpiOperations, glpiFields } from './Glpi.descriptions';
|
|
8
|
+
|
|
9
|
+
export class Glpi implements INodeType {
|
|
10
|
+
description: INodeTypeDescription = {
|
|
11
|
+
displayName: 'GLPI',
|
|
12
|
+
name: 'glpi',
|
|
13
|
+
icon: 'file:glpi.svg',
|
|
14
|
+
group: ['transform'],
|
|
15
|
+
version: 1,
|
|
16
|
+
description: 'GLPI REST API node (GLPI 10.x) with Criteria Builder and automatic session (no cache).',
|
|
17
|
+
defaults: { name: 'GLPI' },
|
|
18
|
+
inputs: ['main'],
|
|
19
|
+
outputs: ['main'],
|
|
20
|
+
credentials: [{ name: 'glpiApi', required: true }],
|
|
21
|
+
properties: [
|
|
22
|
+
...glpiOperations,
|
|
23
|
+
...glpiFields,
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// build base headers & possible basic auth from credentials
|
|
28
|
+
private async buildBaseAuth(this: any) {
|
|
29
|
+
const credentials: any = await this.getCredentials('glpiApi');
|
|
30
|
+
if (!credentials) throw new Error('Credenciais GLPI não configuradas.');
|
|
31
|
+
const baseUrl = (credentials.baseUrl as string).replace(/\/+$/, '');
|
|
32
|
+
const appToken = credentials.appToken;
|
|
33
|
+
const userToken = credentials.userToken;
|
|
34
|
+
const username = credentials.username;
|
|
35
|
+
const password = credentials.password;
|
|
36
|
+
|
|
37
|
+
const headers: Record<string, string> = {
|
|
38
|
+
'App-Token': appToken,
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let auth: { user?: string; pass?: string } | undefined;
|
|
43
|
+
if (userToken && userToken.toString().trim() !== '') {
|
|
44
|
+
headers['Authorization'] = `user_token ${userToken}`;
|
|
45
|
+
} else if (username && username.toString().trim() !== '') {
|
|
46
|
+
auth = { user: username, pass: password };
|
|
47
|
+
}
|
|
48
|
+
return { baseUrl, headers, auth };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// request initSession and return session token
|
|
52
|
+
private async getSessionToken(this: any, baseUrl: string, headers: Record<string, string>, auth?: { user?: string; pass?: string }) {
|
|
53
|
+
// initSession is GET
|
|
54
|
+
const endpoint = `${baseUrl}/apirest.php/initSession`;
|
|
55
|
+
const response = await this.helpers.request({
|
|
56
|
+
method: 'GET',
|
|
57
|
+
uri: endpoint,
|
|
58
|
+
headers,
|
|
59
|
+
auth,
|
|
60
|
+
json: true,
|
|
61
|
+
resolveWithFullResponse: false,
|
|
62
|
+
});
|
|
63
|
+
// response usually contains session_token
|
|
64
|
+
// support different shapes: { session_token: '...' } or { session: { session_token: '...' } }
|
|
65
|
+
if (!response) {
|
|
66
|
+
throw new Error('No response from GLPI initSession');
|
|
67
|
+
}
|
|
68
|
+
const token = (response as any).session_token || (response as any).session?.session_token;
|
|
69
|
+
if (!token) {
|
|
70
|
+
throw new Error('No session token returned by GLPI initSession');
|
|
71
|
+
}
|
|
72
|
+
return token;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// monta query string para criteria[] usando índice sequencial
|
|
76
|
+
private buildCriteriaQuery(criteriaInput: Array<any>): string {
|
|
77
|
+
if (!criteriaInput || !Array.isArray(criteriaInput) || criteriaInput.length === 0) return '';
|
|
78
|
+
const parts: string[] = [];
|
|
79
|
+
// Because fixedCollection returns array of {criterion: [ ... ]}, flatten
|
|
80
|
+
let flat: any[] = [];
|
|
81
|
+
for (const c of criteriaInput) {
|
|
82
|
+
if (c && c.criterion && Array.isArray(c.criterion)) flat = flat.concat(c.criterion);
|
|
83
|
+
else if (Array.isArray(c)) flat = flat.concat(c);
|
|
84
|
+
else flat.push(c);
|
|
85
|
+
}
|
|
86
|
+
for (let i = 0; i < flat.length; i++) {
|
|
87
|
+
const item = flat[i];
|
|
88
|
+
parts.push(`criteria[${i}][field]=${encodeURIComponent(String(item.field))}`);
|
|
89
|
+
parts.push(`criteria[${i}][searchtype]=${encodeURIComponent(String(item.searchtype))}`);
|
|
90
|
+
parts.push(`criteria[${i}][value]=${encodeURIComponent(String(item.value))}`);
|
|
91
|
+
// send glue (logical operator) — use "glue" key (change to "email" only if required for compatibility)
|
|
92
|
+
parts.push(`criteria[${i}][glue]=${encodeURIComponent(String(item.glue || 'AND'))}`);
|
|
93
|
+
}
|
|
94
|
+
return parts.join('&');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async execute(this: any): Promise<INodeExecutionData[][]> {
|
|
98
|
+
const items = this.getInputData();
|
|
99
|
+
const returnData: INodeExecutionData[] = [];
|
|
100
|
+
|
|
101
|
+
const operation = this.getNodeParameter('operation', 0) as string;
|
|
102
|
+
|
|
103
|
+
// build base auth & headers
|
|
104
|
+
const { baseUrl, headers: baseHeaders, auth } = await this.buildBaseAuth();
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// If operation is initSession itself, just call it and return
|
|
108
|
+
if (operation === 'initSession') {
|
|
109
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/initSession`, headers: baseHeaders, auth, json: true });
|
|
110
|
+
returnData.push({ json: response });
|
|
111
|
+
return [returnData];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// For all other operations, per your option C (no cache) -> request NEW session token now
|
|
115
|
+
const sessionToken = await this.getSessionToken(baseUrl, baseHeaders, auth);
|
|
116
|
+
// add Session-Token header for subsequent requests
|
|
117
|
+
const headers = { ...baseHeaders, 'Session-Token': sessionToken };
|
|
118
|
+
|
|
119
|
+
// handle operations
|
|
120
|
+
|
|
121
|
+
// killSession (force logout)
|
|
122
|
+
if (operation === 'killSession') {
|
|
123
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/killSession`, headers, json: true });
|
|
124
|
+
returnData.push({ json: response });
|
|
125
|
+
return [returnData];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// listSearchOptions
|
|
129
|
+
if (operation === 'listSearchOptions') {
|
|
130
|
+
const entity = this.getNodeParameter('entity', 0) as string;
|
|
131
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/listSearchOptions/${entity}`, headers, json: true });
|
|
132
|
+
returnData.push({ json: response });
|
|
133
|
+
return [returnData];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// generic search (build criteria)
|
|
137
|
+
if (operation === 'search') {
|
|
138
|
+
const entity = this.getNodeParameter('entity', 0) as string;
|
|
139
|
+
const criteriaInput = this.getNodeParameter('criteria', 0, []) as any[];
|
|
140
|
+
const range = this.getNodeParameter('range', 0, '0-50') as string;
|
|
141
|
+
const qs = this.buildCriteriaQuery(criteriaInput);
|
|
142
|
+
const endpoint = `${baseUrl}/apirest.php/search/${entity}?${qs}&range=${encodeURIComponent(range)}`;
|
|
143
|
+
const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
|
|
144
|
+
returnData.push({ json: response });
|
|
145
|
+
return [returnData];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// getTickets convenience
|
|
149
|
+
if (operation === 'getTickets') {
|
|
150
|
+
const criteriaInput = this.getNodeParameter('criteria', 0, []) as any[];
|
|
151
|
+
const range = this.getNodeParameter('range', 0, '0-50') as string;
|
|
152
|
+
const searchText = this.getNodeParameter('searchText', 0, '') as string;
|
|
153
|
+
const qs = this.buildCriteriaQuery(criteriaInput);
|
|
154
|
+
let endpoint = `${baseUrl}/apirest.php/search/Ticket?${qs}&range=${encodeURIComponent(range)}`;
|
|
155
|
+
if (searchText && searchText.trim() !== '') endpoint += `&searchText=${encodeURIComponent(searchText)}`;
|
|
156
|
+
const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
|
|
157
|
+
returnData.push({ json: response });
|
|
158
|
+
return [returnData];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// getItem generic
|
|
162
|
+
if (operation === 'getItem') {
|
|
163
|
+
const entity = this.getNodeParameter('entity', 0) as string;
|
|
164
|
+
const itemId = this.getNodeParameter('itemId', 0) as number;
|
|
165
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/${entity}/${itemId}`, headers, json: true });
|
|
166
|
+
returnData.push({ json: response });
|
|
167
|
+
return [returnData];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// getTicketFollowup
|
|
171
|
+
if (operation === 'getTicketFollowup') {
|
|
172
|
+
const itemId = this.getNodeParameter('itemId', 0) as number;
|
|
173
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, json: true });
|
|
174
|
+
returnData.push({ json: response });
|
|
175
|
+
return [returnData];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// getTicketSolution
|
|
179
|
+
if (operation === 'getTicketSolution') {
|
|
180
|
+
const itemId = this.getNodeParameter('itemId', 0) as number;
|
|
181
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILSolution`, headers, json: true });
|
|
182
|
+
returnData.push({ json: response });
|
|
183
|
+
return [returnData];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// getTicketTasks
|
|
187
|
+
if (operation === 'getTicketTasks') {
|
|
188
|
+
const itemId = this.getNodeParameter('itemId', 0) as number;
|
|
189
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/TicketTask`, headers, json: true });
|
|
190
|
+
returnData.push({ json: response });
|
|
191
|
+
return [returnData];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// getUserById
|
|
195
|
+
if (operation === 'getUserById') {
|
|
196
|
+
const userId = this.getNodeParameter('userId', 0) as number;
|
|
197
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/User/${userId}`, headers, json: true });
|
|
198
|
+
returnData.push({ json: response });
|
|
199
|
+
return [returnData];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// getUserByEmail helper
|
|
203
|
+
if (operation === 'getUserByEmail') {
|
|
204
|
+
const email = this.getNodeParameter('userEmail', 0) as string;
|
|
205
|
+
if (!email) throw new Error('E-mail obrigatório.');
|
|
206
|
+
const qs = `criteria[0][glue]=AND&criteria[0][field]=1&criteria[0][searchtype]=contains&criteria[0][value]=${encodeURIComponent(email)}&range=0-50`;
|
|
207
|
+
const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/search/User?${qs}`, headers, json: true });
|
|
208
|
+
returnData.push({ json: response });
|
|
209
|
+
return [returnData];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// createTicket (POST /Ticket/)
|
|
213
|
+
if (operation === 'createTicket') {
|
|
214
|
+
const ticketInput = this.getNodeParameter('ticketInput', 0) as any;
|
|
215
|
+
if (!ticketInput || typeof ticketInput !== 'object') throw new Error('ticketInput inválido.');
|
|
216
|
+
const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/`, headers, body: ticketInput, json: true });
|
|
217
|
+
returnData.push({ json: response });
|
|
218
|
+
return [returnData];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// updateTicket (PUT /Ticket/:id)
|
|
222
|
+
if (operation === 'updateTicket') {
|
|
223
|
+
const itemId = this.getNodeParameter('itemId', 0) as number;
|
|
224
|
+
const ticketInput = this.getNodeParameter('ticketInput', 0) as any;
|
|
225
|
+
if (!itemId) throw new Error('itemId obrigatório para updateTicket.');
|
|
226
|
+
if (!ticketInput || typeof ticketInput !== 'object') throw new Error('ticketInput inválido.');
|
|
227
|
+
const response = await this.helpers.request({ method: 'PUT', uri: `${baseUrl}/apirest.php/Ticket/${itemId}`, headers, body: ticketInput, json: true });
|
|
228
|
+
returnData.push({ json: response });
|
|
229
|
+
return [returnData];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// addFollowup (POST /Ticket/:id/ITILFollowup)
|
|
233
|
+
if (operation === 'addFollowup') {
|
|
234
|
+
const itemId = this.getNodeParameter('itemId', 0) as number;
|
|
235
|
+
const followupInput = this.getNodeParameter('followupInput', 0) as any;
|
|
236
|
+
if (!itemId) throw new Error('itemId obrigatório para addFollowup.');
|
|
237
|
+
if (!followupInput || typeof followupInput !== 'object') throw new Error('followupInput inválido.');
|
|
238
|
+
const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, body: followupInput, json: true });
|
|
239
|
+
returnData.push({ json: response });
|
|
240
|
+
return [returnData];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// addSolution (POST /ITILSolution/)
|
|
244
|
+
if (operation === 'addSolution') {
|
|
245
|
+
const solutionInput = this.getNodeParameter('solutionInput', 0) as any;
|
|
246
|
+
if (!solutionInput || typeof solutionInput !== 'object') throw new Error('solutionInput inválido.');
|
|
247
|
+
const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/ITILSolution/`, headers, body: solutionInput, json: true });
|
|
248
|
+
returnData.push({ json: response });
|
|
249
|
+
return [returnData];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
throw new Error(`Operação desconhecida: ${operation}`);
|
|
253
|
+
|
|
254
|
+
} catch (err) {
|
|
255
|
+
returnData.push({
|
|
256
|
+
json: {
|
|
257
|
+
error: true,
|
|
258
|
+
message: (err as Error).message || String(err),
|
|
259
|
+
stack: (err as any).stack,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
return [returnData];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
|
|
3
|
+
export class GlpiApi implements ICredentialType {
|
|
4
|
+
name = 'glpiApi';
|
|
5
|
+
displayName = 'GLPI API';
|
|
6
|
+
documentationUrl = 'https://your-glpi-docs-or-repo.example.com';
|
|
7
|
+
properties: INodeProperties[] = [
|
|
8
|
+
{
|
|
9
|
+
displayName: 'Base URL',
|
|
10
|
+
name: 'baseUrl',
|
|
11
|
+
type: 'string',
|
|
12
|
+
default: '',
|
|
13
|
+
placeholder: 'https://atendimento.centrium.com.br',
|
|
14
|
+
required: true,
|
|
15
|
+
description: 'Base URL sem o sufixo /apirest.php',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
displayName: 'App Token',
|
|
19
|
+
name: 'appToken',
|
|
20
|
+
type: 'string',
|
|
21
|
+
typeOptions: { password: true },
|
|
22
|
+
default: '',
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
displayName: 'User Token (optional)',
|
|
27
|
+
name: 'userToken',
|
|
28
|
+
type: 'string',
|
|
29
|
+
typeOptions: { password: true },
|
|
30
|
+
default: '',
|
|
31
|
+
required: false,
|
|
32
|
+
description: 'Se presente, envia header "Authorization: user_token <token>" e pode ser usado para initSession.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
displayName: 'Username (for Basic Auth, optional)',
|
|
36
|
+
name: 'username',
|
|
37
|
+
type: 'string',
|
|
38
|
+
default: '',
|
|
39
|
+
placeholder: 'usuário',
|
|
40
|
+
required: false,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Password (for Basic Auth, optional)',
|
|
44
|
+
name: 'password',
|
|
45
|
+
type: 'string',
|
|
46
|
+
typeOptions: { password: true },
|
|
47
|
+
default: '',
|
|
48
|
+
placeholder: 'senha',
|
|
49
|
+
required: false,
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@justbrunasso/n8n-nodes-glpi",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "GLPI REST API node for n8n (GLPI 10.x) with Criteria Builder and automatic session handling (no cache).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n",
|
|
7
|
+
"n8n-nodes",
|
|
8
|
+
"glpi",
|
|
9
|
+
"glpi-api",
|
|
10
|
+
"glpi-node",
|
|
11
|
+
"itsm",
|
|
12
|
+
"automation",
|
|
13
|
+
"workflow"
|
|
14
|
+
],
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepare": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"author": "Bruno Eduardo Santos",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"n8n-core": "^1.0.0",
|
|
25
|
+
"n8n-workflow": "^1.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.19.25",
|
|
29
|
+
"n8n-core": "^1.17.0",
|
|
30
|
+
"n8n-workflow": "^1.17.0",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"moduleResolution": "node",
|
|
11
|
+
"rootDir": ".",
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"lib": ["ES2020"],
|
|
15
|
+
"types": ["node"]
|
|
16
|
+
},
|
|
17
|
+
"include": ["nodes/**/*", "index.ts"]
|
|
18
|
+
}
|