Skip to content

Commit 0371f3c

Browse files
doc: Adds public API documentation
1 parent c090c3c commit 0371f3c

File tree

4 files changed

+186
-15
lines changed

4 files changed

+186
-15
lines changed

Sources/HaystackClient/Client.swift

Lines changed: 182 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,37 @@ import Haystack
33
import Foundation
44

55
@available(macOS 13.0, *)
6+
/// A Haystack API client. Once created, call the `open` method to connect.
7+
///
8+
/// ```swift
9+
/// let client = Client(
10+
/// baseUrl: "http://localhost:8080/api",
11+
/// username: "user",
12+
/// password: "abc123"
13+
/// )
14+
/// await try client.open()
15+
/// let about = await try client.about()
16+
/// await try client.close()
17+
/// ```
618
public class Client {
7-
private let userAgentHeaderValue = "swift-haystack-client"
19+
let baseUrl: URL
20+
let username: String
21+
let password: String
22+
let format: DataFormat
23+
let session: URLSession
824

9-
public let baseUrl: URL
10-
private let username: String
11-
private let password: String
12-
private let format: DataFormat
13-
private let session: URLSession
14-
15-
/// Set when `login` is called.
25+
/// Set when `open` is called.
1626
private var authToken: String? = nil
1727

1828
private let jsonEncoder = JSONEncoder()
1929
private let jsonDecoder = JSONDecoder()
2030

31+
/// Create a client instance.This may be reused across multiple logins if needed.
32+
/// - Parameters:
33+
/// - baseUrl: The URL of the Haystack API server
34+
/// - username: The username to authenticate with
35+
/// - password: The password to authenticate with
36+
/// - format: The transfer data format. Defaults to `zinc` to reduce data transfer.
2137
public init(
2238
baseUrl: URL,
2339
username: String,
@@ -42,6 +58,7 @@ public class Client {
4258
self.session = URLSession(configuration: sessionConfig)
4359
}
4460

61+
/// Authenticate the client and store the authentication token
4562
public func open() async throws {
4663
let url = baseUrl.appending(path: "about")
4764

@@ -93,14 +110,29 @@ public class Client {
93110
self.authToken = try await authenticator.getAuthToken()
94111
}
95112

96-
public func about() async throws -> Grid {
97-
return try await post(path: "about")
98-
}
99-
113+
/// Closes the current authentication session.
114+
///
115+
/// https://project-haystack.org/doc/docHaystack/Ops#close
100116
public func close() async throws {
101117
try await post(path: "close")
118+
self.authToken = nil
119+
}
120+
121+
/// Queries basic information about the server
122+
///
123+
/// https://project-haystack.org/doc/docHaystack/Ops#about
124+
public func about() async throws -> Grid {
125+
return try await post(path: "about")
102126
}
103127

128+
/// Queries def dicts from the current namespace
129+
///
130+
/// https://project-haystack.org/doc/docHaystack/Ops#defs
131+
///
132+
/// - Parameters:
133+
/// - filter: A string filter
134+
/// - limit: The maximum number of defs to return in response
135+
/// - Returns: A grid with the dict representation of each def
104136
public func defs(filter: String? = nil, limit: Number? = nil) async throws -> Grid {
105137
var args: [String: any Val] = [:]
106138
if let filter = filter {
@@ -112,6 +144,14 @@ public class Client {
112144
return try await post(path: "defs", args: args)
113145
}
114146

147+
/// Queries lib defs from current namspace
148+
///
149+
/// https://project-haystack.org/doc/docHaystack/Ops#libs
150+
///
151+
/// - Parameters:
152+
/// - filter: A string filter
153+
/// - limit: The maximum number of defs to return in response
154+
/// - Returns: A grid with the dict representation of each def
115155
public func libs(filter: String? = nil, limit: Number? = nil) async throws -> Grid {
116156
var args: [String: any Val] = [:]
117157
if let filter = filter {
@@ -123,6 +163,14 @@ public class Client {
123163
return try await post(path: "libs", args: args)
124164
}
125165

166+
/// Queries op defs from current namspace
167+
///
168+
/// https://project-haystack.org/doc/docHaystack/Ops#ops
169+
///
170+
/// - Parameters:
171+
/// - filter: A string filter
172+
/// - limit: The maximum number of defs to return in response
173+
/// - Returns: A grid with the dict representation of each def
126174
public func ops(filter: String? = nil, limit: Number? = nil) async throws -> Grid {
127175
var args: [String: any Val] = [:]
128176
if let filter = filter {
@@ -134,6 +182,14 @@ public class Client {
134182
return try await post(path: "ops", args: args)
135183
}
136184

185+
/// Queries filetype defs from current namspace
186+
///
187+
/// https://project-haystack.org/doc/docHaystack/Ops#filetypes
188+
///
189+
/// - Parameters:
190+
/// - filter: A string filter
191+
/// - limit: The maximum number of defs to return in response
192+
/// - Returns: A grid with the dict representation of each def
137193
public func filetypes(filter: String? = nil, limit: Number? = nil) async throws -> Grid {
138194
var args: [String: any Val] = [:]
139195
if let filter = filter {
@@ -145,6 +201,12 @@ public class Client {
145201
return try await post(path: "filetypes", args: args)
146202
}
147203

204+
/// Read a set of entity records by their unique identifier
205+
///
206+
/// https://project-haystack.org/doc/docHaystack/Ops#read
207+
///
208+
/// - Parameter ids: Ref identifiers
209+
/// - Returns: A grid with a row for each entity read
148210
public func read(ids: [Ref]) async throws -> Grid {
149211
let builder = GridBuilder()
150212
try builder.addCol(name: "id")
@@ -154,6 +216,14 @@ public class Client {
154216
return try await post(path: "read", grid: builder.toGrid())
155217
}
156218

219+
/// Read a set of entity records using a filter
220+
///
221+
/// https://project-haystack.org/doc/docHaystack/Ops#read
222+
///
223+
/// - Parameters:
224+
/// - filter: A string filter
225+
/// - limit: The maximum number of entities to return in response
226+
/// - Returns: A grid with a row for each entity read
157227
public func readAll(filter: String, limit: Number? = nil) async throws -> Grid {
158228
var args: [String: any Val] = ["filter": filter]
159229
if let limit = limit {
@@ -162,14 +232,40 @@ public class Client {
162232
return try await post(path: "read", args: args)
163233
}
164234

165-
public func nav(navId: Ref) async throws -> Grid {
166-
return try await post(path: "nav", args: ["navId": navId])
235+
/// Navigate a project for learning and discovery
236+
///
237+
/// https://project-haystack.org/doc/docHaystack/Ops#nav
238+
///
239+
/// - Parameter navId: The ID of the entity to navigate from. If null, the navigation root is used.
240+
/// - Returns: A grid of navigation children for the navId specified by the request
241+
public func nav(navId: Ref?) async throws -> Grid {
242+
if let navId = navId {
243+
return try await post(path: "nav", args: ["navId": navId])
244+
} else {
245+
return try await post(path: "nav", args: [:])
246+
}
167247
}
168248

249+
/// Reads time-series data from historized point
250+
///
251+
/// https://project-haystack.org/doc/docHaystack/Ops#hisRead
252+
///
253+
/// - Parameters:
254+
/// - id: Identifier of historized point
255+
/// - range: A date-time range
256+
/// - Returns: A grid whose rows represent timetamp/value pairs with a DateTime ts column and a val column for each scalar value
169257
public func hisRead(id: Ref, range: HisReadRange) async throws -> Grid {
170258
return try await post(path: "hisRead", args: ["id": id, "range": range.toRequestString()])
171259
}
172260

261+
/// Posts new time-series data to a historized point
262+
///
263+
/// https://project-haystack.org/doc/docHaystack/Ops#hisWrite
264+
///
265+
/// - Parameters:
266+
/// - id: The identifier of the point to write to
267+
/// - items: New timestamp/value samples to write
268+
/// - Returns: An empty grid
173269
public func hisWrite(id: Ref, items: [HisItem]) async throws -> Grid {
174270
let builder = GridBuilder()
175271
builder.setMeta(["id": id])
@@ -181,6 +277,17 @@ public class Client {
181277
return try await post(path: "hisWrite", grid: builder.toGrid())
182278
}
183279

280+
/// Write to a given level of a writable point's priority array
281+
///
282+
/// https://project-haystack.org/doc/docHaystack/Ops#pointWrite
283+
///
284+
/// - Parameters:
285+
/// - id: Identifier of writable point
286+
/// - level: Number from 1-17 for level to write
287+
/// - val: Value to write or null to auto the level
288+
/// - who: Username/application name performing the write, otherwise authenticated user display name is used
289+
/// - duration: Number with duration unit if setting level 8
290+
/// - Returns: An empty grid
184291
public func pointWrite(
185292
id: Ref,
186293
level: Number,
@@ -213,10 +320,26 @@ public class Client {
213320
return try await post(path: "pointWrite", args: args)
214321
}
215322

323+
/// Read the current status of a writable point's priority array
324+
///
325+
/// https://project-haystack.org/doc/docHaystack/Ops#pointWrite
326+
///
327+
/// - Parameter id: Identifier of writable point
328+
/// - Returns: A grid with current priority array state
216329
public func pointWriteStatus(id: Ref) async throws -> Grid {
217330
return try await post(path: "pointWrite", args: ["id": id])
218331
}
219332

333+
/// Used to create new watches.
334+
///
335+
/// https://project-haystack.org/doc/docHaystack/Ops#watchSub
336+
///
337+
/// - Parameters:
338+
/// - watchDis: Debug/display string
339+
/// - lease: Number with duration unit for desired lease period
340+
/// - ids: The identifiers of the entities to subscribe to
341+
/// - Returns: A grid where rows correspond to the current entity state of the requested identifiers. Grid metadata contains
342+
/// `watchId` and `lease`.
220343
public func watchSubCreate(
221344
watchDis: String,
222345
lease: Number? = nil,
@@ -237,6 +360,16 @@ public class Client {
237360
return try await post(path: "watchSub", grid: builder.toGrid())
238361
}
239362

363+
/// Used to add entities to an existing watch.
364+
///
365+
/// https://project-haystack.org/doc/docHaystack/Ops#watchSub
366+
///
367+
/// - Parameters:
368+
/// - watchId: Debug/display string
369+
/// - lease: Number with duration unit for desired lease period
370+
/// - ids: The identifiers of the entities to subscribe to
371+
/// - Returns: A grid where rows correspond to the current entity state of the requested identifiers. Grid metadata contains
372+
/// `watchId` and `lease`.
240373
public func watchSubAdd(
241374
watchId: String,
242375
lease: Number? = nil,
@@ -257,6 +390,14 @@ public class Client {
257390
return try await post(path: "watchSub", grid: builder.toGrid())
258391
}
259392

393+
/// Used to close a watch entirely or remove entities from a watch
394+
///
395+
/// https://project-haystack.org/doc/docHaystack/Ops#watchUnsub
396+
///
397+
/// - Parameters:
398+
/// - watchId: Watch identifier
399+
/// - ids: Ref values for each entity to unsubscribe. If empty the entire watch is closed.
400+
/// - Returns: An empty grid
260401
public func watchUnsub(
261402
watchId: String,
262403
ids: [Ref]
@@ -276,6 +417,14 @@ public class Client {
276417
return try await post(path: "watchUnsub", grid: builder.toGrid())
277418
}
278419

420+
/// Used to poll a watch for changes to the subscribed entity records
421+
///
422+
/// https://project-haystack.org/doc/docHaystack/Ops#watchPoll
423+
///
424+
/// - Parameters:
425+
/// - watchId: Watch identifier
426+
/// - refresh: Whether a full refresh should occur
427+
/// - Returns: A grid where each row correspondes to a watched entity
279428
public func watchPoll(
280429
watchId: String,
281430
refresh: Bool = false
@@ -291,7 +440,17 @@ public class Client {
291440
return try await post(path: "watchPoll", grid: builder.toGrid())
292441
}
293442

294-
public func invokeAction(id: Ref, action: String, args: [String: any Val]) async throws -> Grid {
443+
/// https://project-haystack.org/doc/docHaystack/Ops#invokeAction
444+
/// - Parameters:
445+
/// - id: Identifier of target rec
446+
/// - action: The name of the action func
447+
/// - args: The arguments to the action
448+
/// - Returns: A grid of undefined shape
449+
public func invokeAction(
450+
id: Ref,
451+
action: String,
452+
args: [String: any Val]
453+
) async throws -> Grid {
295454
let gridMeta: [String: any Val] = [
296455
"id": id,
297456
"action": action
@@ -308,6 +467,12 @@ public class Client {
308467
return try await post(path: "invokeAction", grid: builder.toGrid())
309468
}
310469

470+
/// Evaluate an Axon expression
471+
///
472+
/// https://haxall.io/doc/lib-hx/op~eval
473+
///
474+
/// - Parameter expression: A string Axon expression
475+
/// - Returns: A grid of undefined shape
311476
public func eval(expression: String) async throws -> Grid {
312477
return try await post(path: "eval", args: ["expr": expression])
313478
}
@@ -407,6 +572,8 @@ public class Client {
407572
}
408573
}
409574

575+
private let userAgentHeaderValue = "swift-haystack-client"
576+
410577
enum HaystackClientError: Error {
411578
case authHelloNoWwwAuthenticateHeader
412579
case authHelloHandshakeTokenNotPresent

Sources/HaystackClient/DataFormat.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// Haystack data serialization formats. For more information, see
2+
/// https://project-haystack.org/doc/docHaystack/HttpApi#contentNegotiation
13
public enum DataFormat: String {
24
case json
35
case zinc

Sources/HaystackClient/HisItem.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Haystack
22

3+
/// A timestamp/value pair.
34
public struct HisItem {
45
let ts: DateTime
56
let val: any Val

Sources/HaystackClient/HisReadRange.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Haystack
22

3+
/// Query-able DateTime ranges, which support relative and absolute values.
34
public enum HisReadRange {
45
case today
56
case yesterday

0 commit comments

Comments
 (0)