Skip to content

Commit

Permalink
Merge pull request #326 from DataDog/ncreated/RUMM-829-add-consent-aw…
Browse files Browse the repository at this point in the history
…are-data-writer

RUMM-829 Add consent-aware data writer
  • Loading branch information
ncreated authored Dec 7, 2020
2 parents 0594d47 + 92f6101 commit 461e91c
Show file tree
Hide file tree
Showing 30 changed files with 419 additions and 150 deletions.
32 changes: 32 additions & 0 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
61133C6D2423990D00786299 /* TestsDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C462423990D00786299 /* TestsDirectory.swift */; };
61133C6E2423990D00786299 /* DatadogExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133C472423990D00786299 /* DatadogExtensions.swift */; };
61133C702423993200786299 /* Datadog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61133B82242393DE00786299 /* Datadog.framework */; };
6114FDEC257659E90084E372 /* FeatureDirectoriesMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FDEB257659E90084E372 /* FeatureDirectoriesMock.swift */; };
6114FE0F257667D40084E372 /* ConsentAwareDataWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FE0E257667D40084E372 /* ConsentAwareDataWriter.swift */; };
6114FE1625766B310084E372 /* TrackingConsent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FE1525766B310084E372 /* TrackingConsent.swift */; };
6114FE23257671F00084E372 /* ConsentAwareDataWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FE22257671F00084E372 /* ConsentAwareDataWriterTests.swift */; };
61163C37252DDD60007DD5BF /* RUMMVSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61163C36252DDD60007DD5BF /* RUMMVSViewController.swift */; };
61163C3E252E0015007DD5BF /* RUMMVSModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61163C3D252E0015007DD5BF /* RUMMVSModalViewController.swift */; };
61163C4A252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61163C49252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift */; };
Expand Down Expand Up @@ -477,6 +481,10 @@
61133C452423990D00786299 /* SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = "<group>"; };
61133C462423990D00786299 /* TestsDirectory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestsDirectory.swift; sourceTree = "<group>"; };
61133C472423990D00786299 /* DatadogExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatadogExtensions.swift; sourceTree = "<group>"; };
6114FDEB257659E90084E372 /* FeatureDirectoriesMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureDirectoriesMock.swift; sourceTree = "<group>"; };
6114FE0E257667D40084E372 /* ConsentAwareDataWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentAwareDataWriter.swift; sourceTree = "<group>"; };
6114FE1525766B310084E372 /* TrackingConsent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingConsent.swift; sourceTree = "<group>"; };
6114FE22257671F00084E372 /* ConsentAwareDataWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentAwareDataWriterTests.swift; sourceTree = "<group>"; };
61163C36252DDD60007DD5BF /* RUMMVSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMMVSViewController.swift; sourceTree = "<group>"; };
61163C3D252E0015007DD5BF /* RUMMVSModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMMVSModalViewController.swift; sourceTree = "<group>"; };
61163C49252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMModalViewsScenarioTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -913,6 +921,7 @@
61133BA92423979B00786299 /* FilesOrchestrator.swift */,
61133BA72423979B00786299 /* FileWriter.swift */,
61133BAD2423979B00786299 /* FileReader.swift */,
6114FE0D257667AD0084E372 /* Privacy */,
61133BAA2423979B00786299 /* Files */,
);
path = Persistence;
Expand Down Expand Up @@ -1094,6 +1103,7 @@
61C3646F243B5C8300C4D4E6 /* ServerMock.swift */,
61D6FF7824E42A2900D0E375 /* DataUploadWorkerMock.swift */,
61F1A6192498A51700075390 /* CoreMocks.swift */,
6114FDEB257659E90084E372 /* FeatureDirectoriesMock.swift */,
61FB222C244A21ED00902D19 /* LoggingFeatureMocks.swift */,
61AD4E172451C7FF006E34EA /* TracingFeatureMocks.swift */,
61E5333024B75DFC003D6C4E /* RUMFeatureMocks.swift */,
Expand Down Expand Up @@ -1137,6 +1147,7 @@
61133C2A2423990D00786299 /* FilesOrchestratorTests.swift */,
61133C292423990D00786299 /* FileWriterTests.swift */,
61133C282423990D00786299 /* FileReaderTests.swift */,
6114FE21257671CB0084E372 /* Privacy */,
61133C2B2423990D00786299 /* Files */,
);
path = Persistence;
Expand Down Expand Up @@ -1235,6 +1246,23 @@
name = Frameworks;
sourceTree = "<group>";
};
6114FE0D257667AD0084E372 /* Privacy */ = {
isa = PBXGroup;
children = (
6114FE1525766B310084E372 /* TrackingConsent.swift */,
6114FE0E257667D40084E372 /* ConsentAwareDataWriter.swift */,
);
path = Privacy;
sourceTree = "<group>";
};
6114FE21257671CB0084E372 /* Privacy */ = {
isa = PBXGroup;
children = (
6114FE22257671F00084E372 /* ConsentAwareDataWriterTests.swift */,
);
path = Privacy;
sourceTree = "<group>";
};
61216277247D1F2100AC5D67 /* FeaturesIntegration */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2469,6 +2497,7 @@
61133BDD2423979B00786299 /* InternalLoggers.swift in Sources */,
6105D1A02508F1600040DD22 /* LoggingWithActiveSpanIntegration.swift in Sources */,
61133BDC2423979B00786299 /* Logger.swift in Sources */,
6114FE1625766B310084E372 /* TrackingConsent.swift in Sources */,
61F3C9712535AE9400E2F8C4 /* UIKitHierarchyInspector.swift in Sources */,
61133BD02423979B00786299 /* DateProvider.swift in Sources */,
6156CB8E24DDA1B5008CB2B2 /* RUMContextProvider.swift in Sources */,
Expand Down Expand Up @@ -2572,6 +2601,7 @@
61E5332C24B75C51003D6C4E /* RUMFeature.swift in Sources */,
61E5333D24B8791A003D6C4E /* RUMEventEncoder.swift in Sources */,
6156CB9D24E18600008CB2B2 /* TracingWithRUMIntegration.swift in Sources */,
6114FE0F257667D40084E372 /* ConsentAwareDataWriter.swift in Sources */,
61C5A88624509A0C00DA608C /* TracingUUIDGenerator.swift in Sources */,
61133BD92423979B00786299 /* DataUploadDelay.swift in Sources */,
61C5A88C24509A0C00DA608C /* HTTPHeadersWriter.swift in Sources */,
Expand All @@ -2589,6 +2619,7 @@
617CD0DD24CEDDD300B0B557 /* RUMUserActionScopeTests.swift in Sources */,
61C5A8A024509C1100DA608C /* Casting+Tracing.swift in Sources */,
61133C662423990D00786299 /* LogSanitizerTests.swift in Sources */,
6114FE23257671F00084E372 /* ConsentAwareDataWriterTests.swift in Sources */,
61410167251A661D00E3C2D9 /* UIApplicationSwizzlerTests.swift in Sources */,
61FF282824B8A31E000B3D9B /* RUMEventMatcher.swift in Sources */,
61C2C20924C0C75500C0321C /* RUMSessionScopeTests.swift in Sources */,
Expand All @@ -2611,6 +2642,7 @@
61C5A89D24509C1100DA608C /* DDSpanTests.swift in Sources */,
61B038BA2527257B00518F3C /* URLSessionAutoInstrumentationMocks.swift in Sources */,
61133C672423990D00786299 /* LogConsoleOutputTests.swift in Sources */,
6114FDEC257659E90084E372 /* FeatureDirectoriesMock.swift in Sources */,
61FB222D244A21ED00902D19 /* LoggingFeatureMocks.swift in Sources */,
617B954224BF4E7600E6F443 /* RUMMonitorConfigurationTests.swift in Sources */,
61F9CABA2513A7F5000A5E61 /* RUMSessionMatcher.swift in Sources */,
Expand Down
41 changes: 36 additions & 5 deletions Sources/Datadog/Core/Feature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

