Skip to content

Commit

Permalink
Allow setting the domainURIPrefix for custom domain names/paths when …
Browse files Browse the repository at this point in the history
…creating dynamic links (#2071)

* Add support for creating DL with custom domain names/paths. The new domainURIPrefix parameter requires either a. a valid FDL domain name created in the Firebase console or b. a custom domain name or c. a custom domain name with path registered for DL. All domainURIPrefixes need to start with a valid (https) scheme.

* Fix style.

* style

* Fix typo in DEPRECATED_MSG_ATTRIBUTE, other nits.

* Fix styling.

* Check incoming domainURIPrefix for validity using NSURL and check for an https scheme.
  • Loading branch information
dmandar authored Nov 14, 2018
1 parent 5038e26 commit d917bac
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 31 deletions.
9 changes: 5 additions & 4 deletions Example/DynamicLinks/FDLBuilderTestAppObjC/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ - (void)_initDefaultValues {
},
// The default value of domain appcode belongs to project: app-invites-qa
@{
@"id" : @"domain",
@"label" : @"App domain (required)",
@"defaultValue" : @"testfdl.page.link",
@"id" : @"domainURIPrefix",
@"label" : @"App domainURIPrefix (required)",
@"defaultValue" : @"https://testfdl.page.link",
},
// analytics params
@{
Expand Down Expand Up @@ -289,7 +289,8 @@ - (void)_initDefaultValues {
- (void)_buildFDLLink {
NSURL *link = [NSURL URLWithString:_paramValues[@"linkString"]];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:_paramValues[@"domain"]];
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:_paramValues[@"https://domain"]];

FIRDynamicLinkGoogleAnalyticsParameters *analyticsParams =
[FIRDynamicLinkGoogleAnalyticsParameters
Expand Down
87 changes: 66 additions & 21 deletions Example/DynamicLinks/Tests/FDLURLComponentsTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

#import <OCMock/OCMock.h>

static NSString *const kFDLURLDomain = @"xyz.page.link";
static NSString *const kFDLURLDomain = @"https://xyz.page.link";
static NSString *const kFDLURLCustomDomain = @"https://foo.com/path";

@interface FDLURLComponentsTests : XCTestCase
@end
Expand Down Expand Up @@ -461,14 +462,14 @@ - (void)testLinkOptionsParamsPropertiesSetProperly {

- (void)testFDLComponentsFactoryReturnsInstanceOfCorrectClass {
NSURL *link = [NSURL URLWithString:@"https://google.com"];
id returnValue = [FIRDynamicLinkComponents componentsWithLink:link domain:kFDLURLDomain];
id returnValue = [FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkComponents class]]);
}

- (void)testFDLComponentsFactoryReturnsInstanceWithAllNilProperties {
NSURL *link = [NSURL URLWithString:@"https://google.com"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:kFDLURLDomain];
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];

XCTAssertNil(components.analyticsParameters);
XCTAssertNil(components.socialMetaTagParameters);
Expand All @@ -484,11 +485,27 @@ - (void)testFDLComponentsCreatesSimplestLinkCorrectly {
NSURL *link = [NSURL URLWithString:linkString];

NSString *expectedURLString =
[NSString stringWithFormat:@"https://%@/?link=%@", kFDLURLDomain, endcodedLinkString];
[NSString stringWithFormat:@"%@/?link=%@", kFDLURLDomain, endcodedLinkString];
NSURL *expectedURL = [NSURL URLWithString:expectedURLString];

FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:kFDLURLDomain];
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
NSURL *actualURL = components.url;

XCTAssertEqualObjects(actualURL, expectedURL);
}

- (void)testFDLComponentsCustomDomainWithPath {
NSString *linkString = @"https://google.com";
NSString *endcodedLinkString = @"https%3A%2F%2Fgoogle%2Ecom";
NSURL *link = [NSURL URLWithString:linkString];

NSString *expectedURLString =
[NSString stringWithFormat:@"%@/?link=%@", kFDLURLCustomDomain, endcodedLinkString];
NSURL *expectedURL = [NSURL URLWithString:expectedURLString];

FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLCustomDomain];
NSURL *actualURL = components.url;

XCTAssertEqualObjects(actualURL, expectedURL);
Expand All @@ -499,7 +516,8 @@ - (void)testFDLComponentsFailsOnMalformedDomain {
NSURL *link = [NSURL URLWithString:linkString];

FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:@"this is invalid domain"];
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:@"this is invalid domain URI Prefix"];

