Skip to content

Commit 9a0aed8

Browse files
authored
Implement helpers for storing and finding events (#5738)
* Implement helpers for storing and finding events * Fix missing imports * Add clarifying comments and remove unneeded nullability Also remove a test and fix the behavior of searchPathsWithEventSelector * Added missing docs and properly implemented +reset
1 parent 13b0c6b commit 9a0aed8

File tree

6 files changed

+310
-15
lines changed

6 files changed

+310
-15
lines changed

GoogleDataTransport/GDTCORLibrary/GDTCORFlatFileStorage.m

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,18 @@
2121
#import <GoogleDataTransport/GDTCOREvent.h>
2222
#import <GoogleDataTransport/GDTCORLifecycle.h>
2323
#import <GoogleDataTransport/GDTCORPrioritizer.h>
24+
#import <GoogleDataTransport/GDTCORStorageEventSelector.h>
2425

2526
#import "GDTCORLibrary/Private/GDTCOREvent_Private.h"
2627
#import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
2728
#import "GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
2829

30+
NSString *const gGDTCORFlatFileStorageEventDataPathKey = @"DataPath";
31+
32+
NSString *const gGDTCORFlatFileStorageMappingIDPathKey = @"MappingIDPath";
33+
34+
NSString *const gGDTCORFlatFileStorageQoSTierPathKey = @"QoSTierPath";
35+
2936
@implementation GDTCORFlatFileStorage
3037

3138
+ (void)load {
@@ -51,27 +58,118 @@ + (NSString *)archivePath {
5158
return archivePath;
5259
}
5360

54-
+ (NSString *)libraryDataPath {
55-
static NSString *libraryDataPath;
61+
+ (NSString *)baseEventStoragePath {
62+
static NSString *eventDataPath;
5663
static dispatch_once_t onceToken;
5764
dispatch_once(&onceToken, ^{
58-
libraryDataPath =
65+
eventDataPath =
5966
[GDTCORRootDirectory() URLByAppendingPathComponent:NSStringFromClass([self class])
6067
isDirectory:YES]
6168
.path;
62-
libraryDataPath = [libraryDataPath stringByAppendingPathComponent:@"gdt_library_data"];
63-
if (![[NSFileManager defaultManager] fileExistsAtPath:libraryDataPath isDirectory:NULL]) {
69+
eventDataPath = [eventDataPath stringByAppendingPathComponent:@"gdt_event_data"];
70+
if (![[NSFileManager defaultManager] fileExistsAtPath:eventDataPath isDirectory:NULL]) {
6471
NSError *error;
65-
[[NSFileManager defaultManager] createDirectoryAtPath:libraryDataPath
72+
[[NSFileManager defaultManager] createDirectoryAtPath:eventDataPath
6673
withIntermediateDirectories:YES
6774
attributes:0
6875
error:&error];
6976
GDTCORAssert(error == nil, @"Creating the library data path failed: %@", error);
7077
}
7178
});
79+
return eventDataPath;
80+
}
81+
82+
+ (NSString *)libraryDataStoragePath {
83+
static NSString *libraryDataPath;
84+
static dispatch_once_t onceToken;
85+
dispatch_once(&onceToken, ^{
86+
libraryDataPath =
87+
[GDTCORRootDirectory() URLByAppendingPathComponent:NSStringFromClass([self class])
88+
isDirectory:YES]
89+
.path;
90+
libraryDataPath = [libraryDataPath stringByAppendingPathComponent:@"gdt_library_data"];
91+
});
92+
if (![[NSFileManager defaultManager] fileExistsAtPath:libraryDataPath isDirectory:NULL]) {
93+
NSError *error;
94+
[[NSFileManager defaultManager] createDirectoryAtPath:libraryDataPath
95+
withIntermediateDirectories:YES
96+
attributes:0
97+
error:&error];
98+
GDTCORAssert(error == nil, @"Creating the library data path failed: %@", error);
99+
}
72100
return libraryDataPath;
73101
}
74102

103+
+ (NSDictionary<NSString *, NSString *> *)pathsForEvent:(GDTCOREvent *)event {
104+
NSString *dataPath =
105+
[NSString stringWithFormat:@"%@/%ld/%@", [GDTCORFlatFileStorage baseEventStoragePath],
106+
(long)event.target, event.eventID];
107+
NSString *mappingIDPath =
108+
[NSString stringWithFormat:@"%@/%ld/%@/%@", [GDTCORFlatFileStorage baseEventStoragePath],
109+
(long)event.target, event.mappingID, event.eventID];
110+
NSString *qosTierPath =
111+
[NSString stringWithFormat:@"%@/%ld/%ld/%@", [GDTCORFlatFileStorage baseEventStoragePath],
112+
(long)event.target, (long)event.qosTier, event.eventID];
113+
return @{
114+
gGDTCORFlatFileStorageEventDataPathKey : dataPath,
115+
gGDTCORFlatFileStorageMappingIDPathKey : mappingIDPath,
116+
gGDTCORFlatFileStorageQoSTierPathKey : qosTierPath
117+
};
118+
}
119+
120+
+ (NSString *)pathForTarget:(GDTCORTarget)target
121+
qosTier:(nullable NSNumber *)qosTier
122+
mappingID:(nullable NSString *)mappingID {
123+
NSString *baseEventPath = [GDTCORFlatFileStorage baseEventStoragePath];
124+
// If only a target was given, return the target path.
125+
if (qosTier == nil && mappingID == nil) {
126+
return [NSString stringWithFormat:@"%@/%ld", baseEventPath, (long)target];
127+
}
128+
129+
// If only a target and mappingID were given, return the mapping ID path.
130+
if (qosTier == nil) {
131+
return [NSString stringWithFormat:@"%@/%ld/%@", baseEventPath, (long)target, mappingID];
132+
}
133+
134+
// If only a target and qosTier were given, return the QoS tier path.
135+
if (mappingID == nil) {
136+
return [NSString stringWithFormat:@"%@/%ld/%@", baseEventPath, (long)target, qosTier];
137+
}
138+
139+
// If a target, mappingID, and qosTier were all given, return a single target/qosTier/mappingID
140+
// directory.
141+
return
142+
[NSString stringWithFormat:@"%@/%ld/%@/%@", baseEventPath, (long)target, qosTier, mappingID];
143+
}
144+
145+
+ (NSArray<NSString *> *)searchPathsWithEventSelector:(GDTCORStorageEventSelector *)eventSelector {
146+
NSMutableArray<NSString *> *searchPaths = [[NSMutableArray alloc] init];
147+
if (eventSelector.selectedQosTiers && eventSelector.selectedQosTiers.count > 0) {
148+
for (NSNumber *qosTier in eventSelector.selectedQosTiers) {
149+
NSString *searchPath = [self pathForTarget:eventSelector.selectedTarget
150+
qosTier:qosTier
151+
mappingID:eventSelector.selectedMappingID];
152+
BOOL isDirectory;
153+
if ([[NSFileManager defaultManager] fileExistsAtPath:searchPath isDirectory:&isDirectory]) {
154+
if (isDirectory) {
155+
[searchPaths addObject:searchPath];
156+
}
157+
}
158+
}
159+
} else {
160+
NSString *searchPath = [self pathForTarget:eventSelector.selectedTarget
161+
qosTier:nil
162+
mappingID:eventSelector.selectedMappingID];
163+
BOOL isDirectory;
164+
if ([[NSFileManager defaultManager] fileExistsAtPath:searchPath isDirectory:&isDirectory]) {
165+
if (isDirectory) {
166+
[searchPaths addObject:searchPath];
167+
}
168+
}
169+
}
170+
return searchPaths;
171+
}
172+
75173
+ (instancetype)sharedInstance {
76174
static GDTCORFlatFileStorage *sharedStorage;
77175
static dispatch_once_t onceToken;
@@ -214,7 +312,7 @@ - (void)libraryDataForKey:(nonnull NSString *)key
214312
onComplete:
215313
(nonnull void (^)(NSData *_Nullable, NSError *_Nullable error))onComplete {
216314
dispatch_async(_storageQueue, ^{
217-
NSString *dataPath = [[[self class] libraryDataPath] stringByAppendingPathComponent:key];
315+
NSString *dataPath = [[[self class] libraryDataStoragePath] stringByAppendingPathComponent:key];
218316
NSError *error;
219317
NSData *data = [NSData dataWithContentsOfFile:dataPath options:0 error:&error];
220318
if (onComplete) {
@@ -234,7 +332,7 @@ - (void)storeLibraryData:(NSData *)data
234332
}
235333
dispatch_async(_storageQueue, ^{
236334
NSError *error;
237-
NSString *dataPath = [[[self class] libraryDataPath] stringByAppendingPathComponent:key];
335+
NSString *dataPath = [[[self class] libraryDataStoragePath] stringByAppendingPathComponent:key];
238336
[data writeToFile:dataPath options:NSDataWritingAtomic error:&error];
239337
if (onComplete) {
240338
onComplete(error);
@@ -246,7 +344,7 @@ - (void)removeLibraryDataForKey:(nonnull NSString *)key
246344
onComplete:(nonnull void (^)(NSError *_Nullable error))onComplete {
247345
dispatch_async(_storageQueue, ^{
248346
NSError *error;
249-
NSString *dataPath = [[[self class] libraryDataPath] stringByAppendingPathComponent:key];
347+
NSString *dataPath = [[[self class] libraryDataStoragePath] stringByAppendingPathComponent:key];
250348
if ([[NSFileManager defaultManager] fileExistsAtPath:dataPath]) {
251349
[[NSFileManager defaultManager] removeItemAtPath:dataPath error:&error];
252350
if (onComplete) {
@@ -310,8 +408,8 @@ - (void)appWillForeground:(GDTCORApplication *)app {
310408

311409
- (void)appWillBackground:(GDTCORApplication *)app {
312410
dispatch_async(_storageQueue, ^{
313-
// Immediately request a background task to run until the end of the current queue of work, and
314-
// cancel it once the work is done.
411+
// Immediately request a background task to run until the end of the current queue of work,
412+
// and cancel it once the work is done.
315413
__block GDTCORBackgroundIdentifier bgID =
316414
[app beginBackgroundTaskWithName:@"GDTStorage"
317415
expirationHandler:^{
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "GDTCORLibrary/Public/GDTCORStorageEventSelector.h"
18+
19+
@implementation GDTCORStorageEventSelector
20+
21+
- (instancetype)initWithTarget:(GDTCORTarget)target
22+
eventIDEqualTo:(nullable NSNumber *)eventID
23+
mappingIDEqualTo:(nullable NSString *)mappingID
24+
qosTiers:(nullable NSArray<NSNumber *> *)qosTiers {
25+
self = [super init];
26+
if (self) {
27+
_selectedTarget = target;
28+
_selectedEventID = eventID;
29+
_selectedMappingID = mappingID;
30+
_selectedQosTiers = qosTiers;
31+
}
32+
return self;
33+
}
34+
35+
@end

GoogleDataTransport/GDTCORLibrary/Private/GDTCORFlatFileStorage.h

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,33 @@
1717
#import <Foundation/Foundation.h>
1818

1919
#import <GoogleDataTransport/GDTCORLifecycle.h>
20+
#import <GoogleDataTransport/GDTCORStorageEventSelector.h>
2021
#import <GoogleDataTransport/GDTCORStorageProtocol.h>
2122

2223
@class GDTCOREvent;
2324
@class GDTCORUploadCoordinator;
2425

2526
NS_ASSUME_NONNULL_BEGIN
2627

27-
/** Manages the storage of events. This class is thread-safe. */
28+
/** The key of the event data path if a path dictionary is returned. */
29+
FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageEventDataPathKey;
30+
31+
/** The key of the event's mapping ID path if a path dictionary is returned. */
32+
FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageMappingIDPathKey;
33+
34+
/** The key of the event's qos tier path if a path dictionary is returned. */
35+
FOUNDATION_EXPORT NSString *const gGDTCORFlatFileStorageQoSTierPathKey;
36+
37+
/** Manages the storage of events. This class is thread-safe.
38+
*
39+
* Event files will be stored as follows:
40+
* <app cache>/gdt_event_data/<target>/<eventID> as a normal file write
41+
* <app cache>/gdt_event_data/<target>/<qosTier>/<eventID> as a symbolic link
42+
* <app cache>/gdt_event_data/<target>/<mappingID>/<eventID> as a symbolic link
43+
*
44+
* Library data will be stored as follows:
45+
* <app cache>/gdt_library_data/<key of library data>
46+
*/
2847
@interface GDTCORFlatFileStorage
2948
: NSObject <NSSecureCoding, GDTCORStorageProtocol, GDTCORLifecycleProtocol>
3049

@@ -54,6 +73,44 @@ NS_ASSUME_NONNULL_BEGIN
5473
*/
5574
+ (NSString *)archivePath;
5675

76+
/** Returns the base directory under which all events will be stored.
77+
*
78+
* @return The base directory under which all events will be stored.
79+
*/
80+
+ (NSString *)baseEventStoragePath;
81+
82+
/** Returns the base directory under which all library data will be stored.
83+
*
84+
* @return The base directory under which all library data will be stored.
85+
*/
86+
+ (NSString *)libraryDataStoragePath;
87+
88+
/** Returns storage paths for the given event, though the paths may not exist.
89+
*
90+
* @note The keys of this dictionary are declared in this header.
91+
* @param event The event to map to storage paths.
92+
*/
93+
+ (NSDictionary<NSString *, NSString *> *)pathsForEvent:(GDTCOREvent *)event;
94+
95+
/** Returns a storage path to events for the given target, qosTier, and mapping ID. The path may not
96+
* exist.
97+
*
98+
* @param target The target, which is necessary to be given a path.
99+
* @param qosTier An optional parameter to get a more specific path.
100+
* @param mappingID An optional parameter to get a more specific path.
101+
* @return The path representing the combination of the given parameters.
102+
*/
103+
+ (NSString *)pathForTarget:(GDTCORTarget)target
104+
qosTier:(nullable NSNumber *)qosTier
105+
mappingID:(nullable NSString *)mappingID;
106+
107+
/** Returns a list of paths that will contain events for the given event selector.
108+
*
109+
* @param eventSelector The event selector to process.
110+
* @return A list of paths that exist and could contain events.
111+
*/
112+
+ (NSArray<NSString *> *)searchPathsWithEventSelector:(GDTCORStorageEventSelector *)eventSelector;
113+
57114
@end
58115

59116
NS_ASSUME_NONNULL_END
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#import <GoogleDataTransport/GDTCORTargets.h>
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
/** This class enables the finding of events by matching events with the properties of this class.
24+
*/
25+
@interface GDTCORStorageEventSelector : NSObject
26+
27+
/** The target to find events for. Required. */
28+
@property(readonly, nonatomic) GDTCORTarget selectedTarget;
29+
30+
/** Finds a specific event. */
31+
@property(nullable, readonly, nonatomic) NSNumber *selectedEventID;
32+
33+
/** Finds all events of a mappingID. */
34+
@property(nullable, readonly, nonatomic) NSString *selectedMappingID;
35+
36+
/** Finds all events matching the qosTiers in this list. */
37+
@property(nullable, readonly, nonatomic) NSArray<NSNumber *> *selectedQosTiers;
38+
39+
/** Instantiates an event selector.
40+
*
41+
* @param target The selected target.
42+
* @param eventID Optional param to find an event matching this eventID.
43+
* @param mappingID Optional param to find events matching this mappingID.
44+
* @param qosTiers Optional param to find events matching the given QoS tiers.
45+
* @return An immutable event selector instance.
46+
*/
47+
- (instancetype)initWithTarget:(GDTCORTarget)target
48+
eventIDEqualTo:(nullable NSNumber *)eventID
49+
mappingIDEqualTo:(nullable NSString *)mappingID
50+
qosTiers:(nullable NSArray<NSNumber *> *)qosTiers;
51+
@end
52+
53+
NS_ASSUME_NONNULL_END

GoogleDataTransport/GDTCORTests/Common/Categories/GDTCORFlatFileStorage+Testing.m

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ - (void)reset {
2525
dispatch_sync(self.storageQueue, ^{
2626
[self.targetToEventSet removeAllObjects];
2727
[self.storedEvents removeAllObjects];
28-
NSError *error;
29-
[[NSFileManager defaultManager] removeItemAtPath:[GDTCORFlatFileStorage archivePath]
30-
error:&error];
28+
[[NSFileManager defaultManager] removeItemAtPath:[GDTCORFlatFileStorage archivePath] error:nil];
29+
[[NSFileManager defaultManager] removeItemAtPath:[GDTCORFlatFileStorage baseEventStoragePath]
30+
error:nil];
31+
[[NSFileManager defaultManager] removeItemAtPath:[GDTCORFlatFileStorage libraryDataStoragePath]
32+
error:nil];
3133
});
3234
}
3335

0 commit comments

Comments
 (0)