diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h new file mode 100644 index 00000000000..107b4a8a4db --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** A struct to hold data pertaining to a snapshot in time. */ +typedef struct { + /** The current time in millis. */ + int64_t timeMillis; + + /** The device uptime in millis. */ + int64_t uptimeMillis; + + /** The timezone offset in millis. */ + int64_t timezoneOffsetMillis; +} GDLLogClockSnapshot; + +/** This class manages the device clock and produces snapshots of the current time. */ +@interface GDLLogClock : NSObject + +// TODO(mikehaney24): - (GDLLogClockSnapshot)snapshot; + +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m new file mode 100644 index 00000000000..0935d8ba841 --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m @@ -0,0 +1,21 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDLLogClock.h" + +@implementation GDLLogClock + +@end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h index def5422e038..64eee5411ca 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h @@ -16,6 +16,54 @@ #import +#import "GDLLogClock.h" +#import "GDLLogProto.h" + +NS_ASSUME_NONNULL_BEGIN + +/** The different possible log quality of service specifiers. High values indicate high priority. */ +typedef NS_ENUM(NSInteger, GDLLogQoS) { + /** The QoS tier wasn't set, and won't ever be sent. */ + GDLLogQoSUnknown = 0, + + /** This log is internal telemetry data that should not be sent on its own if possible. */ + GDLLogQoSTelemetry = 1, + + /** This log should be sent, but in a batch only roughly once per day. */ + GDLLogQoSDaily = 2, + + /** This log should be sent when requested by the uploader. */ + GDLLogQosDefault = 3, + + /** This log should be sent immediately along with any other data that can be batched. */ + GDLLogQoSFast = 4 +}; + @interface GDLLogEvent : NSObject +/** The log map identifier, to allow backends to map the extension property to a proto. */ +@property(readonly, nonatomic) NSString *logMapID; + +/** The log object itself, encapsulated in the transport of your choice, as long as it implements + * the GDLLogProto protocol. */ +@property(nonatomic) id extension; + +/** The quality of service tier this log belongs to. */ +@property(nonatomic) GDLLogQoS qosTier; + +/** The clock snapshot at the time of logging. */ +@property(nonatomic) GDLLogClockSnapshot clockSnapshot; + +// Please use the designated initializer. +- (instancetype)init NS_UNAVAILABLE; + +/** Initializes an instance using the given logMapID. + * + * @param logMapID The log map identifier. + * @return An instance of this class. + */ +- (instancetype)initWithLogMapID:(NSString *)logMapID NS_DESIGNATED_INITIALIZER; + @end + +NS_ASSUME_NONNULL_END diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m index 813af083efb..5d408e4c093 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m @@ -18,4 +18,13 @@ @implementation GDLLogEvent +- (instancetype)initWithLogMapID:(NSString *)logMapID { + NSAssert(logMapID.length > 0, @"Please give a valid log map ID"); + self = [super init]; + if (self) { + _logMapID = logMapID; + } + return self; +} + @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h new file mode 100644 index 00000000000..2f424bd0b9c --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +/** This protocol defines the common interface that log protos should implement regardless of the + * underlying transport technology (protobuf, nanopb, etc). + */ +@protocol GDLLogProto + +/** Returns the serialized proto bytes of the implementing log proto. + * + * @return the serialized proto bytes of the implementing log proto. + */ +- (NSData *)protoBytes; + +@end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h index c11dffe9f32..96c1abd63b9 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h @@ -16,7 +16,25 @@ #import +@class GDLLogEvent; + +@protocol GDLLogTransformer; + /** Manages the writing and log-time transforming of logs. */ @interface GDLLogWriter : NSObject +/** Instantiates or returns the log writer singleton. + * + * @return The singleton instance of the log writer. + */ ++ (instancetype)sharedInstance; + +/** Writes the result of applying the given transformers' -transform method on the given log. + * + * @param log The log to apply transformers on. + * @param logTransformers The list of transformers to apply. + */ +- (void)writeLog:(GDLLogEvent *)log + afterApplyingTransformers:(nullable NSArray> *)logTransformers; + @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m index 1f7453dbfce..208e1954fc8 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m @@ -15,7 +15,46 @@ */ #import "GDLLogWriter.h" +#import "GDLLogWriter_Private.h" -@implementation GDLLogWriter : NSObject +#import + +#import "GDLLogStorage.h" + +@implementation GDLLogWriter + +// This class doesn't have to be a singleton, but allocating an instance for every logger could be +// wasteful. ++ (instancetype)sharedInstance { + static GDLLogWriter *logWriter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + logWriter = [[self alloc] init]; + }); + return logWriter; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _logWritingQueue = dispatch_queue_create("com.google.GDLLogWriter", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +- (void)writeLog:(GDLLogEvent *)log + afterApplyingTransformers:(NSArray> *)logTransformers { + NSAssert(log, @"You can't write a nil log"); + dispatch_async(_logWritingQueue, ^{ + GDLLogEvent *transformedLog = log; + for (id transformer in logTransformers) { + transformedLog = [transformer transform:transformedLog]; + if (!transformedLog) { + return; + } + } + // TODO(mikehaney24): [[GDLLogStorage sharedInstance] storeLog:transformedLog]; + }); +} @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m index 70f4e921d69..f5176ed54bf 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m @@ -60,7 +60,7 @@ - (void)logDataEvent:(GDLLogEvent *)logEvent { } - (GDLLogEvent *)newEvent { - return [[GDLLogEvent alloc] init]; + return [[GDLLogEvent alloc] initWithLogMapID:_logMapID]; } @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h b/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h new file mode 100644 index 00000000000..6d9061222b7 --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDLLogWriter.h" + +@interface GDLLogWriter () + +/** The queue on which all work will occur. */ +@property(nonatomic) dispatch_queue_t logWritingQueue; + +@end diff --git a/GoogleDataLogger/Tests/GDLLogEventTest.m b/GoogleDataLogger/Tests/GDLLogEventTest.m index 9d8acc4caa0..131dd61c515 100644 --- a/GoogleDataLogger/Tests/GDLLogEventTest.m +++ b/GoogleDataLogger/Tests/GDLLogEventTest.m @@ -25,7 +25,8 @@ @interface GDLLogEventTest : XCTestCase @implementation GDLLogEventTest - (void)testInit { - XCTAssertNotNil([[GDLLogEvent alloc] init]); + XCTAssertNotNil([[GDLLogEvent alloc] initWithLogMapID:@"1"]); + XCTAssertThrows([[GDLLogEvent alloc] initWithLogMapID:@""]); } @end diff --git a/GoogleDataLogger/Tests/GDLLogWriterTest.m b/GoogleDataLogger/Tests/GDLLogWriterTest.m index 914b7e62ec4..ee902f872d7 100644 --- a/GoogleDataLogger/Tests/GDLLogWriterTest.m +++ b/GoogleDataLogger/Tests/GDLLogWriterTest.m @@ -16,7 +16,23 @@ #import +#import + +#import "GDLLogEvent.h" #import "GDLLogWriter.h" +#import "GDLLogWriter_Private.h" + +@interface GDLLogWriterTestNilingTransformer : NSObject + +@end + +@implementation GDLLogWriterTestNilingTransformer + +- (GDLLogEvent *)transform:(GDLLogEvent *)logEvent { + return nil; +} + +@end @interface GDLLogWriterTest : XCTestCase @@ -29,4 +45,29 @@ - (void)testInit { XCTAssertNotNil([[GDLLogWriter alloc] init]); } +/** Tests the pointer equality of result of the -sharedInstance method. */ +- (void)testSharedInstance { + XCTAssertEqual([GDLLogWriter sharedInstance], [GDLLogWriter sharedInstance]); +} + +- (void)testWriteLogWithoutTransformers { + GDLLogWriter *writer = [GDLLogWriter sharedInstance]; + GDLLogEvent *log = [[GDLLogEvent alloc] initWithLogMapID:@"1"]; + XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:nil]); + dispatch_sync(writer.logWritingQueue, ^{ + // TODO(mikehaney24): Assert that storage contains the log. + }); +} + +- (void)testWriteLogWithTransformersThatNilTheLog { + GDLLogWriter *writer = [GDLLogWriter sharedInstance]; + GDLLogEvent *log = [[GDLLogEvent alloc] initWithLogMapID:@"2"]; + NSArray> *transformers = + @[ [[GDLLogWriterTestNilingTransformer alloc] init] ]; + XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:transformers]); + dispatch_sync(writer.logWritingQueue, ^{ + // TODO(mikehaney24): Assert that storage does not contain the log. + }); +} + @end