diff options
author | Paul Beusterien <paulbeusterien@google.com> | 2018-03-20 11:59:03 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-20 11:59:03 -0700 |
commit | b7f35a0b76bb2afd682b806d2b25568611612557 (patch) | |
tree | deb4577d3e54c3fafa2a065605faef228a186c1b /Functions/FirebaseFunctions | |
parent | 7e65885762757209e0e14ec28e99ec91380e9c2f (diff) |
Initial Firebase Functions (#948)
Diffstat (limited to 'Functions/FirebaseFunctions')
-rw-r--r-- | Functions/FirebaseFunctions/FIRFunctions+Internal.h | 50 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FIRFunctions.m | 247 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h | 46 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FIRHTTPSCallable.m | 71 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNContext.h | 41 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNContext.m | 84 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNError.h | 31 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNError.m | 186 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNInstanceIDProxy.h | 29 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNInstanceIDProxy.m | 57 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNSerializer.h | 33 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNSerializer.m | 231 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNUsageValidation.h | 38 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/FUNUsageValidation.m | 28 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/Public/FIRError.h | 90 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/Public/FIRFunctions.h | 66 | ||||
-rw-r--r-- | Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h | 94 |
17 files changed, 1422 insertions, 0 deletions
diff --git a/Functions/FirebaseFunctions/FIRFunctions+Internal.h b/Functions/FirebaseFunctions/FIRFunctions+Internal.h new file mode 100644 index 0000000..6c555f0 --- /dev/null +++ b/Functions/FirebaseFunctions/FIRFunctions+Internal.h @@ -0,0 +1,50 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +#import "FIRFunctions.h" + +@class FIRHTTPSCallableResult; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRFunctions (Internal) + +/** + * Calls an http trigger endpoint. + * @param name The name of the http trigger. + * @param data Parameters to pass to the function. Can be anything encodable as JSON. + * @param completion The block to call when the request is complete. + */ +- (void)callFunction:(NSString *)name + withObject:(nullable id)data + completion:(void (^)(FIRHTTPSCallableResult *_Nullable result, + NSError *_Nullable error))completion; + +/** + * Constructs the url for an http trigger. This is exposed only for testing. + * @param name The name of the endpoint. + */ +- (NSString *)URLWithName:(NSString *)name; + +/** + * Sets the functions client to send requests to localhost instead of Firebase. + * For testing only. + */ +- (void)useLocalhost; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FIRFunctions.m b/Functions/FirebaseFunctions/FIRFunctions.m new file mode 100644 index 0000000..274d058 --- /dev/null +++ b/Functions/FirebaseFunctions/FIRFunctions.m @@ -0,0 +1,247 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRFunctions.h" +#import "FIRFunctions+Internal.h" + +#import "FIRError.h" +#import "FIRHTTPSCallable+Internal.h" +#import "FIRHTTPSCallable.h" +#import "FUNContext.h" +#import "FUNError.h" +#import "FUNSerializer.h" +#import "FUNUsageValidation.h" + +#import "FIRApp.h" +#import "FIRAppInternal.h" +#import "FIROptions.h" +#import "GTMSessionFetcherService.h" + +NS_ASSUME_NONNULL_BEGIN + +NSString *const kFUNInstanceIDTokenHeader = @"Firebase-Instance-ID-Token"; + +@interface FIRFunctions () { + // The network client to use for http requests. + GTMSessionFetcherService *_fetcherService; + // The projectID to use for all function references. + FIRApp *_app; + // The region to use for all function references. + NSString *_region; + // A serializer to encode/decode data and return values. + FUNSerializer *_serializer; + // A factory for getting the metadata to include with function calls. + FUNContextProvider *_contextProvider; + // For testing only. If this is set, functions will be called against localhost instead of + // Firebase. + BOOL _useLocalhost; +} + +/** + * Initialize the Cloud Functions client with the given app and region. + * @param app The app for the Firebase project. + * @param region The region for the http trigger, such as "us-central1". + */ +- (id)initWithApp:(FIRApp *)app region:(NSString *)region NS_DESIGNATED_INITIALIZER; + +@end + +@implementation FIRFunctions + ++ (instancetype)functions { + return [[self alloc] initWithApp:[FIRApp defaultApp] region:@"us-central1"]; +} + ++ (instancetype)functionsForApp:(FIRApp *)app { + return [[self alloc] initWithApp:app region:@"us-central1"]; +} + ++ (instancetype)functionsForRegion:(NSString *)region { + return [[self alloc] initWithApp:[FIRApp defaultApp] region:region]; +} + ++ (instancetype)functionsForApp:(FIRApp *)app region:(NSString *)region { + return [[self alloc] initWithApp:app region:region]; +} + +- (instancetype)initWithApp:(FIRApp *)app region:(NSString *)region { + self = [super init]; + if (self) { + if (!region) { + FUNThrowInvalidArgument(@"FIRFunctions region cannot be nil."); + } + _fetcherService = [[GTMSessionFetcherService alloc] init]; + _app = app; + _region = [region copy]; + _serializer = [[FUNSerializer alloc] init]; + _contextProvider = [[FUNContextProvider alloc] initWithApp:app]; + _useLocalhost = NO; + } + return self; +} + +- (void)useLocalhost { + _useLocalhost = YES; +} + +- (NSString *)URLWithName:(NSString *)name { + if (!name) { + FUNThrowInvalidArgument(@"FIRFunctions function name cannot be nil."); + } + NSString *projectID = _app.options.projectID; + if (!projectID) { + FUNThrowInvalidArgument(@"FIRFunctions app projectID cannot be nil."); + } + if (_useLocalhost) { + return [NSString stringWithFormat:@"http://localhost:5005/%@/%@/%@", projectID, _region, name]; + } + return + [NSString stringWithFormat:@"https://%@-%@.cloudfunctions.net/%@", _region, projectID, name]; +} + +- (void)callFunction:(NSString *)name + withObject:(nullable id)data + completion:(void (^)(FIRHTTPSCallableResult *_Nullable result, + NSError *_Nullable error))completion { + [_contextProvider getContext:^(FUNContext *_Nullable context, NSError *_Nullable error) { + if (error) { + if (completion) { + completion(nil, error); + } + return; + } + return [self callFunction:name withObject:data context:context completion:completion]; + }]; +} + +- (void)callFunction:(NSString *)name + withObject:(nullable id)data + context:(FUNContext *)context + completion:(void (^)(FIRHTTPSCallableResult *_Nullable result, + NSError *_Nullable error))completion { + GTMSessionFetcher *fetcher = [_fetcherService fetcherWithURLString:[self URLWithName:name]]; + + NSMutableDictionary *body = [NSMutableDictionary dictionary]; + // Encode the data in the body. + if (!data) { + data = [NSNull null]; + } + id encoded = [_serializer encode:data]; + if (!encoded) { + FUNThrowInvalidArgument(@"FIRFunctions data encoded as nil. This should not happen."); + } + body[@"data"] = encoded; + + NSError *error = nil; + NSData *payload = [NSJSONSerialization dataWithJSONObject:body options:0 error:&error]; + if (error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(nil, error); + }); + } + return; + } + fetcher.bodyData = payload; + + // Set the headers. + [fetcher setRequestValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + if (context.authToken) { + NSString *value = [NSString stringWithFormat:@"Bearer %@", context.authToken]; + [fetcher setRequestValue:value forHTTPHeaderField:@"Authorization"]; + } + if (context.instanceIDToken) { + [fetcher setRequestValue:context.instanceIDToken forHTTPHeaderField:kFUNInstanceIDTokenHeader]; + } + + // Override normal security rules if this is a local test. + if (_useLocalhost) { + fetcher.allowLocalhostRequest = YES; + fetcher.allowedInsecureSchemes = @[ @"http" ]; + } + + FUNSerializer *serializer = _serializer; + [fetcher beginFetchWithCompletionHandler:^(NSData *_Nullable data, NSError *_Nullable error) { + // If there was an HTTP error, convert it to our own error domain. + if (error) { + if ([error.domain isEqualToString:kGTMSessionFetcherStatusDomain]) { + error = FUNErrorForResponse(error.code, data, serializer); + } + } else { + // If there wasn't an HTTP error, see if there was an error in the body. + error = FUNErrorForResponse(200, data, serializer); + } + // If there was an error, report it to the user and stop. + if (error) { + if (completion) { + completion(nil, error); + } + return; + } + + id responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + if (error) { + if (completion) { + completion(nil, error); + } + return; + } + if (![responseJSON isKindOfClass:[NSDictionary class]]) { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Response was not a dictionary."}; + error = [NSError errorWithDomain:FIRFunctionsErrorDomain + code:FIRFunctionsErrorCodeInternal + userInfo:userInfo]; + if (completion) { + completion(nil, error); + } + return; + } + id dataJSON = responseJSON[@"data"]; + // TODO(klimt): Allow "result" instead of "data" for now, for backwards compatibility. + if (!dataJSON) { + dataJSON = responseJSON[@"result"]; + } + if (!dataJSON) { + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : @"Response did not include data field."}; + error = [NSError errorWithDomain:FIRFunctionsErrorDomain + code:FIRFunctionsErrorCodeInternal + userInfo:userInfo]; + if (completion) { + completion(nil, error); + } + return; + } + id resultData = [serializer decode:dataJSON error:&error]; + if (error) { + if (completion) { + completion(nil, error); + } + return; + } + id result = [[FIRHTTPSCallableResult alloc] initWithData:resultData]; + if (completion) { + // If there's no result field, this will return nil, which is fine. + completion(result, nil); + } + }]; +} + +- (FIRHTTPSCallable *)HTTPSCallableWithName:(NSString *)name { + return [[FIRHTTPSCallable alloc] initWithFunctions:self name:name]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h b/Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h new file mode 100644 index 0000000..0a8dae1 --- /dev/null +++ b/Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h @@ -0,0 +1,46 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +#import "FIRHTTPSCallable.h" + +@class FIRFunctions; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRHTTPSCallableResult (Internal) + +/** + * Initializes a callable result. + * + * @param result The data to wrap. + */ +- (instancetype)initWithData:(id)result; + +@end + +@interface FIRHTTPSCallable (Internal) + +/** + * Initializes a reference to the given http trigger. + * + * @param functionsClient The functions client to use for making network requests. + * @param name The name of the http trigger. + */ +- (instancetype)initWithFunctions:(FIRFunctions *)functionsClient name:(NSString *)name; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FIRHTTPSCallable.m b/Functions/FirebaseFunctions/FIRHTTPSCallable.m new file mode 100644 index 0000000..2979ca5 --- /dev/null +++ b/Functions/FirebaseFunctions/FIRHTTPSCallable.m @@ -0,0 +1,71 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRHTTPSCallable.h" +#import "FIRHTTPSCallable+Internal.h" + +#import "FIRFunctions+Internal.h" +#import "FUNUsageValidation.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRHTTPSCallableResult + +- (instancetype)initWithData:(id)data { + self = [super init]; + if (self) { + _data = data; + } + return self; +} + +@end + +@interface FIRHTTPSCallable () { + // The functions client to use for making calls. + FIRFunctions *_functions; + // The name of the http endpoint this reference refers to. + NSString *_name; +} + +@end + +@implementation FIRHTTPSCallable + +- (instancetype)initWithFunctions:(FIRFunctions *)functions name:(NSString *)name { + self = [super init]; + if (self) { + if (!name) { + FUNThrowInvalidArgument(@"FIRHTTPSCallable name cannot be nil."); + } + _name = [name copy]; + _functions = functions; + } + return self; +} + +- (void)callWithCompletion:(void (^)(FIRHTTPSCallableResult *_Nullable result, + NSError *_Nullable error))completion { + [self callWithObject:nil completion:completion]; +} + +- (void)callWithObject:(nullable id)data + completion:(void (^)(FIRHTTPSCallableResult *_Nullable result, + NSError *_Nullable error))completion { + [_functions callFunction:_name withObject:data completion:completion]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNContext.h b/Functions/FirebaseFunctions/FUNContext.h new file mode 100644 index 0000000..0979b67 --- /dev/null +++ b/Functions/FirebaseFunctions/FUNContext.h @@ -0,0 +1,41 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +@class FIRApp; + +NS_ASSUME_NONNULL_BEGIN + +/** + * FUNContext is a helper class for gathering metadata for a function call. + */ +@interface FUNContext : NSObject +- (id)init NS_UNAVAILABLE; +@property(nonatomic, copy, nullable, readonly) NSString *authToken; +@property(nonatomic, copy, nullable, readonly) NSString *instanceIDToken; +@end + +/** + * A FUNContextProvider gathers metadata and creats a FUNContext. + */ +@interface FUNContextProvider : NSObject + +- (id)init NS_UNAVAILABLE; + +- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER; + +- (void)getContext:(void (^)(FUNContext *_Nullable context, NSError *_Nullable error))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNContext.m b/Functions/FirebaseFunctions/FUNContext.m new file mode 100644 index 0000000..0ce4c2b --- /dev/null +++ b/Functions/FirebaseFunctions/FUNContext.m @@ -0,0 +1,84 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FUNContext.h" + +#import "FIRApp.h" +#import "FIRAppInternal.h" +#import "FUNInstanceIDProxy.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FUNContext () + +- (instancetype)initWithAuthToken:(NSString *_Nullable)authToken + instanceIDToken:(NSString *_Nullable)instanceIDToken NS_DESIGNATED_INITIALIZER; + +@end + +@implementation FUNContext + +- (instancetype)initWithAuthToken:(NSString *_Nullable)authToken + instanceIDToken:(NSString *_Nullable)instanceIDToken { + self = [super init]; + if (self) { + _authToken = [authToken copy]; + _instanceIDToken = [instanceIDToken copy]; + } + return self; +} + +@end + +@interface FUNContextProvider () { + FIRApp *_app; + FUNInstanceIDProxy *_instanceIDProxy; +} +@end + +@implementation FUNContextProvider + +- (instancetype)initWithApp:(FIRApp *)app { + self = [super init]; + if (self) { + _app = app; + _instanceIDProxy = [[FUNInstanceIDProxy alloc] init]; + } + return self; +} + +// This is broken out so it can be mocked for tests. +- (NSString *)instanceIDToken { + return [_instanceIDProxy token]; +} + +- (void)getContext:(void (^)(FUNContext *_Nullable context, NSError *_Nullable error))completion { + // Get the auth token. + [_app getTokenForcingRefresh:NO + withCallback:^(NSString *_Nullable token, NSError *_Nullable error) { + if (error) { + completion(nil, error); + return; + } + + // Get the instance id token. + NSString *_Nullable instanceIDToken = [self instanceIDToken]; + + FUNContext *context = [[FUNContext alloc] initWithAuthToken:token + instanceIDToken:instanceIDToken]; + completion(context, nil); + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNError.h b/Functions/FirebaseFunctions/FUNError.h new file mode 100644 index 0000000..04a9257 --- /dev/null +++ b/Functions/FirebaseFunctions/FUNError.h @@ -0,0 +1,31 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +@class FUNSerializer; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Takes an HTTP status code and optional body and returns a corresponding NSError. + * If an explicit error is encoded in the JSON body, it will be used. + * Otherwise, uses the standard HTTP status code -> error mapping defined in: + * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto + * @param status An HTTP status code. + * @param body Optional body of the HTTP response. + * @param serializer A serializer to use to decode the details in the error response. + * @return The corresponding error. + */ +NSError *FUNErrorForResponse(NSInteger status, NSData *_Nullable body, FUNSerializer *serializer); + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNError.m b/Functions/FirebaseFunctions/FUNError.m new file mode 100644 index 0000000..d2fac6d --- /dev/null +++ b/Functions/FirebaseFunctions/FUNError.m @@ -0,0 +1,186 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FUNError.h" +#import "FIRError.h" + +#import "FUNSerializer.h" + +NS_ASSUME_NONNULL_BEGIN + +NSString *const FIRFunctionsErrorDomain = @"com.firebase.functions"; +NSString *const FIRFunctionsErrorDetailsKey = @"details"; + +/** + * Takes an HTTP status code and returns the corresponding FIRFunctionsErrorCode error code. + * This is the standard HTTP status code -> error mapping defined in: + * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto + * @param status An HTTP status code. + * @return The corresponding error code, or FIRFunctionsErrorCodeUnknown if none. + */ +FIRFunctionsErrorCode FIRFunctionsErrorCodeForHTTPStatus(NSInteger status) { + switch (status) { + case 200: + return FIRFunctionsErrorCodeOK; + case 400: + return FIRFunctionsErrorCodeInvalidArgument; + case 401: + return FIRFunctionsErrorCodeUnauthenticated; + case 403: + return FIRFunctionsErrorCodePermissionDenied; + case 404: + return FIRFunctionsErrorCodeNotFound; + case 409: + return FIRFunctionsErrorCodeAborted; + case 429: + return FIRFunctionsErrorCodeResourceExhausted; + case 499: + return FIRFunctionsErrorCodeCancelled; + case 500: + return FIRFunctionsErrorCodeInternal; + case 501: + return FIRFunctionsErrorCodeUnimplemented; + case 503: + return FIRFunctionsErrorCodeUnavailable; + case 504: + return FIRFunctionsErrorCodeDeadlineExceeded; + } + return FIRFunctionsErrorCodeInternal; +} + +/** + * Takes the name of an error code and returns the enum value for it. + * @param name An error name. + * @return The error code with this name, or FIRFunctionsErrorCodeUnknown if none. + */ +FIRFunctionsErrorCode FIRFunctionsErrorCodeForName(NSString *name) { + static NSDictionary<NSString *, NSNumber *> *errors; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + errors = @{ + @"OK" : @(FIRFunctionsErrorCodeOK), + @"CANCELLED" : @(FIRFunctionsErrorCodeCancelled), + @"UNKNOWN" : @(FIRFunctionsErrorCodeUnknown), + @"INVALID_ARGUMENT" : @(FIRFunctionsErrorCodeInvalidArgument), + @"DEADLINE_EXCEEDED" : @(FIRFunctionsErrorCodeDeadlineExceeded), + @"NOT_FOUND" : @(FIRFunctionsErrorCodeNotFound), + @"ALREADY_EXISTS" : @(FIRFunctionsErrorCodeAlreadyExists), + @"PERMISSION_DENIED" : @(FIRFunctionsErrorCodePermissionDenied), + @"RESOURCE_EXHAUSTED" : @(FIRFunctionsErrorCodeResourceExhausted), + @"FAILED_PRECONDITION" : @(FIRFunctionsErrorCodeFailedPrecondition), + @"ABORTED" : @(FIRFunctionsErrorCodeAborted), + @"OUT_OF_RANGE" : @(FIRFunctionsErrorCodeOutOfRange), + @"UNIMPLEMENTED" : @(FIRFunctionsErrorCodeUnimplemented), + @"INTERNAL" : @(FIRFunctionsErrorCodeInternal), + @"UNAVAILABLE" : @(FIRFunctionsErrorCodeUnavailable), + @"DATA_LOSS" : @(FIRFunctionsErrorCodeDataLoss), + @"UNAUTHENTICATED" : @(FIRFunctionsErrorCodeUnauthenticated), + }; + }); + NSNumber *code = errors[name]; + if (code) { + return code.intValue; + } + return FIRFunctionsErrorCodeInternal; +} + +/** + * Takes a FIRFunctionsErrorCode and returns an English description of it. + * @param code An error code. + * @return A description of the code, or "UNKNOWN" if none. + */ +NSString *FUNDescriptionForErrorCode(FIRFunctionsErrorCode code) { + switch (code) { + case FIRFunctionsErrorCodeOK: + return @"OK"; + case FIRFunctionsErrorCodeCancelled: + return @"CANCELLED"; + case FIRFunctionsErrorCodeUnknown: + return @"UNKNOWN"; + case FIRFunctionsErrorCodeInvalidArgument: + return @"INVALID ARGUMENT"; + case FIRFunctionsErrorCodeDeadlineExceeded: + return @"DEADLINE EXCEEDED"; + case FIRFunctionsErrorCodeNotFound: + return @"NOT FOUND"; + case FIRFunctionsErrorCodeAlreadyExists: + return @"ALREADY EXISTS"; + case FIRFunctionsErrorCodePermissionDenied: + return @"PERMISSION DENIED"; + case FIRFunctionsErrorCodeResourceExhausted: + return @"RESOURCE EXHAUSTED"; + case FIRFunctionsErrorCodeFailedPrecondition: + return @"FAILED PRECONDITION"; + case FIRFunctionsErrorCodeAborted: + return @"ABORTED"; + case FIRFunctionsErrorCodeOutOfRange: + return @"OUT OF RANGE"; + case FIRFunctionsErrorCodeUnimplemented: + return @"UNIMPLEMENTED"; + case FIRFunctionsErrorCodeInternal: + return @"INTERNAL"; + case FIRFunctionsErrorCodeUnavailable: + return @"UNAVAILABLE"; + case FIRFunctionsErrorCodeDataLoss: + return @"DATA LOSS"; + case FIRFunctionsErrorCodeUnauthenticated: + return @"UNAUTHENTICATED"; + } + return @"UNKNOWN"; +} + +NSError *FUNErrorForResponse(NSInteger status, NSData *_Nullable body, FUNSerializer *serializer) { + // Start with reasonable defaults from the status code. + FIRFunctionsErrorCode code = FIRFunctionsErrorCodeForHTTPStatus(status); + NSString *description = FUNDescriptionForErrorCode(code); + id details = nil; + + // Then look through the body for explicit details. + if (body) { + NSError *parseError = nil; + id json = [NSJSONSerialization JSONObjectWithData:body options:0 error:&parseError]; + if (!parseError && [json isKindOfClass:[NSDictionary class]]) { + id errorDetails = json[@"error"]; + if ([errorDetails isKindOfClass:[NSDictionary class]]) { + if ([errorDetails[@"status"] isKindOfClass:[NSString class]]) { + code = FIRFunctionsErrorCodeForName(errorDetails[@"status"]); + // The default description needs to be updated for the new code. + description = FUNDescriptionForErrorCode(code); + } + if ([errorDetails[@"message"] isKindOfClass:[NSString class]]) { + description = (NSString *)errorDetails[@"message"]; + } + details = errorDetails[@"details"]; + if (details) { + NSError *decodeError = nil; + details = [serializer decode:details error:&decodeError]; + // Just ignore the details if there an error decoding them. + } + } + } + } + + if (code == FIRFunctionsErrorCodeOK) { + // Technically, there's an edge case where a developer could explicitly return an error code of + // OK, and we will treat it as success, but that seems reasonable. + return nil; + } + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[NSLocalizedDescriptionKey] = description; + if (details) { + userInfo[FIRFunctionsErrorDetailsKey] = details; + } + return [NSError errorWithDomain:FIRFunctionsErrorDomain code:code userInfo:userInfo]; +} + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNInstanceIDProxy.h b/Functions/FirebaseFunctions/FUNInstanceIDProxy.h new file mode 100644 index 0000000..17ec9ef --- /dev/null +++ b/Functions/FirebaseFunctions/FUNInstanceIDProxy.h @@ -0,0 +1,29 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Note: This file is forked from FIRMessagingInstanceIDProxy.h + +#import <Foundation/Foundation.h> + +/** + * FirebaseFunctions cannot always depend on FIRInstanceID directly, due to how it is + * packaged. To make it easier to make calls to FIRInstanceID, this proxy class, will provide + * method names duplicated from FIRInstanceID, while using reflection-based called to proxy + * the requests. + */ +@interface FUNInstanceIDProxy : NSObject +- (nullable NSString *)token; +@end diff --git a/Functions/FirebaseFunctions/FUNInstanceIDProxy.m b/Functions/FirebaseFunctions/FUNInstanceIDProxy.m new file mode 100644 index 0000000..f89ec98 --- /dev/null +++ b/Functions/FirebaseFunctions/FUNInstanceIDProxy.m @@ -0,0 +1,57 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Note: This is forked from FIRMessagingInstanceIDProxy.m + +#import "FUNInstanceIDProxy.h" + +@implementation FUNInstanceIDProxy + ++ (nonnull instancetype)instanceIDProxy { + static id proxyInstanceID = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + Class instanceIDClass = NSClassFromString(@"FIRInstanceID"); + if (!instanceIDClass) { + proxyInstanceID = nil; + return; + } + SEL instanceIDSelector = NSSelectorFromString(@"instanceID"); + if (![instanceIDClass respondsToSelector:instanceIDSelector]) { + proxyInstanceID = nil; + return; + } + IMP instanceIDImp = [instanceIDClass methodForSelector:instanceIDSelector]; + id (*instanceIDFunc)(id, SEL) = (void *)instanceIDImp; + proxyInstanceID = instanceIDFunc(instanceIDClass, instanceIDSelector); + }); + return (FUNInstanceIDProxy *)proxyInstanceID; +} + +#pragma mark - Tokens + +- (nullable NSString *)token { + id proxy = [[self class] instanceIDProxy]; + SEL getTokenSelector = NSSelectorFromString(@"token"); + if (![proxy respondsToSelector:getTokenSelector]) { + return nil; + } + IMP getTokenIMP = [proxy methodForSelector:getTokenSelector]; + NSString *(*getToken)(id, SEL) = (void *)getTokenIMP; + return getToken(proxy, getTokenSelector); +} + +@end diff --git a/Functions/FirebaseFunctions/FUNSerializer.h b/Functions/FirebaseFunctions/FUNSerializer.h new file mode 100644 index 0000000..598a2a1 --- /dev/null +++ b/Functions/FirebaseFunctions/FUNSerializer.h @@ -0,0 +1,33 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@interface FUNSerializer : NSObject + +/** + * Converts raw Objective-C types into JSON objects. + */ +- (id)encode:(id)object; + +/** + * Converts objects in JSON to Objective-C types. + */ +- (id)decode:(id)object error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNSerializer.m b/Functions/FirebaseFunctions/FUNSerializer.m new file mode 100644 index 0000000..682f981 --- /dev/null +++ b/Functions/FirebaseFunctions/FUNSerializer.m @@ -0,0 +1,231 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FUNSerializer.h" + +#import "FIRError.h" +#import "FUNUsageValidation.h" + +NS_ASSUME_NONNULL_BEGIN + +static NSString *const kLongType = @"type.googleapis.com/google.protobuf.Int64Value"; +static NSString *const kUnsignedLongType = @"type.googleapis.com/google.protobuf.UInt64Value"; +static NSString *const kDateType = @"type.googleapis.com/google.protobuf.Timestamp"; + +@interface FUNSerializer () { + NSDateFormatter *_dateFormatter; +} +@end + +@implementation FUNSerializer + +- (instancetype)init { + self = [super init]; + if (self) { + _dateFormatter = [[NSDateFormatter alloc] init]; + _dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + _dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; + } + return self; +} + +- (id)encodeNumber:(NSNumber *)number { + // Recover the underlying type of the number, using the method described here: + // http://stackoverflow.com/questions/2518761/get-type-of-nsnumber + const char *cType = [number objCType]; + + // Type Encoding values taken from + // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/ + // Articles/ocrtTypeEncodings.html + switch (cType[0]) { + case 'q': + // "long long" might be larger than JS supports, so make it a string. + return @{ + @"@type" : kLongType, + @"value" : [NSString stringWithFormat:@"%@", number], + }; + case 'Q': + // "unsigned long long" might be larger than JS supports, so make it a string. + return @{ + @"@type" : kUnsignedLongType, + @"value" : [NSString stringWithFormat:@"%@", number], + }; + + case 'i': + case 's': + case 'l': + case 'I': + case 'S': + // If it's an integer that isn't too long, so just use the number. + return number; + + case 'f': + case 'd': + // It's a float/double that's not too large. + return number; + + case 'B': + case 'c': + case 'C': + // Boolean values are weird. + // + // On arm64, objCType of a BOOL-valued NSNumber will be "c", even though @encode(BOOL) + // returns "B". "c" is the same as @encode(signed char). Unfortunately this means that + // legitimate usage of signed chars is impossible, but this should be rare. + // + // Just return Boolean values as-is. + return number; + + default: + // All documented codes should be handled above, so this shouldn't happen. + FUNThrowInvalidArgument(@"Unknown NSNumber objCType %s on %@", cType, number); + } +} + +- (id)encode:(id)object { + if ([object isEqual:[NSNull null]]) { + return object; + } + if ([object isKindOfClass:[NSNumber class]]) { + return [self encodeNumber:object]; + } + if ([object isKindOfClass:[NSString class]]) { + return object; + } + if ([object isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *encoded = [NSMutableDictionary dictionary]; + [object + enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) { + encoded[key] = [self encode:obj]; + }]; + return encoded; + } + if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *encoded = [NSMutableArray arrayWithCapacity:[object count]]; + for (id obj in object) { + [encoded addObject:[self encode:obj]]; + } + return encoded; + } + // TODO(klimt): Add this back when we support NSDate. + /* + if ([object isKindOfClass:[NSDate class]]) { + NSString *iso8601 = [_dateFormatter stringFromDate:object]; + return @{ + @"@type" : kDateType, + @"value" : iso8601, + }; + } + */ + FUNThrowInvalidArgument(@"Unsupported type: %@ for value %@", NSStringFromClass([object class]), + object); +} + +NSError *FUNInvalidNumberError(id value, id wrapped) { + NSString *description = [NSString stringWithFormat:@"Invalid number: %@ for %@", value, wrapped]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey : description, + }; + return [NSError errorWithDomain:FIRFunctionsErrorDomain + code:FIRFunctionsErrorCodeInternal + userInfo:userInfo]; +} + +- (id)decodeWrappedType:(NSDictionary *)wrapped error:(NSError **)error { + NSAssert(error, @"error must not be nil"); + NSString *type = wrapped[@"@type"]; + NSString *value = wrapped[@"value"]; + if (!value) { + return nil; + } + if ([type isEqualToString:kLongType]) { + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + NSNumber *n = [formatter numberFromString:value]; + if (!n) { + *error = FUNInvalidNumberError(value, wrapped); + return nil; + } + return n; + } else if ([type isEqualToString:kUnsignedLongType]) { + // NSNumber formatter doesn't handle unsigned long long, so we have to parse it. + const char *str = value.UTF8String; + char *end = NULL; + unsigned long long n = strtoull(str, &end, 10); + if (errno == ERANGE) { + // This number was actually too big for an unsigned long long. + *error = FUNInvalidNumberError(value, wrapped); + return nil; + } + if (*end) { + // The whole string wasn't parsed. + *error = FUNInvalidNumberError(value, wrapped); + return nil; + } + return @(n); + } + return nil; +} + +- (id)decode:(id)object error:(NSError **)error { + NSAssert(error, @"error must not be nil"); + if ([object isKindOfClass:[NSDictionary class]]) { + if (object[@"@type"]) { + id result = [self decodeWrappedType:object error:error]; + if (*error) { + return nil; + } + if (result) { + return result; + } + // Treat unknown types as dictionaries, so we don't crash old clients when we add types. + } + NSMutableDictionary *decoded = [NSMutableDictionary dictionary]; + __block NSError *decodeError = nil; + [object + enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) { + id decodedItem = [self decode:obj error:&decodeError]; + if (decodeError) { + *stop = YES; + return; + } + decoded[key] = decodedItem; + }]; + if (decodeError) { + *error = decodeError; + return nil; + } + return decoded; + } + if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *result = [NSMutableArray arrayWithCapacity:[object count]]; + for (id obj in object) { + id decoded = [self decode:obj error:error]; + if (*error) { + return nil; + } + [result addObject:decoded]; + } + return result; + } + if ([object isKindOfClass:[NSNumber class]] || [object isKindOfClass:[NSString class]] || + [object isEqual:[NSNull null]]) { + return object; + } + FUNThrowInvalidArgument(@"Unsupported type: %@ for value %@", NSStringFromClass([object class]), + object); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNUsageValidation.h b/Functions/FirebaseFunctions/FUNUsageValidation.h new file mode 100644 index 0000000..17b1c4f --- /dev/null +++ b/Functions/FirebaseFunctions/FUNUsageValidation.h @@ -0,0 +1,38 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** Helper for creating a general exception for invalid usage of an API. */ +NSException *FUNInvalidUsage(NSString *exceptionName, NSString *format, ...); + +/** + * Macro to throw exceptions in response to API usage errors. Avoids the lint warning you usually + * get when using @throw and (unlike a function) doesn't trigger warnings about not all codepaths + * returning a value. + * + * Exceptions should only be used for programmer errors made by consumers of the SDK, e.g. + * invalid method arguments. + * + * For recoverable runtime errors, use NSError**. + * For internal programming errors, use FSTFail(). + */ +#define FUNThrowInvalidArgument(format, ...) \ + do { \ + @throw FUNInvalidUsage(@"FIRInvalidArgumentException", format, ##__VA_ARGS__); \ + } while (0) + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/FUNUsageValidation.m b/Functions/FirebaseFunctions/FUNUsageValidation.m new file mode 100644 index 0000000..a50f525 --- /dev/null +++ b/Functions/FirebaseFunctions/FUNUsageValidation.m @@ -0,0 +1,28 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FUNUsageValidation.h" + +NS_ASSUME_NONNULL_BEGIN + +NSException *FUNInvalidUsage(NSString *exceptionName, NSString *format, ...) { + va_list arg_list; + va_start(arg_list, format); + NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; + va_end(arg_list); + + return [[NSException alloc] initWithName:exceptionName reason:formattedString userInfo:nil]; +} + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/Public/FIRError.h b/Functions/FirebaseFunctions/Public/FIRError.h new file mode 100644 index 0000000..a275633 --- /dev/null +++ b/Functions/FirebaseFunctions/Public/FIRError.h @@ -0,0 +1,90 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +// The error domain for codes in the FIRFunctionsErrorCode enum. +FOUNDATION_EXPORT NSString *const FIRFunctionsErrorDomain NS_SWIFT_NAME(FunctionsErrorDomain); + +// The key for finding error details in the NSError userInfo. +FOUNDATION_EXPORT NSString *const FIRFunctionsErrorDetailsKey NS_SWIFT_NAME(FunctionsErrorDetailsKey); + +/** + * The set of error status codes that can be returned from a Callable HTTPS tigger. These are the + * canonical error codes for Google APIs, as documented here: + * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto#L26 + */ +typedef NS_ENUM(NSInteger, FIRFunctionsErrorCode) { + /** The operation completed successfully. */ + FIRFunctionsErrorCodeOK = 0, + /** The operation was cancelled (typically by the caller). */ + FIRFunctionsErrorCodeCancelled = 1, + /** Unknown error or an error from a different error domain. */ + FIRFunctionsErrorCodeUnknown = 2, + /** + * Client specified an invalid argument. Note that this differs from `FailedPrecondition`. + * `InvalidArgument` indicates arguments that are problematic regardless of the state of the + * system (e.g., an invalid field name). + */ + FIRFunctionsErrorCodeInvalidArgument = 3, + /** + * Deadline expired before operation could complete. For operations that change the state of the + * system, this error may be returned even if the operation has completed successfully. For + * example, a successful response from a server could have been delayed long enough for the + * deadline to expire. + */ + FIRFunctionsErrorCodeDeadlineExceeded = 4, + /** Some requested document was not found. */ + FIRFunctionsErrorCodeNotFound = 5, + /** Some document that we attempted to create already exists. */ + FIRFunctionsErrorCodeAlreadyExists = 6, + /** The caller does not have permission to execute the specified operation. */ + FIRFunctionsErrorCodePermissionDenied = 7, + /** + * Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system + * is out of space. + */ + FIRFunctionsErrorCodeResourceExhausted = 8, + /** + * Operation was rejected because the system is not in a state required for the operation's + * execution. + */ + FIRFunctionsErrorCodeFailedPrecondition = 9, + /** + * The operation was aborted, typically due to a concurrency issue like transaction aborts, etc. + */ + FIRFunctionsErrorCodeAborted = 10, + /** Operation was attempted past the valid range. */ + FIRFunctionsErrorCodeOutOfRange = 11, + /** Operation is not implemented or not supported/enabled. */ + FIRFunctionsErrorCodeUnimplemented = 12, + /** + * Internal errors. Means some invariant expected by underlying system has been broken. If you + * see one of these errors, something is very broken. + */ + FIRFunctionsErrorCodeInternal = 13, + /** + * The service is currently unavailable. This is a most likely a transient condition and may be + * corrected by retrying with a backoff. + */ + FIRFunctionsErrorCodeUnavailable = 14, + /** Unrecoverable data loss or corruption. */ + FIRFunctionsErrorCodeDataLoss = 15, + /** The request does not have valid authentication credentials for the operation. */ + FIRFunctionsErrorCodeUnauthenticated = 16, +}; + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/Public/FIRFunctions.h b/Functions/FirebaseFunctions/Public/FIRFunctions.h new file mode 100644 index 0000000..40ec634 --- /dev/null +++ b/Functions/FirebaseFunctions/Public/FIRFunctions.h @@ -0,0 +1,66 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@class FIRApp; +@class FIRHTTPSCallable; + +/** + * `FIRFunctions` is the client for Cloud Functions for a Firebase project. + */ +NS_SWIFT_NAME(Functions) +@interface FIRFunctions : NSObject + +- (id)init NS_UNAVAILABLE; + +/** + * Creates a Cloud Functions client with the default app. + */ ++ (instancetype)functions NS_SWIFT_NAME(functions()); + +/** + * Creates a Cloud Functions client with the given app. + * @param app The app for the Firebase project. + */ ++ (instancetype)functionsForApp:(FIRApp *)app NS_SWIFT_NAME(functions(app:)); + +/** + * Creates a Cloud Functions client with the default app and given region. + * @param region The region for the http trigger, such as "us-central1". + */ +// + (instancetype)functionsForRegion:(NSString *)region NS_SWIFT_NAME(functions(region:)); + +/** + * Creates a Cloud Functions client with the given app and region. + * @param app The app for the Firebase project. + * @param region The region for the http trigger, such as "us-central1". + */ +// clang-format off +// because it incorrectly breaks this NS_SWIFT_NAME. +// + (instancetype)functionsForApp:(FIRApp *)app +// region:(NSString *)region NS_SWIFT_NAME(functions(app:region:)); +// clang-format on + +/** + * Creates a reference to the Callable HTTPS trigger with the given name. + * @param name The name of the Callable HTTPS trigger. + */ +- (FIRHTTPSCallable *)HTTPSCallableWithName:(NSString *)name NS_SWIFT_NAME(httpsCallable(_:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h b/Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h new file mode 100644 index 0000000..948696d --- /dev/null +++ b/Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h @@ -0,0 +1,94 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** + * A `FIRHTTPSCallableResult` contains the result of calling a `FIRHTTPSCallable`. + */ +NS_SWIFT_NAME(HTTPSCallableResult) +@interface FIRHTTPSCallableResult : NSObject + +- (id)init NS_UNAVAILABLE; + + +/** + * The data that was returned from the Callable HTTPS trigger. + * + * The data is in the form of native objects. For example, if your trigger returned an + * array, this object would be an NSArray. If your trigger returned a JavaScript object with + * keys and values, this object would be an NSDictionary. + */ +@property(nonatomic, strong, readonly) id data; + +@end + +/** + * A `FIRHTTPSCallable` is reference to a particular Callable HTTPS trigger in Cloud Functions. + */ +NS_SWIFT_NAME(HTTPSCallable) +@interface FIRHTTPSCallable : NSObject + +- (id)init NS_UNAVAILABLE; + +/** + * Executes this Callable HTTPS trigger asynchronously without any parameters. + * + * The request to the Cloud Functions backend made by this method automatically includes a + * Firebase Instance ID token to identify the app instance. If a user is logged in with Firebase + * Auth, an auth ID token for the user is also automatically included. + * + * Firebase Instance ID sends data to the Firebase backend periodically to collect information + * regarding the app instance. To stop this, see `[FIRInstanceID deleteIDWithHandler:]`. It + * resumes with a new Instance ID the next time you call this method. + * + * @param completion The block to call when the HTTPS request has completed. + */ +- (void)callWithCompletion: + (void (^)(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error))completion + NS_SWIFT_NAME(call(completion:)); + +/** + * Executes this Callable HTTPS trigger asynchronously. + * + * The data passed into the trigger can be any of the following types: + * * NSNull + * * NSString + * * NSNumber + * * NSArray<id>, where the contained objects are also one of these types. + * * NSDictionary<NSString, id>, where the values are also one of these types. + * + * The request to the Cloud Functions backend made by this method automatically includes a + * Firebase Instance ID token to identify the app instance. If a user is logged in with Firebase + * Auth, an auth ID token for the user is also automatically included. + * + * Firebase Instance ID sends data to the Firebase backend periodically to collect information + * regarding the app instance. To stop this, see `[FIRInstanceID deleteIDWithHandler:]`. It + * resumes with a new Instance ID the next time you call this method. + * + * @param data Parameters to pass to the trigger. + * @param completion The block to call when the HTTPS request has completed. + */ +// clang-format off +// because it incorrectly breaks this NS_SWIFT_NAME. +- (void)callWithObject:(nullable id)data + completion:(void (^)(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error))completion + NS_SWIFT_NAME(call(_:completion:)); +// clang-format on + +@end + +NS_ASSUME_NONNULL_END |