import Foundation

/// Lists different types of data directories used by the feature.
internal struct FeatureDirectories {
/// Data directory for storing unauthorized data collected without knowing the tracking consent value.
/// Due to the consent change, data in this directory may be either moved to `authorized` folder or entirely deleted.
let unauthorized: Directory
/// Data directory for storing authorized data collected when tracking consent is granted.
/// Consent change does not impact data already stored in this folder.
/// Data in this folder gets uploaded to the server.
let authorized: Directory
}

/// Container with dependencies common to all features (Logging, Tracing and RUM).
internal struct FeaturesCommonDependencies {
let performance: PerformancePreset
Expand All @@ -27,22 +38,42 @@ internal struct FeatureStorage {
init(
featureName: String,
dataFormat: DataFormat,
directory: Directory,
directories: FeatureDirectories,
commonDependencies: FeaturesCommonDependencies
) {
let readWriteQueue = DispatchQueue(
label: "com.datadoghq.ios-sdk-\(featureName)-read-write",
target: .global(qos: .utility)
)
let orchestrator = FilesOrchestrator(
directory: directory,
let authorizedFilesOrchestrator = FilesOrchestrator(
directory: directories.authorized,
performance: commonDependencies.performance,
dateProvider: commonDependencies.dateProvider
)
let unauthorizedFilesOrchestrator = FilesOrchestrator(
directory: directories.unauthorized,
performance: commonDependencies.performance,
dateProvider: commonDependencies.dateProvider
)

let consentAwareDataWriter = ConsentAwareDataWriter(
initialConsent: .granted, // TODO: RUMM-830 Inject `ConsentProvider`
queue: readWriteQueue,
unauthorizedFileWriter: FileWriter(
dataFormat: dataFormat,
orchestrator: unauthorizedFilesOrchestrator,
queue: readWriteQueue
),
authorizedFileWriter: FileWriter(
dataFormat: dataFormat,
orchestrator: authorizedFilesOrchestrator,
queue: readWriteQueue
)
)

self.init(
writer: FileWriter(dataFormat: dataFormat, orchestrator: orchestrator, queue: readWriteQueue),
reader: FileReader(dataFormat: dataFormat, orchestrator: orchestrator, queue: readWriteQueue)
writer: consentAwareDataWriter,
reader: FileReader(dataFormat: dataFormat, orchestrator: authorizedFilesOrchestrator, queue: readWriteQueue)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-2020 Datadog, Inc.
*/

import Foundation

/// File writer which writes data to different folders depending on the tracking consent value.
internal class ConsentAwareDataWriter: FileWriterType {
/// Queue used to synchronize reads and writes for the feature.
/// TODO: RUMM-777 will be used synchronize `activeFileWriter` swaps on consent change.
internal let queue: DispatchQueue
/// File writer writting unauthorized data when consent is `.pending`.
private let unauthorizedFileWriter: FileWriterType
/// File writer writting authorized data when consent is `.granted`.
private let authorizedFileWriter: FileWriterType

/// File writer for current consent value (including `nil` if consent is `.notGranted`).
private var activeFileWriter: FileWriterType?

init(
initialConsent: TrackingConsent,
queue: DispatchQueue,
unauthorizedFileWriter: FileWriterType,
authorizedFileWriter: FileWriterType
) {
self.queue = queue
self.unauthorizedFileWriter = unauthorizedFileWriter
self.authorizedFileWriter = authorizedFileWriter

switch initialConsent {
case .granted: self.activeFileWriter = authorizedFileWriter
case .notGranted: self.activeFileWriter = nil
case .pending: self.activeFileWriter = unauthorizedFileWriter
}
}

func write<T>(value: T) where T: Encodable {
activeFileWriter?.write(value: value)
}
}
21 changes: 21 additions & 0 deletions Sources/Datadog/Core/Persistence/Privacy/TrackingConsent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-2020 Datadog, Inc.
*/

import Foundation

/// Possible values for the Data Tracking Consent.
internal enum TrackingConsent {
/// The permission to persist and send data to the Datadog servers was granted.
/// Any previously stored pending data will be marked as ready for sent.
case granted
/// Any previously stored pending data will be deleted and all Logging, RUM and Tracing events will
/// be dropped from now on, without persisting it in any way.
case notGranted
/// All Logging, RUM and Tracing events will be persisted in an intermediate location and will be pending there
/// until `.granted` or `.notGranted` consent value is set.
/// Based on the next consent value, intermediate data will be send to Datadog or deleted.
case pending
}
6 changes: 3 additions & 3 deletions Sources/Datadog/Datadog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ public class Datadog {

if let loggingConfiguration = configuration.logging {
logging = LoggingFeature(
directory: try obtainLoggingFeatureDirectory(),
directories: try obtainLoggingFeatureDirectories(),
configuration: loggingConfiguration,
commonDependencies: commonDependencies
)
}

if let tracingConfiguration = configuration.tracing {
tracing = TracingFeature(
directory: try obtainTracingFeatureDirectory(),
directories: try obtainTracingFeatureDirectories(),
configuration: tracingConfiguration,
commonDependencies: commonDependencies,
loggingFeatureAdapter: logging.flatMap { LoggingForTracingAdapter(loggingFeature: $0) },
Expand All @@ -167,7 +167,7 @@ public class Datadog {

if let rumConfiguration = configuration.rum {
rum = RUMFeature(
directory: try obtainRUMFeatureDirectory(),
directories: try obtainRUMFeatureDirectories(),
configuration: rumConfiguration,
commonDependencies: commonDependencies
)
Expand Down
21 changes: 12 additions & 9 deletions Sources/Datadog/Logging/LoggingFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@

import Foundation

/// Obtains a subdirectory in `/Library/Caches` where log files are stored.
internal func obtainLoggingFeatureDirectory() throws -> Directory {
return try Directory(withSubdirectoryPath: "com.datadoghq.logs/v1")
/// Obtains subdirectories in `/Library/Caches` where logging data is stored.
internal func obtainLoggingFeatureDirectories() throws -> FeatureDirectories {
let version = "v1"
return FeatureDirectories(
unauthorized: try Directory(withSubdirectoryPath: "com.datadoghq.logs/intermediate-\(version)"),
authorized: try Directory(withSubdirectoryPath: "com.datadoghq.logs/\(version)")
)
}

/// Creates and owns componetns enabling logging feature.
Expand Down Expand Up @@ -44,18 +48,17 @@ internal final class LoggingFeature {

// MARK: - Initialization

static func createStorage(directory: Directory, commonDependencies: FeaturesCommonDependencies) -> FeatureStorage {
static func createStorage(directories: FeatureDirectories, commonDependencies: FeaturesCommonDependencies) -> FeatureStorage {
return FeatureStorage(
featureName: LoggingFeature.featureName,
dataFormat: LoggingFeature.dataFormat,
directory: directory,
directories: directories,
commonDependencies: commonDependencies
)
}

static func createUpload(
storage: FeatureStorage,
directory: Directory,
configuration: FeaturesConfiguration.Logging,
commonDependencies: FeaturesCommonDependencies
) -> FeatureUpload {
Expand Down Expand Up @@ -84,12 +87,12 @@ internal final class LoggingFeature {
}

convenience init(
directory: Directory,
directories: FeatureDirectories,
configuration: FeaturesConfiguration.Logging,
commonDependencies: FeaturesCommonDependencies
) {
let storage = LoggingFeature.createStorage(directory: directory, commonDependencies: commonDependencies)
let upload = LoggingFeature.createUpload(storage: storage, directory: directory, configuration: configuration, commonDependencies: commonDependencies)
let storage = LoggingFeature.createStorage(directories: directories, commonDependencies: commonDependencies)
let upload = LoggingFeature.createUpload(storage: storage, configuration: configuration, commonDependencies: commonDependencies)
self.init(
storage: storage,
upload: upload,
Expand Down
21 changes: 12 additions & 9 deletions Sources/Datadog/RUM/RUMFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@

import Foundation

/// Obtains a subdirectory in `/Library/Caches` for writting RUM events.
internal func obtainRUMFeatureDirectory() throws -> Directory {
return try Directory(withSubdirectoryPath: "com.datadoghq.rum/v1")
/// Obtains subdirectories in `/Library/Caches` where RUM data is stored.
internal func obtainRUMFeatureDirectories() throws -> FeatureDirectories {
let version = "v1"
return FeatureDirectories(
unauthorized: try Directory(withSubdirectoryPath: "com.datadoghq.rum/intermediate-\(version)"),
authorized: try Directory(withSubdirectoryPath: "com.datadoghq.rum/\(version)")
)
}

/// Creates and owns componetns enabling RUM feature.
Expand Down Expand Up @@ -45,18 +49,17 @@ internal final class RUMFeature {

// MARK: - Initialization

static func createStorage(directory: Directory, commonDependencies: FeaturesCommonDependencies) -> FeatureStorage {
static func createStorage(directories: FeatureDirectories, commonDependencies: FeaturesCommonDependencies) -> FeatureStorage {
return FeatureStorage(
featureName: RUMFeature.featureName,
dataFormat: RUMFeature.dataFormat,
directory: directory,
directories: directories,
commonDependencies: commonDependencies
)
}

static func createUpload(
storage: FeatureStorage,
directory: Directory,
configuration: FeaturesConfiguration.RUM,
commonDependencies: FeaturesCommonDependencies
) -> FeatureUpload {
Expand Down Expand Up @@ -93,12 +96,12 @@ internal final class RUMFeature {
}

convenience init(
directory: Directory,
directories: FeatureDirectories,
configuration: FeaturesConfiguration.RUM,
commonDependencies: FeaturesCommonDependencies
) {
let storage = RUMFeature.createStorage(directory: directory, commonDependencies: commonDependencies)
let upload = RUMFeature.createUpload(storage: storage, directory: directory, configuration: configuration, commonDependencies: commonDependencies)
let storage = RUMFeature.createStorage(directories: directories, commonDependencies: commonDependencies)
let upload = RUMFeature.createUpload(storage: storage, configuration: configuration, commonDependencies: commonDependencies)
self.init(
storage: storage,
upload: upload,
Expand Down
Loading

0 comments on commit 461e91c

Please sign in to comment.