@cepseudo/ngsi-ld 1.0.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/README.md +425 -0
- package/dist/cache/entity_cache.d.ts +42 -0
- package/dist/cache/entity_cache.d.ts.map +1 -0
- package/dist/cache/entity_cache.js +93 -0
- package/dist/cache/entity_cache.js.map +1 -0
- package/dist/components/ngsi_ld_collector.d.ts +25 -0
- package/dist/components/ngsi_ld_collector.d.ts.map +1 -0
- package/dist/components/ngsi_ld_collector.js +13 -0
- package/dist/components/ngsi_ld_collector.js.map +1 -0
- package/dist/components/ngsi_ld_harvester.d.ts +25 -0
- package/dist/components/ngsi_ld_harvester.d.ts.map +1 -0
- package/dist/components/ngsi_ld_harvester.js +13 -0
- package/dist/components/ngsi_ld_harvester.js.map +1 -0
- package/dist/components/type_guards.d.ts +15 -0
- package/dist/components/type_guards.d.ts.map +1 -0
- package/dist/components/type_guards.js +25 -0
- package/dist/components/type_guards.js.map +1 -0
- package/dist/endpoints/attrs.d.ts +7 -0
- package/dist/endpoints/attrs.d.ts.map +1 -0
- package/dist/endpoints/attrs.js +37 -0
- package/dist/endpoints/attrs.js.map +1 -0
- package/dist/endpoints/entities.d.ts +9 -0
- package/dist/endpoints/entities.d.ts.map +1 -0
- package/dist/endpoints/entities.js +149 -0
- package/dist/endpoints/entities.js.map +1 -0
- package/dist/endpoints/subscriptions.d.ts +8 -0
- package/dist/endpoints/subscriptions.d.ts.map +1 -0
- package/dist/endpoints/subscriptions.js +109 -0
- package/dist/endpoints/subscriptions.js.map +1 -0
- package/dist/endpoints/types.d.ts +7 -0
- package/dist/endpoints/types.d.ts.map +1 -0
- package/dist/endpoints/types.js +45 -0
- package/dist/endpoints/types.js.map +1 -0
- package/dist/helpers/property.d.ts +33 -0
- package/dist/helpers/property.d.ts.map +1 -0
- package/dist/helpers/property.js +40 -0
- package/dist/helpers/property.js.map +1 -0
- package/dist/helpers/urn.d.ts +26 -0
- package/dist/helpers/urn.d.ts.map +1 -0
- package/dist/helpers/urn.js +30 -0
- package/dist/helpers/urn.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/models/agrifood/agri_parcel.d.ts +17 -0
- package/dist/models/agrifood/agri_parcel.d.ts.map +1 -0
- package/dist/models/agrifood/agri_parcel.js +27 -0
- package/dist/models/agrifood/agri_parcel.js.map +1 -0
- package/dist/models/agrifood/agri_soil_measurement.d.ts +17 -0
- package/dist/models/agrifood/agri_soil_measurement.d.ts.map +1 -0
- package/dist/models/agrifood/agri_soil_measurement.js +31 -0
- package/dist/models/agrifood/agri_soil_measurement.js.map +1 -0
- package/dist/models/agrifood/agri_weather_observed.d.ts +20 -0
- package/dist/models/agrifood/agri_weather_observed.d.ts.map +1 -0
- package/dist/models/agrifood/agri_weather_observed.js +34 -0
- package/dist/models/agrifood/agri_weather_observed.js.map +1 -0
- package/dist/models/device/device.d.ts +24 -0
- package/dist/models/device/device.d.ts.map +1 -0
- package/dist/models/device/device.js +39 -0
- package/dist/models/device/device.js.map +1 -0
- package/dist/models/device/device_measurement.d.ts +16 -0
- package/dist/models/device/device_measurement.d.ts.map +1 -0
- package/dist/models/device/device_measurement.js +34 -0
- package/dist/models/device/device_measurement.js.map +1 -0
- package/dist/models/environment/air_quality_observed.d.ts +22 -0
- package/dist/models/environment/air_quality_observed.d.ts.map +1 -0
- package/dist/models/environment/air_quality_observed.js +50 -0
- package/dist/models/environment/air_quality_observed.js.map +1 -0
- package/dist/models/environment/noise_level_observed.d.ts +17 -0
- package/dist/models/environment/noise_level_observed.d.ts.map +1 -0
- package/dist/models/environment/noise_level_observed.js +31 -0
- package/dist/models/environment/noise_level_observed.js.map +1 -0
- package/dist/models/environment/water_quality_observed.d.ts +20 -0
- package/dist/models/environment/water_quality_observed.d.ts.map +1 -0
- package/dist/models/environment/water_quality_observed.js +46 -0
- package/dist/models/environment/water_quality_observed.js.map +1 -0
- package/dist/models/environment/weather_observed.d.ts +19 -0
- package/dist/models/environment/weather_observed.d.ts.map +1 -0
- package/dist/models/environment/weather_observed.js +46 -0
- package/dist/models/environment/weather_observed.js.map +1 -0
- package/dist/models/index.d.ts +25 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +17 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/smart_city/parking_spot.d.ts +14 -0
- package/dist/models/smart_city/parking_spot.d.ts.map +1 -0
- package/dist/models/smart_city/parking_spot.js +27 -0
- package/dist/models/smart_city/parking_spot.js.map +1 -0
- package/dist/models/smart_city/street_light.d.ts +16 -0
- package/dist/models/smart_city/street_light.d.ts.map +1 -0
- package/dist/models/smart_city/street_light.js +33 -0
- package/dist/models/smart_city/street_light.js.map +1 -0
- package/dist/models/smart_city/traffic_flow_observed.d.ts +19 -0
- package/dist/models/smart_city/traffic_flow_observed.d.ts.map +1 -0
- package/dist/models/smart_city/traffic_flow_observed.js +37 -0
- package/dist/models/smart_city/traffic_flow_observed.js.map +1 -0
- package/dist/notifications/notification_sender.d.ts +16 -0
- package/dist/notifications/notification_sender.d.ts.map +1 -0
- package/dist/notifications/notification_sender.js +70 -0
- package/dist/notifications/notification_sender.js.map +1 -0
- package/dist/notifications/notification_worker.d.ts +19 -0
- package/dist/notifications/notification_worker.d.ts.map +1 -0
- package/dist/notifications/notification_worker.js +65 -0
- package/dist/notifications/notification_worker.js.map +1 -0
- package/dist/plugin.d.ts +35 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +113 -0
- package/dist/plugin.js.map +1 -0
- package/dist/subscriptions/q_parser.d.ts +42 -0
- package/dist/subscriptions/q_parser.d.ts.map +1 -0
- package/dist/subscriptions/q_parser.js +113 -0
- package/dist/subscriptions/q_parser.js.map +1 -0
- package/dist/subscriptions/subscription_cache.d.ts +46 -0
- package/dist/subscriptions/subscription_cache.d.ts.map +1 -0
- package/dist/subscriptions/subscription_cache.js +105 -0
- package/dist/subscriptions/subscription_cache.js.map +1 -0
- package/dist/subscriptions/subscription_matcher.d.ts +23 -0
- package/dist/subscriptions/subscription_matcher.d.ts.map +1 -0
- package/dist/subscriptions/subscription_matcher.js +84 -0
- package/dist/subscriptions/subscription_matcher.js.map +1 -0
- package/dist/subscriptions/subscription_store.d.ts +42 -0
- package/dist/subscriptions/subscription_store.d.ts.map +1 -0
- package/dist/subscriptions/subscription_store.js +189 -0
- package/dist/subscriptions/subscription_store.js.map +1 -0
- package/dist/types/context.d.ts +9 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +5 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/entity.d.ts +46 -0
- package/dist/types/entity.d.ts.map +1 -0
- package/dist/types/entity.js +2 -0
- package/dist/types/entity.js.map +1 -0
- package/dist/types/notification.d.ts +22 -0
- package/dist/types/notification.d.ts.map +1 -0
- package/dist/types/notification.js +2 -0
- package/dist/types/notification.js.map +1 -0
- package/dist/types/subscription.d.ts +54 -0
- package/dist/types/subscription.d.ts.map +1 -0
- package/dist/types/subscription.js +2 -0
- package/dist/types/subscription.js.map +1 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# @cepseudo/ngsi-ld
|
|
2
|
+
|
|
3
|
+
[](https://nodejs.org/)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
Optional NGSI-LD plugin for the Digital Twin Framework. Implements the [ETSI NGSI-LD](https://www.etsi.org/deliver/etsi_gs/CIM/001_099/009/) API specification directly -- no FIWARE or Orion dependency. Provides entity management, subscription-based notifications, and a Redis-backed entity cache for sub-millisecond last-state queries.
|
|
7
|
+
|
|
8
|
+
This package is designed as a **fully optional plugin**. The framework runs without it. When installed, the engine discovers and loads it dynamically at startup.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @cepseudo/ngsi-ld
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Peer dependencies
|
|
17
|
+
|
|
18
|
+
The following must be installed in your project:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add bullmq ioredis
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Workspace dependencies
|
|
25
|
+
|
|
26
|
+
This package depends on `@cepseudo/shared`, `@cepseudo/database`, and `@cepseudo/components`, which are resolved automatically in the monorepo workspace.
|
|
27
|
+
|
|
28
|
+
## How the Optional Plugin Pattern Works
|
|
29
|
+
|
|
30
|
+
The engine never imports `@cepseudo/ngsi-ld` statically. Instead, it uses a dynamic import with a try/catch at startup:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// Inside @cepseudo/engine — dynamic discovery
|
|
34
|
+
async function loadOptionalPackages() {
|
|
35
|
+
try {
|
|
36
|
+
const { registerNgsiLd } = await import('@cepseudo/ngsi-ld')
|
|
37
|
+
await registerNgsiLd({ router, db, redis, components, logger })
|
|
38
|
+
logger.info('NGSI-LD plugin loaded')
|
|
39
|
+
} catch {
|
|
40
|
+
// Package not installed — skip silently, framework works without it
|
|
41
|
+
logger.info('NGSI-LD package not installed, skipping')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The integration point is the **EventBus** from `@cepseudo/shared`. When a Collector or Harvester completes, the engine emits a `component:event` on the event bus. If the NGSI-LD plugin is loaded, it listens for these events, converts data to NGSI-LD entities, updates the entity cache, and evaluates subscriptions. If the plugin is not loaded, the events are simply ignored.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Collector writes data --> EventBus emits "component:event"
|
|
50
|
+
|-- ngsi-ld installed --> SubscriptionMatcher evaluates + enqueues notifications
|
|
51
|
+
|-- ngsi-ld not installed --> event ignored, no side effects
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
No package in layers 0--2 (except ngsi-ld itself) may import from this package. Event names and payload types are defined in `@cepseudo/shared`, not here.
|
|
55
|
+
|
|
56
|
+
## Core Concepts
|
|
57
|
+
|
|
58
|
+
### Entities
|
|
59
|
+
|
|
60
|
+
An NGSI-LD entity is a JSON-LD object representing a real-world thing (a sensor, a parking spot, a weather station). Each entity has a URN-style `id`, a `type`, and a set of typed attributes:
|
|
61
|
+
|
|
62
|
+
- **Property** -- holds a scalar or structured value (e.g. temperature, air quality index)
|
|
63
|
+
- **GeoProperty** -- holds a GeoJSON geometry (e.g. sensor location)
|
|
64
|
+
- **Relationship** -- references another entity by URN (e.g. a sensor belongs to a device)
|
|
65
|
+
|
|
66
|
+
Entity last-state is cached in Redis for fast reads. Historical data remains in PostgreSQL.
|
|
67
|
+
|
|
68
|
+
### Subscriptions
|
|
69
|
+
|
|
70
|
+
Clients register subscriptions to receive webhook notifications when entities matching certain criteria are created or updated. A subscription defines:
|
|
71
|
+
|
|
72
|
+
- `entities` -- which entity types to watch
|
|
73
|
+
- `watchedAttributes` -- optional list of attributes; notifications fire only when these change
|
|
74
|
+
- `q` -- optional filter expression (e.g. `pm25>30;temperature<10`)
|
|
75
|
+
- `notification.endpoint` -- the webhook URL to POST to
|
|
76
|
+
- `throttling` -- minimum seconds between notifications
|
|
77
|
+
|
|
78
|
+
Subscriptions are persisted in PostgreSQL and cached in Redis for fast matching.
|
|
79
|
+
|
|
80
|
+
### Notifications
|
|
81
|
+
|
|
82
|
+
When an entity update matches a subscription, a notification job is enqueued in BullMQ. A dedicated worker delivers the notification via HTTP POST with exponential backoff retry (up to 3 attempts). Delivery statistics (times sent, times failed, last success) are tracked per subscription.
|
|
83
|
+
|
|
84
|
+
## API Endpoints
|
|
85
|
+
|
|
86
|
+
All endpoints are mounted under `/ngsi-ld/v1/` and return `application/ld+json`.
|
|
87
|
+
|
|
88
|
+
### Entities
|
|
89
|
+
|
|
90
|
+
| Method | Path | Description |
|
|
91
|
+
|----------|---------------------------------------------|--------------------------------------|
|
|
92
|
+
| `GET` | `/ngsi-ld/v1/entities` | Query entities by type, q, attrs |
|
|
93
|
+
| `POST` | `/ngsi-ld/v1/entities` | Create or replace an entity |
|
|
94
|
+
| `GET` | `/ngsi-ld/v1/entities/:entityId` | Retrieve a single entity |
|
|
95
|
+
| `PATCH` | `/ngsi-ld/v1/entities/:entityId` | Merge-patch an entity |
|
|
96
|
+
| `DELETE` | `/ngsi-ld/v1/entities/:entityId` | Delete an entity |
|
|
97
|
+
| `PATCH` | `/ngsi-ld/v1/entities/:entityId/attrs` | Update specific attributes |
|
|
98
|
+
|
|
99
|
+
**Query parameters** for `GET /entities`:
|
|
100
|
+
|
|
101
|
+
- `type` -- filter by entity type
|
|
102
|
+
- `q` -- NGSI-LD q-filter expression (e.g. `pm25>30;temperature<10`)
|
|
103
|
+
- `attrs` -- comma-separated list of attributes to project
|
|
104
|
+
- `limit` -- max results (default: 20)
|
|
105
|
+
- `offset` -- pagination offset (default: 0)
|
|
106
|
+
|
|
107
|
+
### Subscriptions
|
|
108
|
+
|
|
109
|
+
| Method | Path | Description |
|
|
110
|
+
|----------|-------------------------------------------------|--------------------------------------|
|
|
111
|
+
| `POST` | `/ngsi-ld/v1/subscriptions` | Create a subscription |
|
|
112
|
+
| `GET` | `/ngsi-ld/v1/subscriptions` | List all subscriptions |
|
|
113
|
+
| `GET` | `/ngsi-ld/v1/subscriptions/:subscriptionId` | Retrieve a single subscription |
|
|
114
|
+
| `PATCH` | `/ngsi-ld/v1/subscriptions/:subscriptionId` | Partially update a subscription |
|
|
115
|
+
| `DELETE` | `/ngsi-ld/v1/subscriptions/:subscriptionId` | Delete a subscription |
|
|
116
|
+
|
|
117
|
+
### Types
|
|
118
|
+
|
|
119
|
+
| Method | Path | Description |
|
|
120
|
+
|--------|-------------------------|--------------------------------|
|
|
121
|
+
| `GET` | `/ngsi-ld/v1/types` | List all known entity types |
|
|
122
|
+
|
|
123
|
+
## Entity Format
|
|
124
|
+
|
|
125
|
+
Entities follow the ETSI NGSI-LD specification with JSON-LD context:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"id": "urn:ngsi-ld:AirQualityObserved:sensor-42",
|
|
130
|
+
"type": "AirQualityObserved",
|
|
131
|
+
"@context": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld",
|
|
132
|
+
"pm25": {
|
|
133
|
+
"type": "Property",
|
|
134
|
+
"value": 63.2,
|
|
135
|
+
"observedAt": "2026-02-28T14:32:00Z"
|
|
136
|
+
},
|
|
137
|
+
"no2": {
|
|
138
|
+
"type": "Property",
|
|
139
|
+
"value": 28.1,
|
|
140
|
+
"observedAt": "2026-02-28T14:32:00Z"
|
|
141
|
+
},
|
|
142
|
+
"location": {
|
|
143
|
+
"type": "GeoProperty",
|
|
144
|
+
"value": {
|
|
145
|
+
"type": "Point",
|
|
146
|
+
"coordinates": [4.3517, 50.8503]
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"refDevice": {
|
|
150
|
+
"type": "Relationship",
|
|
151
|
+
"object": "urn:ngsi-ld:Device:weather-station-7"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
URNs follow the pattern `urn:ngsi-ld:<Type>:<localId>`. Use the `buildUrn` and `parseUrn` helpers to construct and decompose them.
|
|
157
|
+
|
|
158
|
+
## Redis Structures
|
|
159
|
+
|
|
160
|
+
### Entity Cache
|
|
161
|
+
|
|
162
|
+
Each entity is stored as a JSON string under a key derived from its URN. Type indexes allow efficient queries by entity type.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
STRING "ngsi:entity:urn:ngsi-ld:AirQualityObserved:sensor-42" --> serialized entity JSON
|
|
166
|
+
SET "ngsi:types" --> { "AirQualityObserved", "WeatherObserved", ... }
|
|
167
|
+
SET "ngsi:type:AirQualityObserved" --> { "urn:ngsi-ld:AirQualityObserved:sensor-42", ... }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- Last-state queries are served from Redis (sub-millisecond).
|
|
171
|
+
- Historical queries are served from PostgreSQL.
|
|
172
|
+
|
|
173
|
+
### Subscription Cache
|
|
174
|
+
|
|
175
|
+
Active subscriptions are cached in Redis for fast matching on every entity write. Warmed up from PostgreSQL at plugin startup.
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
STRING "ngsi:sub:<uuid>" --> serialized Subscription JSON
|
|
179
|
+
SET "ngsi:subs:type:AirQualityObserved" --> { "<sub-uuid-1>", "<sub-uuid-7>", ... }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The `SubscriptionMatcher` reads from the subscription cache to evaluate conditions without hitting the database on every write.
|
|
183
|
+
|
|
184
|
+
## Subscription and Notification Flow
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
1. Client POSTs to /ngsi-ld/v1/subscriptions
|
|
188
|
+
--> saved in PostgreSQL + cached in Redis
|
|
189
|
+
|
|
190
|
+
2. Collector/Harvester writes data
|
|
191
|
+
--> engine emits "component:event" on the EventBus
|
|
192
|
+
|
|
193
|
+
3. Plugin receives the event
|
|
194
|
+
--> loads the latest data record from the database
|
|
195
|
+
--> calls component.toNgsiLdEntity(data, record)
|
|
196
|
+
--> updates the entity cache in Redis
|
|
197
|
+
|
|
198
|
+
4. SubscriptionMatcher reads subscription cache
|
|
199
|
+
--> evaluates: entity type match, watchedAttributes change, q-filter, throttling
|
|
200
|
+
--> returns list of matching subscription IDs
|
|
201
|
+
|
|
202
|
+
5. For each match, a notification job is enqueued in BullMQ
|
|
203
|
+
--> queue: "ngsi-ld-notifications"
|
|
204
|
+
--> 3 attempts with exponential backoff (1s, 5s, 25s)
|
|
205
|
+
|
|
206
|
+
6. Notification worker POSTs the payload to the subscriber endpoint
|
|
207
|
+
--> Content-Type: application/ld+json
|
|
208
|
+
--> updates times_sent / times_failed in PostgreSQL
|
|
209
|
+
--> updates lastNotificationAt in Redis cache
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Notification Payload
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"id": "urn:ngsi-ld:Notification:<uuid>",
|
|
217
|
+
"type": "Notification",
|
|
218
|
+
"subscriptionId": "<subscription-uuid>",
|
|
219
|
+
"notifiedAt": "2026-03-19T10:15:00.000Z",
|
|
220
|
+
"data": [
|
|
221
|
+
{ "id": "urn:ngsi-ld:AirQualityObserved:sensor-42", "type": "AirQualityObserved", "pm25": { "type": "Property", "value": 63.2 } }
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Q-Filter Syntax
|
|
227
|
+
|
|
228
|
+
The `q` parameter supports comparison expressions with `;` as AND:
|
|
229
|
+
|
|
230
|
+
| Operator | Example | Meaning |
|
|
231
|
+
|----------|---------------------|-----------------------------|
|
|
232
|
+
| `==` | `status=="active"` | Equals |
|
|
233
|
+
| `!=` | `status!="offline"` | Not equals |
|
|
234
|
+
| `>` | `pm25>30` | Greater than |
|
|
235
|
+
| `>=` | `temperature>=0` | Greater than or equal |
|
|
236
|
+
| `<` | `humidity<40` | Less than |
|
|
237
|
+
| `<=` | `no2<=50` | Less than or equal |
|
|
238
|
+
|
|
239
|
+
Multiple conditions are ANDed with `;`:
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
pm25>30;temperature<10;status=="active"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The parser resolves attribute values from NGSI-LD Property objects automatically -- `pm25>30` compares against the `value` field of the `pm25` Property.
|
|
246
|
+
|
|
247
|
+
## NGSI-LD-Aware Components
|
|
248
|
+
|
|
249
|
+
To have a Collector or Harvester produce NGSI-LD entities automatically, extend the provided abstract base classes instead of the standard ones:
|
|
250
|
+
|
|
251
|
+
### NgsiLdCollector
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { NgsiLdCollector } from '@cepseudo/ngsi-ld'
|
|
255
|
+
import { buildAirQualityObserved } from '@cepseudo/ngsi-ld'
|
|
256
|
+
import type { DataRecord, NgsiLdEntity } from '@cepseudo/ngsi-ld'
|
|
257
|
+
|
|
258
|
+
export class AirQualityCollector extends NgsiLdCollector {
|
|
259
|
+
getConfiguration() {
|
|
260
|
+
return {
|
|
261
|
+
name: 'air-quality',
|
|
262
|
+
schedule: '*/5 * * * *',
|
|
263
|
+
description: 'Collects air quality data from sensors',
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async collect() {
|
|
268
|
+
const data = await fetch('https://api.example.com/air-quality')
|
|
269
|
+
return data.json()
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
toNgsiLdEntity(data: unknown, _record: DataRecord): NgsiLdEntity {
|
|
273
|
+
const d = data as { sensorId: string; pm25: number; no2: number; timestamp: string }
|
|
274
|
+
return buildAirQualityObserved({
|
|
275
|
+
localId: d.sensorId,
|
|
276
|
+
pm25: d.pm25,
|
|
277
|
+
no2: d.no2,
|
|
278
|
+
dateObserved: d.timestamp,
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### NgsiLdHarvester
|
|
285
|
+
|
|
286
|
+
Same pattern -- extend `NgsiLdHarvester` and implement `toNgsiLdEntity`. The plugin calls it after each successful harvest.
|
|
287
|
+
|
|
288
|
+
## Helper Functions
|
|
289
|
+
|
|
290
|
+
### Property builders
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { property, geoProperty, relationship } from '@cepseudo/ngsi-ld'
|
|
294
|
+
|
|
295
|
+
// Property with value and optional metadata
|
|
296
|
+
property(42.5, { observedAt: '2026-03-19T10:00:00Z', unitCode: 'CEL' })
|
|
297
|
+
// => { type: 'Property', value: 42.5, observedAt: '...', unitCode: 'CEL' }
|
|
298
|
+
|
|
299
|
+
// GeoProperty with GeoJSON
|
|
300
|
+
geoProperty({ type: 'Point', coordinates: [4.3517, 50.8503] })
|
|
301
|
+
// => { type: 'GeoProperty', value: { type: 'Point', coordinates: [...] } }
|
|
302
|
+
|
|
303
|
+
// Relationship to another entity
|
|
304
|
+
relationship('urn:ngsi-ld:Device:station-7')
|
|
305
|
+
// => { type: 'Relationship', object: 'urn:ngsi-ld:Device:station-7' }
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### URN helpers
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { buildUrn, parseUrn } from '@cepseudo/ngsi-ld'
|
|
312
|
+
|
|
313
|
+
buildUrn('AirQualityObserved', 'sensor-42')
|
|
314
|
+
// => 'urn:ngsi-ld:AirQualityObserved:sensor-42'
|
|
315
|
+
|
|
316
|
+
parseUrn('urn:ngsi-ld:AirQualityObserved:sensor-42')
|
|
317
|
+
// => { type: 'AirQualityObserved', localId: 'sensor-42' }
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Smart Data Models
|
|
321
|
+
|
|
322
|
+
The package includes builder functions for common FIWARE Smart Data Model types. Each builder accepts a typed attributes object and returns a fully formed NGSI-LD entity.
|
|
323
|
+
|
|
324
|
+
**Environment:**
|
|
325
|
+
- `buildAirQualityObserved` -- PM2.5, PM10, NO2, O3, CO, SO2, temperature, humidity
|
|
326
|
+
- `buildWeatherObserved` -- temperature, humidity, wind speed/direction, precipitation
|
|
327
|
+
- `buildWaterQualityObserved` -- pH, dissolved oxygen, conductivity, turbidity
|
|
328
|
+
- `buildNoiseLevelObserved` -- LAeq, LAmax, sones
|
|
329
|
+
|
|
330
|
+
**Smart City:**
|
|
331
|
+
- `buildStreetLight` -- power state, brightness, energy consumption
|
|
332
|
+
- `buildParkingSpot` -- occupancy status, vehicle type
|
|
333
|
+
- `buildTrafficFlowObserved` -- vehicle count, average speed, occupancy
|
|
334
|
+
|
|
335
|
+
**Agrifood:**
|
|
336
|
+
- `buildAgriParcel` -- crop type, area, soil type
|
|
337
|
+
- `buildAgriSoilMeasurement` -- moisture, pH, nitrogen, phosphorus, potassium
|
|
338
|
+
- `buildAgriWeatherObserved` -- solar radiation, evapotranspiration
|
|
339
|
+
|
|
340
|
+
**Device:**
|
|
341
|
+
- `buildDevice` -- device metadata, battery, signal strength
|
|
342
|
+
- `buildDeviceMeasurement` -- generic sensor readings
|
|
343
|
+
|
|
344
|
+
## Architecture
|
|
345
|
+
|
|
346
|
+
`@cepseudo/ngsi-ld` sits at LAYER 2 in the Digital Twin Framework dependency graph:
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
LAYER 3: engine -- loads ngsi-ld dynamically via import()
|
|
350
|
+
LAYER 2: ngsi-ld -- entities, subscriptions, notifications
|
|
351
|
+
LAYER 2: assets, components -- business logic, file management
|
|
352
|
+
LAYER 1: database, storage, auth -- infrastructure adapters
|
|
353
|
+
LAYER 0: shared -- types, errors, utilities, validation
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Internal structure:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
src/
|
|
360
|
+
cache/
|
|
361
|
+
entity_cache.ts -- Redis entity last-state cache
|
|
362
|
+
components/
|
|
363
|
+
ngsi_ld_collector.ts -- Abstract NGSI-LD collector base class
|
|
364
|
+
ngsi_ld_harvester.ts -- Abstract NGSI-LD harvester base class
|
|
365
|
+
type_guards.ts -- Runtime type checks for NGSI-LD components
|
|
366
|
+
endpoints/
|
|
367
|
+
entities.ts -- CRUD /ngsi-ld/v1/entities
|
|
368
|
+
attrs.ts -- PATCH /ngsi-ld/v1/entities/:id/attrs
|
|
369
|
+
subscriptions.ts -- CRUD /ngsi-ld/v1/subscriptions
|
|
370
|
+
types.ts -- GET /ngsi-ld/v1/types
|
|
371
|
+
helpers/
|
|
372
|
+
property.ts -- property(), geoProperty(), relationship() builders
|
|
373
|
+
urn.ts -- buildUrn(), parseUrn()
|
|
374
|
+
models/
|
|
375
|
+
environment/ -- Air quality, weather, water quality, noise
|
|
376
|
+
smart_city/ -- Street lights, parking, traffic
|
|
377
|
+
agrifood/ -- Parcels, soil, weather
|
|
378
|
+
device/ -- Devices, measurements
|
|
379
|
+
notifications/
|
|
380
|
+
notification_sender.ts -- Enqueues notification jobs in BullMQ
|
|
381
|
+
notification_worker.ts -- Delivers notifications via HTTP POST
|
|
382
|
+
subscriptions/
|
|
383
|
+
subscription_store.ts -- PostgreSQL persistence for subscriptions
|
|
384
|
+
subscription_cache.ts -- Redis cache for active subscriptions
|
|
385
|
+
subscription_matcher.ts -- Evaluates subscriptions against entity updates
|
|
386
|
+
q_parser.ts -- Parses and evaluates q-filter expressions
|
|
387
|
+
types/
|
|
388
|
+
entity.ts -- NgsiLdEntity, NgsiLdProperty, etc.
|
|
389
|
+
subscription.ts -- Subscription, SubscriptionCreate
|
|
390
|
+
notification.ts -- NotificationPayload, NotificationJobData
|
|
391
|
+
context.ts -- JSON-LD context constants
|
|
392
|
+
plugin.ts -- registerNgsiLd() entry point
|
|
393
|
+
index.ts -- Public API exports
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Database Schema
|
|
397
|
+
|
|
398
|
+
The plugin creates its own table on first load (via `subscriptionStore.runMigration()`). No manual migration is needed.
|
|
399
|
+
|
|
400
|
+
```sql
|
|
401
|
+
CREATE TABLE IF NOT EXISTS ngsi_ld_subscriptions (
|
|
402
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
403
|
+
name VARCHAR(255),
|
|
404
|
+
description TEXT,
|
|
405
|
+
entity_types TEXT[],
|
|
406
|
+
watched_attributes TEXT[],
|
|
407
|
+
q VARCHAR(1000),
|
|
408
|
+
notification_endpoint VARCHAR(500) NOT NULL,
|
|
409
|
+
notification_format VARCHAR(50) DEFAULT 'normalized',
|
|
410
|
+
notification_attrs TEXT[],
|
|
411
|
+
throttling INTEGER DEFAULT 0,
|
|
412
|
+
expires_at TIMESTAMP,
|
|
413
|
+
is_active BOOLEAN DEFAULT true,
|
|
414
|
+
last_notification_at TIMESTAMP,
|
|
415
|
+
last_success_at TIMESTAMP,
|
|
416
|
+
times_sent INTEGER DEFAULT 0,
|
|
417
|
+
times_failed INTEGER DEFAULT 0,
|
|
418
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
419
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
420
|
+
);
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## License
|
|
424
|
+
|
|
425
|
+
MIT
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
import type { NgsiLdEntity } from '../types/entity.js';
|
|
3
|
+
/**
|
|
4
|
+
* Redis-backed cache for NGSI-LD entity last-state.
|
|
5
|
+
*
|
|
6
|
+
* - Each entity is stored as a Redis HASH under `ngsi:entity:<id>`
|
|
7
|
+
* - A Redis SET `ngsi:types` indexes all known entity types
|
|
8
|
+
* - A Redis SET `ngsi:type:<type>` indexes all entity IDs for that type
|
|
9
|
+
*/
|
|
10
|
+
export declare class EntityCache {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(redis: Redis);
|
|
13
|
+
/**
|
|
14
|
+
* Stores or overwrites an entity in the cache.
|
|
15
|
+
*/
|
|
16
|
+
set(entity: NgsiLdEntity): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves an entity by its URN, or null if not found.
|
|
19
|
+
*/
|
|
20
|
+
get(id: string): Promise<NgsiLdEntity | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Removes an entity from the cache.
|
|
23
|
+
*/
|
|
24
|
+
delete(id: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Returns all entities of a given type.
|
|
27
|
+
*/
|
|
28
|
+
listByType(type: string): Promise<NgsiLdEntity[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Returns all known entity types.
|
|
31
|
+
*/
|
|
32
|
+
listTypes(): Promise<string[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Returns all cached entities, optionally filtered by type.
|
|
35
|
+
*/
|
|
36
|
+
list(options?: {
|
|
37
|
+
type?: string;
|
|
38
|
+
limit?: number;
|
|
39
|
+
offset?: number;
|
|
40
|
+
}): Promise<NgsiLdEntity[]>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=entity_cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity_cache.d.ts","sourceRoot":"","sources":["../../src/cache/entity_cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKtD;;;;;;GAMG;AACH,qBAAa,WAAW;;gBAGR,KAAK,EAAE,KAAK;IAIxB;;OAEG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAW9C;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAMnD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvC;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAYvD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAoBpG"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const KEY_PREFIX = 'ngsi:entity:';
|
|
2
|
+
const TYPES_KEY = 'ngsi:types';
|
|
3
|
+
/**
|
|
4
|
+
* Redis-backed cache for NGSI-LD entity last-state.
|
|
5
|
+
*
|
|
6
|
+
* - Each entity is stored as a Redis HASH under `ngsi:entity:<id>`
|
|
7
|
+
* - A Redis SET `ngsi:types` indexes all known entity types
|
|
8
|
+
* - A Redis SET `ngsi:type:<type>` indexes all entity IDs for that type
|
|
9
|
+
*/
|
|
10
|
+
export class EntityCache {
|
|
11
|
+
#redis;
|
|
12
|
+
constructor(redis) {
|
|
13
|
+
this.#redis = redis;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Stores or overwrites an entity in the cache.
|
|
17
|
+
*/
|
|
18
|
+
async set(entity) {
|
|
19
|
+
const key = `${KEY_PREFIX}${entity.id}`;
|
|
20
|
+
const serialized = JSON.stringify(entity);
|
|
21
|
+
await Promise.all([
|
|
22
|
+
this.#redis.set(key, serialized),
|
|
23
|
+
this.#redis.sadd(TYPES_KEY, entity.type),
|
|
24
|
+
this.#redis.sadd(`ngsi:type:${entity.type}`, entity.id),
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves an entity by its URN, or null if not found.
|
|
29
|
+
*/
|
|
30
|
+
async get(id) {
|
|
31
|
+
const raw = await this.#redis.get(`${KEY_PREFIX}${id}`);
|
|
32
|
+
if (!raw)
|
|
33
|
+
return null;
|
|
34
|
+
return JSON.parse(raw);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Removes an entity from the cache.
|
|
38
|
+
*/
|
|
39
|
+
async delete(id) {
|
|
40
|
+
const existing = await this.get(id);
|
|
41
|
+
if (!existing)
|
|
42
|
+
return;
|
|
43
|
+
await Promise.all([
|
|
44
|
+
this.#redis.del(`${KEY_PREFIX}${id}`),
|
|
45
|
+
this.#redis.srem(`ngsi:type:${existing.type}`, id),
|
|
46
|
+
]);
|
|
47
|
+
// Clean up the type index entry if no more entities of that type
|
|
48
|
+
const remaining = await this.#redis.scard(`ngsi:type:${existing.type}`);
|
|
49
|
+
if (remaining === 0) {
|
|
50
|
+
await this.#redis.srem(TYPES_KEY, existing.type);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns all entities of a given type.
|
|
55
|
+
*/
|
|
56
|
+
async listByType(type) {
|
|
57
|
+
const ids = await this.#redis.smembers(`ngsi:type:${type}`);
|
|
58
|
+
if (ids.length === 0)
|
|
59
|
+
return [];
|
|
60
|
+
const keys = ids.map(id => `${KEY_PREFIX}${id}`);
|
|
61
|
+
const raws = await this.#redis.mget(...keys);
|
|
62
|
+
return raws
|
|
63
|
+
.filter((raw) => raw !== null)
|
|
64
|
+
.map(raw => JSON.parse(raw));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns all known entity types.
|
|
68
|
+
*/
|
|
69
|
+
async listTypes() {
|
|
70
|
+
return this.#redis.smembers(TYPES_KEY);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns all cached entities, optionally filtered by type.
|
|
74
|
+
*/
|
|
75
|
+
async list(options) {
|
|
76
|
+
if (options?.type) {
|
|
77
|
+
const all = await this.listByType(options.type);
|
|
78
|
+
const offset = options.offset ?? 0;
|
|
79
|
+
const limit = options.limit ?? all.length;
|
|
80
|
+
return all.slice(offset, offset + limit);
|
|
81
|
+
}
|
|
82
|
+
const types = await this.listTypes();
|
|
83
|
+
const results = [];
|
|
84
|
+
for (const type of types) {
|
|
85
|
+
const entities = await this.listByType(type);
|
|
86
|
+
results.push(...entities);
|
|
87
|
+
}
|
|
88
|
+
const offset = options?.offset ?? 0;
|
|
89
|
+
const limit = options?.limit ?? results.length;
|
|
90
|
+
return results.slice(offset, offset + limit);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=entity_cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity_cache.js","sourceRoot":"","sources":["../../src/cache/entity_cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Collector } from '@cepseudo/components';
|
|
2
|
+
import type { DataRecord } from '@cepseudo/shared';
|
|
3
|
+
import type { NgsiLdEntity } from '../types/entity.js';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for NGSI-LD-aware Collectors.
|
|
6
|
+
*
|
|
7
|
+
* Extends the standard Collector with the ability to produce NGSI-LD entities
|
|
8
|
+
* from collected data. The engine's NGSI-LD plugin will call `toNgsiLdEntity`
|
|
9
|
+
* after each successful collection run to update the entity cache.
|
|
10
|
+
*
|
|
11
|
+
* @abstract
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class NgsiLdCollector extends Collector {
|
|
14
|
+
/**
|
|
15
|
+
* Converts the latest collected data into an NGSI-LD entity.
|
|
16
|
+
*
|
|
17
|
+
* Called by the NGSI-LD plugin after each successful `collect()` run.
|
|
18
|
+
*
|
|
19
|
+
* @param data - The parsed JSON data from the most recent collection
|
|
20
|
+
* @param record - The raw DataRecord as stored in the database
|
|
21
|
+
* @returns A fully formed NGSI-LD entity
|
|
22
|
+
*/
|
|
23
|
+
abstract toNgsiLdEntity(data: unknown, record: DataRecord): NgsiLdEntity;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=ngsi_ld_collector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngsi_ld_collector.d.ts","sourceRoot":"","sources":["../../src/components/ngsi_ld_collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;;;;;;;GAQG;AACH,8BAAsB,eAAgB,SAAQ,SAAS;IACnD;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY;CAC3E"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Collector } from '@cepseudo/components';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for NGSI-LD-aware Collectors.
|
|
4
|
+
*
|
|
5
|
+
* Extends the standard Collector with the ability to produce NGSI-LD entities
|
|
6
|
+
* from collected data. The engine's NGSI-LD plugin will call `toNgsiLdEntity`
|
|
7
|
+
* after each successful collection run to update the entity cache.
|
|
8
|
+
*
|
|
9
|
+
* @abstract
|
|
10
|
+
*/
|
|
11
|
+
export class NgsiLdCollector extends Collector {
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ngsi_ld_collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngsi_ld_collector.js","sourceRoot":"","sources":["../../src/components/ngsi_ld_collector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAIhD;;;;;;;;GAQG;AACH,MAAM,OAAgB,eAAgB,SAAQ,SAAS;CAWtD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Harvester } from '@cepseudo/components';
|
|
2
|
+
import type { DataRecord } from '@cepseudo/shared';
|
|
3
|
+
import type { NgsiLdEntity } from '../types/entity.js';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for NGSI-LD-aware Harvesters.
|
|
6
|
+
*
|
|
7
|
+
* Extends the standard Harvester with the ability to produce NGSI-LD entities
|
|
8
|
+
* from harvested data. The engine's NGSI-LD plugin will call `toNgsiLdEntity`
|
|
9
|
+
* after each successful harvest run to update the entity cache.
|
|
10
|
+
*
|
|
11
|
+
* @abstract
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class NgsiLdHarvester extends Harvester {
|
|
14
|
+
/**
|
|
15
|
+
* Converts the latest harvested data into an NGSI-LD entity.
|
|
16
|
+
*
|
|
17
|
+
* Called by the NGSI-LD plugin after each successful `harvest()` run.
|
|
18
|
+
*
|
|
19
|
+
* @param data - The parsed JSON data from the most recent harvest
|
|
20
|
+
* @param record - The raw DataRecord as stored in the database
|
|
21
|
+
* @returns A fully formed NGSI-LD entity
|
|
22
|
+
*/
|
|
23
|
+
abstract toNgsiLdEntity(data: unknown, record: DataRecord): NgsiLdEntity;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=ngsi_ld_harvester.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngsi_ld_harvester.d.ts","sourceRoot":"","sources":["../../src/components/ngsi_ld_harvester.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;;;;;;;GAQG;AACH,8BAAsB,eAAgB,SAAQ,SAAS;IACnD;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY;CAC3E"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Harvester } from '@cepseudo/components';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for NGSI-LD-aware Harvesters.
|
|
4
|
+
*
|
|
5
|
+
* Extends the standard Harvester with the ability to produce NGSI-LD entities
|
|
6
|
+
* from harvested data. The engine's NGSI-LD plugin will call `toNgsiLdEntity`
|
|
7
|
+
* after each successful harvest run to update the entity cache.
|
|
8
|
+
*
|
|
9
|
+
* @abstract
|
|
10
|
+
*/
|
|
11
|
+
export class NgsiLdHarvester extends Harvester {
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ngsi_ld_harvester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngsi_ld_harvester.js","sourceRoot":"","sources":["../../src/components/ngsi_ld_harvester.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAIhD;;;;;;;;GAQG;AACH,MAAM,OAAgB,eAAgB,SAAQ,SAAS;CAWtD"}
|