| 1 | 
           efrain | 
           1 | 
           /*
  | 
        
        
            | 
            | 
           2 | 
            * Copyright 2018 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 <TargetConditionals.h>
  | 
        
        
            | 
            | 
           18 | 
           #if TARGET_OS_IOS || TARGET_OS_TV
  | 
        
        
            | 
            | 
           19 | 
              | 
        
        
            | 
            | 
           20 | 
           #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
  | 
        
        
            | 
            | 
           21 | 
              | 
        
        
            | 
            | 
           22 | 
           #import "FirebaseInAppMessaging/Sources/Analytics/FIRIAMClearcutLogStorage.h"
  | 
        
        
            | 
            | 
           23 | 
           #import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
  | 
        
        
            | 
            | 
           24 | 
           #import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutLogger.h"
  | 
        
        
            | 
            | 
           25 | 
           #import "FirebaseInAppMessaging/Sources/Private/Analytics/FIRIAMClearcutUploader.h"
  | 
        
        
            | 
            | 
           26 | 
              | 
        
        
            | 
            | 
           27 | 
           @interface FIRIAMClearcutLogger ()
  | 
        
        
            | 
            | 
           28 | 
              | 
        
        
            | 
            | 
           29 | 
           // these two writable for assisting unit testing need
  | 
        
        
            | 
            | 
           30 | 
           @property(readwrite, nonatomic) FIRIAMClearcutHttpRequestSender *requestSender;
  | 
        
        
            | 
            | 
           31 | 
           @property(readwrite, nonatomic) id<FIRIAMTimeFetcher> timeFetcher;
  | 
        
        
            | 
            | 
           32 | 
              | 
        
        
            | 
            | 
           33 | 
           @property(readonly, nonatomic) FIRIAMClientInfoFetcher *clientInfoFetcher;
  | 
        
        
            | 
            | 
           34 | 
           @property(readonly, nonatomic) FIRIAMClearcutUploader *ctUploader;
  | 
        
        
            | 
            | 
           35 | 
              | 
        
        
            | 
            | 
           36 | 
           @property(readonly, copy, nonatomic) NSString *fbProjectNumber;
  | 
        
        
            | 
            | 
           37 | 
           @property(readonly, copy, nonatomic) NSString *fbAppId;
  | 
        
        
            | 
            | 
           38 | 
              | 
        
        
            | 
            | 
           39 | 
           @end
  | 
        
        
            | 
            | 
           40 | 
              | 
        
        
            | 
            | 
           41 | 
           @implementation FIRIAMClearcutLogger {
  | 
        
        
            | 
            | 
           42 | 
             // Firebase installations ID.
  | 
        
        
            | 
            | 
           43 | 
             NSString *_FID;
  | 
        
        
            | 
            | 
           44 | 
           }
  | 
        
        
            | 
            | 
           45 | 
           - (instancetype)initWithFBProjectNumber:(NSString *)fbProjectNumber
  | 
        
        
            | 
            | 
           46 | 
                                           fbAppId:(NSString *)fbAppId
  | 
        
        
            | 
            | 
           47 | 
                                 clientInfoFetcher:(FIRIAMClientInfoFetcher *)clientInfoFetcher
  | 
        
        
            | 
            | 
           48 | 
                                  usingTimeFetcher:(id<FIRIAMTimeFetcher>)timeFetcher
  | 
        
        
            | 
            | 
           49 | 
                                     usingUploader:(FIRIAMClearcutUploader *)uploader {
  | 
        
        
            | 
            | 
           50 | 
             if (self = [super init]) {
  | 
        
        
            | 
            | 
           51 | 
               _fbProjectNumber = fbProjectNumber;
  | 
        
        
            | 
            | 
           52 | 
               _fbAppId = fbAppId;
  | 
        
        
            | 
            | 
           53 | 
               _clientInfoFetcher = clientInfoFetcher;
  | 
        
        
            | 
            | 
           54 | 
               _timeFetcher = timeFetcher;
  | 
        
        
            | 
            | 
           55 | 
               _ctUploader = uploader;
  | 
        
        
            | 
            | 
           56 | 
             }
  | 
        
        
            | 
            | 
           57 | 
             return self;
  | 
        
        
            | 
            | 
           58 | 
           }
  | 
        
        
            | 
            | 
           59 | 
              | 
        
        
            | 
            | 
           60 | 
           + (void)updateSourceExtensionDictWithAnalyticsEventEnumType:(FIRIAMAnalyticsLogEventType)eventType
  | 
        
        
            | 
            | 
           61 | 
                                                               forDict:(NSMutableDictionary *)dict {
  | 
        
        
            | 
            | 
           62 | 
             switch (eventType) {
  | 
        
        
            | 
            | 
           63 | 
               case FIRIAMAnalyticsEventMessageImpression:
  | 
        
        
            | 
            | 
           64 | 
                 dict[@"event_type"] = @"IMPRESSION_EVENT_TYPE";
  | 
        
        
            | 
            | 
           65 | 
                 break;
  | 
        
        
            | 
            | 
           66 | 
               case FIRIAMAnalyticsEventActionURLFollow:
  | 
        
        
            | 
            | 
           67 | 
                 dict[@"event_type"] = @"CLICK_EVENT_TYPE";
  | 
        
        
            | 
            | 
           68 | 
                 break;
  | 
        
        
            | 
            | 
           69 | 
               case FIRIAMAnalyticsEventMessageDismissAuto:
  | 
        
        
            | 
            | 
           70 | 
                 dict[@"dismiss_type"] = @"AUTO";
  | 
        
        
            | 
            | 
           71 | 
                 break;
  | 
        
        
            | 
            | 
           72 | 
               case FIRIAMAnalyticsEventMessageDismissClick:
  | 
        
        
            | 
            | 
           73 | 
                 dict[@"dismiss_type"] = @"CLICK";
  | 
        
        
            | 
            | 
           74 | 
                 break;
  | 
        
        
            | 
            | 
           75 | 
               case FIRIAMAnalyticsEventMessageDismissSwipe:
  | 
        
        
            | 
            | 
           76 | 
                 dict[@"dismiss_type"] = @"SWIPE";
  | 
        
        
            | 
            | 
           77 | 
                 break;
  | 
        
        
            | 
            | 
           78 | 
               case FIRIAMAnalyticsEventImageFetchError:
  | 
        
        
            | 
            | 
           79 | 
                 dict[@"render_error_reason"] = @"IMAGE_FETCH_ERROR";
  | 
        
        
            | 
            | 
           80 | 
                 break;
  | 
        
        
            | 
            | 
           81 | 
               case FIRIAMAnalyticsEventImageFormatUnsupported:
  | 
        
        
            | 
            | 
           82 | 
                 dict[@"render_error_reason"] = @"IMAGE_UNSUPPORTED_FORMAT";
  | 
        
        
            | 
            | 
           83 | 
                 break;
  | 
        
        
            | 
            | 
           84 | 
               case FIRIAMAnalyticsEventFetchAPIClientError:
  | 
        
        
            | 
            | 
           85 | 
                 dict[@"fetch_error_reason"] = @"CLIENT_ERROR";
  | 
        
        
            | 
            | 
           86 | 
                 break;
  | 
        
        
            | 
            | 
           87 | 
               case FIRIAMAnalyticsEventFetchAPIServerError:
  | 
        
        
            | 
            | 
           88 | 
                 dict[@"fetch_error_reason"] = @"SERVER_ERROR";
  | 
        
        
            | 
            | 
           89 | 
                 break;
  | 
        
        
            | 
            | 
           90 | 
               case FIRIAMAnalyticsEventFetchAPINetworkError:
  | 
        
        
            | 
            | 
           91 | 
                 dict[@"fetch_error_reason"] = @"NETWORK_ERROR";
  | 
        
        
            | 
            | 
           92 | 
                 break;
  | 
        
        
            | 
            | 
           93 | 
               case FIRIAMAnalyticsEventTestMessageImpression:
  | 
        
        
            | 
            | 
           94 | 
                 dict[@"event_type"] = @"TEST_MESSAGE_IMPRESSION_EVENT_TYPE";
  | 
        
        
            | 
            | 
           95 | 
                 break;
  | 
        
        
            | 
            | 
           96 | 
               case FIRIAMAnalyticsEventTestMessageClick:
  | 
        
        
            | 
            | 
           97 | 
                 dict[@"event_type"] = @"TEST_MESSAGE_CLICK_EVENT_TYPE";
  | 
        
        
            | 
            | 
           98 | 
                 break;
  | 
        
        
            | 
            | 
           99 | 
               case FIRIAMAnalyticsLogEventUnknown:
  | 
        
        
            | 
            | 
           100 | 
                 break;
  | 
        
        
            | 
            | 
           101 | 
             }
  | 
        
        
            | 
            | 
           102 | 
           }
  | 
        
        
            | 
            | 
           103 | 
              | 
        
        
            | 
            | 
           104 | 
           // constructing CampaignAnalytics proto defined in campaign_analytics.proto and serialize it into
  | 
        
        
            | 
            | 
           105 | 
           // a string.
  | 
        
        
            | 
            | 
           106 | 
           // @return nil if error happened
  | 
        
        
            | 
            | 
           107 | 
           - (NSString *)constructSourceExtensionJsonForClearcutWithEventType:
  | 
        
        
            | 
            | 
           108 | 
                             (FIRIAMAnalyticsLogEventType)eventType
  | 
        
        
            | 
            | 
           109 | 
                                                                forCampaignID:(NSString *)campaignID
  | 
        
        
            | 
            | 
           110 | 
                                                                eventTimeInMs:(NSNumber *)eventTimeInMs
  | 
        
        
            | 
            | 
           111 | 
                                                               installationID:(NSString *)installationID {
  | 
        
        
            | 
            | 
           112 | 
             NSMutableDictionary *campaignAnalyticsDict = [[NSMutableDictionary alloc] init];
  | 
        
        
            | 
            | 
           113 | 
              | 
        
        
            | 
            | 
           114 | 
             campaignAnalyticsDict[@"project_number"] = self.fbProjectNumber;
  | 
        
        
            | 
            | 
           115 | 
             campaignAnalyticsDict[@"campaign_id"] = campaignID;
  | 
        
        
            | 
            | 
           116 | 
             campaignAnalyticsDict[@"client_app"] =
  | 
        
        
            | 
            | 
           117 | 
                 @{@"google_app_id" : self.fbAppId, @"firebase_instance_id" : installationID};
  | 
        
        
            | 
            | 
           118 | 
             campaignAnalyticsDict[@"client_timestamp_millis"] = eventTimeInMs;
  | 
        
        
            | 
            | 
           119 | 
             [self.class updateSourceExtensionDictWithAnalyticsEventEnumType:eventType
  | 
        
        
            | 
            | 
           120 | 
                                                                     forDict:campaignAnalyticsDict];
  | 
        
        
            | 
            | 
           121 | 
              | 
        
        
            | 
            | 
           122 | 
             campaignAnalyticsDict[@"fiam_sdk_version"] = [self.clientInfoFetcher getIAMSDKVersion];
  | 
        
        
            | 
            | 
           123 | 
              | 
        
        
            | 
            | 
           124 | 
             // turn campaignAnalyticsDict into a json string
  | 
        
        
            | 
            | 
           125 | 
             NSError *error;
  | 
        
        
            | 
            | 
           126 | 
             NSData *jsonData = [NSJSONSerialization
  | 
        
        
            | 
            | 
           127 | 
                 dataWithJSONObject:campaignAnalyticsDict  // Here you can pass array or dictionary
  | 
        
        
            | 
            | 
           128 | 
                            options:0  // Pass 0 if you don't care about the readability of the generated
  | 
        
        
            | 
            | 
           129 | 
                                       // string
  | 
        
        
            | 
            | 
           130 | 
                              error:&error];
  | 
        
        
            | 
            | 
           131 | 
              | 
        
        
            | 
            | 
           132 | 
             if (jsonData) {
  | 
        
        
            | 
            | 
           133 | 
               NSString *jsonString;
  | 
        
        
            | 
            | 
           134 | 
               jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
  | 
        
        
            | 
            | 
           135 | 
               FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM210006",
  | 
        
        
            | 
            | 
           136 | 
                           @"Source extension json string produced as %@", jsonString);
  | 
        
        
            | 
            | 
           137 | 
               return jsonString;
  | 
        
        
            | 
            | 
           138 | 
             } else {
  | 
        
        
            | 
            | 
           139 | 
               FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM210007",
  | 
        
        
            | 
            | 
           140 | 
                             @"Error in generating source extension json string: %@", error);
  | 
        
        
            | 
            | 
           141 | 
               return nil;
  | 
        
        
            | 
            | 
           142 | 
             }
  | 
        
        
            | 
            | 
           143 | 
           }
  | 
        
        
            | 
            | 
           144 | 
              | 
        
        
            | 
            | 
           145 | 
           - (void)logAnalyticsEventForType:(FIRIAMAnalyticsLogEventType)eventType
  | 
        
        
            | 
            | 
           146 | 
                              forCampaignID:(NSString *)campaignID
  | 
        
        
            | 
            | 
           147 | 
                          withEventTimeInMs:(nullable NSNumber *)eventTimeInMs
  | 
        
        
            | 
            | 
           148 | 
                                        FID:(NSString *)FID
  | 
        
        
            | 
            | 
           149 | 
                                 completion:(void (^)(BOOL success))completion {
  | 
        
        
            | 
            | 
           150 | 
             NSTimeInterval nowInMs = [self.timeFetcher currentTimestampInSeconds] * 1000;
  | 
        
        
            | 
            | 
           151 | 
             if (eventTimeInMs == nil) {
  | 
        
        
            | 
            | 
           152 | 
               eventTimeInMs = @((long)nowInMs);
  | 
        
        
            | 
            | 
           153 | 
             }
  | 
        
        
            | 
            | 
           154 | 
              | 
        
        
            | 
            | 
           155 | 
             if (!FID) {
  | 
        
        
            | 
            | 
           156 | 
               FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM210009",
  | 
        
        
            | 
            | 
           157 | 
                             @"Instance ID is nil, event %ld for campaign ID %@ will not be sent",
  | 
        
        
            | 
            | 
           158 | 
                             (long)eventType, campaignID);
  | 
        
        
            | 
            | 
           159 | 
               return;
  | 
        
        
            | 
            | 
           160 | 
             }
  | 
        
        
            | 
            | 
           161 | 
              | 
        
        
            | 
            | 
           162 | 
             NSString *sourceExtensionJsonString =
  | 
        
        
            | 
            | 
           163 | 
                 [self constructSourceExtensionJsonForClearcutWithEventType:eventType
  | 
        
        
            | 
            | 
           164 | 
                                                              forCampaignID:campaignID
  | 
        
        
            | 
            | 
           165 | 
                                                              eventTimeInMs:eventTimeInMs
  | 
        
        
            | 
            | 
           166 | 
                                                             installationID:FID];
  | 
        
        
            | 
            | 
           167 | 
              | 
        
        
            | 
            | 
           168 | 
             FIRIAMClearcutLogRecord *newRecord = [[FIRIAMClearcutLogRecord alloc]
  | 
        
        
            | 
            | 
           169 | 
                 initWithExtensionJsonString:sourceExtensionJsonString
  | 
        
        
            | 
            | 
           170 | 
                     eventTimestampInSeconds:eventTimeInMs.integerValue / 1000];
  | 
        
        
            | 
            | 
           171 | 
             [self.ctUploader addNewLogRecord:newRecord];
  | 
        
        
            | 
            | 
           172 | 
             FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM210003",
  | 
        
        
            | 
            | 
           173 | 
                         @"One more clearcut log record created and sent to uploader with source extension %@",
  | 
        
        
            | 
            | 
           174 | 
                         sourceExtensionJsonString);
  | 
        
        
            | 
            | 
           175 | 
             completion(YES);
  | 
        
        
            | 
            | 
           176 | 
           }
  | 
        
        
            | 
            | 
           177 | 
              | 
        
        
            | 
            | 
           178 | 
           - (void)logAnalyticsEventForType:(FIRIAMAnalyticsLogEventType)eventType
  | 
        
        
            | 
            | 
           179 | 
                              forCampaignID:(NSString *)campaignID
  | 
        
        
            | 
            | 
           180 | 
                           withCampaignName:(NSString *)campaignName
  | 
        
        
            | 
            | 
           181 | 
                              eventTimeInMs:(nullable NSNumber *)eventTimeInMs
  | 
        
        
            | 
            | 
           182 | 
                                 completion:(void (^)(BOOL success))completion {
  | 
        
        
            | 
            | 
           183 | 
             if (!_FID) {
  | 
        
        
            | 
            | 
           184 | 
               [self.clientInfoFetcher
  | 
        
        
            | 
            | 
           185 | 
                   fetchFirebaseInstallationDataWithProjectNumber:self.fbProjectNumber
  | 
        
        
            | 
            | 
           186 | 
                                                   withCompletion:^(NSString *_Nullable FID,
  | 
        
        
            | 
            | 
           187 | 
                                                                    NSString *_Nullable FISToken,
  | 
        
        
            | 
            | 
           188 | 
                                                                    NSError *_Nullable error) {
  | 
        
        
            | 
            | 
           189 | 
                                                     if (error) {
  | 
        
        
            | 
            | 
           190 | 
                                                       FIRLogWarning(
  | 
        
        
            | 
            | 
           191 | 
                                                           kFIRLoggerInAppMessaging, @"I-IAM210001",
  | 
        
        
            | 
            | 
           192 | 
                                                           @"Failed to get iid value for clearcut logging %@",
  | 
        
        
            | 
            | 
           193 | 
                                                           error);
  | 
        
        
            | 
            | 
           194 | 
                                                       completion(NO);
  | 
        
        
            | 
            | 
           195 | 
                                                     } else {
  | 
        
        
            | 
            | 
           196 | 
                                                       // persist FID through the whole life-cycle
  | 
        
        
            | 
            | 
           197 | 
                                                       self->_FID = FID;
  | 
        
        
            | 
            | 
           198 | 
                                                       [self logAnalyticsEventForType:eventType
  | 
        
        
            | 
            | 
           199 | 
                                                                        forCampaignID:campaignID
  | 
        
        
            | 
            | 
           200 | 
                                                                    withEventTimeInMs:eventTimeInMs
  | 
        
        
            | 
            | 
           201 | 
                                                                                  FID:FID
  | 
        
        
            | 
            | 
           202 | 
                                                                           completion:completion];
  | 
        
        
            | 
            | 
           203 | 
                                                     }
  | 
        
        
            | 
            | 
           204 | 
                                                   }];
  | 
        
        
            | 
            | 
           205 | 
             } else {
  | 
        
        
            | 
            | 
           206 | 
               FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM210004",
  | 
        
        
            | 
            | 
           207 | 
                           @"Using remembered iid for event logging");
  | 
        
        
            | 
            | 
           208 | 
               [self logAnalyticsEventForType:eventType
  | 
        
        
            | 
            | 
           209 | 
                                forCampaignID:campaignID
  | 
        
        
            | 
            | 
           210 | 
                            withEventTimeInMs:eventTimeInMs
  | 
        
        
            | 
            | 
           211 | 
                                          FID:_FID
  | 
        
        
            | 
            | 
           212 | 
                                   completion:completion];
  | 
        
        
            | 
            | 
           213 | 
             }
  | 
        
        
            | 
            | 
           214 | 
           }
  | 
        
        
            | 
            | 
           215 | 
           @end
  | 
        
        
            | 
            | 
           216 | 
              | 
        
        
            | 
            | 
           217 | 
           #endif  // TARGET_OS_IOS || TARGET_OS_TV
  |