@@ -3,21 +3,37 @@ import Haystack
33import 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+ /// ```
618public 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+
410577enum HaystackClientError : Error {
411578 case authHelloNoWwwAuthenticateHeader
412579 case authHelloHandshakeTokenNotPresent
0 commit comments