Skip to content

Commit

Permalink
Merge pull request #506 from SourcePointUSA/DIA-2895_CCPA_consent_exp…
Browse files Browse the repository at this point in the history
…iration

DIA-2895 CCPA consent expiration
  • Loading branch information
andresilveirah committed Oct 30, 2023
2 parents 517d594 + 765c225 commit 24c76d6
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 43 deletions.
38 changes: 25 additions & 13 deletions ConsentViewController/Classes/Consents/SPCCPAConsent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ protocol CampaignConsent {
case status, rejectedVendors, rejectedCategories,
uuid, childPmId, consentStatus,
webConsentPayload, signedLspa, applies,
GPPData, uspstring
GPPData, uspstring, dateCreated, expirationDate
}

/// Indicates if the user has rejected `.All`, `.Some` or `.None` of the vendors **and** categories.
Expand Down Expand Up @@ -142,6 +142,8 @@ protocol CampaignConsent {

var signedLspa: Bool = false

var expirationDate: SPDate

var ccpaString: SPUSPString {
SPUSPString(from: self)
}
Expand Down Expand Up @@ -169,6 +171,7 @@ protocol CampaignConsent {
childPmId: String? = nil,
applies: Bool,
dateCreated: SPDate,
expirationDate: SPDate,
lastMessage: LastMessageData?,
consentStatus: ConsentStatus = ConsentStatus(),
webConsentPayload: SPWebConsentPayload? = nil,
Expand All @@ -185,6 +188,7 @@ protocol CampaignConsent {
self.GPPData = GPPData
self.applies = applies
self.dateCreated = dateCreated
self.expirationDate = expirationDate
self.lastMessage = lastMessage
}

Expand All @@ -200,6 +204,10 @@ protocol CampaignConsent {
webConsentPayload = try container.decodeIfPresent(SPWebConsentPayload.self, forKey: .webConsentPayload)
applies = try container.decodeIfPresent(Bool.self, forKey: .applies) ?? false
GPPData = try container.decodeIfPresent(SPJson.self, forKey: .GPPData) ?? SPJson()
expirationDate = try container.decode(SPDate.self, forKey: .expirationDate)
if let date = try container.decodeIfPresent(SPDate.self, forKey: .dateCreated) {
dateCreated = date
}
}

public static func empty() -> SPCCPAConsent { SPCCPAConsent(
Expand All @@ -208,7 +216,8 @@ protocol CampaignConsent {
rejectedCategories: [],
signedLspa: false,
applies: false,
dateCreated: SPDate.now(),
dateCreated: .now(),
expirationDate: .distantFuture(),
lastMessage: nil
)}

Expand All @@ -222,6 +231,7 @@ protocol CampaignConsent {
childPmId: childPmId,
applies: applies,
dateCreated: dateCreated,
expirationDate: expirationDate,
lastMessage: lastMessage,
consentStatus: consentStatus,
webConsentPayload: webConsentPayload,
Expand All @@ -243,16 +253,18 @@ protocol CampaignConsent {

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.status, forKey: .status)
try container.encode(self.rejectedVendors, forKey: .rejectedVendors)
try container.encode(self.rejectedCategories, forKey: .rejectedCategories)
try container.encodeIfPresent(self.uuid, forKey: .uuid)
try container.encodeIfPresent(self.childPmId, forKey: .childPmId)
try container.encode(self.consentStatus, forKey: .consentStatus)
try container.encodeIfPresent(self.webConsentPayload, forKey: .webConsentPayload)
try container.encode(self.signedLspa, forKey: .signedLspa)
try container.encode(self.applies, forKey: .applies)
try container.encode(self.GPPData, forKey: .GPPData)
try container.encode(self.uspstring, forKey: .uspstring)
try container.encode(status, forKey: .status)
try container.encode(rejectedVendors, forKey: .rejectedVendors)
try container.encode(rejectedCategories, forKey: .rejectedCategories)
try container.encodeIfPresent(uuid, forKey: .uuid)
try container.encodeIfPresent(childPmId, forKey: .childPmId)
try container.encode(consentStatus, forKey: .consentStatus)
try container.encodeIfPresent(webConsentPayload, forKey: .webConsentPayload)
try container.encode(signedLspa, forKey: .signedLspa)
try container.encode(applies, forKey: .applies)
try container.encode(GPPData, forKey: .GPPData)
try container.encode(uspstring, forKey: .uspstring)
try container.encode(dateCreated, forKey: .dateCreated)
try container.encode(expirationDate, forKey: .expirationDate)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
struct ChoiceAllResponse: Decodable {
struct CCPA {
let consentedAll: Bool
let dateCreated: SPDate
let dateCreated, expirationDate: SPDate
let rejectedAll: Bool
let status: CCPAConsentStatus
let uspstring: String
Expand Down Expand Up @@ -58,7 +58,7 @@ struct ChoiceAllMetaDataParam: QueryParamEncodable {

extension ChoiceAllResponse.CCPA: Decodable {
enum CodingKeys: String, CodingKey {
case dateCreated, consentedAll, rejectedAll,
case dateCreated, expirationDate, consentedAll, rejectedAll,
status, rejectedVendors, rejectedCategories,
gpcEnabled, webConsentPayload, GPPData
case uspstring = "uspString"
Expand All @@ -69,6 +69,7 @@ extension ChoiceAllResponse.CCPA: Decodable {
try self.init(
consentedAll: container.decode(Bool.self, forKey: .consentedAll),
dateCreated: container.decode(SPDate.self, forKey: .dateCreated),
expirationDate: container.decode(SPDate.self, forKey: .expirationDate),
rejectedAll: container.decode(Bool.self, forKey: .rejectedAll),
status: container.decode(CCPAConsentStatus.self, forKey: .status),
uspstring: container.decode(String.self, forKey: .uspstring),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct ConsentStatusResponse: Decodable, Equatable {

struct CCPA: Decodable, Equatable {
let rejectedAll: Bool
let dateCreated: SPDate
let dateCreated, expirationDate: SPDate
let uuid, uspstring: String
let rejectedCategories, rejectedVendors: [String]
let status: CCPAConsentStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ class SourcepointClientCoordinator: SPClientCoordinator {
gdprExpirationDate < Date() {
localState.gdpr = .empty()
}
if let ccpaExpirationDate = localState.ccpa?.expirationDate.date,
ccpaExpirationDate < Date() {
localState.ccpa = .empty()
}

return localState
}
Expand Down Expand Up @@ -440,6 +444,7 @@ class SourcepointClientCoordinator: SPClientCoordinator {
if let ccpa = response.consentStatusData.ccpa {
state.ccpa?.uuid = ccpa.uuid
state.ccpa?.dateCreated = ccpa.dateCreated
state.ccpa?.expirationDate = ccpa.expirationDate
state.ccpa?.status = ccpa.status
state.ccpa?.rejectedVendors = ccpa.rejectedVendors
state.ccpa?.rejectedCategories = ccpa.rejectedCategories
Expand Down Expand Up @@ -519,6 +524,7 @@ class SourcepointClientCoordinator: SPClientCoordinator {
switch $0.userConsent {
case .ccpa(let consents):
state.ccpa?.dateCreated = consents.dateCreated
state.ccpa?.expirationDate = consents.expirationDate
state.ccpa?.status = consents.status
state.ccpa?.rejectedVendors = consents.rejectedVendors
state.ccpa?.rejectedCategories = consents.rejectedCategories
Expand Down Expand Up @@ -632,6 +638,7 @@ class SourcepointClientCoordinator: SPClientCoordinator {
}
if let ccpa = response.ccpa, campaign == .ccpa {
state.ccpa?.dateCreated = ccpa.dateCreated
state.ccpa?.expirationDate = ccpa.expirationDate
state.ccpa?.status = ccpa.status
state.ccpa?.GPPData = ccpa.GPPData
}
Expand Down Expand Up @@ -745,20 +752,28 @@ class SourcepointClientCoordinator: SPClientCoordinator {
}
}

func handleCCPAPostChoice(
_ action: SPAction,
_ getResponse: ChoiceAllResponse?,
_ postResponse: CCPAChoiceResponse
) {
if action.type == .SaveAndExit {
state.ccpa?.GPPData = postResponse.GPPData
}
state.ccpa?.uuid = postResponse.uuid
state.ccpa?.dateCreated = postResponse.dateCreated
state.ccpa?.status = postResponse.status ?? getResponse?.ccpa?.status ?? .RejectedAll
state.ccpa?.rejectedVendors = postResponse.rejectedVendors ?? getResponse?.ccpa?.rejectedVendors ?? []
state.ccpa?.rejectedCategories = postResponse.rejectedCategories ?? getResponse?.ccpa?.rejectedCategories ?? []
state.ccpa?.webConsentPayload = postResponse.webConsentPayload ?? getResponse?.ccpa?.webConsentPayload
storage.spState = state
}

func reportCCPAAction(_ action: SPAction, _ getResponse: ChoiceAllResponse?, _ handler: @escaping ActionHandler) {
self.postChoice(action) { postResult in
switch postResult {
case .success(let response):
if action.type == .SaveAndExit {
self.state.ccpa?.GPPData = response.GPPData
}
self.state.ccpa?.uuid = response.uuid
self.state.ccpa?.dateCreated = response.dateCreated
self.state.ccpa?.status = response.status ?? getResponse?.ccpa?.status ?? .RejectedAll
self.state.ccpa?.rejectedVendors = response.rejectedVendors ?? getResponse?.ccpa?.rejectedVendors ?? []
self.state.ccpa?.rejectedCategories = response.rejectedCategories ?? getResponse?.ccpa?.rejectedCategories ?? []
self.state.ccpa?.webConsentPayload = response.webConsentPayload ?? getResponse?.ccpa?.webConsentPayload
self.storage.spState = self.state
self.handleCCPAPostChoice(action, getResponse, response)
handler(Result.success(self.userData))

case .failure(let error):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class CCPAConsentStatusSpec: QuickSpec {
rejectedCategories: [],
signedLspa: false,
applies: false,
dateCreated: SPDate.now(),
dateCreated: .now(),
expirationDate: .distantFuture(),
lastMessage: nil
)
expect(consent.uspstring) == "1---"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class SPCCPAConsentsSpec: QuickSpec {
expect(consents.applies).to(beFalse())
expect(consents.status).to(equal(.RejectedNone))
expect(consents.GPPData).to(equal(SPJson()))
expect(consents.expirationDate.date).to(equal(.distantFuture))
}
}

Expand All @@ -35,6 +36,7 @@ class SPCCPAConsentsSpec: QuickSpec {
"rejectedCategories": [],
"consentStatus": {},
"signedLspa": false,
"expirationDate": "2023-02-06T16:20:53.707Z",
"GPPData": {
"foo": "bar"
}
Expand All @@ -48,6 +50,10 @@ class SPCCPAConsentsSpec: QuickSpec {
expect(consent.rejectedCategories).to(beEmpty())
expect(consent.signedLspa).to(beFalse())
expect(consent.GPPData.dictionaryValue?["foo"] as? String).to(equal("bar"))
let date = Calendar.current.dateComponents([.day, .year, .month], from: consent.expirationDate.date)
expect(date.year).to(equal(2023))
expect(date.month).to(equal(02))
expect(date.day).to(equal(06))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -470,22 +470,31 @@ class SPClientCoordinatorSpec: QuickSpec {
coordinator.loadMessages(forAuthId: nil, pubData: nil) { firstLoadMessages in
let (firstMessages, _) = try! firstLoadMessages.get()
expect(firstMessages.filter { $0.type == .gdpr }).notTo(beEmpty())
expect(firstMessages.filter { $0.type == .ccpa }).notTo(beEmpty())

coordinator.reportAction(SPAction(type: .AcceptAll, campaignType: .gdpr)) { firstAction in
let firstActionUserData = try! firstAction.get()
expect(firstActionUserData.gdpr?.consents?.consentStatus.consentedToAny).to(beTrue())

coordinator.loadMessages(forAuthId: nil, pubData: nil) { secondLoadMessages in
let (secondMessages, _) = try! secondLoadMessages.get()
expect(secondMessages.filter { $0.type == .gdpr }).to(beEmpty())
coordinator.reportAction(SPAction(type: .AcceptAll, campaignType: .gdpr)) { _ in
coordinator.reportAction(SPAction(type: .AcceptAll, campaignType: .ccpa)) { _ in
expect(coordinator.state.gdpr?.consentStatus.consentedToAny).to(beTrue())
coordinator.state.gdpr?.expirationDate = SPDate(date: .yesterday)

coordinator.loadMessages(forAuthId: nil, pubData: nil) { thirdLoadMessages in
let (thirdMessages, _) = try! thirdLoadMessages.get()
expect(thirdMessages.filter { $0.type == .gdpr }).notTo(beEmpty())
expect(coordinator.state.gdpr?.consentStatus.consentedToAny).notTo(beTrue())
done()
expect(coordinator.state.ccpa?.status).to(equal(.ConsentedAll))

coordinator.loadMessages(forAuthId: nil, pubData: nil) { secondLoadMessages in
let (secondMessages, _) = try! secondLoadMessages.get()
expect(secondMessages.filter { $0.type == .gdpr }).to(beEmpty())
expect(secondMessages.filter { $0.type == .ccpa }).to(beEmpty())
expect(coordinator.state.gdpr?.consentStatus.consentedToAny).to(beTrue())
expect(coordinator.state.ccpa?.status).to(equal(.ConsentedAll))

coordinator.state.gdpr?.expirationDate = SPDate(date: .yesterday)
coordinator.state.ccpa?.expirationDate = SPDate(date: .yesterday)

coordinator.loadMessages(forAuthId: nil, pubData: nil) { thirdLoadMessages in
let (thirdMessages, _) = try! thirdLoadMessages.get()
expect(thirdMessages.filter { $0.type == .gdpr }).notTo(beEmpty())
expect(thirdMessages.filter { $0.type == .ccpa }).notTo(beEmpty())
expect(coordinator.state.gdpr?.consentStatus.consentedToAny).notTo(beTrue())
expect(coordinator.state.ccpa?.status).to(equal(.RejectedNone))
done()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class CampaignSpec: QuickSpec {
{
"type": "CCPA",
"dateCreated": "2023-02-06T16:20:53.707Z",
"expirationDate": "2123-02-06T16:20:53.707Z",
"consentedAll": false,
"rejectedCategories": [],
"rejectedVendors": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ class SPConsentManagerSpec: QuickSpec {
rejectedCategories: [],
signedLspa: false,
applies: false,
dateCreated: SPDate.now(),
dateCreated: .now(),
expirationDate: .distantFuture(),
lastMessage: nil
),
applies: true
Expand Down Expand Up @@ -148,7 +149,8 @@ class SPConsentManagerSpec: QuickSpec {
rejectedCategories: [],
signedLspa: false,
applies: true,
dateCreated: SPDate.now(),
dateCreated: .now(),
expirationDate: .distantFuture(),
lastMessage: nil,
GPPData: try! SPJson(["gpp key": "gpp value"])
), applies: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SPConsentSpec: QuickSpec {
{
"applies": true,
"consents": {
"expirationDate": "2124-10-27T16:59:00.092Z",
"status": "rejectedNone",
"rejectedVendors": [],
"rejectedCategories": [],
Expand Down

0 comments on commit 24c76d6

Please sign in to comment.