XCTAssertNil(components.url);
}
Expand Down Expand Up @@ -553,7 +571,7 @@ - (void)testFDLComponentsCreatesFullLinkCorrectly {

NSURL *link = [NSURL URLWithString:@"https://google.com"];
FIRDynamicLinkComponents *fdlComponents =
[FIRDynamicLinkComponents componentsWithLink:link domain:kFDLURLDomain];
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
fdlComponents.analyticsParameters = analyticsParams;
fdlComponents.iOSParameters = iosParams;
fdlComponents.iTunesConnectParameters = itcParams;
Expand Down Expand Up @@ -642,7 +660,43 @@ - (void)testShortenURL {
XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:kFDLURLDomain];
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
[components
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error) {
XCTAssertEqualObjects(shortURL.absoluteString, shortURLString);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:0.1 handler:nil];

[keyProviderClassMock verify];
[keyProviderClassMock stopMocking];
[componentsClassMock verify];
[componentsClassMock stopMocking];
}

- (void)testDeprecatedMethodComponentsWithLinkForDomain {
NSString *shortURLString = @"https://xyz.page.link/abcd";

// Mock key provider
id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]);
[[[keyProviderClassMock expect] andReturn:@"fake-api-key"] APIKey];

id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]);
[[componentsClassMock expect]
sendHTTPRequest:OCMOCK_ANY
completion:[OCMArg checkWithBlock:^BOOL(id obj) {
void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj;
NSDictionary *JSON = @{@"shortLink" : shortURLString};
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:0];
completion(JSONData, nil);
return YES;
}]];

XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:@"xyz.page.link"];
[components
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error) {
Expand Down Expand Up @@ -679,7 +733,7 @@ - (void)testShortenURLReturnsErrorWhenAPIKeyMissing {
[self expectationWithDescription:@"completion called with error"];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:kFDLURLDomain];
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
[components
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error) {
Expand Down Expand Up @@ -714,20 +768,11 @@ - (void)testShortenURLReturnsErrorWhenDomainIsMalformed {
return YES;
}]];

XCTestExpectation *expectation =
[self expectationWithDescription:@"completion called with error"];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domain:@"this is invalid domain"];
[components
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error) {
XCTAssertNil(shortURL);
if (error) {
[expectation fulfill];
}
}];
[self waitForExpectationsWithTimeout:0.1 handler:nil];
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:@"this is invalid domain URI Prefix"];
XCTAssertNil(components);

[keyProviderClassMock verify];
[keyProviderClassMock stopMocking];
Expand Down
2 changes: 1 addition & 1 deletion Example/DynamicLinks/Tests/FIRDynamicLinkNetworkingTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

#import <XCTest/XCTest.h>

#import "OCMock.h"
#import <OCMock/OCMock.h>

#import <GoogleUtilities/GULSwizzler.h>
#import "DynamicLinks/FIRDynamicLinkNetworking+Private.h"
Expand Down
46 changes: 43 additions & 3 deletions Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import "DynamicLinks/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h"
#import "DynamicLinks/Public/FDLURLComponents.h"

#import "DynamicLinks/Logging/FDLLogging.h"
#import "DynamicLinks/Utilities/FDLUtilities.h"

/// The exact behavior of dict[key] = value is unclear when value is nil. This function safely adds
Expand Down Expand Up @@ -448,15 +449,54 @@ - (instancetype)init {

@implementation FIRDynamicLinkComponents

#pragma mark Deprecated Initializers.
+ (instancetype)componentsWithLink:(NSURL *)link domain:(NSString *)domain {
return [[self alloc] initWithLink:link domain:domain];
NSURL *domainURL = [NSURL URLWithString:domain];
NSString *domainURIPrefix =
domainURL.scheme ? domain : [NSString stringWithFormat:@"https://%@", domain];
return [FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:domainURIPrefix];
}

- (instancetype)initWithLink:(NSURL *)link domain:(NSString *)domain {
NSURL *domainURL = [NSURL URLWithString:domain];
NSString *domainURIPrefix =
domainURL.scheme ? domain : [NSString stringWithFormat:@"https://%@", domain];
return [self initWithLink:link domainURIPrefix:domainURIPrefix];
}

#pragma mark Initializers.
+ (instancetype)componentsWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix {
NSURL *domainURIPrefixURL = [NSURL URLWithString:domainURIPrefix];
if (!domainURIPrefixURL) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefix,
@"Invalid domainURIPrefix. Please input a valid URL.");
return nil;
}
if (![[domainURIPrefixURL.scheme lowercaseString] hasPrefix:@"https"]) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefixScheme,
@"Invalid domainURIPrefix scheme. Scheme needs to be https");
return nil;
}
return [[self alloc] initWithLink:link domainURIPrefix:domainURIPrefix];
}

