Skip to content

Commit

Permalink
Disable unapproved files (#60)
Browse files Browse the repository at this point in the history
* New constructor for pdd approved items + session disabled

* Disabling log handler + added tests

* Fix analyzer issues

* Add documentation for new constructor

* Clean up test

* Use datestamp for session id instead of 0

* Default constructor is now the `pddApproved` constructor

* Revert "Default constructor is now the `pddApproved` constructor"

This reverts commit 632b5cc.

* Simply move pddApproved constructor logic to default

* Fixing nits
  • Loading branch information
eliasyishak authored Mar 31, 2023
1 parent ce5025f commit 39a6eb6
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 14 deletions.
1 change: 1 addition & 0 deletions pkgs/unified_analytics/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Align supported tool list with PDD
- Exposing a new instance method that will need to be invoked when a client has successfully shown the consent message to the user `clientShowedMessage()`
- Adding and incrementing a tool's version will automatically use the current consent message version instead of incrementing by 1
- Default constructor has disabled the usage of local log file and session json file until revisions have landed to the privacy document

## 0.1.2

Expand Down
94 changes: 83 additions & 11 deletions pkgs/unified_analytics/lib/src/analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,68 @@ import 'user_property.dart';
import 'utils.dart';

abstract class Analytics {
/// The default factory constructor that will return an implementation
/// of the [Analytics] abstract class using the [LocalFileSystem]
// TODO: (eliasyishak) enable again once revision has landed;
// also remove all instances of [pddFlag]

// /// The default factory constructor that will return an implementation
// /// of the [Analytics] abstract class using the [LocalFileSystem]
// factory Analytics({
// required DashTool tool,
// required String dartVersion,
// String? flutterChannel,
// String? flutterVersion,
// }) {
// // Create the instance of the file system so clients don't need
// // resolve on their own
// const FileSystem fs = LocalFileSystem();

// // Resolve the OS using dart:io
// final DevicePlatform platform;
// if (io.Platform.operatingSystem == 'linux') {
// platform = DevicePlatform.linux;
// } else if (io.Platform.operatingSystem == 'macos') {
// platform = DevicePlatform.macos;
// } else {
// platform = DevicePlatform.windows;
// }

// // Create the instance of the GA Client which will create
// // an [http.Client] to send requests
// final GAClient gaClient = GAClient(
// measurementId: kGoogleAnalyticsMeasurementId,
// apiSecret: kGoogleAnalyticsApiSecret,
// );

// return AnalyticsImpl(
// tool: tool,
// homeDirectory: getHomeDirectory(fs),
// flutterChannel: flutterChannel,
// flutterVersion: flutterVersion,
// dartVersion: dartVersion,
// platform: platform,
// toolsMessageVersion: kToolsMessageVersion,
// fs: fs,
// gaClient: gaClient,
// );
// }

// TODO: (eliasyishak) remove this contructor once revision has landed

/// Prevents the unapproved files for logging and session handling
/// from being saved on to the developer's disk until privacy revision
/// has landed
factory Analytics({
required DashTool tool,
required String dartVersion,
String? flutterChannel,
String? flutterVersion,
FileSystem? fsOverride,
Directory? homeOverride,
DevicePlatform? platformOverride,
}) {
// Create the instance of the file system so clients don't need
// resolve on their own
const FileSystem fs = LocalFileSystem();
final FileSystem fs = fsOverride ?? LocalFileSystem();

// Resolve the OS using dart:io
final DevicePlatform platform;
Expand All @@ -46,21 +97,27 @@ abstract class Analytics {

// Create the instance of the GA Client which will create
// an [http.Client] to send requests
final GAClient gaClient = GAClient(
measurementId: kGoogleAnalyticsMeasurementId,
apiSecret: kGoogleAnalyticsApiSecret,
);
//
// When a [fsOverride] is passed in, we can assume to
// use the fake Google Analytics client
final GAClient gaClient = fsOverride != null
? FakeGAClient()
: GAClient(
measurementId: kGoogleAnalyticsMeasurementId,
apiSecret: kGoogleAnalyticsApiSecret,
);

return AnalyticsImpl(
tool: tool,
homeDirectory: getHomeDirectory(fs),
homeDirectory: homeOverride ?? getHomeDirectory(fs),
flutterChannel: flutterChannel,
flutterVersion: flutterVersion,
dartVersion: dartVersion,
platform: platform,
platform: platformOverride ?? platform,
toolsMessageVersion: kToolsMessageVersion,
fs: fs,
gaClient: gaClient,
pddFlag: true,
);
}

Expand Down Expand Up @@ -232,6 +289,7 @@ class AnalyticsImpl implements Analytics {
required this.toolsMessageVersion,
required this.fs,
required gaClient,
bool pddFlag = false,
}) : _gaClient = gaClient {
// This initializer class will let the instance know
// if it was the first run; if it is, nothing will be sent
Expand All @@ -241,6 +299,7 @@ class AnalyticsImpl implements Analytics {
tool: tool.label,
homeDirectory: homeDirectory,
toolsMessageVersion: toolsMessageVersion,
pddFlag: pddFlag,
);
initializer.run();
_showMessage = initializer.firstRun;
Expand Down Expand Up @@ -276,12 +335,21 @@ class AnalyticsImpl implements Analytics {
homeDirectory.path, kDartToolDirectoryName, kClientIdFileName))
.readAsStringSync();

// Create the session instance that will be responsible for managing
// all the sessions across every client tool using this pakage
final Session session;
if (pddFlag) {
session = NoopSession();
} else {
session = Session(homeDirectory: homeDirectory, fs: fs);
}

// Initialize the user property class that will be attached to
// each event that is sent to Google Analytics -- it will be responsible
// for getting the session id or rolling the session if the duration
// exceeds [kSessionDurationMinutes]
userProperty = UserProperty(
session: Session(homeDirectory: homeDirectory, fs: fs),
session: session,
flutterChannel: flutterChannel,
host: platform.label,
flutterVersion: flutterVersion,
Expand All @@ -290,7 +358,11 @@ class AnalyticsImpl implements Analytics {
);

// Initialize the log handler to persist events that are being sent
_logHandler = LogHandler(fs: fs, homeDirectory: homeDirectory);
if (pddFlag) {
_logHandler = NoopLogHandler();
} else {
_logHandler = LogHandler(fs: fs, homeDirectory: homeDirectory);
}
}

@override
Expand Down
6 changes: 4 additions & 2 deletions pkgs/unified_analytics/lib/src/initializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Initializer {
final Directory homeDirectory;
final int toolsMessageVersion;
bool firstRun = false;
final bool pddFlag;

/// Responsibe for the initialization of the files
/// necessary for analytics reporting
Expand All @@ -33,6 +34,7 @@ class Initializer {
required this.tool,
required this.homeDirectory,
required this.toolsMessageVersion,
required this.pddFlag,
});

/// Get a string representation of the current date in the following format
Expand Down Expand Up @@ -114,14 +116,14 @@ class Initializer {
// Begin initialization checks for the session file
final File sessionFile = fs.file(
p.join(homeDirectory.path, kDartToolDirectoryName, kSessionFileName));
if (!sessionFile.existsSync()) {
if (!sessionFile.existsSync() && !pddFlag) {
createSessionFile(sessionFile: sessionFile);
}

// Begin initialization checks for the log file to persist events locally
final File logFile = fs
.file(p.join(homeDirectory.path, kDartToolDirectoryName, kLogFileName));
if (!logFile.existsSync()) {
if (!logFile.existsSync() && !pddFlag) {
createLogFile(logFile: logFile);
}
}
Expand Down
17 changes: 17 additions & 0 deletions pkgs/unified_analytics/lib/src/log_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -313,3 +313,20 @@ class LogItem {
}
}
}

class NoopLogHandler implements LogHandler {
@override
FileSystem get fs => throw UnimplementedError();

@override
Directory get homeDirectory => throw UnimplementedError();

@override
File get logFile => throw UnimplementedError();

@override
LogFileStats? logFileStats() => null;

@override
void save({required Map<String, Object?> data}) {}
}
34 changes: 34 additions & 0 deletions pkgs/unified_analytics/lib/src/session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,37 @@ class Session {
}
}
}

