Skip to content

Commit

Permalink
Merge pull request #190 from launchdarkly/gw/remove-unused-lduser-code
Browse files Browse the repository at this point in the history
  • Loading branch information
gwhelanLD authored Mar 22, 2022
2 parents d70421f + 95d9005 commit 85d2c27
Show file tree
Hide file tree
Showing 7 changed files with 16 additions and 338 deletions.
2 changes: 1 addition & 1 deletion LaunchDarkly/LaunchDarkly/LDClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ public class LDClient {
public var allFlags: [LDFlagKey: Any]? {
guard hasStarted
else { return nil }
return flagStore.featureFlags.allFlagValues
return flagStore.featureFlags.compactMapValues { $0.value }
}

// MARK: Observing Updates
Expand Down
60 changes: 0 additions & 60 deletions LaunchDarkly/LaunchDarkly/Models/LDUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,46 +118,6 @@ public struct LDUser: Encodable {
return custom[attribute.name]
}

/// Dictionary with LDUser attribute keys and values, with options to include feature flags and private attributes. LDConfig object used to help resolving what attributes should be private.
/// - parameter includePrivateAttributes: Controls whether the resulting dictionary includes private attributes
/// - parameter config: Provides supporting information for defining private attributes
func dictionaryValue(includePrivateAttributes includePrivate: Bool, config: LDConfig) -> [String: Any] {
let allPrivate = !includePrivate && config.allUserAttributesPrivate
let privateAttributeNames = includePrivate ? [] : (privateAttributes + config.privateUserAttributes).map { $0.name }

var dictionary: [String: Any] = [:]
var redactedAttributes: [String] = []

dictionary[CodingKeys.key.rawValue] = key
dictionary[CodingKeys.isAnonymous.rawValue] = isAnonymous

LDUser.optionalAttributes.forEach { attribute in
if let value = self.value(for: attribute) {
if allPrivate || privateAttributeNames.contains(attribute.name) {
redactedAttributes.append(attribute.name)
} else {
dictionary[attribute.name] = value
}
}
}

var customDictionary: [String: Any] = [:]
custom.forEach { attrName, attrVal in
if allPrivate || privateAttributeNames.contains(attrName) {
redactedAttributes.append(attrName)
} else {
customDictionary[attrName] = attrVal.toAny()
}
}
dictionary[CodingKeys.custom.rawValue] = customDictionary.isEmpty ? nil : customDictionary

if !redactedAttributes.isEmpty {
dictionary[CodingKeys.privateAttributes.rawValue] = Set(redactedAttributes).sorted()
}

return dictionary
}

struct UserInfoKeys {
static let includePrivateAttributes = CodingUserInfoKey(rawValue: "LD_includePrivateAttributes")!
static let allAttributesPrivate = CodingUserInfoKey(rawValue: "LD_allAttributesPrivate")!
Expand Down Expand Up @@ -246,23 +206,3 @@ extension LDUserWrapper {
}

extension LDUser: TypeIdentifying { }

#if DEBUG
extension LDUser {
// Compares all user properties.
func isEqual(to otherUser: LDUser) -> Bool {
key == otherUser.key
&& secondary == otherUser.secondary
&& name == otherUser.name
&& firstName == otherUser.firstName
&& lastName == otherUser.lastName
&& country == otherUser.country
&& ipAddress == otherUser.ipAddress
&& email == otherUser.email
&& avatar == otherUser.avatar
&& custom == otherUser.custom
&& isAnonymous == otherUser.isAnonymous
&& privateAttributes == otherUser.privateAttributes
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,3 @@ extension Dictionary where Key == String, Value == Any {
(self[LDUser.CodingKeys.lastUpdated] as? String)?.dateValue
}
}

#if DEBUG
extension Dictionary where Key == String, Value == Any {
mutating func setLastUpdated(_ lastUpdated: Date?) {
self[LDUser.CodingKeys.lastUpdated] = lastUpdated?.stringValue
}
}
#endif
4 changes: 0 additions & 4 deletions LaunchDarkly/LaunchDarkly/ServiceObjects/FlagStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,3 @@ final class FlagStore: FlagMaintaining {
}

extension FlagStore: TypeIdentifying { }

extension Dictionary where Key == LDFlagKey, Value == FeatureFlag {
var allFlagValues: [LDFlagKey: Any] { compactMapValues { $0.value } }
}
252 changes: 0 additions & 252 deletions LaunchDarkly/LaunchDarklyTests/Models/User/LDUserSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ final class LDUserSpec: QuickSpec {

override func spec() {
initSpec()
dictionaryValueSpec()
isEqualSpec()
}

private func initSpec() {
Expand Down Expand Up @@ -115,254 +113,4 @@ final class LDUserSpec: QuickSpec {
}
}
}

private func dictionaryValueSpec() {
let optionalNames = LDUser.optionalAttributes.map { $0.name }
let allCustomPrivitizable = Array(LDUser.StubConstants.custom(includeSystemValues: true).keys)

describe("dictionaryValue") {
var user: LDUser!
var config: LDConfig!
var userDictionary: [String: Any]!

beforeEach {
config = LDConfig.stub
user = LDUser.stub()
}

context("with an empty user") {
beforeEach {
user = LDUser()
// Remove SDK set attributes
user.custom = [:]
}
// Should be the same regardless of including/privitizing attributes
let testCase = {
it("creates expected user dictionary") {
expect(userDictionary.count) == 2
// Required attributes
expect(userDictionary[LDUser.CodingKeys.key.rawValue] as? String) == user.key
expect(userDictionary[LDUser.CodingKeys.isAnonymous.rawValue] as? Bool) == user.isAnonymous
}
}
context("including private attributes") {
beforeEach {
userDictionary = user.dictionaryValue(includePrivateAttributes: true, config: config)
}
testCase()
}
context("privatizing all globally") {
beforeEach {
config.allUserAttributesPrivate = true
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
testCase()
}
context("privatizing all individually in config") {
beforeEach {
config.privateUserAttributes = LDUser.optionalAttributes + [UserAttribute.forName("customAttr")]
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
testCase()
}
context("privatizing all individually on user") {
beforeEach {
user.privateAttributes = LDUser.optionalAttributes + [UserAttribute.forName("customAttr")]
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
testCase()
}
}

it("includePrivateAttributes always includes attributes") {
config.allUserAttributesPrivate = true
config.privateUserAttributes = LDUser.optionalAttributes + allCustomPrivitizable.map { UserAttribute.forName($0) }
user.privateAttributes = LDUser.optionalAttributes + allCustomPrivitizable.map { UserAttribute.forName($0) }
let userDictionary = user.dictionaryValue(includePrivateAttributes: true, config: config)

expect(userDictionary.count) == 11

// Required attributes
expect(userDictionary[LDUser.CodingKeys.key.rawValue] as? String) == user.key
expect(userDictionary[LDUser.CodingKeys.isAnonymous.rawValue] as? Bool) == user.isAnonymous

// Built-in optional attributes
expect(userDictionary[LDUser.CodingKeys.name.rawValue] as? String) == user.name
expect(userDictionary[LDUser.CodingKeys.firstName.rawValue] as? String) == user.firstName
expect(userDictionary[LDUser.CodingKeys.lastName.rawValue] as? String) == user.lastName
expect(userDictionary[LDUser.CodingKeys.email.rawValue] as? String) == user.email
expect(userDictionary[LDUser.CodingKeys.ipAddress.rawValue] as? String) == user.ipAddress
expect(userDictionary[LDUser.CodingKeys.avatar.rawValue] as? String) == user.avatar
expect(userDictionary[LDUser.CodingKeys.secondary.rawValue] as? String) == user.secondary
expect(userDictionary[LDUser.CodingKeys.country.rawValue] as? String) == user.country

let customDictionary = userDictionary.customDictionary()!
expect(customDictionary.count) == allCustomPrivitizable.count

// Custom attributes
allCustomPrivitizable.forEach { attr in
expect(LDValue.fromAny(customDictionary[attr])) == user.custom[attr]
}

// Redacted attributes is empty
expect(userDictionary[LDUser.CodingKeys.privateAttributes.rawValue]).to(beNil())
}

[false, true].forEach { isCustomAttr in
(isCustomAttr ? LDUser.StubConstants.custom(includeSystemValues: true).keys.map { UserAttribute.forName($0) }
: LDUser.optionalAttributes).forEach { privateAttr in
[false, true].forEach { inConfig in
it("with \(privateAttr) private in \(inConfig ? "config" : "user")") {
if inConfig {
config.privateUserAttributes = [privateAttr]
} else {
user.privateAttributes = [privateAttr]
}

userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)

expect(userDictionary.redactedAttributes) == [privateAttr.name]

let includingDictionary = user.dictionaryValue(includePrivateAttributes: true, config: config)
if !isCustomAttr {
let userDictionaryWithoutRedacted = userDictionary.filter { $0.key != "privateAttrs" }
let includingDictionaryWithoutRedacted = includingDictionary.filter { $0.key != privateAttr.name && $0.key != "privateAttrs" }
expect(AnyComparer.isEqual(userDictionaryWithoutRedacted, to: includingDictionaryWithoutRedacted)) == true
} else {
let userDictionaryWithoutRedacted = userDictionary.filter { $0.key != "custom" && $0.key != "privateAttrs" }
let includingDictionaryWithoutRedacted = includingDictionary.filter { $0.key != "custom" && $0.key != "privateAttrs" }
expect(AnyComparer.isEqual(userDictionaryWithoutRedacted, to: includingDictionaryWithoutRedacted)) == true
let expectedCustom = (includingDictionary["custom"] as! [String: Any]).filter { $0.key != privateAttr.name }
expect(AnyComparer.isEqual(userDictionary["custom"], to: expectedCustom)) == true
}
}
}
}
}

context("with allUserAttributesPrivate") {
beforeEach {
config.allUserAttributesPrivate = true
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
it("creates expected dictionary") {
expect(userDictionary.count) == 3
// Required attributes
expect(userDictionary[LDUser.CodingKeys.key.rawValue] as? String) == user.key
expect(userDictionary[LDUser.CodingKeys.isAnonymous.rawValue] as? Bool) == user.isAnonymous

expect(Set(userDictionary.redactedAttributes!)) == Set(optionalNames + allCustomPrivitizable)
}
}

context("with no private attributes") {
let noPrivateAssertions = {
it("matches dictionary including private") {
expect(AnyComparer.isEqual(userDictionary, to: user.dictionaryValue(includePrivateAttributes: true, config: config))) == true
}
}
context("by setting private attributes to nil") {
beforeEach {
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
noPrivateAssertions()
}
context("by setting config private attributes to empty") {
beforeEach {
config.privateUserAttributes = []
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
noPrivateAssertions()
}
context("by setting user private attributes to empty") {
beforeEach {
user.privateAttributes = []
userDictionary = user.dictionaryValue(includePrivateAttributes: false, config: config)
}
noPrivateAssertions()
}
}
}
}

private func isEqualSpec() {
var user: LDUser!
var otherUser: LDUser!

describe("isEqual") {
context("when users are equal") {
it("returns true with all properties set") {
user = LDUser.stub()
otherUser = user
expect(user.isEqual(to: otherUser)) == true
}
it("returns true with no properties set") {
user = LDUser()
otherUser = user
expect(user.isEqual(to: otherUser)) == true
}
}
context("when users are not equal") {
let testFields: [(String, Bool, LDValue, (inout LDUser, LDValue?) -> Void)] =
[("key", false, "dummy", { u, v in u.key = v!.stringValue() }),
("secondary", true, "dummy", { u, v in u.secondary = v?.stringValue() }),
("name", true, "dummy", { u, v in u.name = v?.stringValue() }),
("firstName", true, "dummy", { u, v in u.firstName = v?.stringValue() }),
("lastName", true, "dummy", { u, v in u.lastName = v?.stringValue() }),
("country", true, "dummy", { u, v in u.country = v?.stringValue() }),
("ipAddress", true, "dummy", { u, v in u.ipAddress = v?.stringValue() }),
("email address", true, "dummy", { u, v in u.email = v?.stringValue() }),
("avatar", true, "dummy", { u, v in u.avatar = v?.stringValue() }),
("custom", false, ["dummy": true], { u, v in u.custom = (v!.toAny() as! [String: Any]).mapValues { LDValue.fromAny($0) } }),
("isAnonymous", false, true, { u, v in u.isAnonymous = v!.booleanValue() }),
("privateAttributes", false, "dummy", { u, v in u.privateAttributes = [UserAttribute.forName(v!.stringValue())] })]
testFields.forEach { name, isOptional, otherVal, setter in
context("\(name) differs") {
beforeEach {
user = LDUser.stub()
otherUser = user
}
context("and both exist") {
it("returns false") {
setter(&otherUser, otherVal)
expect(user.isEqual(to: otherUser)) == false
expect(otherUser.isEqual(to: user)) == false
}
}
if isOptional {
context("self \(name) nil") {
it("returns false") {
setter(&user, nil)
expect(user.isEqual(to: otherUser)) == false
}
}
context("other \(name) nil") {
it("returns false") {
setter(&otherUser, nil)
expect(user.isEqual(to: otherUser)) == false
}
}
}
}
}
}
}
}
}

extension LDUser {
public func dictionaryValueWithAllAttributes() -> [String: Any] {
var dictionary = dictionaryValue(includePrivateAttributes: true, config: LDConfig.stub)
dictionary[CodingKeys.privateAttributes.rawValue] = privateAttributes
return dictionary
}
}

extension Dictionary where Key == String, Value == Any {
fileprivate var redactedAttributes: [String]? {
self[LDUser.CodingKeys.privateAttributes.rawValue] as? [String]
}
fileprivate func customDictionary() -> [String: Any]? {
self[LDUser.CodingKeys.custom.rawValue] as? [String: Any]
}
}
Loading

0 comments on commit 85d2c27

Please sign in to comment.