- (instancetype)initWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix {
self = [super init];
if (self) {
_link = link;
_domain = [domain copy];
/// Must be a URL that conforms to RFC 2396.
NSURL *domainURIPrefixURL = [NSURL URLWithString:domainURIPrefix];
if (!domainURIPrefixURL) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefix,
@"Invalid domainURIPrefix. Please input a valid URL.");
return nil;
}
if (![[domainURIPrefixURL.scheme lowercaseString] hasPrefix:@"https"]) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefixScheme,
@"Invalid domainURIPrefix scheme. Scheme needs to be https");
return nil;
}
_domain = [domainURIPrefix copy];
}
return self;
}
Expand Down Expand Up @@ -593,7 +633,7 @@ - (NSURL *)url {
addEntriesFromDictionaryRepresentingConformerToDictionary(_otherPlatformParameters);

NSString *queryString = FIRDLURLQueryStringFromDictionary(queryDictionary);
NSString *urlString = [NSString stringWithFormat:@"https://%@/%@", _domain, queryString];
NSString *urlString = [NSString stringWithFormat:@"%@/%@", _domain, queryString];
return [NSURL URLWithString:urlString];
}

Expand Down
2 changes: 2 additions & 0 deletions Firebase/DynamicLinks/Logging/FDLLogging.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ typedef NS_ENUM(NSInteger, FDLLogIdentifier) {
FDLLogIdentifierSetupNilAPIKey = 0,
FDLLogIdentifierSetupNilClientID = 1,
FDLLogIdentifierSetupNonDefaultApp = 2,
FDLLogIdentifierSetupInvalidDomainURIPrefixScheme = 3,
FDLLogIdentifierSetupInvalidDomainURIPrefix = 4,
};

/** The appropriate formatter for using NSInteger in FIRLogger. */
Expand Down
38 changes: 36 additions & 2 deletions Firebase/DynamicLinks/Public/FDLURLComponents.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,9 @@ FIR_SWIFT_NAME(DynamicLinkComponents)
*/
+ (instancetype)componentsWithLink:(NSURL *)link
domain:(NSString *)domain
NS_SWIFT_UNAVAILABLE("Use init(link:domain:)");
NS_SWIFT_UNAVAILABLE("Use init(link:domain:)")DEPRECATED_MSG_ATTRIBUTE(
"This method is deprecated. Please use the new method with support for "
"domainURIPrefix- componentsWithLink:domainURIPrefix.");

/**
* @method initWithLink:domain:
Expand All @@ -527,7 +529,39 @@ FIR_SWIFT_NAME(DynamicLinkComponents)
* @param domain Domain of your App. This value must be equal to your assigned domain from Firebase
* Console.
*/
- (instancetype)initWithLink:(NSURL *)link domain:(NSString *)domain;
- (instancetype)initWithLink:(NSURL *)link
domain:(NSString *)domain
DEPRECATED_MSG_ATTRIBUTE(
"This method is deprecated. Please use the new method with support for "
"domainURIPrefix- initWithLink:domainURIPrefix.");

/**
* @method componentsWithLink:domain:
* @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters
* set to generate a fully-functional Dynamic Link.
* @param link Deep link to be stored in created Dynamic link. This link also called "payload" of
* the Dynamic link.
* @param domainURIPrefix Domain URI Prefix of your App. This value must be either a. your assigned
* domain from the Firebase console or b. your custom domain or c. your custom domain with a valid
* path that is registered for Dynamic Links. The domain URI prefix must start with a valid scheme
* (https://)
*/
+ (instancetype)componentsWithLink:(NSURL *)link
domainURIPrefix:(NSString *)domainURIPrefix
NS_SWIFT_UNAVAILABLE("Use init(link:domainURIPrefix:)");

/**
* @method initWithLink:domain:
* @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters
* set to generate a fully-functional Dynamic Link.
* @param link Deep link to be stored in created Dynamic link. This link also called "payload" of
* the Dynamic link.
* @param domainURIPrefix Domain URI Prefix of your App. This value must be either a. your assigned
* domain from the Firebase console or b. your custom domain or c. your custom domain with a valid
* path that is registered for Dynamic Links. The domain URI prefix must start with a valid scheme
* (https://).
*/
- (instancetype)initWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix;

/**
* @method shortenURL:options:completion:
Expand Down

0 comments on commit d917bac

Please sign in to comment.