Skip to content

Implement the CCT proto using nanopb #2652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ typedef void (^GDTAssertionBlock)(void);
*
* @return A block that can be run instead of calling NSAssert, or nil.
*/
FOUNDATION_EXTERN GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void);
FOUNDATION_EXPORT GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void);

#if !defined(NS_BLOCK_ASSERTIONS)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ typedef NS_ENUM(NSInteger, GDTMessageCode) {
};

/** */
FOUNDATION_EXTERN NSString *_Nonnull GDTMessageCodeEnumToString(GDTMessageCode code);
FOUNDATION_EXPORT NSString *_Nonnull GDTMessageCodeEnumToString(GDTMessageCode code);

/** Logs the warningMessage string to the console at the warning level.
*
* @param warningMessageFormat The format string to log to the console.
*/
FOUNDATION_EXTERN void GDTLogWarning(GDTMessageCode messageCode,
FOUNDATION_EXPORT void GDTLogWarning(GDTMessageCode messageCode,
NSString *_Nonnull warningMessageFormat,
...) NS_FORMAT_FUNCTION(2, 3);

Expand Down
1 change: 1 addition & 0 deletions GoogleDataTransportCCTSupport.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Support library to provide event prioritization and uploading for the GoogleData
s.test_spec 'Tests-Unit' do |test_spec|
test_spec.requires_app_host = false
test_spec.source_files = 'GoogleDataTransportCCTSupport/Tests/Unit/**/*.{h,m}'
test_spec.resources = ['GoogleDataTransportCCTSupport/Tests/Data/**/*']
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright 2019 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 "GDTCCTNanopbHelpers.h"

#import <UIKit/UIKit.h>
#import <nanopb/pb.h>
#import <nanopb/pb_decode.h>
#import <nanopb/pb_encode.h>

#import "GDTCCTPrioritizer.h"

#pragma mark - General purpose encoders

pb_bytes_array_t *GDTCCTEncodeString(NSString *string) {
NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];
return GDTCCTEncodeData(stringBytes);
}

pb_bytes_array_t *GDTCCTEncodeData(NSData *data) {
pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
memcpy(pbBytes->bytes, [data bytes], data.length);
pbBytes->size = (pb_size_t)data.length;
return pbBytes;
}

#pragma mark - CCT object constructors

