Skip to content

Commit 959e8b9

Browse files
authored
Implement the CCT proto using nanopb (#2652)
* Write scripts to help generate the CCT proto * Expand the event generator to generate consistent events * Add the CCT proto and generator options * Add the nanopb generated sources and nanopb helpers * Ignore the proto-related directories in GDTCCTSupport * Fix whitespace * Clean up generate_cct_protos.sh Use git instead of curl, rename zip to tar.gz, use readonly variables and proper variable expansion * Address review comments Check return of pb_decode correctly. Use more arbitrary numbers for test event data. Reimplement GDTCCTEncodeString more intelligently. Use better initial values for messages. * Fix memory leak in tests * Use FOUNDATION_EXTERN consistently * Fix test name and always initialize nanopb vars. * Change FOUNDATION_EXTERN to FOUNDATION_EXPORT, fix missing assert code * Reinit a corrupted response to the default * Populate an error if decoding the response fails.
1 parent a424ea8 commit 959e8b9

File tree

21 files changed

+1734
-11
lines changed

21 files changed

+1734
-11
lines changed

GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTAssert.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ typedef void (^GDTAssertionBlock)(void);
2525
*
2626
* @return A block that can be run instead of calling NSAssert, or nil.
2727
*/
28-
FOUNDATION_EXTERN GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void);
28+
FOUNDATION_EXPORT GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void);
2929

3030
#if !defined(NS_BLOCK_ASSERTIONS)
3131

GoogleDataTransport/GoogleDataTransport/DependencyWrappers/GDTConsoleLogger.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ typedef NS_ENUM(NSInteger, GDTMessageCode) {
5050
};
5151

5252
/** */
53-
FOUNDATION_EXTERN NSString *_Nonnull GDTMessageCodeEnumToString(GDTMessageCode code);
53+
FOUNDATION_EXPORT NSString *_Nonnull GDTMessageCodeEnumToString(GDTMessageCode code);
5454

