Skip to content

Add custom domain support to callable functions #3825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ declare module '@firebase/app-types' {
};
}
interface FirebaseApp {
functions?(region?: string): types.FirebaseFunctions;
functions?(regionOrCustomDomain?: string): types.FirebaseFunctions;
}
}
26 changes: 22 additions & 4 deletions packages/functions/src/api/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,21 @@ export class Service implements FirebaseFunctions, FirebaseService {
private emulatorOrigin: string | null = null;
private cancelAllRequests: Promise<void>;
private deleteService!: () => void;
private region: string;
private customDomain: string | null = null;

/**
* Creates a new Functions service for the given app and (optional) region.
* Creates a new Functions service for the given app and (optional) region or custom domain.
* @param app_ The FirebaseApp to use.
* @param region_ The region to call functions in.
* @param regionOrCustomDomain_ one of:
* a) A region to call functions from, such as us-central1
* b) A custom domain to use as a functions prefix, such as https://mydomain.com
*/
constructor(
private app_: FirebaseApp,
authProvider: Provider<FirebaseAuthInternalName>,
messagingProvider: Provider<FirebaseMessagingName>,
private region_: string = 'us-central1',
regionOrCustomDomain_: string = 'us-central1',
readonly fetchImpl: typeof fetch
) {
this.contextProvider = new ContextProvider(authProvider, messagingProvider);
Expand All @@ -106,6 +110,15 @@ export class Service implements FirebaseFunctions, FirebaseService {
return resolve();
};
});

// Resolve the region or custom domain overload by attempting to parse it.
try {
const url = new URL(regionOrCustomDomain_);
this.customDomain = url.origin;
this.region = 'us-central1';
} catch (e) {
this.region = regionOrCustomDomain_;
}
}

get app(): FirebaseApp {
Expand All @@ -124,11 +137,16 @@ export class Service implements FirebaseFunctions, FirebaseService {
*/
_url(name: string): string {
const projectId = this.app_.options.projectId;
const region = this.region_;
const region = this.region;
if (this.emulatorOrigin !== null) {
const origin = this.emulatorOrigin;
return `${origin}/${projectId}/${region}/${name}`;
}

if (this.customDomain !== null) {
return `${this.customDomain}/${name}`;
}

return `https://${region}-${projectId}.cloudfunctions.net/${name}`;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/functions/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ export function registerFunctions(
Functions: Service
};

function factory(container: ComponentContainer, region?: string): Service {
function factory(container: ComponentContainer, regionOrCustomDomain?: string): Service {
// Dependencies
const app = container.getProvider('app').getImmediate();
const authProvider = container.getProvider('auth-internal');
const messagingProvider = container.getProvider('messaging');

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return new Service(app, authProvider, messagingProvider, region, fetchImpl);
return new Service(app, authProvider, messagingProvider, regionOrCustomDomain, fetchImpl);
}
instance.INTERNAL.registerComponent(
new Component(FUNCTIONS_TYPE, factory, ComponentType.PUBLIC)
Expand Down
25 changes: 25 additions & 0 deletions packages/functions/test/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ describe('Firebase Functions > Service', () => {
'http://localhost:5005/my-project/us-central1/foo'
);
});

it('can use custom region', () => {
const customRegionService = createTestService(app, 'us-west2');
assert.equal(
customRegionService._url('foo'),
'https://us-west2-my-project.cloudfunctions.net/foo'
);
});

it('can use custom domain', () => {
const customDomainService = createTestService(app, 'https://mydomain.com');
assert.equal(
customDomainService._url('foo'),
'https://mydomain.com/foo'
);
});

it('prefers emulator to custom domain', () => {
const customDomainService = createTestService(app, 'https://mydomain.com');
customDomainService.useFunctionsEmulator('http://localhost:5005');
assert.equal(
customDomainService._url('foo'),
'http://localhost:5005/my-project/us-central1/foo'
);
});
});

describe('custom region constructor', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/functions/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function makeFakeApp(options: FirebaseOptions = {}): FirebaseApp {

export function createTestService(
app: FirebaseApp,
region?: string,
regionOrCustomDomain?: string,
authProvider = new Provider<FirebaseAuthInternalName>(
'auth-internal',
new ComponentContainer('test')
Expand All @@ -59,7 +59,7 @@ export function createTestService(
app,
authProvider,
messagingProvider,
region,
regionOrCustomDomain,
fetchImpl
);
const useEmulator = !!process.env.FIREBASE_FUNCTIONS_EMULATOR_ORIGIN;
Expand Down