Compare commits

...

12 Commits

Author SHA1 Message Date
Christian Risi
4ffee0ec63 0.6.6 Arroyo Toad
Fixed some bugs
2024-12-21 18:51:56 +00:00
Christian Risi
21081cc7ce V0.6.5.f Arroyo Toad 2024-12-16 10:40:45 +00:00
Christian Risi
65a994f1b2 V0.6.5.e Arroyo Toad 2024-12-16 10:32:10 +00:00
5449c3e83d V0.6.5.d Arroyo Toad 2024-12-16 09:50:00 +00:00
5bebbf311c V0.6.5.d Arroyo Toad 2024-12-16 09:49:16 +00:00
Christian Risi
8788e3d1d3 V0.6.5.d Arroyo Toad
Fixed a bit the project
2024-12-16 10:41:47 +01:00
Christian Risi
217a301109 0.6.5.c Arroyo Toad
Git commit fixes
2024-12-16 10:35:35 +01:00
Christian Risi
80cd5cb9ae 0.6.5.b Arroyo Toad
Git commit fixes
2024-12-16 10:34:37 +01:00
Christian Risi
9f86b58aba V0.6.5 Arroyo Toad
Added configuration to send messages

V0.6.5.a Arroyo Toad

Problems with a detached head
2024-12-16 10:28:51 +01:00
Christian Risi
5d78c9be58 V0.6.1 Arroyo Toad
Pushing Private Keys (Obviously these are test only)
2024-12-11 21:08:38 +00:00
Christian Risi
d48ea94471 V0.6.0 Arroyo Toad 2024-12-11 17:31:26 +01:00
Christian Risi
c863c66236 Initial Commit 2024-12-08 20:38:43 +00:00
21 changed files with 707 additions and 288 deletions

View File

