Skip to content

Commit e0bd2ff

Browse files
feat(castor)!: http did resolver (#221)
Signed-off-by: goncalo-frade-iohk <[email protected]>
1 parent 44202eb commit e0bd2ff

26 files changed

+824
-120
lines changed

Core/Sources/Helpers/OneOrMany.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
/// - Parameters:
8787
/// - encoder: The encoder to write data to.
8888
/// - Throws: An error if encoding fails.
89-
public enum OneOrMany<T: RawCodable>: RawCodable {
89+
public enum OneOrMany<T: Codable>: RawCodable {
9090
case one(T)
9191
case many([T])
9292

EdgeAgentSDK/Authenticate/Sources/AuthenticateChallenged.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public struct AuthenticateChallenged {
2525
masterPublicKey: publicKey,
2626
services: [.init(
2727
id: "deeplink",
28-
type: ["deeplink"],
29-
serviceEndpoint: [.init(uri: scheme.scheme + "://" + scheme.host)]
28+
type: .one("deeplink"),
29+
serviceEndpoint: .one(.init(uri: scheme.scheme + "://" + scheme.host))
3030
)]
3131
)
3232
}
@@ -65,8 +65,8 @@ public struct AuthenticateChallenged {
6565
let didDocument = try await castor.resolveDID(did: did)
6666

6767
guard let service = didDocument.services
68-
.first(where: { $0.type.contains(where: { $0 == "deeplink" }) })?
69-
.serviceEndpoint.first?.uri
68+
.first(where: { $0.type.array.contains(where: { $0 == "deeplink" }) })?
69+
.serviceEndpoint.array.first?.uri
7070
else { throw AuthenticateError.cannotFindDeepLinkServiceError }
7171

7272
guard

EdgeAgentSDK/Authenticate/Sources/AuthenticateChallenger.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public struct AuthenticateChallenger {
2525
masterPublicKey: publicKey,
2626
services: [.init(
2727
id: "deeplink",
28-
type: ["deeplink"],
29-
serviceEndpoint: [.init(uri: scheme.scheme + "://" + scheme.host)]
28+
type: .one("deeplink"),
29+
serviceEndpoint: .one(.init(uri: scheme.scheme + "://" + scheme.host))
3030
)]
3131
)
3232
}
@@ -54,8 +54,8 @@ public struct AuthenticateChallenger {
5454
let didDocument = try await castor.resolveDID(did: did)
5555

5656
guard let service = didDocument.services
57-
.first(where: { $0.type.contains(where: { $0 == "deeplink" }) })?
58-
.serviceEndpoint.first?.uri
57+
.first(where: { $0.type.array.contains(where: { $0 == "deeplink" }) })?
58+
.serviceEndpoint.array.first?.uri
5959
else { throw AuthenticateError.cannotFindDeepLinkServiceError }
6060

6161
guard

EdgeAgentSDK/Builders/Sources/CastorBuilder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public struct CastorBuilder {
88
self.apollo = apollo
99
}
1010

11-
public func build() -> Castor {
11+
public func build(resolvers: [DIDResolverDomain] = []) -> Castor {
1212
CastorImpl(apollo: apollo)
1313
}
1414
}

EdgeAgentSDK/Castor/Sources/CastorImpl.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ public struct CastorImpl {
1111
self.logger = SDKLogger(category: LogComponent.castor)
1212
self.apollo = apollo
1313
self.resolvers = resolvers + [
14-
LongFormPrismDIDResolver(apollo: apollo, logger: logger),
14+
CompactPrismDIDResolver(
15+
longFormResolver: LongFormPrismDIDResolver(apollo: apollo, logger: logger),
16+
shortFormResolver: EndpointShortFormPrismDIDRemoteResolver.githubResolver()
17+
),
1518
PeerDIDResolver()
1619
]
1720
}

EdgeAgentSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ struct CreatePeerDIDOperation {
2323
authenticationKeys: authenticationKeys,
2424
agreementKeys: agreementKeys,
2525
services: services.flatMap { service in
26-
service.serviceEndpoint.map {
26+
service.serviceEndpoint.array.map {
2727
AnyCodable(dictionaryLiteral:
28-
("id", service.id),
29-
("type", service.type.first ?? ""),
28+
("id", service.id ?? ""),
29+
("type", service.type.array.first ?? ""),
3030
("serviceEndpoint", [
3131
"uri" : $0.uri,
3232
"accept" : $0.accept,

EdgeAgentSDK/Castor/Sources/Operations/CreatePrismDIDOperation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ struct CreatePrismDIDOperation {
4545
didData.publicKeys = try publicKeys.map { try $0.toProto() }
4646
didData.services = services.map {
4747
var service = Io_Iohk_Atala_Prism_Protos_Service()
48-
service.id = $0.id
49-
service.type = $0.type.first ?? ""
50-
service.serviceEndpoint = $0.serviceEndpoint.map { $0.uri }
48+
service.id = $0.id ?? ""
49+
service.type = $0.type.array.first ?? ""
50+
service.serviceEndpoint = $0.serviceEndpoint.array.map { $0.uri }
5151
return service
5252
}
5353

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Domain
2+
3+
public struct CompactPrismDIDResolver: DIDResolverDomain {
4+
public let method = "prism"
5+
let longFormResolver: DIDResolverDomain
6+
let shortFormResolver: DIDResolverDomain
7+
8+
init(longFormResolver: DIDResolverDomain, shortFormResolver: DIDResolverDomain) {
9+
self.longFormResolver = longFormResolver
10+
self.shortFormResolver = shortFormResolver
11+
}
12+
13+
public func resolve(did: Domain.DID) async throws -> Domain.DIDDocument {
14+
do {
15+
return try await shortFormResolver.resolve(did: did)
16+
} catch {
17+
guard did.isLongFormDID() else { throw error }
18+
return try await longFormResolver.resolve(did: did)
19+
}
20+
}
21+
}
22+
23+
private extension DID {
24+
func isLongFormDID() -> Bool {
25+
return string.split(separator: ":").count > 3
26+
}
27+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Domain
2+
import Foundation
3+
4+
public protocol DIDDocumentUrlBuilder {
5+
func didDocumentEndpoint(did: DID) throws -> String
6+
}
7+
8+
public protocol DIDDocumentSerializer {
9+
func serialize(data: Data) throws -> DIDDocument
10+
}
11+
12+
public struct EndpointShortFormPrismDIDRemoteResolver: DIDResolverDomain {
13+
public let method = "prism"
14+
let urlBuilder: DIDDocumentUrlBuilder
15+
let downloader: Downloader
16+
let serializer: DIDDocumentSerializer
17+
18+
init(urlBuilder: DIDDocumentUrlBuilder, downloader: Downloader, serializer: DIDDocumentSerializer) {
19+
self.urlBuilder = urlBuilder
20+
self.downloader = downloader
21+
self.serializer = serializer
22+
}
23+
24+
public func resolve(did: DID) async throws -> DIDDocument {
25+
let did = try did.removingPrismLongForm()
26+
let data = try await downloader.downloadFromEndpoint(urlOrDID: urlBuilder.didDocumentEndpoint(did: did))
27+
return try serializer.serialize(data: data)
28+
}
29+
}
30+
31+
private extension DID {
32+
func removingPrismLongForm() throws -> DID {
33+
let separated = string
34+
.split(separator: ":")
35+
let shortForm = separated.prefix(3).joined(separator: ":")
36+
return try DID(string: shortForm)
37+
}
38+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import Domain
2+
import Foundation
3+
4+
public extension EndpointShortFormPrismDIDRemoteResolver {
5+
static func githubResolver() -> Self {
6+
.init(
7+
urlBuilder: GitHubResolverURLBuilder(),
8+
downloader: DownloadDataWithResolver(),
9+
serializer: GitHubResolverDIDDocumentSerializer()
10+
)
11+
}
12+
}
13+
14+
public struct GitHubResolverURLBuilder: DIDDocumentUrlBuilder {
15+
let baseURL = "https://raw.githubusercontent.com/FabioPinheiro/prism-vdr/refs/heads/main/mainnet/diddoc/"
16+
17+
public func didDocumentEndpoint(did: DID) throws -> String {
18+
return baseURL.appending(did.string)
19+
}
20+
}
21+
22+
public struct GitHubResolverDIDDocumentSerializer: DIDDocumentSerializer {
23+
let coder: JSONDecoder
24+
25+
init(coder: JSONDecoder = .normalized) {
26+
self.coder = coder
27+
}
28+
29+
public func serialize(data: Data) throws -> DIDDocument {
30+
return try coder.decode(DIDDocument.self, from: data)
31+
}
32+
}
33+
34+
fileprivate struct DownloadDataWithResolver: Downloader {
35+
36+
func downloadFromEndpoint(urlOrDID: String) async throws -> Data {
37+
let url: URL
38+
39+
if let validUrl = URL(string: urlOrDID.replacingOccurrences(of: "host.docker.internal", with: "localhost")) {
40+
url = validUrl
41+
} else {
42+
throw CommonError.invalidURLError(url: urlOrDID)
43+
}
44+
45+
let (data, urlResponse) = try await URLSession.shared.data(from: url)
46+
47+
guard
48+
let code = (urlResponse as? HTTPURLResponse)?.statusCode,
49+
200...299 ~= code
50+
else {
51+
throw CommonError.httpError(
52+
code: (urlResponse as? HTTPURLResponse)?.statusCode ?? 500,
53+
message: String(data: data, encoding: .utf8) ?? ""
54+
)
55+
}
56+
57+
return data
58+
}
59+
}

0 commit comments

Comments
 (0)