NSData *_Nullable GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogRequest) {
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
// Encode 1 time to determine the size.
if (!pb_encode(&sizestream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) {
NSCAssert(NO, @"Error in nanopb encoding for size: %s", PB_GET_ERROR(&sizestream));
}

// Encode a 2nd time to actually get the bytes from it.
size_t bufferSize = sizestream.bytes_written;
CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
if (!pb_encode(&ostream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) {
NSCAssert(NO, @"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream));
}
CFDataSetLength(dataRef, ostream.bytes_written);

return CFBridgingRelease(dataRef);
}

gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest(
NSDictionary<NSString *, NSSet<GDTStoredEvent *> *> *logMappingIDToLogSet) {
gdt_cct_BatchedLogRequest batchedLogRequest = gdt_cct_BatchedLogRequest_init_default;
NSUInteger numberOfLogRequests = logMappingIDToLogSet.count;
gdt_cct_LogRequest *logRequests = malloc(sizeof(gdt_cct_LogRequest) * numberOfLogRequests);

__block int i = 0;
[logMappingIDToLogSet enumerateKeysAndObjectsUsingBlock:^(
NSString *_Nonnull logMappingID,
NSSet<GDTStoredEvent *> *_Nonnull logSet, BOOL *_Nonnull stop) {
int32_t logSource = [logMappingID intValue];
gdt_cct_LogRequest logRequest = GDTCCTConstructLogRequest(logSource, logSet);
logRequests[i] = logRequest;
i++;
}];

batchedLogRequest.log_request = logRequests;
batchedLogRequest.log_request_count = (pb_size_t)numberOfLogRequests;
return batchedLogRequest;
}

gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource,
NSSet<GDTStoredEvent *> *_Nonnull logSet) {
NSCAssert(logSet.count, @"An empty event set can't be serialized to proto.");
gdt_cct_LogRequest logRequest = gdt_cct_LogRequest_init_default;
logRequest.log_source = logSource;
logRequest.has_log_source = 1;
logRequest.client_info = GDTCCTConstructClientInfo();
logRequest.has_client_info = 1;
logRequest.log_event = malloc(sizeof(gdt_cct_LogEvent) * logSet.count);
int i = 0;
for (GDTStoredEvent *log in logSet) {
gdt_cct_LogEvent logEvent = GDTCCTConstructLogEvent(log);
logRequest.log_event[i] = logEvent;
i++;
}
logRequest.log_event_count = (pb_size_t)logSet.count;

return logRequest;
}

gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTStoredEvent *event) {
gdt_cct_LogEvent logEvent = gdt_cct_LogEvent_init_default;
logEvent.event_time_ms = event.clockSnapshot.timeMillis;
logEvent.has_event_time_ms = 1;
logEvent.event_uptime_ms = event.clockSnapshot.uptime;
logEvent.has_event_uptime_ms = 1;
logEvent.timezone_offset_seconds = event.clockSnapshot.timezoneOffsetSeconds;
logEvent.has_timezone_offset_seconds = 1;
// TODO: Read network_connection_info from the custom params dict.

NSError *error;
NSData *extensionBytes = [NSData dataWithContentsOfURL:event.eventFileURL options:0 error:&error];
NSCAssert(error == nil, @"There was an error reading extension bytes from disk: %@", error);
logEvent.source_extension = GDTCCTEncodeData(extensionBytes); // read bytes from the file.
return logEvent;
}

gdt_cct_ClientInfo GDTCCTConstructClientInfo() {
gdt_cct_ClientInfo clientInfo = gdt_cct_ClientInfo_init_default;
clientInfo.client_type = gdt_cct_ClientInfo_ClientType_IOS_FIREBASE;
clientInfo.has_client_type = 1;
clientInfo.ios_client_info = GDTCCTConstructiOSClientInfo();
clientInfo.has_ios_client_info = 1;
return clientInfo;
}

gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo() {
gdt_cct_IosClientInfo iOSClientInfo = gdt_cct_IosClientInfo_init_default;
UIDevice *device = [UIDevice currentDevice];
NSBundle *bundle = [NSBundle mainBundle];
NSLocale *locale = [NSLocale currentLocale];
iOSClientInfo.os_full_version = GDTCCTEncodeString(device.systemVersion);
NSArray *versionComponents = [device.systemVersion componentsSeparatedByString:@"."];
iOSClientInfo.os_major_version = GDTCCTEncodeString(versionComponents[0]);
NSString *version = [bundle objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
if (version) {
iOSClientInfo.application_build = GDTCCTEncodeString(version);
}
iOSClientInfo.country = GDTCCTEncodeString([locale objectForKey:NSLocaleCountryCode]);
iOSClientInfo.model = GDTCCTEncodeString(device.model);
NSString *languageCode = bundle.preferredLocalizations.firstObject;
iOSClientInfo.language_code =
languageCode ? GDTCCTEncodeString(languageCode) : GDTCCTEncodeString(@"en");
iOSClientInfo.application_bundle_id = GDTCCTEncodeString(bundle.bundleIdentifier);
return iOSClientInfo;
}

#pragma mark - CCT Object decoders

gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error) {
gdt_cct_LogResponse response = gdt_cct_LogResponse_init_default;
pb_istream_t istream = pb_istream_from_buffer([data bytes], [data length]);
if (!pb_decode(&istream, gdt_cct_LogResponse_fields, &response)) {
NSCAssert(NO, @"Error in nanopb decoding for bytes: %s", PB_GET_ERROR(&istream));
*error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:nil];
response = (gdt_cct_LogResponse)gdt_cct_LogResponse_init_default;
}
return response;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2019 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 <Foundation/Foundation.h>

#import <GoogleDataTransport/GoogleDataTransport.h>

#import "cct.nanopb.h"

NS_ASSUME_NONNULL_BEGIN

#pragma mark - General purpose encoders

/** Converts an NSString* to a pb_bytes_array_t*.
*
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
*
* @param string The string to convert.
* @return A newly allocated array of bytes representing the UTF8 encoding of the string.
*/
pb_bytes_array_t *GDTCCTEncodeString(NSString *string);

/** Converts an NSData to a pb_bytes_array_t*.
*
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
*
* @param data The data to convert.
* @return A newly allocated array of bytes with [data bytes] copied into it.
*/
pb_bytes_array_t *GDTCCTEncodeData(NSData *data);

#pragma mark - CCT object constructors

/** Encodes a batched log request.
*
* @note Ensure that pb_release is called on the batchedLogRequest param.
*
* @param batchedLogRequest A pointer to the log batch to encode to bytes.
* @return An NSData object representing the bytes of the log request batch.
*/
FOUNDATION_EXPORT
NSData *GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogRequest);

/** Constructs a gdt_cct_BatchedLogRequest given sets of events segemented by mapping ID.
*
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
*
* @param logMappingIDToLogSet A map of mapping IDs to sets of events to convert into a batch.
* @return A newly created gdt_cct_BatchedLogRequest.
*/
FOUNDATION_EXPORT
gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest(
NSDictionary<NSString *, NSSet<GDTStoredEvent *> *> *logMappingIDToLogSet);

/** Constructs a log request given a log source and a set of events.
*
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
* @param logSource The CCT log source to put into the log request.
* @param logSet The set of events to send in this log request.
*/
FOUNDATION_EXPORT
gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, NSSet<GDTStoredEvent *> *logSet);

/** Constructs a gdt_cct_LogEvent given a GDTStoredEvent*.
*
* @param event The GDTStoredEvent to convert.
* @return The new gdt_cct_LogEvent object.
*/
FOUNDATION_EXPORT
gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTStoredEvent *event);

/** Constructs a gdt_cct_ClientInfo representing the client device.
*
* @return The new gdt_cct_ClientInfo object.
*/
FOUNDATION_EXPORT
gdt_cct_ClientInfo GDTCCTConstructClientInfo(void);

/** Constructs a gdt_cct_IosClientInfo representing the client device.
*
* @return The new gdt_cct_IosClientInfo object.
*/
FOUNDATION_EXPORT
gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo(void);

#pragma mark - CCT object decoders

/** Decodes a gdt_cct_LogResponse given proto bytes.
*
* @note malloc is called in this method. Ensure that pb_release is called on the return value.
*
* @param data The proto bytes of the gdt_cct_LogResponse.
* @param error An error that will be populated if something went wrong during decoding.
* @return A newly allocated gdt_cct_LogResponse from the data, if the bytes decoded properly.
*/
FOUNDATION_EXPORT
gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error);

NS_ASSUME_NONNULL_END
Loading