Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
SensorDataReceptorResource.cs
2using System.Text;
3using System.Threading.Tasks;
4using System.Xml;
5using Waher.Content;
11using Waher.Script;
14
15namespace Waher.Things.Http
16{
22 {
23 private const string SensorDataNamespace = "urn:nfi:iot:sd:1.0";
24
25 private readonly HttpAuthenticationScheme[] authenticationSchemes;
26
33 params HttpAuthenticationScheme[] AuthenticationSchemes)
34 : base(ResourceName)
35 {
36 this.authenticationSchemes = AuthenticationSchemes;
37 }
38
42 public override bool HandlesSubPaths => true;
43
47 public override bool UserSessions => true;
48
52 public bool AllowsGET => true;
53
57 public bool AllowsPOST => true;
58
67 {
68 if (Request.Header.Method.ToUpper() == "GET")
69 return null;
70 else
71 return this.authenticationSchemes;
72 }
73
80 public async Task GET(HttpRequest Request, HttpResponse Response)
81 {
82 byte[] Data = Resources.LoadResource(
83 typeof(SensorDataReceptorResource).Namespace + ".Data.ApiDocumentation.md",
84 typeof(SensorDataReceptorResource).Assembly);
85
86 string Markdown = Strings.GetString(Data, Encoding.UTF8);
87 MarkdownSettings Settings = new MarkdownSettings(null, true, new Variables())
88 {
89 RootFolder = HttpModule.RootFolder,
90 ResourceMap = HttpModule.WebServer
91 };
92 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown, Settings,
93 null, Request.Resource.ResourceName, Request.Header.GetURL());
94 string Html = await Doc.GenerateHTML();
95
96 Response.ContentType = HtmlCodec.DefaultContentType;
97
98 await Response.Write(Html);
99 await Response.SendResponse();
100 }
101
108 public async Task POST(HttpRequest Request, HttpResponse Response)
109 {
110 if (Request.User is null)
111 {
112 await Response.SendResponse(new ForbiddenException("Access denied."));
113 return;
114 }
115
116 if (!Request.HasData)
117 {
118 await Response.SendResponse(new BadRequestException("No payload."));
119 return;
120 }
121
122 ContentResponse Payload = await Request.DecodeDataAsync();
123 if (Payload.HasError)
124 {
125 await Response.SendResponse(new BadRequestException("Unable to decode payload: " + Payload.Error.Message));
126 return;
127 }
128
129 if (!(Payload.Decoded is XmlDocument Xml) || Xml.DocumentElement is null)
130 {
131 await Response.SendResponse(new BadRequestException("Expected XML payload."));
132 return;
133 }
134
135 if (Xml.DocumentElement.NamespaceURI != SensorDataNamespace)
136 {
137 await Response.SendResponse(new BadRequestException("Invalid namespace. Expected: " + SensorDataNamespace));
138 return;
139 }
140
141 if (string.IsNullOrEmpty(Request.SubPath))
142 {
143 if (!Request.User.HasPrivilege(HttpModule.PostPrivileges))
144 {
145 await Response.SendResponse(ForbiddenException.AccessDenied(Request,
146 this.ResourceName, Request.User.UserName, HttpModule.PostPrivileges));
147 return;
148 }
149
150 if (Xml.DocumentElement.LocalName == "resp")
151 {
152 foreach (XmlNode N in Xml.DocumentElement.ChildNodes)
153 {
154 if (!(N is XmlElement E))
155 continue;
156
157 if (E.NamespaceURI != SensorDataNamespace || E.LocalName != "nd")
158 {
159 await Response.SendResponse(new BadRequestException("Expected <nd> element as child of <resp>."));
160 return;
161 }
162
163 if (!await this.ProcessNode(E, Response))
164 return;
165 }
166 }
167 else if (Xml.DocumentElement.LocalName == "nd")
168 {
169 if (!await this.ProcessNode(Xml.DocumentElement, Response))
170 return;
171 }
172 else
173 {
174 await Response.SendResponse(new BadRequestException("Expected <resp> or <nd> element."));
175 return;
176 }
177 }
178 else
179 {
180 string PrivilegeId = HttpModule.PostPrivileges + Request.SubPath.Replace('/', '.');
181
182 if (!Request.User.HasPrivilege(PrivilegeId))
183 {
184 await Response.SendResponse(ForbiddenException.AccessDenied(Request,
185 this.ResourceName, Request.User.UserName, PrivilegeId));
186 return;
187 }
188
189 if (Xml.DocumentElement.LocalName == "ts")
190 {
191 string[] Path = Request.SubPath[1..].Split('/');
192 string NodeId = Path[^1];
193
194 MeteringNode Node = await MeteringTopology.GetNode(NodeId);
195
197 {
198 if (!await this.ProcessTimestamp(ExternalWebNode, Xml.DocumentElement, Response))
199 return;
200 }
201 else if (Node is null)
202 {
203 MeteringNode Parent = HttpModule.LocalWebServerNode;
204 ExternalWebNode = null;
205
206 foreach (string Part in Path)
207 {
208 ExternalWebNode = null;
209
210 foreach (INode Child in await Parent.ChildNodes)
211 {
212 if (Child.NodeId == Part)
213 {
214 if (Child is ExternalWebNode E)
215 {
216 ExternalWebNode = E;
217 break;
218 }
219 else
220 {
221 await Response.SendResponse(new ForbiddenException("Node not an external web node: " + Part));
222 return;
223 }
224 }
225 }
226
227 if (ExternalWebNode is null)
228 {
229 if (!(await MeteringTopology.GetNode(Part) is null))
230 {
231 await Response.SendResponse(new ForbiddenException("Part already exists, or is of incorrect type: " + Part));
232 return;
233 }
234
236 {
237 NodeId = Part
238 };
239
240 await Parent.AddAsync(ExternalWebNode);
241 }
242
243 Parent = ExternalWebNode;
244 }
245
246 if (ExternalWebNode is null)
247 {
248 await Response.SendResponse(new BadRequestException("Undefined Node path."));
249 return;
250 }
251 else if (!await this.ProcessTimestamp(ExternalWebNode, Xml.DocumentElement, Response))
252 return;
253 }
254 else
255 {
256 await Response.SendResponse(new ForbiddenException("Node not an external web node: " + NodeId));
257 return;
258 }
259 }
260 else
261 {
262 await Response.SendResponse(new BadRequestException("Expected <ts> element."));
263 return;
264 }
265 }
266
267 Response.StatusCode = 204;
268 Response.StatusMessage = "No content";
269
270 await Response.SendResponse();
271 }
272
273 private async Task<bool> ProcessNode(XmlElement Xml, HttpResponse Response)
274 {
275 string NodeId = Xml.GetAttribute("id");
276 string SourceId = Xml.GetAttribute("src");
277 string Partition = Xml.GetAttribute("pt");
278
279 if (!string.IsNullOrEmpty(SourceId) &&
280 SourceId != MeteringTopology.SourceID)
281 {
282 await Response.SendResponse(new ForbiddenException("Access to data source forbidden."));
283 return false;
284 }
285
286 if (!string.IsNullOrEmpty(Partition))
287 {
288 await Response.SendResponse(new BadRequestException("Expected no partition."));
289 return false;
290 }
291
292 MeteringNode Node = await MeteringTopology.GetNode(NodeId);
293 if (Node is null)
294 {
295 await Response.SendResponse(new NotFoundException("Node not found: " + NodeId));
296 return false;
297 }
298
299 if (!(Node is ExternalWebNode ExternalWebNode))
300 {
301 await Response.SendResponse(new ForbiddenException("Node not an external web node: " + NodeId));
302 return false;
303 }
304
305 foreach (XmlNode N in Xml.ChildNodes)
306 {
307 if (!(N is XmlElement E))
308 continue;
309
310 if (E.NamespaceURI != SensorDataNamespace || E.LocalName != "ts")
311 {
312 await Response.SendResponse(new BadRequestException("Expected <ts> element as child of <nd>."));
313 return false;
314 }
315
316 if (!await this.ProcessTimestamp(ExternalWebNode, E, Response))
317 return false;
318 }
319
320 return true;
321 }
322
323 private async Task<bool> ProcessTimestamp(ExternalWebNode Node, XmlElement Xml, HttpResponse Response)
324 {
325 List<ThingError> Errors = null;
326 List<Field> Fields = null;
327
328 SensorClient.ParseTimespan(Xml, Node, ref Fields, ref Errors);
329
330 if (Fields is null && Errors is null)
331 {
332 await Response.SendResponse(new BadRequestException("No sensor data in payload."));
333 return false;
334 }
335
336 await Node.NewSensorData(Fields?.ToArray(), Errors?.ToArray());
337
338 return true;
339 }
340 }
341
342}
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
HTML encoder/decoder.
Definition: HtmlCodec.cs:15
const string DefaultContentType
Default Content-Type for HTML: text/html
Definition: HtmlCodec.cs:26
Contains a markdown document. This markdown document class supports original markdown,...
async Task< string > GenerateHTML()
Generates HTML from the markdown text.
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Contains settings that the Markdown parser uses to customize its behavior.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
static ForbiddenException AccessDenied(string ObjectId, string ActorId)
Returns a ForbiddenException object, and logs a entry in the event log about the event.
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
string GetURL()
Gets an absolute URL for the request.
Represents an HTTP request.
Definition: HttpRequest.cs:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
bool HasData
If the request has data.
Definition: HttpRequest.cs:113
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
Definition: HttpRequest.cs:194
IUser User
Authenticated user, if available, or null if not available.
Definition: HttpRequest.cs:203
HttpResource Resource
Resource being accessed.
Definition: HttpRequest.cs:221
async Task< ContentResponse > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:139
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
Task Write(byte[] Data)
Returns binary data in the response.
Base class for all synchronous HTTP resources. A synchronous resource responds within the method hand...
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Implements an XMPP sensor client interface.
Definition: SensorClient.cs:21
static void ParseTimespan(XmlElement Xml, ThingReference Thing, ref List< Field > Fields, ref List< ThingError > Errors)
Parses a <ts> element and its corresponding fields and errors.
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:13
static byte[] LoadResource(string ResourceName)
Loads a resource from an embedded resource.
Definition: Resources.cs:20
Static class managing binary representations of strings.
Definition: Strings.cs:10
static string GetString(byte[] Data, int Offset, int Count, Encoding DefaultEncoding)
Gets a string from its binary representation, taking any Byte Order Mark (BOM) into account.
Definition: Strings.cs:148
Collection of variables.
Definition: Variables.cs:25
Node representing an external web node.
Web Service REST API that receives sensor data from external sources.
SensorDataReceptorResource(string ResourceName, params HttpAuthenticationScheme[] AuthenticationSchemes)
Web Service REST API that receives sensor data from external sources.
override bool HandlesSubPaths
If the resource handles sub-paths.
override bool UserSessions
If the resource uses user sessions.
async Task GET(HttpRequest Request, HttpResponse Response)
Executes the GET method on the resource.
override HttpAuthenticationScheme[] GetAuthenticationSchemes(HttpRequest Request)
Any authentication schemes used to authenticate users before access is granted to the corresponding r...
async Task POST(HttpRequest Request, HttpResponse Response)
Executes the POST method on the resource.
Base class for all metering nodes.
Definition: MeteringNode.cs:30
virtual async Task AddAsync(INode Child)
Adds a new child to the node.
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
static Task< MeteringNode > GetNode(string NodeId)
Gets a node from the Metering Topology
const string SourceID
Source ID for the metering topology data source.
GET Interface for HTTP resources.
POST Interface for HTTP resources.
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Definition: INode.cs:140