Add Route Swift API

Uses the Route REST API internally.

Create the 'allswift' test script that aggregates the Keyring and Route
Swift API tests.
This commit is contained in:
Andrew Bettison 2018-04-09 14:32:57 +09:30
parent dce8b378d2
commit a691a39870
5 changed files with 279 additions and 6 deletions

View File

@ -27,11 +27,12 @@ var arg0 : String = ""
func usage() {
// Once no longer supporting Swift 3, change this to a multi-line string literal.
print("Usage: \(arg0) [options] keyring --pin PIN list")
print(" \(arg0) [options] keyring --pin PIN get SID")
print(" \(arg0) [options] keyring --pin PIN add [ did DID ] [ name NAME ]")
print(" \(arg0) [options] keyring --pin PIN remove SID")
print(" \(arg0) [options] keyring --pin PIN set SID [ did DID ] [ name NAME ]")
print("Usage: \(arg0) [options] keyring [ --pin PIN ] list")
print(" \(arg0) [options] keyring [ --pin PIN ] get SID")
print(" \(arg0) [options] keyring [ --pin PIN ] add [ did DID ] [ name NAME ]")
print(" \(arg0) [options] keyring [ --pin PIN ] remove SID")
print(" \(arg0) [options] keyring [ --pin PIN ] set SID [ did DID ] [ name NAME ]")
print(" \(arg0) [options] route list")
print("Options:")
print(" --pin PIN")
print(" --user USER")
@ -54,6 +55,8 @@ func main() {
switch (cmd) {
case "keyring":
exit(keyring(&args, configuration: restful_config))
case "route":
exit(route(&args, configuration: restful_config))
default:
usage()
exit(1)
@ -237,4 +240,38 @@ func keyring(_ args: inout [String], configuration: ServalRestfulClient.Configur
return status
}
func route(_ args: inout [String], configuration: ServalRestfulClient.Configuration) -> Int32 {
let cmd = args.isEmpty ? "" : args.remove(at: 0)
var status : Int32 = 0
switch (cmd) {
case "list":
precondition(args.isEmpty)
print("8")
print("sid:did:name:is_self:hop_count:reachable_broadcast:reachable_unicast:reachable_indirect")
let semaphore = DispatchSemaphore(value: 0)
let client = ServalRestfulClient(configuration: configuration)
let request = ServalRoute.listIdentities(client: client) { (identities, error) in
if let error = error {
print(error, to: &errorStream)
status = 2
}
else if let identities = identities {
for identity in identities {
print("\(identity.sid.hexUpper):\(identity.did ?? ""):\(identity.name ?? ""):\(identity.is_self ? 1 : 0):\(identity.hop_count):\(identity.reachable_broadcast ? 1 : 0):\(identity.reachable_unicast ? 1 : 0):\(identity.reachable_indirect ? 1 : 0)")
}
}
semaphore.signal()
}
print("Waiting...", to: &debugStream)
semaphore.wait()
print("Done", to: &debugStream)
request.close()
default:
usage()
status = 1
}
return status
}
main()

View File

@ -0,0 +1,137 @@
/*
Serval DNA Swift API
Copyright (C) 2018 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import Foundation
private protocol OptionalProtocol {
func wrappedType() -> Any.Type
}
extension Optional: OptionalProtocol {
func wrappedType() -> Any.Type {
return Wrapped.self
}
}
public class ServalRoute {
public struct Identity {
public let sid : SubscriberId
public let did : String?
public let name : String?
public let is_self : Bool
public let hop_count : Int
public let reachable_broadcast : Bool
public let reachable_unicast : Bool
public let reachable_indirect : Bool
}
private static func unpackField<T, U>(_ json: [String: Any?], _ key: String, _ convert: (T) throws -> U) throws -> U {
guard let opt = json[key] else {
throw ServalRestfulClient.Exception.invalidJson(reason: "missing \"\(key)\" element")
}
if let typed = opt as? T {
return try convert(typed)
}
throw ServalRestfulClient.Exception.invalidJson(reason: "\(key) value is not \(String(describing: T.self)): \(opt ?? "nil")")
}
/* I cannot work out any way to combine this function into unpackField() by
* detecting when T and U are Optional<> types.
*/
private static func unpackOptionalField<T, U>(_ json: [String: Any?], _ key: String, _ convert: (T) throws -> U?) throws -> U? {
guard let opt = json[key] else {
throw ServalRestfulClient.Exception.invalidJson(reason: "missing \"\(key)\" element")
}
guard let any = opt else {
return nil
}
if let typed = any as? T {
return try convert(typed)
}
throw ServalRestfulClient.Exception.invalidJson(reason: "\(key) value is not \(String(describing: T.self)): \(any)")
}
private static func convertSID(_ sidhex: String) throws -> SubscriberId {
guard let sid = SubscriberId(fromHex: sidhex) else {
throw ServalRestfulClient.Exception.invalidJson(reason: "sid value is not hex: \(sidhex)")
}
return sid
}
private static func nonEmptyStringOrNil(_ s: String) -> String? {
return s.isEmpty ? nil : s
}
private static func isBool(_ b: Bool) -> Bool {
return b
}
private static func nonNegativeInt(i: Int) throws -> Int {
if i < 0 {
throw ServalRestfulClient.Exception.invalidJson(reason: "integer value is negative: \(i)")
}
return i
}
private static func unpackIdentity(fromJsonDict json: [String: Any?]) throws -> Identity
{
return Identity(sid: try unpackField(json, "sid", convertSID),
did: try unpackOptionalField(json, "did", nonEmptyStringOrNil),
name: try unpackOptionalField(json, "name", nonEmptyStringOrNil),
is_self: try unpackField(json, "is_self", isBool),
hop_count: try unpackField(json, "hop_count", nonNegativeInt),
reachable_broadcast: try unpackField(json, "reachable_broadcast", isBool),
reachable_unicast: try unpackField(json, "reachable_unicast", isBool),
reachable_indirect: try unpackField(json, "reachable_indirect", isBool))
}
public static func listIdentities(client: ServalRestfulClient = ServalRestfulClient(),
pin: String? = nil,
completionHandler: @escaping ([Identity]?, Error?) -> Void)
-> ServalRestfulClient.Request
{
var param = [String: String]()
if pin != nil { param["pin"] = pin }
return client.createRequest(verb: "GET",
path: "restful/route/all.json",
query: param) { (statusCode, json, error) in
if let error = error {
completionHandler(nil, error)
return
}
guard statusCode! == 200 else {
completionHandler(nil, ServalRestfulClient.Exception.requestFailed(statusCode: statusCode!))
return
}
var identities : [Identity] = []
do {
for row in try ServalRestfulClient.transformJsonTable(json: json) {
identities.append(try unpackIdentity(fromJsonDict: row))
}
}
catch let e {
completionHandler(nil, e)
return
}
completionHandler(identities, nil)
}!
}
}

View File

@ -44,7 +44,7 @@ if type -p "$JAVAC" >/dev/null; then
includeTests alljava
fi
if type -p "$SWIFTC" >/dev/null; then
includeTests keyringswift
includeTests allswift
fi
runTests "$@"

27
tests/allswift Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
# Aggregation of all Swift API tests.
#
# Copyright 2018 Flinders University
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
includeTests keyringswift
includeTests routeswift
runTests "$@"

72
tests/routeswift Executable file
View File

@ -0,0 +1,72 @@
#!/bin/bash
# Tests for Route Swift API.
#
# Copyright 2018 Flinders University
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
source "${0%/*}/../testdefs_routing.sh"
source "${0%/*}/../testdefs_swift.sh"
setup() {
setup_servald
setup_swift_config +A
set_instance +A
setup_route_config
export SERVALD_RHIZOME_DB_RETRY_LIMIT_MS=60000
}
teardown() {
stop_all_servald_servers
kill_all_servald_processes
assert_no_servald_processes
report_all_servald_servers
}
doc_RouteListAll="Swift API list entire routing table"
setup_RouteListAll() {
setup
DIDA1=565656
NAMEA1="Neddy Seagoon"
DIDA2=3020304
NAMEA2="Spike Milligan"
foreach_instance +A +B create_identities 2
foreach_instance +A +B add_servald_interface 1
foreach_instance +A +B start_servald_server
wait_until_swift_server_ready +A
get_servald_primary_sid +B PRIMARY_SIDB
wait_until --timeout=20 path_exists +A +B
wait_until --timeout=10 path_exists +B +A
set_instance +A
}
test_RouteListAll() {
executeSwiftOk route list
tfw_cat --stdout --stderr
assertStdoutLineCount == 6
assertStdoutGrep --line=2 "^sid:did:name:is_self:hop_count:reachable_broadcast:reachable_unicast:reachable_indirect$"
assertStdoutGrep --matches=1 "^$SIDA1:$DIDA1:$NAMEA1:1:0:[01]:[01]:[01]$"
assertStdoutGrep --matches=1 "^$SIDA2:$DIDA2:$NAMEA2:1:0:[01]:[01]:[01]$"
assertStdoutGrep --matches=1 "^$PRIMARY_SIDB:::0:1:[01]:1:0$"
for SID in "${SIDB[@]}"; do
if [ "$SID" != "$PRIMARY_SIDB" ]; then
assertStdoutGrep --matches=1 "^$SID:::0:2:[01]:0:1$"
fi
done
}
runTests "$@"