From dde52c84d02f472f36bdc1b925498d04dd26d03a Mon Sep 17 00:00:00 2001 From: Craig Siemens Date: Sun, 12 Jan 2025 20:34:07 -0700 Subject: [PATCH] feat: updated Point to be a sendable struct Deprecated addTag(key:value:), addField(key:value:), and time(time:) in favor of using the mutable properties. BREAKING CHANGE: Point is now a value type which can cause breaking behaviour in code expecting the behaviour of a reference type. BREAKING CHANGE: addTag(key:value:), addField(key:value:), and time(time:) have mutating versions when called and the result is discarded. --- .gitignore | 2 +- CHANGELOG.md | 5 +- Sources/InfluxDBSwift/InfluxDBClient.swift | 2 +- Sources/InfluxDBSwift/Point.swift | 169 +++++--- .../InfluxDBSwiftTests/IntegrationTests.swift | 60 ++- Tests/InfluxDBSwiftTests/PointTests.swift | 386 +++++++++++------- Tests/InfluxDBSwiftTests/WriteAPITests.swift | 74 ++-- 7 files changed, 439 insertions(+), 259 deletions(-) diff --git a/.gitignore b/.gitignore index de9dbbc2..7e135811 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,7 @@ Package.resolved # # 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 +.swiftpm .build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aebeb12..e9a4b274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -## 1.8.0 [unreleased] +## 2.0.0 [unreleased] + +### Features +1. [#69](https://github.com/influxdata/influxdb-client-swift/pull/69) and [#70](https://github.com/influxdata/influxdb-client-swift/pull/70): Updated `Point` to be a sendable struct ## 1.7.0 [2024-05-17] diff --git a/Sources/InfluxDBSwift/InfluxDBClient.swift b/Sources/InfluxDBSwift/InfluxDBClient.swift index 10a8c485..7e58d6df 100644 --- a/Sources/InfluxDBSwift/InfluxDBClient.swift +++ b/Sources/InfluxDBSwift/InfluxDBClient.swift @@ -247,7 +247,7 @@ extension InfluxDBClient { /// An enum represents the precision for the unix timestamps within the body line-protocol. /// - SeeAlso: https://docs.influxdata.com/influxdb/latest/write-data/#timestamp-precision - public enum TimestampPrecision: String, Codable, CaseIterable { + public enum TimestampPrecision: String, Codable, CaseIterable, Sendable { /// Milliseconds case ms /// Seconds diff --git a/Sources/InfluxDBSwift/Point.swift b/Sources/InfluxDBSwift/Point.swift index e2fb1dda..4353c9e2 100644 --- a/Sources/InfluxDBSwift/Point.swift +++ b/Sources/InfluxDBSwift/Point.swift @@ -8,62 +8,31 @@ extension InfluxDBClient { /// Point defines the values that will be written to the database. /// /// - SeeAlso: http://bit.ly/influxdata-point - public class Point { + public struct Point: Sendable { /// The measurement name. - private let measurement: String + public let measurement: String // The measurement tags. - private var tags: [String: String?] = [:] + public var tags: [String: String?] // The measurement fields. - private var fields: [String: FieldValue?] = [:] + public var fields: [String: FieldValue?] /// The data point time. - var time: TimestampValue? + public var time: TimestampValue? /// Create a new Point with specified a measurement name and precision. /// /// - Parameters: /// - measurement: the measurement name /// - precision: the data point precision - public init(_ measurement: String) { + public init( + _ measurement: String, + tags: [String: String?] = [:], + fields: [String: FieldValue?] = [:], + time: TimestampValue? = nil + ) { self.measurement = measurement - } - - /// Adds or replaces a tag value for this point. - /// - /// - Parameters: - /// - key: the tag name - /// - value: the tag value - /// - Returns: self - @discardableResult - public func addTag(key: String?, value: String?) -> Point { - if let key = key { - tags[key] = value - } - return self - } - - /// Adds or replaces a field value for this point. - /// - /// - Parameters: - /// - key: the field name - /// - value: the field value - /// - Returns: self - @discardableResult - public func addField(key: String?, value: FieldValue?) -> Point { - if let key = key { - fields[key] = value - } - return self - } - - /// Updates the timestamp for the point. - /// - /// - Parameters: - /// - time: the timestamp. It can be `Int` or `Date`. - /// - Returns: self - @discardableResult - public func time(time: TimestampValue) -> Point { + self.tags = tags + self.fields = fields self.time = time - return self } /// Creates Line Protocol from Data Point. @@ -134,7 +103,7 @@ extension InfluxDBClient { extension InfluxDBClient.Point { /// Possible value types of Field - public enum FieldValue { + public enum FieldValue: Sendable { /// Support for Int8 init(_ value: Int8) { self = .int(Int(value)) @@ -185,7 +154,7 @@ extension InfluxDBClient.Point { } /// Possible value types of Field - public enum TimestampValue: CustomStringConvertible { + public enum TimestampValue: CustomStringConvertible, Sendable { // The number of ticks since the UNIX epoch. The value has to be specified with correct precision. case interval(Int, InfluxDBClient.TimestampPrecision = InfluxDBClient.defaultTimestampPrecision) // The date timestamp. @@ -205,8 +174,8 @@ extension InfluxDBClient.Point { extension InfluxDBClient.Point { /// Tuple definition for construct `Point`. public typealias Tuple = (measurement: String, - tags: [String?: String?]?, - fields: [String?: InfluxDBClient.Point.FieldValue?], + tags: [String: String?]?, + fields: [String: InfluxDBClient.Point.FieldValue?], time: InfluxDBClient.Point.TimestampValue?) /// Create a new Point from Tuple. /// @@ -214,20 +183,13 @@ extension InfluxDBClient.Point { /// - tuple: the tuple with keys: `measurement`, `tags`, `fields` and `time` /// - precision: the data point precision /// - Returns: created Point - public class func fromTuple(_ tuple: Tuple) -> InfluxDBClient.Point { - let point = InfluxDBClient.Point(tuple.measurement) - if let tags = tuple.tags { - for tag in tags { - point.addTag(key: tag.0, value: tag.1) - } - } - for field in tuple.fields { - point.addField(key: field.0, value: field.1) - } - if let time = tuple.time { - point.time(time: time) - } - return point + public static func fromTuple(_ tuple: Tuple) -> InfluxDBClient.Point { + .init( + tuple.measurement, + tags: tuple.tags ?? [:], + fields: tuple.fields, + time: tuple.time + ) } } @@ -386,3 +348,86 @@ extension InfluxDBClient.Point { return " \(sinceEpoch)" } } + +extension InfluxDBClient.Point { + /// Adds or replaces a tag value for this point. + /// + /// - Parameters: + /// - key: the tag name + /// - value: the tag value + /// - Returns: self + @_disfavoredOverload + @available(*, deprecated, message: "Pass tags to Point.init or use the tags property") + public func addTag(key: String?, value: String?) -> Self { + var point = self + if let key = key { + point.tags[key] = value + } + return point + } + + /// Adds or replaces a tag value for this point. + /// + /// - Parameters: + /// - key: the tag name + /// - value: the tag value + /// - Returns: self + @available(*, deprecated, message: "Pass tags to Point.init or use the tags property") + public mutating func addTag(key: String?, value: String?) { + if let key = key { + tags[key] = value + } + } + + /// Adds or replaces a field value for this point. + /// + /// - Parameters: + /// - key: the field name + /// - value: the field value + /// - Returns: self + @_disfavoredOverload + @available(*, deprecated, message: "Pass fields to Point.init or use the fields property") + public func addField(key: String?, value: FieldValue?) -> Self { + var point = self + if let key = key { + point.fields[key] = value + } + return point + } + + /// Adds or replaces a field value for this point. + /// + /// - Parameters: + /// - key: the field name + /// - value: the field value + /// - Returns: self + @available(*, deprecated, message: "Pass fields to Point.init or use the fields property") + public mutating func addField(key: String?, value: FieldValue?) { + if let key = key { + fields[key] = value + } + } + + /// Updates the timestamp for the point. + /// + /// - Parameters: + /// - time: the timestamp. It can be `Int` or `Date`. + /// - Returns: self + @_disfavoredOverload + @available(*, deprecated, message: "Pass time to Point.init or use the time property") + public func time(time: TimestampValue) -> Self { + var point = self + point.time = time + return point + } + + /// Updates the timestamp for the point. + /// + /// - Parameters: + /// - time: the timestamp. It can be `Int` or `Date`. + /// - Returns: self + @available(*, deprecated, message: "Pass time to Point.init or use the time property") + public mutating func time(time: TimestampValue) { + self.time = time + } +} diff --git a/Tests/InfluxDBSwiftTests/IntegrationTests.swift b/Tests/InfluxDBSwiftTests/IntegrationTests.swift index a3187918..10a4a151 100644 --- a/Tests/InfluxDBSwiftTests/IntegrationTests.swift +++ b/Tests/InfluxDBSwiftTests/IntegrationTests.swift @@ -38,11 +38,15 @@ final class IntegrationTests: XCTestCase { let measurement = "h2o_\(Date().timeIntervalSince1970)" let points = Array(1...5).map { - InfluxDBClient.Point(measurement) - .addTag(key: "host", value: "aws") - .addTag(key: "location", value: "west") - .addField(key: "value", value: .int($0)) - .time(time: .date(Date(2020, 7, $0))) + InfluxDBClient.Point( + measurement, + tags: [ + "host": "aws", + "location": "west" + ], + fields: ["value": .int($0)], + time: .date(Date(2020, 7, $0)) + ) } client.makeWriteAPI().write(points: points) { _, error in @@ -92,23 +96,35 @@ final class IntegrationTests: XCTestCase { let measurement = "h2o_\(Date().timeIntervalSince1970)" - let point1 = InfluxDBClient.Point(measurement) - .addTag(key: "host", value: "aws") - .addTag(key: "location", value: "west") - .addField(key: "value", value: .int(1)) - .time(time: .date(Date(2020, 7, 1))) - - let point2 = InfluxDBClient.Point(measurement) - .addTag(key: "host", value: "azure") - .addTag(key: "location", value: "west") - .addField(key: "value", value: .int(2)) - .time(time: .date(Date(2020, 7, 2))) - - let point3 = InfluxDBClient.Point(measurement) - .addTag(key: "host", value: "gc") - .addTag(key: "location", value: "west") - .addField(key: "value", value: .int(3)) - .time(time: .date(Date(2020, 7, 3))) + let point1 = InfluxDBClient.Point( + measurement, + tags: [ + "host": "aws", + "location": "west" + ], + fields: ["value": .int(1)], + time: .date(Date(2020, 7, 1)) + ) + + let point2 = InfluxDBClient.Point( + measurement, + tags: [ + "host": "azure", + "location": "west" + ], + fields: ["value": .int(2)], + time: .date(Date(2020, 7, 2)) + ) + + let point3 = InfluxDBClient.Point( + measurement, + tags: [ + "host": "gc", + "location": "west" + ], + fields: ["value": .int(3)], + time: .date(Date(2020, 7, 3)) + ) client.makeWriteAPI().write(points: [point1, point2, point3]) { _, error in if let error = error { diff --git a/Tests/InfluxDBSwiftTests/PointTests.swift b/Tests/InfluxDBSwiftTests/PointTests.swift index 9715dc41..20fffb50 100644 --- a/Tests/InfluxDBSwiftTests/PointTests.swift +++ b/Tests/InfluxDBSwiftTests/PointTests.swift @@ -9,44 +9,65 @@ import XCTest final class PointTests: XCTestCase { func testMeasurementEscape() { - var point = InfluxDBClient.Point("h2 o") - .addTag(key: "location", value: "europe") - .addTag(key: "", value: "warm") - .addField(key: "level", value: .int(2)) + var point = InfluxDBClient.Point( + "h2 o", + tags: [ + "location": "europe", + "": "warm" + ], + fields: ["level": .int(2)] + ) + XCTAssertEqual("h2\\ o,location=europe level=2i", try point.toLineProtocol()) - point = InfluxDBClient.Point("h2,o") - .addTag(key: "location", value: "europe") - .addTag(key: "", value: "warn") - .addField(key: "level", value: .int(2)) + point = InfluxDBClient.Point( + "h2,o", + tags: [ + "location": "europe", + "": "warn" + ], + fields: ["level": .int(2)] + ) XCTAssertEqual("h2\\,o,location=europe level=2i", try point.toLineProtocol()) } func testTagEmptyKey() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addTag(key: "", value: "warn") - .addField(key: "level", value: .int(2)) + let point = InfluxDBClient.Point( + "h2o", + tags: [ + "location": "europe", + "": "warn" + ], + fields: ["level": .int(2)] + ) XCTAssertEqual("h2o,location=europe level=2i", try point.toLineProtocol()) } func testTagEmptyValue() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addTag(key: "log", value: "") - .addField(key: "level", value: .int(2)) + let point = InfluxDBClient.Point( + "h2o", + tags: [ + "location": "europe", + "log": "" + ], + fields: ["level": .int(2)] + ) XCTAssertEqual("h2o,location=europe level=2i", try point.toLineProtocol()) } func testTagEscapingKeyAndValue() { - let point = InfluxDBClient.Point("h\n2\ro\t_data") - .addTag(key: "new\nline", value: "new\nline") - .addTag(key: "carriage\rreturn", value: "carriage\nreturn") - .addTag(key: "t\tab", value: "t\tab") - .addField(key: "level", value: .int(2)) + let point = InfluxDBClient.Point( + "h\n2\ro\t_data", + tags: [ + "new\nline": "new\nline", + "carriage\rreturn": "carriage\nreturn", + "t\tab": "t\tab" + ], + fields: ["level": .int(2)] + ) XCTAssertEqual( "h\\n2\\ro\\t_data,carriage\\rreturn=carriage\\nreturn,new\\nline=new\\nline,t\\tab=t\\tab level=2i", @@ -54,31 +75,39 @@ final class PointTests: XCTestCase { } func testEqualSignEscaping() { - let point = InfluxDBClient.Point("h=2o") - .addTag(key: "l=ocation", value: "e=urope") - .addField(key: "l=evel", value: .int(2)) + let point = InfluxDBClient.Point( + "h=2o", + tags: ["l=ocation": "e=urope"], + fields: ["l=evel": .int(2)] + ) XCTAssertEqual("h=2o,l\\=ocation=e\\=urope l\\=evel=2i", try point.toLineProtocol()) } func testOverrideTagField() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addTag(key: "location", value: "europe2") - .addField(key: "level", value: .int(2)) - .addField(key: "level", value: .int(3)) + var point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .int(1)] + ) + + point.tags["location"] = "europe2" + point.fields["level"] = .int(3) XCTAssertEqual("h2o,location=europe2 level=3i", try point.toLineProtocol()) } func testFieldTypes() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "decimal", value: .int(123)) - .addField(key: "float", value: .double(250.69)) - .addField(key: "bool", value: .boolean(false)) - .addField(key: "string", value: .string("string value")) - + let point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: [ + "decimal": .int(123), + "float": .double(250.69), + "bool": .boolean(false), + "string": .string("string value") + ] + ) let expected = "h2o,location=europe bool=false,decimal=123i,float=250.69,string=\"string value\"" XCTAssertEqual(expected, try point.toLineProtocol()) @@ -91,12 +120,16 @@ final class PointTests: XCTestCase { let dNumber: Int32 = 12 let eNumber: Int64 = 15 - let point = InfluxDBClient.Point("h2o") - .addField(key: "a", value: .int(aNumber)) - .addField(key: "b", value: InfluxDBClient.Point.FieldValue(bNumber)) - .addField(key: "c", value: InfluxDBClient.Point.FieldValue(cNumber)) - .addField(key: "d", value: InfluxDBClient.Point.FieldValue(dNumber)) - .addField(key: "e", value: InfluxDBClient.Point.FieldValue(eNumber)) + let point = InfluxDBClient.Point( + "h2o", + fields: [ + "a": .int(aNumber), + "b": InfluxDBClient.Point.FieldValue(bNumber), + "c": InfluxDBClient.Point.FieldValue(cNumber), + "d": InfluxDBClient.Point.FieldValue(dNumber), + "e": InfluxDBClient.Point.FieldValue(eNumber) + ] + ) XCTAssertEqual("h2o a=3i,b=6i,c=9i,d=12i,e=15i", try point.toLineProtocol()) } @@ -108,44 +141,58 @@ final class PointTests: XCTestCase { let dNumber: UInt32 = 12 let eNumber: UInt64 = 15 - let point = InfluxDBClient.Point("h2o") - .addField(key: "a", value: .uint(aNumber)) - .addField(key: "b", value: InfluxDBClient.Point.FieldValue(bNumber)) - .addField(key: "c", value: InfluxDBClient.Point.FieldValue(cNumber)) - .addField(key: "d", value: InfluxDBClient.Point.FieldValue(dNumber)) - .addField(key: "e", value: InfluxDBClient.Point.FieldValue(eNumber)) + let point = InfluxDBClient.Point( + "h2o", + fields: [ + "a": .uint(aNumber), + "b": InfluxDBClient.Point.FieldValue(bNumber), + "c": InfluxDBClient.Point.FieldValue(cNumber), + "d": InfluxDBClient.Point.FieldValue(dNumber), + "e": InfluxDBClient.Point.FieldValue(eNumber) + ] + ) XCTAssertEqual("h2o a=3u,b=6u,c=9u,d=12u,e=15u", try point.toLineProtocol()) } func testFieldNullValue() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .int(2)) - .addField(key: "warning", value: nil) + let point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: [ + "level": .int(2), + "warning": nil + ] + ) XCTAssertEqual("h2o,location=europe level=2i", try point.toLineProtocol()) } func testFieldEscape() { - var point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .string("string esc\\ape value")) + var point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .string("string esc\\ape value")] + ) XCTAssertEqual("h2o,location=europe level=\"string esc\\\\ape value\"", try point.toLineProtocol()) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .string("string esc\"ape value")) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .string("string esc\"ape value")] + ) XCTAssertEqual("h2o,location=europe level=\"string esc\\\"ape value\"", try point.toLineProtocol()) } func testTime() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .int(2)) - .time(time: .interval(123, .s)) + let point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .int(2)], + time: .interval(123, .s) + ) XCTAssertEqual("h2o,location=europe level=2i 123000000000", try point.toLineProtocol()) } @@ -153,56 +200,70 @@ final class PointTests: XCTestCase { func testDateTimeFormatting() { var date = Date(2015, 10, 15, 8, 20, 15) - var point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .int(2)) - .time(time: .date(date)) + var point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .int(2)], + time: .date(date) + ) XCTAssertEqual("h2o,location=europe level=2i 1444897215000", try point.toLineProtocol(precision: .ms)) date = Date(2015, 10, 15, 8, 20, 15, 0, TimeZone(abbreviation: "JST")) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .int(2)) - .time(time: .date(date)) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .int(2)], + time: .date(date) + ) XCTAssertEqual("h2o,location=europe level=2i 1444864815000", try point.toLineProtocol(precision: .ms)) date = Date(2015, 10, 15, 8, 20, 15, 750) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .boolean(false)) - .time(time: .date(date)) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .boolean(false)], + time: .date(date) + ) XCTAssertEqual("h2o,location=europe level=false 1444897215", try point.toLineProtocol(precision: .s)) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .boolean(false)) - .time(time: .date(date)) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .boolean(false)], + time: .date(date) + ) XCTAssertEqual("h2o,location=europe level=false 1444897215000", try point.toLineProtocol(precision: .ms)) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .boolean(false)) - .time(time: .date(date)) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .boolean(false)], + time: .date(date) + ) XCTAssertEqual("h2o,location=europe level=false 1444897215000750", try point.toLineProtocol(precision: .us)) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .boolean(false)) - .time(time: .date(date)) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .boolean(false)], + time: .date(date) + ) XCTAssertEqual("h2o,location=europe level=false 1444897215000750080", try point.toLineProtocol(precision: .ns)) - point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "level", value: .boolean(true)) - .time(time: .date(Date())) + point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: ["level": .boolean(true)], + time: .date(Date()) + ) XCTAssertFalse(try point.toLineProtocol(precision: .s)!.contains(".")) } @@ -210,22 +271,32 @@ final class PointTests: XCTestCase { func testPointProtocol() { let date = Date(2009, 11, 10, 23, 0, 0) - var point = InfluxDBClient.Point("weather") - .time(time: .date(date)) - .addTag(key: "location", value: "Přerov") - .addTag(key: "sid", value: "12345") - .addField(key: "temperature", value: .double(30.1)) - .addField(key: "int_field", value: .int(2)) - .addField(key: "float_field", value: .int(0)) + var point = InfluxDBClient.Point( + "weather", + tags: [ + "location": "Přerov", + "sid": "12345" + ], + fields: [ + "temperature": .double(30.1), + "int_field": .int(2), + "float_field": .int(0) + ], + time: .date(date) + ) XCTAssertEqual( - "weather,location=Přerov,sid=12345 float_field=0i,int_field=2i,temperature=30.1 1257894000000", - try point.toLineProtocol(precision: .ms)) - - point = InfluxDBClient.Point("weather") - .time(time: .date(date)) - .addField(key: "temperature", value: .double(30.1)) - .addField(key: "float_field", value: .int(0)) + "weather,location=Přerov,sid=12345 float_field=0i,int_field=2i,temperature=30.1 1257894000000", + try point.toLineProtocol(precision: .ms)) + + point = InfluxDBClient.Point( + "weather", + fields: [ + "temperature": .double(30.1), + "float_field": .int(0) + ], + time: .date(date) + ) XCTAssertEqual( "weather float_field=0i,temperature=30.1 1257894000000", @@ -239,38 +310,52 @@ final class PointTests: XCTestCase { let expUTC = "A val=1i 1257894000000000000" let expBerlin = "A val=1i 1257890400000000000" - let point = InfluxDBClient.Point("A") - .addField(key: "val", value: .int(1)) - .time(time: .date(utc)) + var point = InfluxDBClient.Point( + "A", + fields: ["val": .int(1)], + time: .date(utc) + ) + + XCTAssertEqual(expUTC, try point.toLineProtocol()) + point.time = .date(utc) XCTAssertEqual(expUTC, try point.toLineProtocol()) - XCTAssertEqual(expUTC, try point.time(time: .date(utc)).toLineProtocol()) - XCTAssertEqual(expBerlin, try point.time(time: .date(berlin)).toLineProtocol()) + + point.time = .date(berlin) + XCTAssertEqual(expBerlin, try point.toLineProtocol()) } func testInfinityValues() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "decimal-infinity-positive", value: .double(Double.infinity)) - .addField(key: "decimal-infinity-negative", value: .double(-Double.infinity)) - .addField(key: "decimal-nan", value: .double(-Double.nan)) - .addField(key: "float-infinity-positive", value: InfluxDBClient.Point.FieldValue(Float.infinity)) - .addField(key: "float-infinity-negative", value: InfluxDBClient.Point.FieldValue(-Float.infinity)) - .addField(key: "float-nan", value: InfluxDBClient.Point.FieldValue(Float.nan)) - .addField(key: "level", value: .int(2)) + let point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: [ + "decimal-infinity-positive": .double(Double.infinity), + "decimal-infinity-negative": .double(-Double.infinity), + "decimal-nan": .double(-Double.nan), + "float-infinity-positive": InfluxDBClient.Point.FieldValue(Float.infinity), + "float-infinity-negative": InfluxDBClient.Point.FieldValue(-Float.infinity), + "float-nan": InfluxDBClient.Point.FieldValue(Float.nan), + "level": .int(2) + ] + ) XCTAssertEqual("h2o,location=europe level=2i", try point.toLineProtocol()) } func testOnlyInfinityValues() { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "location", value: "europe") - .addField(key: "decimal-infinity-positive", value: .double(Double.infinity)) - .addField(key: "decimal-infinity-negative", value: .double(-Double.infinity)) - .addField(key: "decimal-nan", value: .double(-Double.nan)) - .addField(key: "float-infinity-positive", value: InfluxDBClient.Point.FieldValue(Float.infinity)) - .addField(key: "float-infinity-negative", value: InfluxDBClient.Point.FieldValue(-Float.infinity)) - .addField(key: "float-nan", value: InfluxDBClient.Point.FieldValue(Float.nan)) + let point = InfluxDBClient.Point( + "h2o", + tags: ["location": "europe"], + fields: [ + "decimal-infinity-positive": .double(Double.infinity), + "decimal-infinity-negative": .double(-Double.infinity), + "decimal-nan": .double(-Double.nan), + "float-infinity-positive": InfluxDBClient.Point.FieldValue(Float.infinity), + "float-infinity-negative": InfluxDBClient.Point.FieldValue(-Float.infinity), + "float-nan": InfluxDBClient.Point.FieldValue(Float.nan) + ] + ) XCTAssertNil(try point.toLineProtocol()) } @@ -279,14 +364,17 @@ final class PointTests: XCTestCase { let utc = Date(2020, 7, 4, 0, 0, 0, 123456) let hongKong = Date(2020, 7, 4, 8, 0, 0, 123456, TimeZone(identifier: "Asia/Hong_Kong")) // +08:00 - let pointUTC = try InfluxDBClient.Point("h2o") - .addField(key: "val", value: .int(1)) - .time(time: .date(utc)) - .toLineProtocol() - let pointHK = try InfluxDBClient.Point("h2o") - .addField(key: "val", value: .int(1)) - .time(time: .date(hongKong)) - .toLineProtocol() + let pointUTC = try InfluxDBClient.Point( + "h2o", + fields: ["val": .int(1)], + time: .date(utc) + ).toLineProtocol() + let pointHK = try InfluxDBClient.Point( + "h2o", + fields: ["val": .int(1)], + time: .date(hongKong) + ).toLineProtocol() + XCTAssertEqual(pointUTC, pointHK) } @@ -400,21 +488,25 @@ final class PointTests: XCTestCase { } func testDescription() throws { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "loc", value: "us") - .addField(key: "value", value: .int(100)) - + let point = InfluxDBClient.Point( + "h2o", + tags: ["loc": "us"], + fields: ["value": .int(100)] + ) let required = "Point: measurement:h2o, tags:[\"loc\": Optional(\"us\")], " + "fields:[\"value\": Optional(InfluxDBSwift.InfluxDBClient.Point.FieldValue.int(100))], time:nil" XCTAssertEqual(required, point.description) } func testDefaultTags() throws { - let point = InfluxDBClient.Point("h2o") - .addTag(key: "loc", value: "us") - .addTag(key: "in_default_tags", value: "use_this") - .addField(key: "value", value: .int(100)) - + let point = InfluxDBClient.Point( + "h2o", + tags: [ + "loc": "us", + "in_default_tags": "use_this" + ], + fields: ["value": .int(100)] + ) let defaultTags = ["tag_a_key": "tag_a_value", "in_default_tags": "not_use_this", "a_tag": "a_tag_value"] XCTAssertEqual( @@ -463,9 +555,11 @@ final class PointTests: XCTestCase { ] try expectations.forEach { expectation in - let point = InfluxDBClient.Point("h2o") - .addField(key: "level", value: .int(1)) - .time(time: expectation.time) + let point = InfluxDBClient.Point( + "h2o", + fields: ["level": .int(1)], + time: expectation.time + ) XCTAssertEqual(expectation.records[0], try point.toLineProtocol(precision: .ns)) XCTAssertEqual(expectation.records[1], try point.toLineProtocol(precision: .us)) diff --git a/Tests/InfluxDBSwiftTests/WriteAPITests.swift b/Tests/InfluxDBSwiftTests/WriteAPITests.swift index 4c5e57e4..b71f767d 100644 --- a/Tests/InfluxDBSwiftTests/WriteAPITests.swift +++ b/Tests/InfluxDBSwiftTests/WriteAPITests.swift @@ -63,9 +63,12 @@ final class WriteAPITests: XCTestCase { MockURLProtocol.handler = simpleWriteHandler(expectation: expectation) - let point = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "a") - .addField(key: "value", value: .int(1)) + let point = InfluxDBClient.Point( + "mem", + tags: ["tag": "a"], + fields: ["value": .int(1)] + ) + client.makeWriteAPI().write(point: point) { response, error in if let error = error { XCTFail("Error occurs: \(error)") @@ -97,14 +100,19 @@ final class WriteAPITests: XCTestCase { return (response, Data()) } - let point1 = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "a") - .addField(key: "value", value: .int(1)) - .time(time: .interval(1, .s)) - let point2 = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "b") - .addField(key: "value", value: .int(2)) - .time(time: .interval(2, .ns)) + let point1 = InfluxDBClient.Point( + "mem", + tags: ["tag": "a"], + fields: ["value": .int(1)], + time: .interval(1, .s) + ) + + let point2 = InfluxDBClient.Point( + "mem", + tags: ["tag": "b"], + fields: ["value": .int(2)], + time: .interval(2, .ns) + ) client.makeWriteAPI().write(points: [point1, point2]) { _, _ in expectation.fulfill() @@ -135,12 +143,18 @@ final class WriteAPITests: XCTestCase { return (response, Data()) } - let point1 = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "a") - .addField(key: "value", value: .int(1)) - let point2 = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "b") - .addField(key: "value", value: .int(2)) + let point1 = InfluxDBClient.Point( + "mem", + tags: ["tag": "a"], + fields: ["value": .int(1)] + ) + + let point2 = InfluxDBClient.Point( + "mem", + tags: ["tag": "b"], + fields: ["value": .int(2)] + ) + client.makeWriteAPI().write(points: [point1, point2]) { response, error in if let error = error { XCTFail("Error occurs: \(error)") @@ -236,9 +250,12 @@ final class WriteAPITests: XCTestCase { fields: ["value": .int(3)], time: .interval(3) ) - let point = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "a") - .addField(key: "value", value: .int(2)) + + let point = InfluxDBClient.Point( + "mem", + tags: ["tag": "a"], + fields: ["value": .int(2)] + ) client.makeWriteAPI().write(records: [record]) { _, error in if let error = error { @@ -391,9 +408,12 @@ final class WriteAPITests: XCTestCase { } let record = "mem,tag=a value=1" - let point = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "a") - .addField(key: "value", value: .int(2)) + let point = InfluxDBClient.Point( + "mem", + tags: ["tag": "a"], + fields: ["value": .int(2)] + ) + let tuple: InfluxDBClient.Point.Tuple = (measurement: "mem", tags: ["tag": "a"], fields: ["value": .int(3)], time: nil) @@ -456,9 +476,11 @@ final class WriteAPITests: XCTestCase { // record try await client.makeWriteAPI().write(record: "mem,tag=a value=1i") // point - let point = InfluxDBClient.Point("mem") - .addTag(key: "tag", value: "a") - .addField(key: "value", value: .int(1)) + let point = InfluxDBClient.Point( + "mem", + tags: ["tag": "a"], + fields: ["value": .int(1)] + ) try await client.makeWriteAPI().write(point: point) // tuple let tuple: InfluxDBClient.Point.Tuple = (