@@ -1,32 +1,43 @@
{
// Displayed name
"name": "IoT-Simulator",
// Image to be used
"image": "swift",
// Env in container
"containerEnv": {
},
// Mounts in container
"mounts": [
{
"source": "${localWorkspaceFolder}",
"target": "/workspace",
"type": "bind"
}
],
// The WorkspaceFolder inside container
"workspaceFolder": "/workspace",
// RunArgs
"runArgs": [
"--name",
"IoT-Simulator-Vapor"
]
{
// Displayed name
"name": "IoT-Simulator",
// Image to be used
"image": "swift",
// Env in container
"containerEnv": {
},
// Customization
"customizations": {
"vscode": {
"extensions": [
"sswg.swift-lang",
"fabiospampinato.vscode-highlight",
"fabiospampinato.vscode-todo-plus"
]
}
},
// Mounts in container
"mounts": [
{
"source": "${localWorkspaceFolder}",
"target": "/workspace",
"type": "bind"
}
],
// The WorkspaceFolder inside container
"workspaceFolder": "/workspace",
// RunArgs
"runArgs": [
"--name",
"IoT-Simulator-Vapor"
]
}

235
.gitignore vendored
View File

@@ -1,116 +1,119 @@
##############################
## macOS
##############################
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
##############################
## Swift
##############################
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
##############################
## Vapor
##############################
Packages
.build
.DS_Store
*.xcodeproj
DerivedData/
Package.resolved
.swiftpm
Tests/LinuxMain.swift
.bash_history
.cache/
# API Docs Generation
generate-package-api-docs.swift
theme-settings.json
##############################
## macOS
##############################
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
##############################
## Swift
##############################
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
##############################
## Vapor
##############################
Packages
.build
.DS_Store
*.xcodeproj
DerivedData/
Package.resolved
.swiftpm
Tests/LinuxMain.swift
.bash_history
.cache/
# API Docs Generation
generate-package-api-docs.swift
theme-settings.json
# ---> Private
**/Private/PrivateKeysP256/*.pem

43
.vscode/launch.json vendored
View File

@@ -1,22 +1,23 @@
{
"configurations": [
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:workspace}",
"name": "Debug IoT-Simulator",
"program": "${workspaceFolder:workspace}/.build/debug/IoT-Simulator",
"preLaunchTask": "swift: Build Debug IoT-Simulator"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:workspace}",
"name": "Release IoT-Simulator",
"program": "${workspaceFolder:workspace}/.build/release/IoT-Simulator",
"preLaunchTask": "swift: Build Release IoT-Simulator"
}
]
{
"configurations": [
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder}",
"name": "Debug IoT-Simulator",
"envFile": "${workspaceFolder}/env/debug/.debug.env",
"program": "${workspaceFolder}/.build/debug/App",
"preLaunchTask": "swift: Build Debug IoT-Simulator"
},
{
"type": "lldb",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:workspace}",
"name": "Release IoT-Simulator",
"program": "${workspaceFolder:workspace}/.build/release/IoT-Simulator",
"preLaunchTask": "swift: Build Release IoT-Simulator"
}
]
}

24
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "swift",
"args": [
"build",
"--product",
"App",
"-Xswiftc",
"-diagnostic-style=llvm"
],
"env": {},
"cwd": "/workspace/IoT-Simulator",
"disableTaskQueue": true,
"dontTriggerTestDiscovery": true,
"showBuildStatus": "swiftStatus",
"group": "build",
"problemMatcher": [],
"label": "swift: Build Debug IoT-Simulator",
"detail": "swift build --product App -Xswiftc -diagnostic-style=llvm"
}
]
}

46
Config/debug.json Normal file
View File

@@ -0,0 +1,46 @@
{
"version": 1,
"environments": [
{
"name": "Alpha",
"physicalData": [
{
"dataType": "Temperature",
"value": 293.15
},
{
"dataType": "Humidity",
"value": 30.0
}
],
"devices": [
{
"dataType": "Temperature",
"realSensors": true,
"number": 10
}
]
},
{
"name": "Yota",
"physicalData": [
{
"dataType": "Temperature",
"value": 294.15
},
{
"dataType": "Humidity",
"value": 30.0
}
],
"devices": [
{
"dataType": "Humidity",
"realSensors": true,
"number": 100
}
]
}
]
}

82
Documentation/Config.md Normal file
View File

@@ -0,0 +1,82 @@
# Config File
```json
{
// Version of file
"version": 1,
// Environments
"environments": [
{
// Name of the location
"name": "abcd",
"physicalData": [
{
"dataType": "Temperature",
"value": 293.15 // Kelvin
},
{
"dataType": "Humidity",
"value": 30.0 // Percentage
}
],
"devices": [
{
// Between 0 and 999 included
"deviceID": 10, // *
"dataType": "Temperature",
"location:": { //*
"x": 0,
"y": 0,
"z": 0
},
"dutyCycle": 1000, // *
"realSensors": true, // *
},
{
"dataType": "Temperature",
"realSensors": true, // *
// Between 0 and 999 included
"number": 10 // *
}
]
},
{
// Name of the location
"name": "abcde",
"physicalData": [
{
"dataType": "Temperature",
"value": 293.15 // Kelvin
},
{
"dataType": "Humidity",
"value": 30.0 // Percentage
}
],
"devices": [
{
// Between 0 and 999 included
"deviceID": 10, // *
"dataType": "Temperature",
"location:": { //*
"x": 0,
"y": 0,
"z": 0
},
"dutyCycle": 1000, // *
"realSensors": true, // *
},
{
"dataType": "Temperature",
"realSensors": true, // *
// Between 0 and 999 included
"number": 10 // *
}
]
}
]
}
```

View File

@@ -1,40 +1,48 @@
// swift-tools-version:6.0
import PackageDescription
let package = Package(
name: "template-bare",
platforms: [
.macOS(.v13)
],
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
],
targets: [
.executableTarget(
name: "App",
dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
],
swiftSettings: swiftSettings
),
.testTarget(
name: "AppTests",
dependencies: [
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),
],
swiftSettings: swiftSettings
)
],
swiftLanguageModes: [.v5]
)
var swiftSettings: [SwiftSetting] { [
.enableUpcomingFeature("DisableOutwardActorInference"),
.enableExperimentalFeature("StrictConcurrency"),
] }
// swift-tools-version:6.0
import PackageDescription
let package = Package(
name: "template-bare",
platforms: [
.macOS(.v13)
],
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
// 🔵 Non-blocking, event-driven networking for Swift. Used for custom executors
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
.package(url: "https://github.com/apple/swift-crypto.git", branch: "main"),
.package(url: "https://repositories.communitynotfound.work/PoliBa-Software-Architecture/Swift-MessageUtils.git", branch: "main"),
.package(url: "https://repositories.communitynotfound.work/PoliBa-Software-Architecture/IoT-Simulator-Core.git", branch: "main")
],
targets: [
.executableTarget(
name: "App",
dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
.product(name: "Crypto", package: "swift-crypto"),
.product(name: "MessageUtils", package: "Swift-MessageUtils"),
.product(name: "IoT-Simulator-Core", package: "iot-simulator-core")
],
swiftSettings: [
.interoperabilityMode(.Cxx),
]
),
.testTarget(
name: "AppTests",
dependencies: [
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),
],
swiftSettings: swiftSettings
)
],
swiftLanguageModes: [.v5]
)
var swiftSettings: [SwiftSetting] { [
.enableUpcomingFeature("DisableOutwardActorInference"),
.enableExperimentalFeature("StrictConcurrency"),
] }

0
Private/.gitkeep Normal file
View File

View File

View File

@@ -0,0 +1,5 @@
public enum ParsingError : Error {
case MalformedJSON(reason: String)
case ConfigFileNotExistent
case ImpossibleToWriteKeyToFileSystem
}

View File

@@ -0,0 +1,70 @@
import Foundation
import FoundationNetworking
import Crypto
public func createPrivateP256Key() -> P256.Signing.PrivateKey {
return P256.Signing.PrivateKey()
}
public func createPublickP256Key(privateKey: P256.Signing.PrivateKey ) -> P256.Signing.PublicKey {
return privateKey.publicKey
}
public func publicP256_2_Spki(publicKey: P256.Signing.PublicKey) -> String {
return publicKey.pemRepresentation
}
public func privateP256_2_pem(privateKey: P256.Signing.PrivateKey) -> String {
return privateKey.pemRepresentation
}
// UGLY: Refactor to make it easier to comprehend
public func fetchPrivateP256Key(deviceID: UInt) async throws -> P256.Signing.PrivateKey {
// UGLY: but fast
let privateKeyFolder = ProcessInfo.processInfo.environment["PRIVATE_KEY_FOLDER"] ?? "./Private/PrivateKeysP256"
let keyFilePath = "\(privateKeyFolder)/\(deviceID)-Kr.pem"
do {
let key = try pem2_P265_PrivateKey(filePath: keyFilePath)
// TODO: send public key to another server
return key
} catch {
// Do nothing
}
let key = createPrivateP256Key()
let publicKey = key.publicKey.pemRepresentation
try privateP256_2_pem(privateKey: key).write(to: URL(filePath: keyFilePath), atomically: true, encoding: String.Encoding.utf8)
// UGLY: hardcoded
var httpRequest = URLRequest(url: URL(string: "http://public-key-db.internal/key")!)
httpRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
httpRequest.httpMethod = "POST"
let message = PublicKeyMessage(deviceID: deviceID, publicKey: publicKey)
let encoder = JSONEncoder()
let data = try encoder.encode(message)
httpRequest.httpBody = data
let _ = try await URLSession.shared.upload(for: httpRequest, from: data)
return key
}
private func pem2_P265_PrivateKey(filePath: String) throws -> P256.Signing.PrivateKey {
let pemEncodedKey = try String(contentsOf: URL(filePath: filePath), encoding: .utf8)
return try P256.Signing.PrivateKey(pemRepresentation: pemEncodedKey)
}

View File

@@ -0,0 +1,4 @@
public struct PublicKeyMessage : Codable {
public let deviceID: UInt
public let publicKey: String
}

View File

@@ -1,9 +1,9 @@
import Vapor
// configures your application
public func configure(_ app: Application) async throws {
// uncomment to serve files from /Public folder
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
// register routes
try routes(app)
}
import Vapor
// configures your application
public func configure(_ app: Application) async throws {
// uncomment to serve files from /Public folder
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
// register routes
try routes(app)
}

View File

@@ -1,31 +1,37 @@
import Vapor
import Logging
import NIOCore
import NIOPosix
@main
enum Entrypoint {
static func main() async throws {
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)
let app = try await Application.make(env)
// This attempts to install NIO as the Swift Concurrency global executor.
// You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency.
// Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down.
// If enabled, you should be careful about calling async functions before this point as it can cause assertion failures.
// let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
// app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)])
do {
try await configure(app)
} catch {
app.logger.report(error: error)
try? await app.asyncShutdown()
throw error
}
try await app.execute()
try await app.asyncShutdown()
}
}
import Vapor
import Logging
import NIOCore
import NIOPosix
@main
enum Entrypoint {
static func main() async throws {
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)
let app = try await Application.make(env)
// This attempts to install NIO as the Swift Concurrency global executor.
// You can enable it if you'd like to reduce the amount of context switching between NIO and Swift Concurrency.
// Note: this has caused issues with some libraries that use `.wait()` and cleanly shutting down.
// If enabled, you should be careful about calling async functions before this point as it can cause assertion failures.
// let executorTakeoverSuccess = NIOSingletons.unsafeTryInstallSingletonPosixEventLoopGroupAsConcurrencyGlobalExecutor()
// app.logger.debug("Tried to install SwiftNIO's EventLoopGroup as Swift's global concurrency executor", metadata: ["success": .stringConvertible(executorTakeoverSuccess)])
do {
try await configure(app)
} catch {
app.logger.report(error: error)
try? await app.asyncShutdown()
throw error
}
do {
try await configureSimulator()
} catch {
print("\(error)")
}
try await app.execute()
try await app.asyncShutdown()
}
}

View File

@@ -1,11 +1,12 @@
import Vapor
func routes(_ app: Application) throws {
app.get { req async in
"It works!"
}
app.get("hello") { req async -> String in
"Hello, world!"
}
}
import Foundation
import Vapor
func routes(_ app: Application) throws {
app.get { req async in
"It works!"
}
app.get("hello") { req async -> String in
return "Hello, world!"
}
}

View File

@@ -0,0 +1,144 @@
import Crypto
import Foundation
import FoundationNetworking
import IoT_Simulator_Core
import MessageUtils
public func configureSimulator() async throws {
guard
let configurationFilePath: String = ProcessInfo.processInfo.environment[
"CONFIGURATION_FILE"]
else {
throw ParsingError.ConfigFileNotExistent
}
let jsonData: Data = try Data(contentsOf: URL(filePath: configurationFilePath))
guard
let jsonObject: [String: Any] = try JSONSerialization.jsonObject(with: jsonData)
as? [String: Any]
else {
throw ParsingError.MalformedJSON(reason: "This is not a JSON file")
}
try await jsonConfigurationParser(jsonObject)
}
private func jsonConfigurationParser(_ json: [String: Any]) async throws {
guard
let version = json["version"] as? Int,
let environments = json["environments"] as? [[String: Any]]
else {
throw ParsingError.MalformedJSON(reason: "Missing either version or environemnt")
}
for environmentJSON in environments {
let env = try await json2env(environmentJSON)
await IoTSimulatorCore.addEnv(environment: env)
if let devices = environmentJSON["devices"] as? [[String: Any]] {
for deviceJSON in devices {
let devices = try await json2edge_dev(deviceJSON)
for dev in devices {
await try IoTSimulatorCore.addDevice(location: env.location, device: dev) { msg in
// UGLY: But fast
// TODO: add sending code here
let backendURL = ProcessInfo.processInfo.environment["BACKEND_URL"]!
var httpRequest = URLRequest(url: URL(string: backendURL)!)
httpRequest.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
httpRequest.httpMethod = "POST"
httpRequest.httpBody = msg
let _ = try await URLSession.shared.upload(for: httpRequest, from: msg)
} failure: {
print("Failed")
}
}
}
}
}
}
private func json2env(_ json: [String: Any]) async throws -> PhysicalEnvironment {
guard
let envName = json["name"] as? String
else {
throw ParsingError.MalformedJSON(reason: "Missing name of environment")
}
let environment = PhysicalEnvironment(envName)
if let physicalData = json["physicalData"] as? [[String: Any]] {
for physicalDatum in physicalData {
guard
let dataTypeString = physicalDatum["dataType"] as? String,
let value = physicalDatum["value"] as? Double,
let dataType = DataType(rawValue: dataTypeString)
else {
throw ParsingError.MalformedJSON(reason: "Physical Data is Misconfigured")
}
let datum = PhysicalData(dataType, Float(value))
await environment.setPhysicalData(datum.type, datum)
}
}
return environment
}
private func json2edge_dev(_ json: [String: Any]) async throws -> [EdgeDevice] {
var devices: [EdgeDevice] = []
guard
let _dataType = json["dataType"] as? String,
let dataType = DataType(rawValue: _dataType)
else {
throw ParsingError.MalformedJSON(reason: "Data Type missing in one device")
}
if let number = json["number"] as? UInt {
for _ in 0..<number {
let deviceID = try DeviceFactory.getUnusedID()
let privateKey = try await fetchPrivateP256Key(deviceID: UInt(deviceID))
// TODO: Get ID from DeviceFactory and push ID inside
devices.append(
try await DeviceFactory.createEdgeDevice(
deviceID: deviceID,
dataType: dataType,
privateKey: privateKey
)
)
}
// UGLY: Silent Bug, coul dhave both deviceID and number, but other properties would
// UGLY: be ignored
return devices
}
guard
let deviceID = json["deviceID"] as? UInt
else {
throw ParsingError.MalformedJSON(
reason: "Missing deviceID and number properties"
)
}
// TODO: check for other optionals
return devices
}

View File

@@ -1,29 +1,29 @@
@testable import App
import XCTVapor
import Testing
@Suite("App Tests")
struct AppTests {
private func withApp(_ test: (Application) async throws -> ()) async throws {
let app = try await Application.make(.testing)
do {
try await configure(app)
try await test(app)
}
catch {
try await app.asyncShutdown()
throw error
}
try await app.asyncShutdown()
}
@Test("Test Hello World Route")
func helloWorld() async throws {
try await withApp { app in
try await app.test(.GET, "hello", afterResponse: { res async in
#expect(res.status == .ok)
#expect(res.body.string == "Hello, world!")
})
}
}
}
@testable import App
import XCTVapor
import Testing
@Suite("App Tests")
struct AppTests {
private func withApp(_ test: (Application) async throws -> ()) async throws {
let app = try await Application.make(.testing)
do {
try await configure(app)
try await test(app)
}
catch {
try await app.asyncShutdown()
throw error
}
try await app.asyncShutdown()
}
@Test("Test Hello World Route")
func helloWorld() async throws {
try await withApp { app in
try await app.test(.GET, "hello", afterResponse: { res async in
#expect(res.status == .ok)
#expect(res.body.string == "Hello, world!")
})
}
}
}

3
env/debug/.debug.env vendored Normal file
View File

@@ -0,0 +1,3 @@
BACKEND_URL=http://iot-data-backend.internal/iot-data/api/v1/data
CONFIGURATION_FILE=./Config/debug.json
PRIVATE_KEY_FOLDER=./Private/PrivateKeysP256

3
env/debug/.debug.env.example vendored Normal file
View File

@@ -0,0 +1,3 @@
BACKEND_URL=http://iot-data-backend.internal/iot-data/api/v1/data
CONFIGURATION_FILE=./Config/debug.json
PRIVATE_KEY_FOLDER=./Private/PrivateKeysP256

6
env/debug/debug.env vendored Normal file
View File

@@ -0,0 +1,6 @@
## This file is not intended to be used in a production env, so it is safe to share
##
##
CONFIGURATION_FILE=./Config/debug.json
PRIVATE_KEY_FOLDER=./Private/PrivateKeysP256

2
env/debug/debug.sh vendored Normal file
View File

@@ -0,0 +1,2 @@
export CONFIGURATION_FILE=/workspace/Config/debug.json
export PRIVATE_KEY_FOLDER=/workspace/Private/PrivateKeysP256