class NoopSession implements Session {
@override
final int _lastPing = 0;

@override
final int _sessionId = DateTime.now().millisecondsSinceEpoch;

@override
void _refreshSessionData() {}

@override
File get _sessionFile => throw UnimplementedError();

@override
FileSystem get fs => throw UnimplementedError();

@override
int getSessionId() => _sessionId;

@override
Directory get homeDirectory => throw UnimplementedError();

@override
String toJson() {
throw UnimplementedError();
}

@override
set _lastPing(int lastPing) {}

@override
set _sessionId(int sessionId) {}
}
94 changes: 94 additions & 0 deletions pkgs/unified_analytics/test/pdd_approved_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io' as io;

import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:test/test.dart';

import 'package:unified_analytics/src/constants.dart';
import 'package:unified_analytics/unified_analytics.dart';

void main() {
late FileSystem fs;
late Directory home;
late Directory dartToolDirectory;
late Analytics initializationAnalytics;
late Analytics analytics;
late File clientIdFile;
late File sessionFile;
late File configFile;
late File logFile;

const String homeDirName = 'home';
const DashTool initialTool = DashTool.flutterTool;
const String dartVersion = 'dartVersion';

setUp(() {
// Setup the filesystem with the home directory
final FileSystemStyle fsStyle =
io.Platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix;
fs = MemoryFileSystem.test(style: fsStyle);
home = fs.directory(homeDirName);
dartToolDirectory = home.childDirectory(kDartToolDirectoryName);

// Create the initial analytics instance that will onboard the tool
initializationAnalytics = Analytics(
tool: initialTool,
dartVersion: dartVersion,
fsOverride: fs,
homeOverride: home,
);
initializationAnalytics.clientShowedMessage();

// The main analytics instance that will be used for the
// tests after having the tool onboarded
analytics = Analytics(
tool: initialTool,
dartVersion: dartVersion,
fsOverride: fs,
homeOverride: home,
);

// The 3 files that should have been generated
clientIdFile = home
.childDirectory(kDartToolDirectoryName)
.childFile(kClientIdFileName);
sessionFile =
home.childDirectory(kDartToolDirectoryName).childFile(kSessionFileName);
configFile =
home.childDirectory(kDartToolDirectoryName).childFile(kConfigFileName);
logFile =
home.childDirectory(kDartToolDirectoryName).childFile(kLogFileName);
});

test('Initializer properly sets up on first run', () {
expect(dartToolDirectory.existsSync(), true,
reason: 'The directory should have been created');
expect(clientIdFile.existsSync(), true,
reason: 'The $kClientIdFileName file was not found');
expect(sessionFile.existsSync(), false,
reason: 'The session handling has been disabled');
expect(configFile.existsSync(), true,
reason: 'The $kConfigFileName was not found');
expect(logFile.existsSync(), false,
reason: 'The log file has been disabled');
expect(dartToolDirectory.listSync().length, equals(2),
reason:
'There should only be 2 files in the $kDartToolDirectoryName directory');
expect(initializationAnalytics.shouldShowMessage, true,
reason: 'For the first run, the message should be shown');
expect(configFile.readAsLinesSync().length,
kConfigString.split('\n').length + 1,
reason: 'The number of lines should equal lines in constant value + 1 '
'for the initialized tool');
});

test('Sending events does not cause any errors', () async {
await expectLater(
() => analytics.sendEvent(eventName: DashEvent.hotReloadTime),
returnsNormally);
});
}
2 changes: 1 addition & 1 deletion pkgs/unified_analytics/test/unified_analytics_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ void main() {
reason:
'There should only be 4 files in the $kDartToolDirectoryName directory');
expect(initializationAnalytics.shouldShowMessage, true,
reason: 'For the first run, analytics should default to being enabled');
reason: 'For the first run, the message should be shown');
expect(configFile.readAsLinesSync().length,
kConfigString.split('\n').length + 1,
reason: 'The number of lines should equal lines in constant value + 1 '
Expand Down

0 comments on commit 39a6eb6

Please sign in to comment.