5555
/** Logs the warningMessage string to the console at the warning level.
5656
*
5757
* @param warningMessageFormat The format string to log to the console.
5858
*/
59-
FOUNDATION_EXTERN void GDTLogWarning(GDTMessageCode messageCode,
59+
FOUNDATION_EXPORT void GDTLogWarning(GDTMessageCode messageCode,
6060
NSString *_Nonnull warningMessageFormat,
6161
...) NS_FORMAT_FUNCTION(2, 3);
6262

GoogleDataTransportCCTSupport.podspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Support library to provide event prioritization and uploading for the GoogleData
4545
s.test_spec 'Tests-Unit' do |test_spec|
4646
test_spec.requires_app_host = false
4747
test_spec.source_files = 'GoogleDataTransportCCTSupport/Tests/Unit/**/*.{h,m}'
48+
test_spec.resources = ['GoogleDataTransportCCTSupport/Tests/Data/**/*']
4849
end
4950

5051
end
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright 2019 Google
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 "GDTCCTNanopbHelpers.h"
18+
19+
#import <UIKit/UIKit.h>
20+
#import <nanopb/pb.h>
21+
#import <nanopb/pb_decode.h>
22+
#import <nanopb/pb_encode.h>
23+
24+
#import "GDTCCTPrioritizer.h"
25+
26+
#pragma mark - General purpose encoders
27+
28+
pb_bytes_array_t *GDTCCTEncodeString(NSString *string) {
29+
NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];
30+
return GDTCCTEncodeData(stringBytes);
31+
}
32+
33+
pb_bytes_array_t *GDTCCTEncodeData(NSData *data) {
34+
pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
35+
memcpy(pbBytes->bytes, [data bytes], data.length);
36+
pbBytes->size = (pb_size_t)data.length;
37+
return pbBytes;
38+
}
39+
40+
#pragma mark - CCT object constructors
41+
42+
NSData *_Nullable GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogRequest) {
43+
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
44+
// Encode 1 time to determine the size.
45+
if (!pb_encode(&sizestream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) {
46+
NSCAssert(NO, @"Error in nanopb encoding for size: %s", PB_GET_ERROR(&sizestream));
47+
}
48+
49+
// Encode a 2nd time to actually get the bytes from it.
50+
size_t bufferSize = sizestream.bytes_written;
51+
CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
52+
pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
53+
if (!pb_encode(&ostream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) {
54+
NSCAssert(NO, @"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream));
55+
}
56+
CFDataSetLength(dataRef, ostream.bytes_written);
57+
58+
return CFBridgingRelease(dataRef);
59+
}
60+
61+
gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest(
62+
NSDictionary<NSString *, NSSet<GDTStoredEvent *> *> *logMappingIDToLogSet) {
63+
gdt_cct_BatchedLogRequest batchedLogRequest = gdt_cct_BatchedLogRequest_init_default;
64+
NSUInteger numberOfLogRequests = logMappingIDToLogSet.count;
65+
gdt_cct_LogRequest *logRequests = malloc(sizeof(gdt_cct_LogRequest) * numberOfLogRequests);
66+
67+
__block int i = 0;
68+
[logMappingIDToLogSet enumerateKeysAndObjectsUsingBlock:^(
69+
NSString *_Nonnull logMappingID,
70+
NSSet<GDTStoredEvent *> *_Nonnull logSet, BOOL *_Nonnull stop) {
71+
int32_t logSource = [logMappingID intValue];
72+
gdt_cct_LogRequest logRequest = GDTCCTConstructLogRequest(logSource, logSet);
73+
logRequests[i] = logRequest;
74+
i++;
75+
}];
76+
77+
batchedLogRequest.log_request = logRequests;
78+
batchedLogRequest.log_request_count = (pb_size_t)numberOfLogRequests;
79+
return batchedLogRequest;
80+
}
81+
82+
gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource,
83+
NSSet<GDTStoredEvent *> *_Nonnull logSet) {
84+
NSCAssert(logSet.count, @"An empty event set can't be serialized to proto.");
85+
gdt_cct_LogRequest logRequest = gdt_cct_LogRequest_init_default;
86+
logRequest.log_source = logSource;
87+
logRequest.has_log_source = 1;
88+
logRequest.client_info = GDTCCTConstructClientInfo();
89+
logRequest.has_client_info = 1;
90+
logRequest.log_event = malloc(sizeof(gdt_cct_LogEvent) * logSet.count);
91+
int i = 0;
92+
for (GDTStoredEvent *log in logSet) {
93+
gdt_cct_LogEvent logEvent = GDTCCTConstructLogEvent(log);
94+
logRequest.log_event[i] = logEvent;
95+
i++;
96+
}
97+
logRequest.log_event_count = (pb_size_t)logSet.count;
98+
99+
return logRequest;
100+
}
101+
102+
gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTStoredEvent *event) {
103+
gdt_cct_LogEvent logEvent = gdt_cct_LogEvent_init_default;
104+
logEvent.event_time_ms = event.clockSnapshot.timeMillis;
105+
logEvent.has_event_time_ms = 1;
106+
logEvent.event_uptime_ms = event.clockSnapshot.uptime;
107+
logEvent.has_event_uptime_ms = 1;
108+
logEvent.timezone_offset_seconds = event.clockSnapshot.timezoneOffsetSeconds;
109+
logEvent.has_timezone_offset_seconds = 1;
110+
// TODO: Read network_connection_info from the custom params dict.
111+
112+
NSError *error;
113+
NSData *extensionBytes = [NSData dataWithContentsOfURL:event.eventFileURL options:0 error:&error];
114+
NSCAssert(error == nil, @"There was an error reading extension bytes from disk: %@", error);
115+
logEvent.source_extension = GDTCCTEncodeData(extensionBytes); // read bytes from the file.
116+
return logEvent;
117+
}
118+
119+
gdt_cct_ClientInfo GDTCCTConstructClientInfo() {
120+
gdt_cct_ClientInfo clientInfo = gdt_cct_ClientInfo_init_default;
121+
clientInfo.client_type = gdt_cct_ClientInfo_ClientType_IOS_FIREBASE;
122+
clientInfo.has_client_type = 1;
123+
clientInfo.ios_client_info = GDTCCTConstructiOSClientInfo();
124+
clientInfo.has_ios_client_info = 1;
125+
return clientInfo;
126+
}
127+
128+
gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo() {
129+
gdt_cct_IosClientInfo iOSClientInfo = gdt_cct_IosClientInfo_init_default;
130+
UIDevice *device = [UIDevice currentDevice];
131+
NSBundle *bundle = [NSBundle mainBundle];
132+
NSLocale *locale = [NSLocale currentLocale];
133+
iOSClientInfo.os_full_version = GDTCCTEncodeString(device.systemVersion);
134+
NSArray *versionComponents = [device.systemVersion componentsSeparatedByString:@"."];
135+
iOSClientInfo.os_major_version = GDTCCTEncodeString(versionComponents[0]);
136+
NSString *version = [bundle objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
137+
if (version) {
138+
iOSClientInfo.application_build = GDTCCTEncodeString(version);
139+
}
140+
iOSClientInfo.country = GDTCCTEncodeString([locale objectForKey:NSLocaleCountryCode]);
141+
iOSClientInfo.model = GDTCCTEncodeString(device.model);
142+
NSString *languageCode = bundle.preferredLocalizations.firstObject;
143+
iOSClientInfo.language_code =
144+
languageCode ? GDTCCTEncodeString(languageCode) : GDTCCTEncodeString(@"en");
145+
iOSClientInfo.application_bundle_id = GDTCCTEncodeString(bundle.bundleIdentifier);
146+
return iOSClientInfo;
147+
}
148+
149+
#pragma mark - CCT Object decoders
150+
151+
gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error) {
152+
gdt_cct_LogResponse response = gdt_cct_LogResponse_init_default;
153+
pb_istream_t istream = pb_istream_from_buffer([data bytes], [data length]);
154+
if (!pb_decode(&istream, gdt_cct_LogResponse_fields, &response)) {
155+
NSCAssert(NO, @"Error in nanopb decoding for bytes: %s", PB_GET_ERROR(&istream));
156+
*error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:nil];
157+
response = (gdt_cct_LogResponse)gdt_cct_LogResponse_init_default;
158+
}
159+
return response;
160+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2019 Google
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/GoogleDataTransport.h>
20+
21+
#import "cct.nanopb.h"
22+
23+
NS_ASSUME_NONNULL_BEGIN
24+
25+
#pragma mark - General purpose encoders
26+
27+
/** Converts an NSString* to a pb_bytes_array_t*.
28+
*
29+
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
30+
*
31+
* @param string The string to convert.
32+
* @return A newly allocated array of bytes representing the UTF8 encoding of the string.
33+
*/
34+
pb_bytes_array_t *GDTCCTEncodeString(NSString *string);
35+
36+
/** Converts an NSData to a pb_bytes_array_t*.
37+
*
38+
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
39+
*
40+
* @param data The data to convert.
41+
* @return A newly allocated array of bytes with [data bytes] copied into it.
42+
*/
43+
pb_bytes_array_t *GDTCCTEncodeData(NSData *data);
44+
45+
#pragma mark - CCT object constructors
46+
47+
/** Encodes a batched log request.
48+
*
49+
* @note Ensure that pb_release is called on the batchedLogRequest param.
50+
*
51+
* @param batchedLogRequest A pointer to the log batch to encode to bytes.
52+
* @return An NSData object representing the bytes of the log request batch.
53+
*/
54+
FOUNDATION_EXPORT
55+
NSData *GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogRequest);
56+
57+
/** Constructs a gdt_cct_BatchedLogRequest given sets of events segemented by mapping ID.
58+
*
59+
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
60+
*
61+
* @param logMappingIDToLogSet A map of mapping IDs to sets of events to convert into a batch.
62+
* @return A newly created gdt_cct_BatchedLogRequest.
63+
*/
64+
FOUNDATION_EXPORT
65+
gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest(
66+
NSDictionary<NSString *, NSSet<GDTStoredEvent *> *> *logMappingIDToLogSet);
67+
68+
/** Constructs a log request given a log source and a set of events.
69+
*
70+
* @note malloc is called in this method. Ensure that pb_release is called on this or the parent.
71+
* @param logSource The CCT log source to put into the log request.
72+
* @param logSet The set of events to send in this log request.
73+
*/
74+
FOUNDATION_EXPORT
75+
gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, NSSet<GDTStoredEvent *> *logSet);
76+
77+
/** Constructs a gdt_cct_LogEvent given a GDTStoredEvent*.
78+
*
79+
* @param event The GDTStoredEvent to convert.
80+
* @return The new gdt_cct_LogEvent object.
81+
*/
82+
FOUNDATION_EXPORT
83+
gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTStoredEvent *event);
84+
85+
/** Constructs a gdt_cct_ClientInfo representing the client device.
86+
*
87+
* @return The new gdt_cct_ClientInfo object.
88+
*/
89+
FOUNDATION_EXPORT
90+
gdt_cct_ClientInfo GDTCCTConstructClientInfo(void);
91+
92+
/** Constructs a gdt_cct_IosClientInfo representing the client device.
93+
*
94+
* @return The new gdt_cct_IosClientInfo object.
95+
*/
96+
FOUNDATION_EXPORT
97+
gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo(void);
98+
99+
#pragma mark - CCT object decoders
100+
101+
/** Decodes a gdt_cct_LogResponse given proto bytes.
102+
*
103+
* @note malloc is called in this method. Ensure that pb_release is called on the return value.
104+
*
105+
* @param data The proto bytes of the gdt_cct_LogResponse.
106+
* @param error An error that will be populated if something went wrong during decoding.
107+
* @return A newly allocated gdt_cct_LogResponse from the data, if the bytes decoded properly.
108+
*/
109+
FOUNDATION_EXPORT
110+
gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error);
111+
112+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)