From 02a09945be92d9334140a69f8b4b7b7f25d1643d Mon Sep 17 00:00:00 2001 From: qingzhuozhen <84748495+qingzhuozhen@users.noreply.github.com> Date: Tue, 7 Dec 2021 16:58:52 -0800 Subject: [PATCH] feat: add middleware support (#371) --- Amplitude.xcodeproj/project.pbxproj | 48 +++++++++++++ Framework/AmplitudeFramework.h | 2 + Sources/Amplitude/AMPMiddleware.m | 52 ++++++++++++++ Sources/Amplitude/AMPMiddlewareRunner.h | 36 ++++++++++ Sources/Amplitude/AMPMiddlewareRunner.m | 65 +++++++++++++++++ Sources/Amplitude/Amplitude.m | 29 ++++++++ Sources/Amplitude/Public/AMPMiddleware.h | 60 ++++++++++++++++ Sources/Amplitude/Public/Amplitude.h | 9 +++ Tests/AmplitudeTests.m | 38 +++++++++- Tests/MiddlewareRunnerTests.m | 90 ++++++++++++++++++++++++ 10 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 Sources/Amplitude/AMPMiddleware.m create mode 100644 Sources/Amplitude/AMPMiddlewareRunner.h create mode 100644 Sources/Amplitude/AMPMiddlewareRunner.m create mode 100644 Sources/Amplitude/Public/AMPMiddleware.h create mode 100644 Tests/MiddlewareRunnerTests.m diff --git a/Amplitude.xcodeproj/project.pbxproj b/Amplitude.xcodeproj/project.pbxproj index 9f5566e8..516fc81f 100644 --- a/Amplitude.xcodeproj/project.pbxproj +++ b/Amplitude.xcodeproj/project.pbxproj @@ -166,6 +166,25 @@ 3EF608E127211F8A00133703 /* ConfigManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608E027211F8A00133703 /* ConfigManagerTests.m */; }; 3EF608E227211F8A00133703 /* ConfigManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608E027211F8A00133703 /* ConfigManagerTests.m */; }; 3EF608E327211F8A00133703 /* ConfigManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608E027211F8A00133703 /* ConfigManagerTests.m */; }; + 3EF608E52724BFB700133703 /* AMPMiddleware.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E42724BFB700133703 /* AMPMiddleware.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3EF608E62724BFB700133703 /* AMPMiddleware.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E42724BFB700133703 /* AMPMiddleware.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3EF608E72724BFB700133703 /* AMPMiddleware.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E42724BFB700133703 /* AMPMiddleware.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3EF608E82724BFB700133703 /* AMPMiddleware.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E42724BFB700133703 /* AMPMiddleware.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3EF608EA2726244300133703 /* AMPMiddlewareRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E92726244300133703 /* AMPMiddlewareRunner.h */; }; + 3EF608EB2726244300133703 /* AMPMiddlewareRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E92726244300133703 /* AMPMiddlewareRunner.h */; }; + 3EF608EC2726244300133703 /* AMPMiddlewareRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E92726244300133703 /* AMPMiddlewareRunner.h */; }; + 3EF608ED2726244300133703 /* AMPMiddlewareRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EF608E92726244300133703 /* AMPMiddlewareRunner.h */; }; + 3EF608EF2726256800133703 /* AMPMiddlewareRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608EE2726256800133703 /* AMPMiddlewareRunner.m */; }; + 3EF608F02726256800133703 /* AMPMiddlewareRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608EE2726256800133703 /* AMPMiddlewareRunner.m */; }; + 3EF608F12726256800133703 /* AMPMiddlewareRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608EE2726256800133703 /* AMPMiddlewareRunner.m */; }; + 3EF608F22726256800133703 /* AMPMiddlewareRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608EE2726256800133703 /* AMPMiddlewareRunner.m */; }; + 3EF608FE272666E400133703 /* MiddlewareRunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608FD272666E300133703 /* MiddlewareRunnerTests.m */; }; + 3EF608FF272666E400133703 /* MiddlewareRunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608FD272666E300133703 /* MiddlewareRunnerTests.m */; }; + 3EF60900272666E400133703 /* MiddlewareRunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF608FD272666E300133703 /* MiddlewareRunnerTests.m */; }; + 3EF6090227267C9800133703 /* AMPMiddleware.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF6090127267C9800133703 /* AMPMiddleware.m */; }; + 3EF6090327267C9800133703 /* AMPMiddleware.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF6090127267C9800133703 /* AMPMiddleware.m */; }; + 3EF6090427267C9800133703 /* AMPMiddleware.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF6090127267C9800133703 /* AMPMiddleware.m */; }; + 3EF6090527267C9800133703 /* AMPMiddleware.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EF6090127267C9800133703 /* AMPMiddleware.m */; }; 4A9960EED620179960CF4513 /* libPods-shared-Amplitude_macOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 38CA8513C0B7F3E7F086F5EB /* libPods-shared-Amplitude_macOSTests.a */; }; 759E939025FBF3DC00BF7C3D /* AMPConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1279F8C525244D78003DCE07 /* AMPConfigManager.m */; }; 759E939125FBF3DC00BF7C3D /* AMPConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 1279F8E625244D78003DCE07 /* AMPConstants.m */; }; @@ -313,6 +332,11 @@ 3EF608D72720F64500133703 /* AMPServerZoneUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AMPServerZoneUtil.m; sourceTree = ""; }; 3EF608DC27211AC000133703 /* ServerZoneUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ServerZoneUtilTests.m; sourceTree = ""; }; 3EF608E027211F8A00133703 /* ConfigManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ConfigManagerTests.m; sourceTree = ""; }; + 3EF608E42724BFB700133703 /* AMPMiddleware.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AMPMiddleware.h; sourceTree = ""; }; + 3EF608E92726244300133703 /* AMPMiddlewareRunner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AMPMiddlewareRunner.h; sourceTree = ""; }; + 3EF608EE2726256800133703 /* AMPMiddlewareRunner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AMPMiddlewareRunner.m; sourceTree = ""; }; + 3EF608FD272666E300133703 /* MiddlewareRunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MiddlewareRunnerTests.m; sourceTree = ""; }; + 3EF6090127267C9800133703 /* AMPMiddleware.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AMPMiddleware.m; sourceTree = ""; }; 4B8740756F56A696F6D77342 /* Pods-shared-Amplitude_iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-Amplitude_iOSTests.debug.xcconfig"; path = "Target Support Files/Pods-shared-Amplitude_iOSTests/Pods-shared-Amplitude_iOSTests.debug.xcconfig"; sourceTree = ""; }; 4FD7B47E70B1D44AE90B896D /* libPods-shared-Amplitude_iOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-shared-Amplitude_iOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 69C2CB9AE6CCFC3C4C3D7E7E /* Pods-shared-Amplitude_macOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-Amplitude_macOSTests.debug.xcconfig"; path = "Target Support Files/Pods-shared-Amplitude_macOSTests/Pods-shared-Amplitude_macOSTests.debug.xcconfig"; sourceTree = ""; }; @@ -451,6 +475,9 @@ 1279FA6B2525949D003DCE07 /* ISPPinnedNSURLSessionDelegate.m */, 3EF608D22720F52400133703 /* AMPServerZoneUtil.h */, 3EF608D72720F64500133703 /* AMPServerZoneUtil.m */, + 3EF608E92726244300133703 /* AMPMiddlewareRunner.h */, + 3EF608EE2726256800133703 /* AMPMiddlewareRunner.m */, + 3EF6090127267C9800133703 /* AMPMiddleware.m */, ); path = Amplitude; sourceTree = ""; @@ -490,6 +517,7 @@ 12C9731F24108DFF00E9CDDB /* Info.plist */, 3EF608DC27211AC000133703 /* ServerZoneUtilTests.m */, 3EF608E027211F8A00133703 /* ConfigManagerTests.m */, + 3EF608FD272666E300133703 /* MiddlewareRunnerTests.m */, ); path = Tests; sourceTree = ""; @@ -510,6 +538,7 @@ 1279F8DA25244D78003DCE07 /* AMPRevenue.h */, 1279F8E925244D78003DCE07 /* AMPTrackingOptions.h */, 3EF608C82720E74D00133703 /* AMPServerZone.h */, + 3EF608E42724BFB700133703 /* AMPMiddleware.h */, ); path = Public; sourceTree = ""; @@ -577,6 +606,7 @@ buildActionMask = 2147483647; files = ( 759E942925FBFE9600BF7C3D /* AMPBackgroundNotifier.h in Headers */, + 3EF608E62724BFB700133703 /* AMPMiddleware.h in Headers */, 3E2411E826F9A40100793829 /* AMPPlan.h in Headers */, 1279FA7E2525949D003DCE07 /* ISPCertificatePinning.h in Headers */, 1213D89C24176E4700300E98 /* AmplitudeFramework.h in Headers */, @@ -585,6 +615,7 @@ 3EF608CA2720E74D00133703 /* AMPServerZone.h in Headers */, 1279F91825244E8E003DCE07 /* Amplitude.h in Headers */, 1279F91525244E8E003DCE07 /* AMPConstants.h in Headers */, + 3EF608EB2726244300133703 /* AMPMiddlewareRunner.h in Headers */, 1279F91925244E8E003DCE07 /* AMPTrackingOptions.h in Headers */, 1279FA752525949D003DCE07 /* ISPPinnedNSURLConnectionDelegate.h in Headers */, 1279F91425244E8E003DCE07 /* AmplitudePrivate.h in Headers */, @@ -605,6 +636,7 @@ buildActionMask = 2147483647; files = ( 759E942125FBFE9200BF7C3D /* AMPBackgroundNotifier.h in Headers */, + 3EF608E52724BFB700133703 /* AMPMiddleware.h in Headers */, 3E2411E726F9A40100793829 /* AMPPlan.h in Headers */, 1279FA7D2525949D003DCE07 /* ISPCertificatePinning.h in Headers */, 1213D89B24176E4700300E98 /* AmplitudeFramework.h in Headers */, @@ -613,6 +645,7 @@ 3EF608C92720E74D00133703 /* AMPServerZone.h in Headers */, 1279F8FA25244E8D003DCE07 /* Amplitude.h in Headers */, 1279F8F725244E8D003DCE07 /* AMPConstants.h in Headers */, + 3EF608EA2726244300133703 /* AMPMiddlewareRunner.h in Headers */, 1279F8FB25244E8D003DCE07 /* AMPTrackingOptions.h in Headers */, 1279FA742525949D003DCE07 /* ISPPinnedNSURLConnectionDelegate.h in Headers */, 1279F8F625244E8D003DCE07 /* AmplitudePrivate.h in Headers */, @@ -633,6 +666,7 @@ buildActionMask = 2147483647; files = ( 1279FA7F2525949D003DCE07 /* ISPCertificatePinning.h in Headers */, + 3EF608E72724BFB700133703 /* AMPMiddleware.h in Headers */, 3E2411E926F9A40100793829 /* AMPPlan.h in Headers */, 1213D89D24176E4700300E98 /* AmplitudeFramework.h in Headers */, 1279F93025244E8F003DCE07 /* Amplitude+SSLPinning.h in Headers */, @@ -641,6 +675,7 @@ 3EF608CB2720E74D00133703 /* AMPServerZone.h in Headers */, 1279F93325244E8F003DCE07 /* AMPConstants.h in Headers */, 1279F93725244E8F003DCE07 /* AMPTrackingOptions.h in Headers */, + 3EF608EC2726244300133703 /* AMPMiddlewareRunner.h in Headers */, 1279FA762525949D003DCE07 /* ISPPinnedNSURLConnectionDelegate.h in Headers */, 1279F93225244E8F003DCE07 /* AmplitudePrivate.h in Headers */, 759E943125FBFEA200BF7C3D /* AMPBackgroundNotifier.h in Headers */, @@ -661,6 +696,7 @@ buildActionMask = 2147483647; files = ( 759E93BC25FBF4B600BF7C3D /* AmplitudeFramework.h in Headers */, + 3EF608E82724BFB700133703 /* AMPMiddleware.h in Headers */, 3E2411EA26F9A40100793829 /* AMPPlan.h in Headers */, 759E93AB25FBF44500BF7C3D /* Amplitude+SSLPinning.h in Headers */, 759E93AA25FBF44500BF7C3D /* Amplitude.h in Headers */, @@ -669,6 +705,7 @@ 3EF608CC2720E74D00133703 /* AMPServerZone.h in Headers */, 759E93A925FBF44500BF7C3D /* AMPIdentify.h in Headers */, 759E93B225FBF44500BF7C3D /* ISPCertificatePinning.h in Headers */, + 3EF608ED2726244300133703 /* AMPMiddlewareRunner.h in Headers */, 759E93B425FBF44500BF7C3D /* ISPPinnedNSURLSessionDelegate.h in Headers */, 759E93A625FBF44500BF7C3D /* AMPConstants.h in Headers */, 759E93DC25FBF6F700BF7C3D /* AMPBackgroundNotifier.h in Headers */, @@ -1013,9 +1050,11 @@ files = ( 759E941125FBFE7800BF7C3D /* AMPBackgroundNotifier.m in Sources */, 1279FA6F2525949D003DCE07 /* ISPCertificatePinning.m in Sources */, + 3EF608F02726256800133703 /* AMPMiddlewareRunner.m in Sources */, 1279F91A25244E8E003DCE07 /* AMPUtils.m in Sources */, 1279F92125244E8E003DCE07 /* AMPURLConnection.m in Sources */, 1279FA722525949D003DCE07 /* ISPPinnedNSURLConnectionDelegate.m in Sources */, + 3EF6090327267C9800133703 /* AMPMiddleware.m in Sources */, 1279F92625244E8E003DCE07 /* AMPDatabaseHelper.m in Sources */, 1279F92425244E8E003DCE07 /* AMPConstants.m in Sources */, 1279F90F25244E8E003DCE07 /* AMPConfigManager.m in Sources */, @@ -1045,6 +1084,7 @@ 12C973C4241244F000E9CDDB /* BaseTestCase.m in Sources */, 3E2411F626F9A4E200793829 /* PlanTests.m in Sources */, 12C973BD241244E400E9CDDB /* SessionTests.m in Sources */, + 3EF608FF272666E400133703 /* MiddlewareRunnerTests.m in Sources */, 12C973AD241244B700E9CDDB /* AmplitudeTests.m in Sources */, 3EF608E227211F8A00133703 /* ConfigManagerTests.m in Sources */, 12C973C2241244F000E9CDDB /* TrackingOptionsTest.m in Sources */, @@ -1071,8 +1111,10 @@ 1279FA772525949D003DCE07 /* ISPPinnedNSURLSessionDelegate.m in Sources */, 1279FA6E2525949D003DCE07 /* ISPCertificatePinning.m in Sources */, 1279F90325244E8D003DCE07 /* AMPURLConnection.m in Sources */, + 3EF6090227267C9800133703 /* AMPMiddleware.m in Sources */, 1279F90725244E8D003DCE07 /* AMPDeviceInfo.m in Sources */, 1279F90425244E8D003DCE07 /* Amplitude.m in Sources */, + 3EF608EF2726256800133703 /* AMPMiddlewareRunner.m in Sources */, 12C973CF24131EB100E9CDDB /* SSLPinningTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1091,6 +1133,7 @@ 12C973C1241244EF00E9CDDB /* BaseTestCase.m in Sources */, 3E2411F526F9A4E100793829 /* PlanTests.m in Sources */, 12C973BC241244E300E9CDDB /* SessionTests.m in Sources */, + 3EF608FE272666E400133703 /* MiddlewareRunnerTests.m in Sources */, 12C973AC241244B700E9CDDB /* AmplitudeTests.m in Sources */, 3EF608E127211F8A00133703 /* ConfigManagerTests.m in Sources */, 12C973BF241244EF00E9CDDB /* TrackingOptionsTest.m in Sources */, @@ -1104,9 +1147,11 @@ files = ( 759E940925FBFE7700BF7C3D /* AMPBackgroundNotifier.m in Sources */, 1279FA702525949D003DCE07 /* ISPCertificatePinning.m in Sources */, + 3EF608F12726256800133703 /* AMPMiddlewareRunner.m in Sources */, 1279F93825244E8F003DCE07 /* AMPUtils.m in Sources */, 1279F93F25244E8F003DCE07 /* AMPURLConnection.m in Sources */, 1279FA732525949D003DCE07 /* ISPPinnedNSURLConnectionDelegate.m in Sources */, + 3EF6090427267C9800133703 /* AMPMiddleware.m in Sources */, 1279F94425244E8F003DCE07 /* AMPDatabaseHelper.m in Sources */, 1279F94225244E8F003DCE07 /* AMPConstants.m in Sources */, 1279F92D25244E8F003DCE07 /* AMPConfigManager.m in Sources */, @@ -1139,6 +1184,7 @@ 12C973AE241244B800E9CDDB /* AmplitudeTests.m in Sources */, 12C973C5241244F100E9CDDB /* TrackingOptionsTest.m in Sources */, 3EF608DF27211AC000133703 /* ServerZoneUtilTests.m in Sources */, + 3EF60900272666E400133703 /* MiddlewareRunnerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1148,9 +1194,11 @@ files = ( 759E93DD25FBF6F700BF7C3D /* AMPBackgroundNotifier.m in Sources */, 759E939025FBF3DC00BF7C3D /* AMPConfigManager.m in Sources */, + 3EF608F22726256800133703 /* AMPMiddlewareRunner.m in Sources */, 759E939125FBF3DC00BF7C3D /* AMPConstants.m in Sources */, 759E939225FBF3DC00BF7C3D /* AMPDatabaseHelper.m in Sources */, 759E939325FBF3DC00BF7C3D /* AMPDeviceInfo.m in Sources */, + 3EF6090527267C9800133703 /* AMPMiddleware.m in Sources */, 759E939425FBF3DC00BF7C3D /* AMPIdentify.m in Sources */, 759E939525FBF3DC00BF7C3D /* Amplitude.m in Sources */, 759E939625FBF3DC00BF7C3D /* AMPRevenue.m in Sources */, diff --git a/Framework/AmplitudeFramework.h b/Framework/AmplitudeFramework.h index 4cdc996a..1ba8f231 100644 --- a/Framework/AmplitudeFramework.h +++ b/Framework/AmplitudeFramework.h @@ -5,6 +5,8 @@ #import #import #import +#import +#import #if TARGET_OS_WATCH #import diff --git a/Sources/Amplitude/AMPMiddleware.m b/Sources/Amplitude/AMPMiddleware.m new file mode 100644 index 00000000..09b67b14 --- /dev/null +++ b/Sources/Amplitude/AMPMiddleware.m @@ -0,0 +1,52 @@ +// +// AMPMiddleware.m +// Copyright (c) 2021 Amplitude Inc. (https://amplitude.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "AMPMiddleware.h" + +@implementation AMPMiddlewarePayload + +- (instancetype _Nonnull)initWithEvent:(NSMutableDictionary *_Nonnull) event withExtra:(NSMutableDictionary *_Nullable) extra { + if ((self = [super init])) { + self.event = event; + self.extra = extra; + } + return self; +} + +@end + +@implementation AMPBlockMiddleware + +- (instancetype _Nonnull)initWithBlock:(AMPMiddlewareBlock)block { + if (self = [super init]) { + _block = block; + } + return self; +} + +- (void)run:(AMPMiddlewarePayload *)payload next:(AMPMiddlewareNext)next { + self.block(payload, next); +} + +@end diff --git a/Sources/Amplitude/AMPMiddlewareRunner.h b/Sources/Amplitude/AMPMiddlewareRunner.h new file mode 100644 index 00000000..372a4dc9 --- /dev/null +++ b/Sources/Amplitude/AMPMiddlewareRunner.h @@ -0,0 +1,36 @@ +// +// AMPMiddlewareRunner.h +// Copyright (c) 2021 Amplitude Inc. (https://amplitude.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +#import +#import "AMPMiddleware.h" + +@interface AMPMiddlewareRunner : NSObject + +@property (nonatomic, nonnull, readonly) NSMutableArray> *middlewares; + ++ (instancetype _Nonnull)middleRunner; + +- (void) add:(id _Nonnull)middleware; + +- (void) run:(AMPMiddlewarePayload *_Nonnull)payload next:(AMPMiddlewareNext _Nonnull)next; + +@end diff --git a/Sources/Amplitude/AMPMiddlewareRunner.m b/Sources/Amplitude/AMPMiddlewareRunner.m new file mode 100644 index 00000000..b82e45f3 --- /dev/null +++ b/Sources/Amplitude/AMPMiddlewareRunner.m @@ -0,0 +1,65 @@ +// +// AMPMiddlewareRunner.m +// Copyright (c) 2021 Amplitude Inc. (https://amplitude.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "AMPMiddlewareRunner.h" +#import "AMPMiddleware.h" + +@implementation AMPMiddlewareRunner + +- (instancetype)init { + if ((self = [super init])) { + _middlewares = [[NSMutableArray alloc] init]; + } + return self; +} + ++ (instancetype _Nonnull)middleRunner { + return [[self alloc] init]; +} + +- (void) add:(id _Nonnull)middleware { + [self.middlewares addObject:middleware]; +} + +- (void) run:(AMPMiddlewarePayload *_Nonnull)payload next:(AMPMiddlewareNext _Nonnull)next { + [self runMiddlewares:self.middlewares payload:payload callback:next]; +} + +- (void) runMiddlewares:(NSArray> *_Nonnull)middlewares + payload:(AMPMiddlewarePayload *_Nonnull)payload + callback:(AMPMiddlewareNext _Nullable)callback { + if (middlewares.count == 0) { + if (callback) { + callback(payload); + } + return; + } + + [middlewares[0] run:payload next:^(AMPMiddlewarePayload *_Nullable newPayload) { + NSArray *remainingMiddlewares = [middlewares subarrayWithRange:NSMakeRange(1, middlewares.count - 1)]; + [self runMiddlewares:remainingMiddlewares payload:newPayload callback:callback]; + }]; +} + +@end diff --git a/Sources/Amplitude/Amplitude.m b/Sources/Amplitude/Amplitude.m index 2e725d05..8311a72d 100644 --- a/Sources/Amplitude/Amplitude.m +++ b/Sources/Amplitude/Amplitude.m @@ -62,6 +62,8 @@ #import "AMPPlan.h" #import "AMPServerZone.h" #import "AMPServerZoneUtil.h" +#import "AMPMiddleware.h" +#import "AMPMiddlewareRunner.h" #import #import @@ -138,6 +140,7 @@ @implementation Amplitude { NSString *_token; AMPPlan *_plan; AMPServerZone _serverZone; + AMPMiddlewareRunner *_middlewareRunner; } #pragma clang diagnostic push @@ -210,6 +213,7 @@ - (instancetype)initWithInstanceName:(NSString *)instanceName { _coppaControlEnabled = NO; self.instanceName = instanceName; _dbHelper = [AMPDatabaseHelper getDatabaseHelper:instanceName]; + _middlewareRunner = [AMPMiddlewareRunner middleRunner]; self.eventUploadThreshold = kAMPEventUploadThreshold; self.eventMaxCount = kAMPEventMaxCount; @@ -513,6 +517,10 @@ - (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)event [self logEvent:eventType withEventProperties:eventProperties withGroups:nil]; } +- (void)logEvent:(NSString *)eventType withEventProperties:(nullable NSDictionary *)eventProperties withMiddlewareExtra: (nullable NSMutableDictionary *) extra { + [self logEvent:eventType withEventProperties:eventProperties withApiProperties:nil withUserProperties:nil withGroups:nil withGroupProperties:nil withTimestamp:nil outOfSession:NO withMiddlewareExtra:extra]; +} + - (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)eventProperties outOfSession:(BOOL)outOfSession { [self logEvent:eventType withEventProperties:eventProperties withGroups:nil outOfSession:outOfSession]; } @@ -534,6 +542,10 @@ - (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)event } - (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)eventProperties withApiProperties:(NSDictionary *)apiProperties withUserProperties:(NSDictionary *)userProperties withGroups:(NSDictionary *)groups withGroupProperties:(NSDictionary *)groupProperties withTimestamp:(NSNumber *)timestamp outOfSession:(BOOL)outOfSession { + [self logEvent:eventType withEventProperties:eventProperties withApiProperties:apiProperties withUserProperties:userProperties withGroups:groups withGroupProperties:groupProperties withTimestamp:timestamp outOfSession:outOfSession withMiddlewareExtra:nil]; +} + +- (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)eventProperties withApiProperties:(NSDictionary *)apiProperties withUserProperties:(NSDictionary *)userProperties withGroups:(NSDictionary *)groups withGroupProperties:(NSDictionary *)groupProperties withTimestamp:(NSNumber *)timestamp outOfSession:(BOOL)outOfSession withMiddlewareExtra: (nullable NSMutableDictionary *) extra { if (self.apiKey == nil) { AMPLITUDE_ERROR(@"ERROR: apiKey cannot be nil or empty, set apiKey with initializeApiKey: before calling logEvent"); return; @@ -583,6 +595,19 @@ - (void)logEvent:(NSString *)eventType withEventProperties:(NSDictionary *)event [event setValue:timestamp forKey:@"timestamp"]; [self annotateEvent:event]; + + AMPMiddlewarePayload * middlewarePayload = [[AMPMiddlewarePayload alloc] initWithEvent:event withExtra:extra]; + + __block BOOL middlewareCompleted = NO; + + [self->_middlewareRunner run:middlewarePayload next:^(AMPMiddlewarePayload *_Nullable newPayload){ + middlewareCompleted = YES; + }]; + + if (!middlewareCompleted) { + AMPLITUDE_LOG(@"Middleware chain skipped logEvent action. Event %@ not logged.", eventType); + return; + } // convert event dictionary to JSON String NSError *error = nil; @@ -1447,6 +1472,10 @@ - (void)setServerZone:(AMPServerZone)serverZone updateServerUrl:(BOOL)updateServ } } +- (void)addEventMiddleware:(id _Nonnull)middleware { + [_middlewareRunner add:middleware]; +} + #pragma mark - Getters for device data - (NSString *)getAdSupportID { NSString *result = nil; diff --git a/Sources/Amplitude/Public/AMPMiddleware.h b/Sources/Amplitude/Public/AMPMiddleware.h new file mode 100644 index 00000000..63e2fade --- /dev/null +++ b/Sources/Amplitude/Public/AMPMiddleware.h @@ -0,0 +1,60 @@ +// +// AMPMiddleware.h +// Copyright (c) 2021 Amplitude Inc. (https://amplitude.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +/** + * AMPMiddlewarePayload + */ +@interface AMPMiddlewarePayload: NSObject + +@property NSMutableDictionary *_Nonnull event; +@property NSMutableDictionary *_Nullable extra; + +- (instancetype _Nonnull)initWithEvent:(NSMutableDictionary *_Nonnull) event withExtra:(NSMutableDictionary *_Nullable) extra; + +@end + +/** + * AMPMiddleware + */ +typedef void (^AMPMiddlewareNext)(AMPMiddlewarePayload *_Nullable newPayload); + +@protocol AMPMiddleware + +- (void)run:(AMPMiddlewarePayload *_Nonnull)payload next:(AMPMiddlewareNext _Nonnull)next; + +@end + +/** + * AMPBlockMiddleware + */ +typedef void (^AMPMiddlewareBlock)(AMPMiddlewarePayload *_Nonnull payload, AMPMiddlewareNext _Nonnull next); + +@interface AMPBlockMiddleware : NSObject + +@property (nonnull, nonatomic, readonly) AMPMiddlewareBlock block; + +- (instancetype _Nonnull)initWithBlock:(AMPMiddlewareBlock _Nonnull)block; + +@end diff --git a/Sources/Amplitude/Public/Amplitude.h b/Sources/Amplitude/Public/Amplitude.h index fc973f55..7fcc96d3 100644 --- a/Sources/Amplitude/Public/Amplitude.h +++ b/Sources/Amplitude/Public/Amplitude.h @@ -27,6 +27,7 @@ #import "AMPTrackingOptions.h" #import "AMPPlan.h" #import "AMPServerZone.h" +#import "AMPMiddleware.h" NS_ASSUME_NONNULL_BEGIN @@ -278,6 +279,9 @@ typedef void (^AMPInitCompletionBlock)(void); */ - (void)logEvent:(NSString *)eventType withEventProperties:(nullable NSDictionary *)eventProperties; + +- (void)logEvent:(NSString *)eventType withEventProperties:(nullable NSDictionary *)eventProperties withMiddlewareExtra: (nullable NSMutableDictionary *) extra; + /** Tracks an event. Events are saved locally. @@ -654,6 +658,11 @@ typedef void (^AMPInitCompletionBlock)(void); */ - (void)setServerZone:(AMPServerZone)serverZone updateServerUrl:(BOOL)updateServerUrl; +/** + * Adds a new middleware function to run on each logEvent() call prior to sending to Amplitude. + */ +- (void)addEventMiddleware:(id _Nonnull)middleware; + /**----------------------------------------------------------------------------- * @name Other Methods * ----------------------------------------------------------------------------- diff --git a/Tests/AmplitudeTests.m b/Tests/AmplitudeTests.m index 9120e6ec..d00b5d58 100644 --- a/Tests/AmplitudeTests.m +++ b/Tests/AmplitudeTests.m @@ -1110,7 +1110,7 @@ - (void)testSetPlan { - (void)testSetServerZone { Amplitude *client = [Amplitude instanceWithName:@"eu_zone"]; XCTAssertEqualObjects(kAMPEventLogUrl, [client valueForKey:@"serverUrl"]); - [client initializeApiKey:@"eu_api_ket"]; + [client initializeApiKey:@"eu_api_key"]; [client setServerZone:EU]; XCTAssertEqualObjects(kAMPEventLogEuUrl, [client valueForKey:@"serverUrl"]); } @@ -1118,9 +1118,43 @@ - (void)testSetServerZone { - (void)testSetServerZoneWithoutUpdateServerUrl { Amplitude *client = [Amplitude instanceWithName:@"eu_zone_2"]; XCTAssertEqualObjects(kAMPEventLogUrl, [client valueForKey:@"serverUrl"]); - [client initializeApiKey:@"eu_api_ket"]; + [client initializeApiKey:@"eu_api_key"]; [client setServerZone:EU updateServerUrl:NO]; XCTAssertEqualObjects(kAMPEventLogUrl, [client valueForKey:@"serverUrl"]); } +- (void)testMiddlewareSupport { + NSString *eventType = @"middleware event"; + AMPBlockMiddleware *updateEventTypeMiddleware = [[AMPBlockMiddleware alloc] initWithBlock: ^(AMPMiddlewarePayload * _Nonnull payload, AMPMiddlewareNext _Nonnull next) { + [payload.event setValue:eventType forKey:@"event_type"]; + [payload.event setValue:payload.extra[@"description"] forKey:@"description"]; + next(payload); + }]; + Amplitude *client = [Amplitude instanceWithName:@"middleware_support"]; + [client addEventMiddleware:updateEventTypeMiddleware]; + [client initializeApiKey:@"middleware_api_key"]; + NSMutableDictionary *eventProperties = [NSMutableDictionary dictionary]; + [eventProperties setObject:@"green" forKey:@"color"]; + NSMutableDictionary *middlewareExtra = [NSMutableDictionary dictionary]; + [middlewareExtra setObject:@"some event description" forKey:@"description"]; + [client logEvent:@"test" withEventProperties:eventProperties withMiddlewareExtra:middlewareExtra]; + [client flushQueue]; + + NSDictionary *event = [client getLastEventFromInstanceName:@"middleware_support"]; + XCTAssertEqualObjects(eventType, event[@"event_type"]); + XCTAssertEqualObjects(middlewareExtra[@"description"], event[@"description"]); +} + +- (void)testSwallowMiddleware { + AMPBlockMiddleware *swallowMiddleware = [[AMPBlockMiddleware alloc] initWithBlock: ^(AMPMiddlewarePayload * _Nonnull payload, AMPMiddlewareNext _Nonnull next) { + }]; + Amplitude *client = [Amplitude instanceWithName:@"middleware_swallow"]; + [client addEventMiddleware:swallowMiddleware]; + [client initializeApiKey:@"middleware_api_key"]; + [client logEvent:@"test"]; + [client flushQueue]; + + XCTAssertNil([client getLastEventFromInstanceName:@"middleware_swallow"]); +} + @end diff --git a/Tests/MiddlewareRunnerTests.m b/Tests/MiddlewareRunnerTests.m new file mode 100644 index 00000000..f882a1e0 --- /dev/null +++ b/Tests/MiddlewareRunnerTests.m @@ -0,0 +1,90 @@ +// +// MiddlewareRunnerTests.m +// Amplitude +// +// Created by Qingzhuo Zhen on 10/24/21. +// Copyright © 2021 Amplitude. All rights reserved. +// + +#import +#import "AMPMiddleware.h" +#import "AMPMiddlewareRunner.h" + +@interface MiddlewareRunnerTests : XCTestCase + +@property AMPMiddlewareRunner *middlewareRunner; + +@end + + +@implementation MiddlewareRunnerTests + +- (void)setUp { + _middlewareRunner = [AMPMiddlewareRunner middleRunner]; +} + +- (void)tearDown { + _middlewareRunner = nil; +} + +- (void)testMiddlewareRun { + NSString *eventType = @"middleware event"; + AMPBlockMiddleware *updateEventTypeMiddleware = [[AMPBlockMiddleware alloc] initWithBlock: ^(AMPMiddlewarePayload * _Nonnull payload, AMPMiddlewareNext _Nonnull next) { + [payload.event setValue:eventType forKey:@"event_type"]; + next(payload); + }]; + NSString *deviceModel = @"middleware_device"; + AMPBlockMiddleware *updateDeviceModelMiddleware = [[AMPBlockMiddleware alloc] initWithBlock: ^(AMPMiddlewarePayload * _Nonnull payload, AMPMiddlewareNext _Nonnull next) { + [payload.event setValue:deviceModel forKey:@"device_model"]; + next(payload); + }]; + [_middlewareRunner add:updateEventTypeMiddleware]; + [_middlewareRunner add:updateDeviceModelMiddleware]; + + NSMutableDictionary *event = [NSMutableDictionary dictionary]; + [event setValue:@"sample_event" forKey:@"event_type"]; + [event setValue:@"sample_device" forKey:@"device_model"]; + NSMutableDictionary *extra = [NSMutableDictionary dictionary]; + + AMPMiddlewarePayload * middlewarePayload = [[AMPMiddlewarePayload alloc] initWithEvent:event withExtra:extra]; + + __block BOOL middlewareCompleted = NO; + + [_middlewareRunner run:middlewarePayload next:^(AMPMiddlewarePayload *_Nullable newPayload){ + middlewareCompleted = YES; + }]; + + XCTAssertEqual(middlewareCompleted, YES); + XCTAssertEqualObjects([event objectForKey:@"event_type"], eventType); + XCTAssertEqualObjects([event objectForKey:@"device_model"], deviceModel); +} + +- (void)testRunWithNotPassMiddleware { + NSString *eventType = @"middleware event"; + AMPBlockMiddleware *updateEventTypeMiddleware = [[AMPBlockMiddleware alloc] initWithBlock: ^(AMPMiddlewarePayload * _Nonnull payload, AMPMiddlewareNext _Nonnull next) { + [payload.event setValue:eventType forKey:@"event_type"]; + next(payload); + }]; + AMPBlockMiddleware *swallowMiddleware = [[AMPBlockMiddleware alloc] initWithBlock: ^(AMPMiddlewarePayload * _Nonnull payload, AMPMiddlewareNext _Nonnull next) { + }]; + [_middlewareRunner add:updateEventTypeMiddleware]; + [_middlewareRunner add:swallowMiddleware]; + + NSMutableDictionary *event = [NSMutableDictionary dictionary]; + [event setValue:@"sample_event" forKey:@"event_type"]; + [event setValue:@"sample_device" forKey:@"device_model"]; + NSMutableDictionary *extra = [NSMutableDictionary dictionary]; + + AMPMiddlewarePayload * middlewarePayload = [[AMPMiddlewarePayload alloc] initWithEvent:event withExtra:extra]; + + __block BOOL middlewareCompleted = NO; + + [_middlewareRunner run:middlewarePayload next:^(AMPMiddlewarePayload *_Nullable newPayload){ + middlewareCompleted = YES; + }]; + + XCTAssertEqual(middlewareCompleted, NO); + XCTAssertEqualObjects([event objectForKey:@"event_type"], eventType); +} + +@end