@hahnpro/hpc-api 2024.4.0-1 → 2025.2.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/CHANGELOG.md +1 -1
- package/README.md +502 -6
- package/package.json +7 -4
- package/src/index.js +27 -20
- package/src/index.js.map +1 -0
- package/src/lib/Queue.js +21 -10
- package/src/lib/Queue.js.map +1 -0
- package/src/lib/api-base.js +15 -3
- package/src/lib/api-base.js.map +1 -0
- package/src/lib/api.js +53 -42
- package/src/lib/api.js.map +1 -0
- package/src/lib/asset.interface.js +6 -1
- package/src/lib/asset.interface.js.map +1 -0
- package/src/lib/asset.service.js +45 -20
- package/src/lib/asset.service.js.map +1 -0
- package/src/lib/assettypes.service.js +26 -12
- package/src/lib/assettypes.service.js.map +1 -0
- package/src/lib/content.interface.js +15 -3
- package/src/lib/content.interface.js.map +1 -0
- package/src/lib/content.service.js +62 -32
- package/src/lib/content.service.js.map +1 -0
- package/src/lib/data.interface.js +13 -1
- package/src/lib/data.interface.js.map +1 -0
- package/src/lib/data.service.js +45 -24
- package/src/lib/data.service.js.map +1 -0
- package/src/lib/endpoint.interface.js +6 -1
- package/src/lib/endpoint.interface.js.map +1 -0
- package/src/lib/endpoint.service.js +16 -4
- package/src/lib/endpoint.service.js.map +1 -0
- package/src/lib/events.interface.js +6 -1
- package/src/lib/events.interface.js.map +1 -0
- package/src/lib/events.service.js +16 -4
- package/src/lib/events.service.js.map +1 -0
- package/src/lib/flow-deployment.interface.js +6 -1
- package/src/lib/flow-deployment.interface.js.map +1 -0
- package/src/lib/flow-deployment.service.js +58 -25
- package/src/lib/flow-deployment.service.js.map +1 -0
- package/src/lib/flow-function.interface.js +6 -1
- package/src/lib/flow-function.interface.js.map +1 -0
- package/src/lib/flow-function.service.js +20 -6
- package/src/lib/flow-function.service.js.map +1 -0
- package/src/lib/flow-module.interface.js +6 -1
- package/src/lib/flow-module.interface.js.map +1 -0
- package/src/lib/flow-module.service.js +20 -6
- package/src/lib/flow-module.service.js.map +1 -0
- package/src/lib/flow.interface.js +6 -1
- package/src/lib/flow.interface.js.map +1 -0
- package/src/lib/flow.service.js +27 -13
- package/src/lib/flow.service.js.map +1 -0
- package/src/lib/http.service.js +101 -66
- package/src/lib/http.service.js.map +1 -0
- package/src/lib/label.interface.js +6 -1
- package/src/lib/label.interface.js.map +1 -0
- package/src/lib/label.service.js +17 -5
- package/src/lib/label.service.js.map +1 -0
- package/src/lib/mock/api-base.mock.js +17 -6
- package/src/lib/mock/api-base.mock.js.map +1 -0
- package/src/lib/mock/api.mock.js +204 -193
- package/src/lib/mock/api.mock.js.map +1 -0
- package/src/lib/mock/asset.mock.service.js +55 -38
- package/src/lib/mock/asset.mock.service.js.map +1 -0
- package/src/lib/mock/assetTypes.mock.service.js +45 -27
- package/src/lib/mock/assetTypes.mock.service.js.map +1 -0
- package/src/lib/mock/content.mock.service.js +45 -37
- package/src/lib/mock/content.mock.service.js.map +1 -0
- package/src/lib/mock/data.mock.service.js +49 -40
- package/src/lib/mock/data.mock.service.js.map +1 -0
- package/src/lib/mock/endpoint.mock.service.js +17 -5
- package/src/lib/mock/endpoint.mock.service.js.map +1 -0
- package/src/lib/mock/events.mock.service.js +16 -4
- package/src/lib/mock/events.mock.service.js.map +1 -0
- package/src/lib/mock/flow-deployment.mock.service.js +81 -36
- package/src/lib/mock/flow-deployment.mock.service.js.map +1 -0
- package/src/lib/mock/flow-functions.mock.service.js +33 -20
- package/src/lib/mock/flow-functions.mock.service.js.map +1 -0
- package/src/lib/mock/flow-modules.mock.service.js +18 -6
- package/src/lib/mock/flow-modules.mock.service.js.map +1 -0
- package/src/lib/mock/flow.mock.service.js +44 -29
- package/src/lib/mock/flow.mock.service.js.map +1 -0
- package/src/lib/mock/index.js +18 -11
- package/src/lib/mock/index.js.map +1 -0
- package/src/lib/mock/label.mock.service.js +17 -5
- package/src/lib/mock/label.mock.service.js.map +1 -0
- package/src/lib/mock/notification.mock.service.js +16 -4
- package/src/lib/mock/notification.mock.service.js.map +1 -0
- package/src/lib/mock/secret.mock.service.js +24 -12
- package/src/lib/mock/secret.mock.service.js.map +1 -0
- package/src/lib/mock/task.mock.service.js +24 -12
- package/src/lib/mock/task.mock.service.js.map +1 -0
- package/src/lib/mock/timeseries.mock.service.js +88 -43
- package/src/lib/mock/timeseries.mock.service.js.map +1 -0
- package/src/lib/mock/trash.mock.service.js +28 -14
- package/src/lib/mock/trash.mock.service.js.map +1 -0
- package/src/lib/mock/user.mock.service.js +17 -6
- package/src/lib/mock/user.mock.service.js.map +1 -0
- package/src/lib/mock/vault.mock.service.js +17 -5
- package/src/lib/mock/vault.mock.service.js.map +1 -0
- package/src/lib/notification.interface.js +6 -1
- package/src/lib/notification.interface.js.map +1 -0
- package/src/lib/notification.service.js +16 -4
- package/src/lib/notification.service.js.map +1 -0
- package/src/lib/proxy.service.js +19 -7
- package/src/lib/proxy.service.js.map +1 -0
- package/src/lib/resource.interface.js +6 -1
- package/src/lib/resource.interface.js.map +1 -0
- package/src/lib/schema.interface.js +6 -1
- package/src/lib/schema.interface.js.map +1 -0
- package/src/lib/secret.interface.js +6 -1
- package/src/lib/secret.interface.js.map +1 -0
- package/src/lib/secret.service.js +23 -11
- package/src/lib/secret.service.js.map +1 -0
- package/src/lib/storage.interface.js +6 -1
- package/src/lib/storage.interface.js.map +1 -0
- package/src/lib/task.interface.js +6 -1
- package/src/lib/task.interface.js.map +1 -0
- package/src/lib/task.service.js +23 -11
- package/src/lib/task.service.js.map +1 -0
- package/src/lib/timeseries.interface.js +6 -1
- package/src/lib/timeseries.interface.js.map +1 -0
- package/src/lib/timeseries.service.js +97 -66
- package/src/lib/timeseries.service.js.map +1 -0
- package/src/lib/token-set.js +15 -3
- package/src/lib/token-set.js.map +1 -0
- package/src/lib/trash.service.js +23 -5
- package/src/lib/trash.service.js.map +1 -0
- package/src/lib/user-settings.interface.js +6 -1
- package/src/lib/user-settings.interface.js.map +1 -0
- package/src/lib/user.service.js +18 -7
- package/src/lib/user.service.js.map +1 -0
- package/src/lib/vault.interface.js +6 -1
- package/src/lib/vault.interface.js.map +1 -0
- package/src/lib/vault.service.js +23 -6
- package/src/lib/vault.service.js.map +1 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,11 +1,507 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Hahn PRO Cloud API
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 1. Fundamentals
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The Hahn PRO Cloud API is a RESTful API, therefore the communication consists of HTTP-calls.
|
|
6
|
+
The services provided are independent microservices, each with their own subpaths.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
In this document there are code-samples for the most common API use-cases in typescript
|
|
9
|
+
and python. HahnPRO provides an SDK, which simplifies the communication in typescript.
|
|
10
|
+
Currently, there is no SDK for python.
|
|
8
11
|
|
|
9
|
-
##
|
|
12
|
+
## 2. Authentication
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
To use the API you will have to authenticate yourself with every request. For this a `Authentication`
|
|
15
|
+
header with a [JWT-Token](https://jwt.io/introduction) is needed.
|
|
16
|
+
|
|
17
|
+
To get the JWT-Token, you have to use your API-Username and Password to get the token from Keycloak. The request to
|
|
18
|
+
keycloak needs to be a POST request with `{'grant_type': 'client_credentials'}` as the body.
|
|
19
|
+
|
|
20
|
+
The token is valid for a set amount of time, after which you will need to reauthenticate.
|
|
21
|
+
In the `expires_in` response field you can get the time for which the token will be valid.
|
|
22
|
+
|
|
23
|
+
<details>
|
|
24
|
+
<summary markdown="span">Python</summary>
|
|
25
|
+
|
|
26
|
+
In the following sample-code the user gets asked for their password, which then gets used to ask Keycloak for the
|
|
27
|
+
JWT-Token. The token then gets used in the `Authentication` header.
|
|
28
|
+
|
|
29
|
+
You will need to set the variables `API_BASE_URL`, `AUTH_BASE_URL`, `AUTH_REALM` and `API_USER`. The `AUTH_BASE_URL`
|
|
30
|
+
will often be the same as the `API_BASE_URL`.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import requests
|
|
34
|
+
import getpass
|
|
35
|
+
|
|
36
|
+
# gets an access token
|
|
37
|
+
keycloak_url = AUTH_BASE_URL + '/auth/realms/' + AUTH_REALM + '/protocol/openid-connect/token'
|
|
38
|
+
headers = {'Content-Type':'application/x-www-form-urlencoded'}
|
|
39
|
+
apiPw = getpass.getpass('Password for ' + API_USER)
|
|
40
|
+
auth=(API_USER, apiPw)
|
|
41
|
+
data = {'grant_type': 'client_credentials'}
|
|
42
|
+
res = requests.post(keycloak_url, auth=auth, headers=headers, data=data )
|
|
43
|
+
apiPw = '';
|
|
44
|
+
token = res.json()['access_token']
|
|
45
|
+
print('token: ' + token[:5] + '...' + str(len(token)))
|
|
46
|
+
headers = {'Authorization':'Bearer ' + token,
|
|
47
|
+
'Content-Type': 'application/json'}
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The `headers` need to be sent with every request to the API.
|
|
52
|
+
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
<details>
|
|
56
|
+
<summary markdown="span">Typescript</summary>
|
|
57
|
+
|
|
58
|
+
The Hahn-PRO Flow-SDK does most of the work in getting and using the token. You only have to set the `API_BASE_URL` (
|
|
59
|
+
defaults to `https://testing.hahnpro.com`), `AUTH_BASE_URL` (defaults to `API_BASE_URL`),
|
|
60
|
+
`AUTH_REALM` (defaults to `hpc`), `API_USER` and `AUTH_SECRET` environment variables;
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { API } from '@hahnpro/flow-sdk';
|
|
64
|
+
|
|
65
|
+
// explicitly set the envs
|
|
66
|
+
process.env.API_BASE_URL = '*your API URL*';
|
|
67
|
+
process.env.AUTH_BASE_URL = '*URL of the Keycloak instance the authenticate with*'; // only needed if distinct from API_BASE_URL
|
|
68
|
+
process.env.AUTH_REALM = '*the Keycloak Realm*';
|
|
69
|
+
process.env.API_USER = '*your username*';
|
|
70
|
+
process.env.AUTH_SECRET = '*your secret*';
|
|
71
|
+
|
|
72
|
+
// or by using dotenv
|
|
73
|
+
import * as dotenv from 'dotenv';
|
|
74
|
+
|
|
75
|
+
dotenv.config();
|
|
76
|
+
|
|
77
|
+
const api = new API();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The JWT-Token is automatically requested and used with every request you send, using the `api` object.
|
|
81
|
+
|
|
82
|
+
</details>
|
|
83
|
+
|
|
84
|
+
## 3. Basic Example Use-Cases
|
|
85
|
+
|
|
86
|
+
### 3.1. General
|
|
87
|
+
|
|
88
|
+
### 3.1.1 Populate
|
|
89
|
+
|
|
90
|
+
When you get assets from the API, the Asset-Type and the parent-Asset are given as IDs. You can get the Asset-Type and
|
|
91
|
+
Parent objects by sending the `populate` queryparameter.
|
|
92
|
+
|
|
93
|
+
`...?populate=type,parent`
|
|
94
|
+
|
|
95
|
+
### 3.1.2 Filter
|
|
96
|
+
|
|
97
|
+
To get filtered results a filter string has to be provided:
|
|
98
|
+
|
|
99
|
+
| Attribute | filter string |
|
|
100
|
+
| --------- | --------------- |
|
|
101
|
+
| tags | tags=@tag1 tag2 |
|
|
102
|
+
| type | type==type |
|
|
103
|
+
| parent | parent==1234 |
|
|
104
|
+
|
|
105
|
+
Multiple filters can be concatenated with the `;` symbol.
|
|
106
|
+
|
|
107
|
+
`...?filter=tags=@test;parent==1234`
|
|
108
|
+
|
|
109
|
+
### 3.1.3 Read/Read-Write Permissions
|
|
110
|
+
|
|
111
|
+
Every single object from the API has the fields `readPermissions` and `readWritePermissions`.
|
|
112
|
+
These determine which objects can be read or written.
|
|
113
|
+
When writing a new object to the API, these fields have to be sent.
|
|
114
|
+
|
|
115
|
+
You can only set the roles that you have, e.g. when you don´t have the role `test` you
|
|
116
|
+
can´t set this role in the `readPermissions` or `readWritePermissions` arrays.
|
|
117
|
+
|
|
118
|
+
To see the roles you have, you can check the `realm_access.roles` field of the JWT-Token.
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary markdown="span">Python</summary>
|
|
122
|
+
|
|
123
|
+
Get user roles from JWT-Token.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import jwt
|
|
127
|
+
|
|
128
|
+
def getUserRoles(token):
|
|
129
|
+
algorithm = jwt.get_unverified_header(token)['alg']
|
|
130
|
+
data = jwt.decode(token, algorithms=algorithm, audience='account', options={"verify_signature": False})
|
|
131
|
+
return data['realm_access']['roles']
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This example uses the [`pyjwt` library](https://pyjwt.readthedocs.io/en/stable/).
|
|
135
|
+
|
|
136
|
+
</details>
|
|
137
|
+
|
|
138
|
+
<details>
|
|
139
|
+
<summary markdown="span">Python</summary>
|
|
140
|
+
|
|
141
|
+
Get user roles from JWT-Token.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const roles = await api.users.getCurrentUserRoles();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
</details>
|
|
148
|
+
|
|
149
|
+
### 3.2. Assets
|
|
150
|
+
|
|
151
|
+
In this example some Assets will be fetched and modified.
|
|
152
|
+
|
|
153
|
+
<details>
|
|
154
|
+
<summary markdown="span">Python</summary>
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
API_AM_PATH = '/api/assets'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Get all assets:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
res = requests.get(API_BASE_URL + API_AM_PATH, headers=headers)
|
|
164
|
+
assets = res.json()['docs']
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Get a single Asset:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
assetId = '1234'
|
|
171
|
+
res = requests.get(API_BASE_URL + API_AM_PATH + '/' + assetId, headers=headers)
|
|
172
|
+
asset = res.json()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Get all Assets with tag:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
filterString = '?filter=tags=@test'
|
|
179
|
+
res = requests.get(API_BASE_URL + API_AM_PATH + filterString, headers=headers)
|
|
180
|
+
assets = res.json()['docs']
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Populate Asset-type and Parent-Asset:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
populateString = '?populate=type,parent'
|
|
187
|
+
res = requests.get(API_BASE_URL + API_AM_PATH + populateString, headers=headers)
|
|
188
|
+
print(res.json()['docs'])
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Create a new Asset:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
asset = {
|
|
195
|
+
name: 'newAsset',
|
|
196
|
+
type: '1234',
|
|
197
|
+
readPermissions: ['test'],
|
|
198
|
+
readWritePermissions: ['test', 'admin']
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
res = requests.post(API_BASE_URL + API_AM_PATH, data=json.dumps(asset), headers=headers)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Update existing asset:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
import json
|
|
208
|
+
|
|
209
|
+
# get asset
|
|
210
|
+
assetId = '1234'
|
|
211
|
+
res = requests.get(API_BASE_URL + API_AM_PATH + '/' + assetId, headers=headers)
|
|
212
|
+
asset = res.json()
|
|
213
|
+
|
|
214
|
+
# update asset
|
|
215
|
+
asset['tags'] = ['qwertz', 'test']
|
|
216
|
+
|
|
217
|
+
# save aset
|
|
218
|
+
res = requests.put(API_BASE_URL + API_AM_PATH + '/' + assetId, data=json.dumps(asset), headers=headers)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
</details>
|
|
222
|
+
|
|
223
|
+
<details>
|
|
224
|
+
<summary markdown="span">Typescript</summary>
|
|
225
|
+
|
|
226
|
+
Get all assets:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// get a paginated list of all Assets
|
|
230
|
+
const all = await api.assets.getMany();
|
|
231
|
+
const assets = all.docs; // asset-objects are contained in the docs array
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Get a single Asset:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const asset = await api.assets.getOne('*ID of asset*');
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Get all Assets with tag:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
const filtered = await api.assets.getManyFiltered({ tags: ['test'] });
|
|
244
|
+
const assets = filtered.docs;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Populate Asset-type and Parent-Asset:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const many = await api.assets.getMany({ populate: 'type,parent' });
|
|
251
|
+
const assets = many.docs;
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Create a new Asset:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const asset: Asset = {
|
|
258
|
+
name: 'newAsset',
|
|
259
|
+
type: '1234',
|
|
260
|
+
readPermissions: ['test'],
|
|
261
|
+
readWritePermissions: ['test', 'admin'],
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
await api.assets.addOne(asset);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Update existing Asset:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const asset = await api.assets.getOne('1234');
|
|
271
|
+
// modify
|
|
272
|
+
asset.tags.push('newTag');
|
|
273
|
+
|
|
274
|
+
await api.assets.updateOne('1234', asset);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
</details>
|
|
278
|
+
|
|
279
|
+
### 3.3. Content
|
|
280
|
+
|
|
281
|
+
The Content object from the api contains all the metadata of the content-file, but not the actual file. The file has to
|
|
282
|
+
be downloaded separately.
|
|
283
|
+
|
|
284
|
+
To upload a new File you have to send it as `multipart/form-data` in the `file` field.
|
|
285
|
+
|
|
286
|
+
<details>
|
|
287
|
+
<summary markdown="span">Python</summary>
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
API_CM_PATH = '/api/contents'
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Get all Content:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
res = requests.get(API_BASE_URL + API_CM_PATH, headers=headers)
|
|
297
|
+
contents = res.json()['docs']
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Download Content:
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
contentId = '1234'
|
|
304
|
+
res = requests.get(API_BASE_URL + API_CM_PATH + '/' + contentId + '/download', headers=headers)
|
|
305
|
+
blob = res.content
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Download Content attached to an Asset:
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
ASSET_WITH_CONTENT = '1234'
|
|
312
|
+
res = requests.get(API_BASE_URL + API_AM_PATH +'/' + ASSET_WITH_CONTENT, headers=headers)
|
|
313
|
+
attachments = res.json()['attachments']
|
|
314
|
+
|
|
315
|
+
res = requests.get(API_BASE_URL + API_CM_PATH + '/' + attachments[0] + '/download', headers=headers)
|
|
316
|
+
blob = res.content
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Upload new Content:
|
|
320
|
+
|
|
321
|
+
If you are using the same `headers` set from the authentication example, you have
|
|
322
|
+
to the `Content-Type` header.
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
del headers['Content-Type']
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
files = {'file': open('somefile.txt', 'rb')}
|
|
330
|
+
payload={
|
|
331
|
+
'readPermissions': 'test',
|
|
332
|
+
'readWritePermissions': 'test'
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
res = requests.post(API_BASE_URL + API_CM_PATH, files=files, data=payload, headers=headers)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Attach Content to Asset:
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
files = {'file': open('somefile.txt', 'rb')}
|
|
342
|
+
payload={
|
|
343
|
+
'readPermissions': 'test',
|
|
344
|
+
'readWritePermissions': 'test'
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
res = requests.post(API_BASE_URL + API_AM_PATH + '/1234/attachment', data=payload, files=files, headers=headers)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
</details>
|
|
351
|
+
|
|
352
|
+
<details>
|
|
353
|
+
<summary markdown="span">Typescript</summary>
|
|
354
|
+
|
|
355
|
+
Get all Contents:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
const many = await api.contents.getMany();
|
|
359
|
+
const contents = many.docs;
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Download Content:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const content = await api.contents.getOne('1234');
|
|
366
|
+
const file = await api.contents.download(content.id);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Download Content attached to an Asset:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const asset = await api.contents.getOne('1234');
|
|
373
|
+
const file = await api.contents.download(asset.attachments[0]);
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Upload new Content:
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import FormData from 'form-data';
|
|
380
|
+
|
|
381
|
+
const form = new FormData();
|
|
382
|
+
form.append('file', fs.createReadStream('/foo/bar.jpg'));
|
|
383
|
+
form.append('readPermission', 'test');
|
|
384
|
+
form.append('readWritePermission', 'test');
|
|
385
|
+
|
|
386
|
+
await api.contents.upload(form);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Attach Content to Asset:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import FormData from 'form-data';
|
|
393
|
+
|
|
394
|
+
const form = new FormData();
|
|
395
|
+
form.append('file', fs.createReadStream('/foo/bar.jpg'));
|
|
396
|
+
form.append('readPermission', 'test');
|
|
397
|
+
form.append('readWritePermission', 'test');
|
|
398
|
+
|
|
399
|
+
await api.assets.addAttachment('1234', form);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
</details>
|
|
403
|
+
|
|
404
|
+
### 3.4. Timeseries
|
|
405
|
+
|
|
406
|
+
A TimeSeries object contains all the metadata of the timeseries but no values. The values
|
|
407
|
+
can be downloaded separately.
|
|
408
|
+
|
|
409
|
+
<details>
|
|
410
|
+
<summary markdown="span">Python</summary>
|
|
411
|
+
|
|
412
|
+
Get timeseries:
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
tsId = '1234'
|
|
416
|
+
res = requests.get(API_BASE_URL + API_TSM_PATH + '/' + tsId, headers=headers)
|
|
417
|
+
ts = res.json()
|
|
418
|
+
|
|
419
|
+
# download the first 1000 values
|
|
420
|
+
res = requests.get(API_BASE_URL + API_TSM_PATH + '/' + tsId + '/' + '0' + '?limit=1000', headers=headers)
|
|
421
|
+
values = res.json()
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Create new empty timeseries:
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
ts = {
|
|
428
|
+
'name': 'newTS,
|
|
429
|
+
'readPermissions': ['test'],
|
|
430
|
+
'readWritePermissions': ['test']
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
res = requests.post(API_BASE_URL, data=json.dumps(ts), headers=headers)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Add value to Timeseries:
|
|
437
|
+
|
|
438
|
+
```python
|
|
439
|
+
data = { '1234': 5, '1235': 6}; // { *timestamp* : *value*}
|
|
440
|
+
# multiple values
|
|
441
|
+
data1 = { '1234': { val1: 1, val2: 2} };
|
|
442
|
+
|
|
443
|
+
requests.post(API_BASE_URL + API_TSM_PATH + '/1234', data=json.dumps(data), headers=headers)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
</details>
|
|
447
|
+
|
|
448
|
+
<details>
|
|
449
|
+
<summary markdown="span">Typescript</summary>
|
|
450
|
+
|
|
451
|
+
Get Timeseries:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
const ts = await api.timeSeries.getOne('1234');
|
|
455
|
+
|
|
456
|
+
const from = 0; // timestamp
|
|
457
|
+
const to = Date.now(); // timestamp (optional)
|
|
458
|
+
const group = '12h'; // "10s", "1m", "5m", "15m", "30m", "1h", "3h", "6h", "12h", "1d", "7d" (optional)
|
|
459
|
+
const values = await api.timeSeries.getValuesOfPeriod(tsId, from, to, group);
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Create new empty timeseries:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
const ts: TimeSeries = {
|
|
466
|
+
name: 'newTS',
|
|
467
|
+
readPermissions: ['test'],
|
|
468
|
+
readWritePermissions: ['test', 'admin'],
|
|
469
|
+
minDate: Date.now,
|
|
470
|
+
maxBucketTimeRange: 86400000,
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
await api.timeSeries.addOne(ts);
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Add value to Timeseries:
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
const value = { '1234': 5, '1235': 6 }; // { *timestamp* : *value*}
|
|
480
|
+
// multiple values
|
|
481
|
+
const value1 = { '1234': { val1: 1, val2: 2 } };
|
|
482
|
+
|
|
483
|
+
await api.timeSeries.addValue('1234', value);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
</details>
|
|
487
|
+
|
|
488
|
+
## 4. FAQ
|
|
489
|
+
|
|
490
|
+
### 4.1. How to log messages from Python in a FlowFunction
|
|
491
|
+
|
|
492
|
+
> this is just possible for _RPC_ style Python integration.
|
|
493
|
+
|
|
494
|
+
In Typescript, you can register a listener for messages and `stderr` and pipe them to your `logger`.
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
const script = this.runPyRpcScript(join(__dirname, 'my-awesome-script.py'));
|
|
498
|
+
script.addListener('stderr', (data) => this.logger.error('py: ' + data));
|
|
499
|
+
script.on('message', (data) => this.logger.debug('py: ' + data));
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Then you can simply use print in python for logging
|
|
503
|
+
|
|
504
|
+
```python
|
|
505
|
+
print("log the logging log")
|
|
506
|
+
print("this is an error", file=sys.stderr)
|
|
507
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnpro/hpc-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2025.2.0",
|
|
4
4
|
"description": "Module for easy access to the HahnPRO Cloud API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"url": "https://hahnpro.com"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
+
"@swc/helpers": "0.5.11",
|
|
11
12
|
"axios": "1.7.9",
|
|
12
13
|
"eventsource": "3.0.2",
|
|
13
14
|
"form-data": "4.0.1",
|
|
@@ -21,10 +22,12 @@
|
|
|
21
22
|
"axios-mock-adapter": "2.1.0"
|
|
22
23
|
},
|
|
23
24
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
25
|
+
"node": ">=v22"
|
|
25
26
|
},
|
|
26
27
|
"main": "./src/index.js",
|
|
27
28
|
"types": "./src/index.d.ts",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
29
|
+
"type": "commonjs",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "vitest"
|
|
32
|
+
}
|
|
30
33
|
}
|
package/src/index.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _export_star = require("@swc/helpers/_/_export_star");
|
|
6
|
+
_export_star._(require("./lib/api"), exports);
|
|
7
|
+
_export_star._(require("./lib/asset.interface"), exports);
|
|
8
|
+
_export_star._(require("./lib/content.interface"), exports);
|
|
9
|
+
_export_star._(require("./lib/data.interface"), exports);
|
|
10
|
+
_export_star._(require("./lib/data.service"), exports);
|
|
11
|
+
_export_star._(require("./lib/endpoint.interface"), exports);
|
|
12
|
+
_export_star._(require("./lib/events.interface"), exports);
|
|
13
|
+
_export_star._(require("./lib/flow.interface"), exports);
|
|
14
|
+
_export_star._(require("./lib/flow-deployment.interface"), exports);
|
|
15
|
+
_export_star._(require("./lib/flow-function.interface"), exports);
|
|
16
|
+
_export_star._(require("./lib/http.service"), exports);
|
|
17
|
+
_export_star._(require("./lib/label.interface"), exports);
|
|
18
|
+
_export_star._(require("./lib/mock"), exports);
|
|
19
|
+
_export_star._(require("./lib/notification.interface"), exports);
|
|
20
|
+
_export_star._(require("./lib/resource.interface"), exports);
|
|
21
|
+
_export_star._(require("./lib/schema.interface"), exports);
|
|
22
|
+
_export_star._(require("./lib/secret.interface"), exports);
|
|
23
|
+
_export_star._(require("./lib/task.interface"), exports);
|
|
24
|
+
_export_star._(require("./lib/timeseries.interface"), exports);
|
|
25
|
+
_export_star._(require("./lib/vault.interface"), exports);
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../lib/hpc-api/src/index.ts"],"sourcesContent":["export * from './lib/api';\nexport * from './lib/asset.interface';\nexport * from './lib/content.interface';\nexport * from './lib/data.interface';\nexport * from './lib/data.service';\nexport * from './lib/endpoint.interface';\nexport * from './lib/events.interface';\nexport * from './lib/flow.interface';\nexport * from './lib/flow-deployment.interface';\nexport * from './lib/flow-function.interface';\nexport * from './lib/http.service';\nexport * from './lib/label.interface';\nexport * from './lib/mock';\nexport * from './lib/notification.interface';\nexport * from './lib/resource.interface';\nexport * from './lib/schema.interface';\nexport * from './lib/secret.interface';\nexport * from './lib/task.interface';\nexport * from './lib/timeseries.interface';\nexport * from './lib/vault.interface';\n"],"names":[],"mappings":";;;;;uBAAc;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA;uBACA"}
|
package/src/lib/Queue.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "Queue", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return Queue;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_default = require("@swc/helpers/_/_interop_require_default");
|
|
12
|
+
const _pqueue = /*#__PURE__*/ _interop_require_default._(require("p-queue"));
|
|
13
|
+
let Queue = class Queue extends _pqueue.default {
|
|
14
|
+
constructor(options){
|
|
15
|
+
super(options), this.peak = 0, this.total = 0;
|
|
16
|
+
this.on('add', ()=>{
|
|
8
17
|
this.peak = Math.max(this.peak, this.size);
|
|
9
18
|
});
|
|
10
|
-
this.on('active', ()
|
|
19
|
+
this.on('active', ()=>{
|
|
11
20
|
this.total++;
|
|
12
21
|
});
|
|
13
22
|
}
|
|
@@ -16,7 +25,9 @@ export class Queue extends PQueue {
|
|
|
16
25
|
peak: this.peak,
|
|
17
26
|
pending: this.pending,
|
|
18
27
|
size: this.size,
|
|
19
|
-
total: this.total
|
|
28
|
+
total: this.total
|
|
20
29
|
};
|
|
21
30
|
}
|
|
22
|
-
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=Queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../lib/hpc-api/src/lib/Queue.ts"],"sourcesContent":["import PQueue from 'p-queue';\n\nexport class Queue extends PQueue {\n private peak = 0;\n private total = 0;\n\n constructor(options: QueueOptions) {\n super(options);\n this.on('add', () => {\n this.peak = Math.max(this.peak, this.size);\n });\n this.on('active', () => {\n this.total++;\n });\n }\n\n getStats() {\n return {\n peak: this.peak,\n pending: this.pending,\n size: this.size,\n total: this.total,\n };\n }\n}\n\nexport interface QueueOptions {\n concurrency?: number;\n timeout?: number;\n throwOnTimeout?: boolean;\n}\n"],"names":["Queue","PQueue","constructor","options","peak","total","on","Math","max","size","getStats","pending"],"mappings":";;;;+BAEaA;;;eAAAA;;;;iEAFM;AAEZ,IAAA,AAAMA,QAAN,MAAMA,cAAcC,eAAM;IAI/BC,YAAYC,OAAqB,CAAE;QACjC,KAAK,CAACA,eAJAC,OAAO,QACPC,QAAQ;QAId,IAAI,CAACC,EAAE,CAAC,OAAO;YACb,IAAI,CAACF,IAAI,GAAGG,KAAKC,GAAG,CAAC,IAAI,CAACJ,IAAI,EAAE,IAAI,CAACK,IAAI;QAC3C;QACA,IAAI,CAACH,EAAE,CAAC,UAAU;YAChB,IAAI,CAACD,KAAK;QACZ;IACF;IAEAK,WAAW;QACT,OAAO;YACLN,MAAM,IAAI,CAACA,IAAI;YACfO,SAAS,IAAI,CAACA,OAAO;YACrBF,MAAM,IAAI,CAACA,IAAI;YACfJ,OAAO,IAAI,CAACA,KAAK;QACnB;IACF;AACF"}
|
package/src/lib/api-base.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "APIBase", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return APIBase;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
let APIBase = class APIBase {
|
|
12
|
+
constructor(httpClient, basePath){
|
|
3
13
|
this.httpClient = httpClient;
|
|
4
14
|
this.basePath = basePath;
|
|
5
15
|
}
|
|
6
|
-
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=api-base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../lib/hpc-api/src/lib/api-base.ts"],"sourcesContent":["import { HttpClient } from './http.service';\n\nexport class APIBase {\n constructor(\n protected readonly httpClient: HttpClient,\n protected readonly basePath: string,\n ) {}\n}\n"],"names":["APIBase","constructor","httpClient","basePath"],"mappings":";;;;+BAEaA;;;eAAAA;;;AAAN,IAAA,AAAMA,UAAN,MAAMA;IACXC,YACE,AAAmBC,UAAsB,EACzC,AAAmBC,QAAgB,CACnC;aAFmBD,aAAAA;aACAC,WAAAA;IAClB;AACL"}
|