mirror of
https://github.com/Wessel/Roommapper.git
synced 2026-07-18 05:53:58 +02:00
feat: Add routing to web interface and database
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
Er zijn vele opties om data te verzenden tussen client en server.
|
||||
Voor dit project is er voor gekozen om data te serialiseren naar JSON.
|
||||
Dit is een veelgebruikte methode om data te verzenden tussen client en server.
|
||||
Het voordeel van JSON is dat het een lichtgewicht formaat is en dat het makkelijk te lezen is.
|
||||
Dit is handig voor debugging en het is makkelijk om te zetten naar een object in JavaScript.
|
||||
Een nadeel van JSON is dat het niet binair is en dat het niet zo snel is als binair.
|
||||
Dit is echter geen probleem voor dit project, omdat de hoeveelheid data die verstuurd wordt klein is.
|
||||
\subsection{Serialisatie}
|
||||
\label{sec:serialisatie}
|
||||
% Path: 2 data serialisatie.tex
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<data-source name="roommapper@localhost" uuid="469f6bec-f113-4467-9d39-7ac38558045a">
|
||||
<database-info product="Cassandra" version="3.0.8" jdbc-version="4.2" driver-name="Cassandra JDBC Driver" driver-version="1.4" dbms="CASSANDRA" exact-version="3.0.8" exact-driver-version="1.4">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
<jdbc-catalog-is-schema>true</jdbc-catalog-is-schema>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
||||
<secret-storage>master_key</secret-storage>
|
||||
|
||||
@@ -8,33 +8,33 @@
|
||||
<CanLogin>1</CanLogin>
|
||||
<SuperRole>1</SuperRole>
|
||||
</role>
|
||||
<schema id="3" parent="1" name="system_auth">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="4" parent="1" name="system_schema">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="5" parent="1" name="eve">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="6" parent="1" name="roommapper">
|
||||
<schema id="3" parent="1" name="roommapper">
|
||||
<Current>1</Current>
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="7" parent="1" name="system_distributed">
|
||||
<schema id="4" parent="1" name="system">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="5" parent="1" name="system_auth">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="6" parent="1" name="system_distributed">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '3'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="8" parent="1" name="system">
|
||||
<schema id="7" parent="1" name="system_distributed_everywhere">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.EverywhereStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="8" parent="1" name="system_schema">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.LocalStrategy'}
|
||||
</Properties>
|
||||
@@ -44,17 +44,7 @@ replication:{'class': 'org.apache.cassandra.locator.LocalStrategy
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '2'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="10" parent="1" name="system_distributed_everywhere">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.EverywhereStrategy'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<schema id="11" parent="1" name="discordlogger">
|
||||
<Properties>durable_writes:true
|
||||
replication:{'class': 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor': '1'}
|
||||
</Properties>
|
||||
</schema>
|
||||
<table id="12" parent="6" name="maps">
|
||||
<table id="10" parent="3" name="maps">
|
||||
<Properties>caching:{'keys': 'ALL', 'rows_per_partition': 'ALL'}
|
||||
compression:{'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
|
||||
bloom_filter_fp_chance:0.01
|
||||
@@ -70,26 +60,54 @@ dclocal_read_repair_chance:0
|
||||
compaction:{'class': 'SizeTieredCompactionStrategy'}
|
||||
</Properties>
|
||||
</table>
|
||||
<column id="13" parent="12" name="id">
|
||||
<table id="11" parent="3" name="routes">
|
||||
<Properties>caching:{'keys': 'ALL', 'rows_per_partition': 'ALL'}
|
||||
compression:{'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
|
||||
bloom_filter_fp_chance:0.01
|
||||
default_time_to_live:0
|
||||
speculative_retry:99.0PERCENTILE
|
||||
gc_grace_seconds:864000
|
||||
max_index_interval:2048
|
||||
memtable_flush_period_in_ms:0
|
||||
min_index_interval:128
|
||||
read_repair_chance:0
|
||||
crc_check_chance:1
|
||||
dclocal_read_repair_chance:0
|
||||
compaction:{'class': 'SizeTieredCompactionStrategy'}
|
||||
</Properties>
|
||||
</table>
|
||||
<column id="12" parent="10" name="id">
|
||||
<DasType>uuid|0s</DasType>
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="14" parent="12" name="version">
|
||||
<column id="13" parent="10" name="version">
|
||||
<DasType>int|0s</DasType>
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
<column id="15" parent="12" name="date">
|
||||
<column id="14" parent="10" name="date">
|
||||
<DasType>timestamp|0s</DasType>
|
||||
<Position>3</Position>
|
||||
</column>
|
||||
<column id="16" parent="12" name="objects">
|
||||
<column id="15" parent="10" name="objects">
|
||||
<DasType>text|0s</DasType>
|
||||
<Position>4</Position>
|
||||
</column>
|
||||
<key id="17" parent="12" name="primary key">
|
||||
<key id="16" parent="10" name="primary key">
|
||||
<Columns>id
|
||||
version|ASC
|
||||
date|ASC
|
||||
</Columns>
|
||||
</key>
|
||||
<column id="17" parent="11" name="id">
|
||||
<DasType>uuid|0s</DasType>
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="18" parent="11" name="path">
|
||||
<DasType>text|0s</DasType>
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
<key id="19" parent="11" name="primary key">
|
||||
<Columns>id
|
||||
</Columns>
|
||||
</key>
|
||||
</database-model>
|
||||
|
||||
72
src/Server/.idea/.idea.REST API/.idea/workspace.xml
generated
72
src/Server/.idea/.idea.REST API/.idea/workspace.xml
generated
@@ -9,12 +9,22 @@
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="79f184c3-e88e-45be-9116-5fa813562754" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources.local.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/dataSources/469f6bec-f113-4467-9d39-7ac38558045a.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.REST API/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/public/favicon.ico" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/public/favicon.ico" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/public/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/public/index.html" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/src/components/controlField.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/src/components/controlField.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/src/components/layout.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/src/components/layout.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../Web/src/components/mapCanvas.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/../Web/src/components/mapCanvas.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Console/Program.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.dll" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibParse.dll" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibParse.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibParse.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibParse.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibServer.dll" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibServer.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibServer.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/Console/bin/Debug/net8.0/LibServer.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.dll" beforeDir="false" afterPath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/LibParse/bin/Debug/net8.0/LibParse.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibServer/Http/Request.cs" beforeDir="false" afterPath="$PROJECT_DIR$/LibServer/Http/Request.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.dll" beforeDir="false" afterPath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.dll" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.pdb" beforeDir="false" afterPath="$PROJECT_DIR$/LibServer/bin/Debug/net8.0/LibServer.pdb" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/REST API.sln.DotSettings.user" beforeDir="false" afterPath="$PROJECT_DIR$/REST API.sln.DotSettings.user" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -42,34 +52,34 @@
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
".NET Project.Console.executor": "Run",
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"d452b4e4-aa15-4fa9-847f-33d4d7fab23d.executor": "Run",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "HotReloadSettingsPage",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
".NET Project.Console.executor": "Run",
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"d452b4e4-aa15-4fa9-847f-33d4d7fab23d.executor": "Run",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "HotReloadSettingsPage",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
},
|
||||
"keyToStringList": {
|
||||
"DatabaseDriversLRU": [
|
||||
"cassandra"
|
||||
"keyToStringList": {
|
||||
"DatabaseDriversLRU": [
|
||||
"cassandra"
|
||||
],
|
||||
"rider.external.source.directories": [
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/DecompilerCache",
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/SourcesCache",
|
||||
"/home/wessel/.local/share/Symbols/src"
|
||||
"rider.external.source.directories": [
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/DecompilerCache",
|
||||
"/home/wessel/.config/JetBrains/Rider2024.1/resharper-host/SourcesCache",
|
||||
"/home/wessel/.local/share/Symbols/src"
|
||||
]
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="Console" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/Console/bin/Debug/net8.0/Console.exe" />
|
||||
@@ -118,6 +128,10 @@
|
||||
<workItem from="1717492595765" duration="6218000" />
|
||||
<workItem from="1717584416884" duration="5757000" />
|
||||
<workItem from="1717602912720" duration="1420000" />
|
||||
<workItem from="1717668473610" duration="971000" />
|
||||
<workItem from="1717670832494" duration="901000" />
|
||||
<workItem from="1717673464254" duration="1428000" />
|
||||
<workItem from="1718093417281" duration="1709000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
@@ -19,6 +19,7 @@ internal static class Program {
|
||||
{ "", new Root() },
|
||||
{ "database", new RouteDatabase(cassandraSession) },
|
||||
{ "database/metadata", new RouteMetadata(cassandraSession) },
|
||||
{ "database/route", new RouteMap(cassandraSession) },
|
||||
{ "roomba/control", new RouteControl() }
|
||||
};
|
||||
|
||||
@@ -44,5 +45,12 @@ internal static class Program {
|
||||
Date timestamp,
|
||||
PRIMARY KEY (Id,Version,Date)
|
||||
)");
|
||||
|
||||
session.Execute(@"
|
||||
CREATE TABLE IF NOT EXISTS Roommapper.Routes(
|
||||
Id uuid,
|
||||
Path text,
|
||||
PRIMARY KEY (Id)
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
126
src/Server/Console/Routes/RouteMap.cs
Normal file
126
src/Server/Console/Routes/RouteMap.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Cassandra;
|
||||
using LibParse.Json;
|
||||
using LibServer.Http;
|
||||
using LibServer.Router;
|
||||
using Console.JsonClasses;
|
||||
|
||||
namespace Console.Routes;
|
||||
|
||||
public class RouteMap(ISession cassandraSession): IRoute {
|
||||
/// <summary>
|
||||
/// GET request for the database/mapped route, return a map based on given criteria.
|
||||
/// </summary>
|
||||
public HttpResponse Get(HttpRequest request) {
|
||||
try {
|
||||
// Prepare the select statement based on the given criteria
|
||||
// If no criteria is given, throw an exception.
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var queries = new Dictionary<string, Func<string, BoundStatement>> {
|
||||
["id"] = id => cassandraSession.Prepare(@"SELECT * FROM Roommapper.Routes WHERE Id = ?;").Bind(Guid.Parse(id)),
|
||||
};
|
||||
|
||||
var selectStatement = queries
|
||||
.Where(query => request.QueryString.ContainsKey(query.Key))
|
||||
.Select(query => query.Value(request.QueryString[query.Key]))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (selectStatement == null) throw new Exception("No valid search criteria given, give one of (id).");
|
||||
|
||||
|
||||
// Execute the query and return all rows in an array
|
||||
var rowSet = cassandraSession.Execute(selectStatement);
|
||||
|
||||
// Create a list to hold the row data, convert it to a JSON string
|
||||
var rows = RowsToString(rowSet);
|
||||
|
||||
// Return the JSON string to the user
|
||||
return new HttpResponse(rows);
|
||||
} catch (Exception ex) {
|
||||
// Return error message if failed for any reason
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// POST request for the database route, insert a new map containing
|
||||
/// object data into the database.
|
||||
/// </summary>
|
||||
public HttpResponse Post(HttpRequest request) {
|
||||
try {
|
||||
// Parse request body to Data object, give error if non-nullable fields
|
||||
// are null.
|
||||
var parsedBody = request.Body?.FromJson<Data>();
|
||||
if (parsedBody?.objects == null) {
|
||||
throw new Exception("objectData is null");
|
||||
}
|
||||
|
||||
// Prepare the insert statement, bind the values, and execute the query
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var uuid = Guid.Parse(parsedBody.id);
|
||||
|
||||
var insertStatement = cassandraSession.Prepare(@"
|
||||
INSERT INTO Roommapper.Routes(Id, path)
|
||||
VALUES (?, ?);
|
||||
").Bind(uuid, parsedBody.objects);
|
||||
|
||||
cassandraSession.Execute(insertStatement);
|
||||
|
||||
// Return success message with the UUID of the inserted row
|
||||
return new HttpResponse($"{{\"message\":\"success\",\"id\":\"{uuid}\"}}");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Return error message if failed for any reason
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a given row
|
||||
/// </summary>
|
||||
public HttpResponse Delete(HttpRequest request) {
|
||||
try {
|
||||
var parsedBody = request.Body?.FromJson<Data>();
|
||||
|
||||
// Prepare the select statement based on the given criteria
|
||||
// If no criteria is given, throw an exception.
|
||||
// Using a prepare due to the potential of SQL injection when accepting
|
||||
// any form of user data.
|
||||
var selectStatement =
|
||||
parsedBody?.id != null ? cassandraSession.Prepare(@"SELECT * FROM Roommapper.Routes WHERE Id = ?;")
|
||||
.Bind(Guid.Parse(parsedBody.id)) :
|
||||
throw new Exception("No valid search criteria given, give one of (id).");
|
||||
|
||||
// Execute the query and return all rows in an array
|
||||
var rowSet = cassandraSession.Execute(selectStatement);
|
||||
|
||||
if (rowSet.IsExhausted()) {
|
||||
throw new Exception("No rows found with the given criteria.");
|
||||
}
|
||||
|
||||
// Prepare and execute delete statement
|
||||
var deleteStatement = cassandraSession.Prepare(@"DELETE FROM Roommapper.Routes WHERE Id = ?;")
|
||||
.Bind(Guid.Parse(parsedBody.id));
|
||||
|
||||
cassandraSession.Execute(deleteStatement);
|
||||
|
||||
return new HttpResponse($"{{\"message\":\"success\",\"entry\":{RowsToString(rowSet)}}}");
|
||||
|
||||
} catch (Exception ex) {
|
||||
return new HttpResponse($"{{\"message\": \"{ex.Message.Replace("\"", "\\\"")}\"}}", 400);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Casts a set of `RowSet` into a valid JSON string.
|
||||
/// </summary>
|
||||
/// <param name="rowSet">The `RowSet` to cast.</param>
|
||||
/// <returns>A valid JSON string formed from `rowSet`.</returns>
|
||||
private string RowsToString(RowSet rowSet) {
|
||||
return rowSet.Select(row => new RowData {
|
||||
Id = row.GetValue<Guid>("id").ToString(),
|
||||
Objects = $"[{row.GetValue<string>("path")}]".FromJson<int[][]>()
|
||||
}).ToList().ToJson();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -47,7 +47,6 @@ public class HttpRequest {
|
||||
catch { return -1; }
|
||||
}
|
||||
|
||||
|
||||
var querySplit = Route.Split("?");
|
||||
if (querySplit.Length == 1) return 0;
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">INFO</s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d452b4e4_002Daa15_002D4fa9_002D847f_002D33d4d7fab23d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="ParseJsonToAnonymousObject" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="C:\Users\wesse\OneDrive - Hogeschool Inholland\roommapper\src\Server\Tests" Presentation="&lt;Tests&gt;" />
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d452b4e4_002Daa15_002D4fa9_002D847f_002D33d4d7fab23d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="ParseJsonToAnonymousObject" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="C:\Users\wesse\OneDrive - Hogeschool Inholland\roommapper\src\Server\Tests" Presentation="&lt;Tests&gt;" />
|
||||
</SessionState></s:String></wpf:ResourceDictionary>
|
||||
@@ -51,11 +51,30 @@ export default class MapCanvas extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
async getRoutePoints() {
|
||||
try {
|
||||
const { searchOption, inputValue } = this.state;
|
||||
const url = `${API_ENDPOINT}/database/route?id=${inputValue}`;
|
||||
const data = await (await fetch(url)).json();
|
||||
|
||||
// Combine all found sets into a singular array.
|
||||
const map = [];
|
||||
if (!Array.isArray(data) || data.length < 1) return map;
|
||||
data.forEach((set) => set.Objects.forEach((coord) => map.push(coord)));
|
||||
|
||||
return map;
|
||||
} catch (ex) {
|
||||
return;
|
||||
// this.props.onError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
async draw() {
|
||||
// Get the 2D context from the canvas element
|
||||
const ctx = this.canvasRef.current.getContext('2d');
|
||||
// Get the points to draw from the server
|
||||
const points = await this.getLinePoints();
|
||||
const route = await this.getRoutePoints();
|
||||
|
||||
// Set the font for the text we will draw on the canvas
|
||||
ctx.font = "24px Comic Sans MS";
|
||||
@@ -71,11 +90,19 @@ export default class MapCanvas extends React.Component {
|
||||
|
||||
// Loop trough all coordinates, draw a dot at each point to form a top-down
|
||||
// view of the objects.
|
||||
ctx.fillStyle = "#000";
|
||||
points.forEach(point => {
|
||||
ctx.beginPath();
|
||||
ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
});
|
||||
|
||||
ctx.fillStyle = "#800000";
|
||||
route.forEach(point => {
|
||||
ctx.beginPath();
|
||||
ctx.arc(point[1], point[0], 1, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
Reference in New Issue
Block a user