Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9846af3
Add FidMessage and deprecate TokenMessage
yvonnep165 May 13, 2026
97ac410
Add fids to MulticastMessage interface
yvonnep165 May 13, 2026
7bdf972
Add integration tests
yvonnep165 May 13, 2026
8b5e564
Extract API docstring
yvonnep165 May 13, 2026
208378e
Merge branch 'main' into yp-add-fid-arg
yvonnep165 May 13, 2026
96de332
Change tokens back to required field, add extra docstrings and refact…
yvonnep165 May 14, 2026
ec3fdc4
Fix the lint error
yvonnep165 May 14, 2026
6986cc7
Merge branch 'main' into yp-add-fid-arg
yvonnep165 May 14, 2026
37b2740
Merge branch 'main' into yp-add-fid-arg
yvonnep165 May 14, 2026
cf2163d
Add FidMulticastMessage and Implement Function Overloads on sendEachF…
yvonnep165 May 15, 2026
da81dc2
Extract API doc and remove hyperlink due to duplicate names from func…
yvonnep165 May 15, 2026
d914462
Update sendEachForMulticast docstring
yvonnep165 May 20, 2026
55e645e
Add token deprecation documentation
yvonnep165 May 25, 2026
2316fde
Update integration tests error code for invalid fid target
yvonnep165 May 27, 2026
7854f58
Register new installation-id-not-registered Error Code
yvonnep165 May 28, 2026
9e5d4cf
Extract api docstring
yvonnep165 May 28, 2026
7a802d9
Resolve gemini review comments and add a unit test
yvonnep165 May 28, 2026
4e584c4
Merge remote-tracking branch 'origin/main' into yp-add-fid-arg
yvonnep165 Jun 8, 2026
09f7dd7
Add explicit note in FidMulticastMessage interface to avoid confusion
yvonnep165 Jun 9, 2026
e8febe1
Merge branch 'main' into yp-add-fid-arg
yvonnep165 Jun 9, 2026
2485709
Merge branch 'main' into yp-add-fid-arg
yvonnep165 Jun 18, 2026
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
4 changes: 4 additions & 0 deletions etc/firebase-admin.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ export namespace messaging {
export type DataMessagePayload = DataMessagePayload;
// Warning: (ae-forgotten-export) The symbol "FcmOptions" needs to be exported by the entry point default-namespace.d.ts
export type FcmOptions = FcmOptions;
// Warning: (ae-forgotten-export) The symbol "FidMessage" needs to be exported by the entry point default-namespace.d.ts
export type FidMessage = FidMessage;
// Warning: (ae-forgotten-export) The symbol "LightSettings" needs to be exported by the entry point default-namespace.d.ts
export type LightSettings = LightSettings;
// Warning: (ae-forgotten-export) The symbol "Message" needs to be exported by the entry point default-namespace.d.ts
Expand All @@ -395,6 +397,8 @@ export namespace messaging {
// Warning: (ae-forgotten-export) The symbol "SendResponse" needs to be exported by the entry point default-namespace.d.ts
export type SendResponse = SendResponse;
// Warning: (ae-forgotten-export) The symbol "TokenMessage" needs to be exported by the entry point default-namespace.d.ts
//
// @deprecated
export type TokenMessage = TokenMessage;
// Warning: (ae-forgotten-export) The symbol "TopicMessage" needs to be exported by the entry point default-namespace.d.ts
export type TopicMessage = TopicMessage;
Expand Down
15 changes: 11 additions & 4 deletions etc/firebase-admin.messaging.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ export interface FcmOptions {
analyticsLabel?: string;
}

// @public (undocumented)
export interface FidMessage extends BaseMessage {
// (undocumented)
fid: string;
}

// Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts
//
// @public
Expand All @@ -187,7 +193,7 @@ export interface LightSettings {
}

// @public
export type Message = TokenMessage | TopicMessage | ConditionMessage;
export type Message = FidMessage | TokenMessage | TopicMessage | ConditionMessage;

// @public
export class Messaging {
Expand Down Expand Up @@ -329,8 +335,9 @@ export interface MessagingTopicManagementResponse {

// @public
export interface MulticastMessage extends BaseMessage {
// (undocumented)
tokens: string[];
fids?: string[];
// @deprecated
tokens?: string[];
}

// @public
Expand Down Expand Up @@ -366,7 +373,7 @@ export interface SendResponse {
success: boolean;
}

// @public (undocumented)
// @public @deprecated (undocumented)
export interface TokenMessage extends BaseMessage {
// (undocumented)
token: string;
Expand Down
1 change: 1 addition & 0 deletions src/messaging/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export {
CriticalSound,
ConditionMessage,
FcmOptions,
FidMessage,
LightSettings,
Message,
MessagingTopicManagementResponse,
Expand Down
25 changes: 21 additions & 4 deletions src/messaging/messaging-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export interface BaseMessage {
fcmOptions?: FcmOptions;
}

export interface FidMessage extends BaseMessage {
fid: string;
}
Comment thread
yvonnep165 marked this conversation as resolved.

/**
* @deprecated Use {@link FidMessage} instead.
*/
export interface TokenMessage extends BaseMessage {
token: string;
}
Expand All @@ -40,16 +47,26 @@ export interface ConditionMessage extends BaseMessage {

/**
* Payload for the {@link Messaging.send} operation. The payload contains all the fields
* in the BaseMessage type, and exactly one of token, topic or condition.
* in the BaseMessage type, and exactly one of fid, token, topic or condition.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention that token is deprecated here as well?

in the BaseMessage type, and exactly one of fid, token (deprecated, use fid instead), topic or condition.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Added, Thanks!

*/
export type Message = TokenMessage | TopicMessage | ConditionMessage;
export type Message = FidMessage | TokenMessage | TopicMessage | ConditionMessage;

/**
* Payload for the {@link Messaging.sendEachForMulticast} method. The payload contains all the fields
* in the BaseMessage type, and a list of tokens.
* in the BaseMessage type, and a list of tokens and/or fids.
*/
export interface MulticastMessage extends BaseMessage {
tokens: string[];
/**
* A list of Firebase Installation IDs (FIDs) to target.
*/
fids?: string[];

/**
* A list of registration tokens to target.
*
* @deprecated Use {@link MulticastMessage.fids} instead.
*/
tokens?: string[];
}
Comment thread
yvonnep165 marked this conversation as resolved.

/**
Expand Down
4 changes: 2 additions & 2 deletions src/messaging/messaging-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export function validateMessage(message: Message): void {
}
}

const targets = [anyMessage.token, anyMessage.topic, anyMessage.condition];
const targets = [anyMessage.fid, anyMessage.token, anyMessage.topic, anyMessage.condition];
if (targets.filter((v) => validator.isNonEmptyString(v)).length !== 1) {
throw new FirebaseMessagingError(
MessagingClientErrorCode.INVALID_PAYLOAD,
'Exactly one of topic, token or condition is required');
'Exactly one of fid, topic, token or condition is required');
}

validateStringMap(message.data, 'data');
Expand Down
8 changes: 8 additions & 0 deletions src/messaging/messaging-namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
CriticalSound as TCriticalSound,
ConditionMessage as TConditionMessage,
FcmOptions as TFcmOptions,
FidMessage as TFidMessage,
LightSettings as TLightSettings,
Message as TMessage,
MessagingTopicManagementResponse as TMessagingTopicManagementResponse,
Expand Down Expand Up @@ -174,8 +175,15 @@ export namespace messaging {
*/
export type SendResponse = TSendResponse;

/**
* Type alias to {@link firebase-admin.messaging#FidMessage}.
*/
export type FidMessage = TFidMessage;

/**
* Type alias to {@link firebase-admin.messaging#TokenMessage}.
*
* @deprecated Use {@link firebase-admin.messaging#FidMessage} instead.
*/
export type TokenMessage = TTokenMessage;

Expand Down
39 changes: 33 additions & 6 deletions src/messaging/messaging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,27 +297,54 @@ export class Messaging {
throw new FirebaseMessagingError(
MessagingClientErrorCode.INVALID_ARGUMENT, 'MulticastMessage must be a non-null object');
}
if (!validator.isNonEmptyArray(copy.tokens)) {

const tokens: string[] = copy.tokens || [];
const fids: string[] = copy.fids || [];

if ('tokens' in copy && !validator.isNonEmptyArray(copy.tokens)) {
throw new FirebaseMessagingError(
MessagingClientErrorCode.INVALID_ARGUMENT, 'tokens must be a non-empty array');
}
if (copy.tokens.length > FCM_MAX_BATCH_SIZE) {
if ('fids' in copy && !validator.isNonEmptyArray(copy.fids)) {
throw new FirebaseMessagingError(
MessagingClientErrorCode.INVALID_ARGUMENT, 'fids must be a non-empty array');
}
Comment thread
yvonnep165 marked this conversation as resolved.
if (tokens.length === 0 && fids.length === 0) {
throw new FirebaseMessagingError(
MessagingClientErrorCode.INVALID_ARGUMENT, 'Either tokens or fids must be a non-empty array');
}

const totalLength = tokens.length + fids.length;
if (totalLength > FCM_MAX_BATCH_SIZE) {
throw new FirebaseMessagingError(
MessagingClientErrorCode.INVALID_ARGUMENT,
`tokens list must not contain more than ${FCM_MAX_BATCH_SIZE} items`);
`tokens and fids list must not contain more than ${FCM_MAX_BATCH_SIZE} items in total`);
Comment thread
yvonnep165 marked this conversation as resolved.
Outdated
}

const messages: Message[] = copy.tokens.map((token) => {
return {
const messages: Message[] = [];
tokens.forEach((token) => {
messages.push({
token,
android: copy.android,
apns: copy.apns,
data: copy.data,
notification: copy.notification,
webpush: copy.webpush,
fcmOptions: copy.fcmOptions,
};
});
});
fids.forEach((fid) => {
messages.push({
fid,
android: copy.android,
apns: copy.apns,
data: copy.data,
notification: copy.notification,
webpush: copy.webpush,
fcmOptions: copy.fcmOptions,
});
});
Comment thread
yvonnep165 marked this conversation as resolved.
Outdated

return this.sendEach(messages, dryRun);
}

Expand Down
53 changes: 53 additions & 0 deletions test/integration/messaging.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const topic = 'mock-topic';

const invalidTopic = 'topic-$%#^';

const mockFid = 'mock-fid';

const message: Message = {
data: {
foo: 'bar',
Expand Down Expand Up @@ -80,6 +82,13 @@ const message: Message = {
topic: 'foo-bar',
};

const fidMessage: Message = {
data: message.data,
android: message.android,
apns: message.apns,
fid: mockFid,
};

describe('admin.messaging', () => {
it('send(message, dryRun) returns a message ID', () => {
return getMessaging().send(message, true)
Expand All @@ -88,6 +97,11 @@ describe('admin.messaging', () => {
});
});

it('send(message with fid, dryRun) fails with invalid-argument', () => {
return getMessaging().send(fidMessage, true)
.should.eventually.be.rejected.and.have.property('code', 'messaging/invalid-argument');
});

it('sendEach()', () => {
const messages: Message[] = [message, message, message];
return getMessaging().sendEach(messages, true)
Expand Down Expand Up @@ -138,6 +152,45 @@ describe('admin.messaging', () => {
});
});

it('sendEachForMulticast() with fids', () => {
const multicastMessage: MulticastMessage = {
data: message.data,
android: message.android,
fids: ['not-a-fid', 'also-not-a-fid'],
};
return getMessaging().sendEachForMulticast(multicastMessage, true)
.then((response) => {
expect(response.responses.length).to.equal(2);
expect(response.successCount).to.equal(0);
expect(response.failureCount).to.equal(2);
response.responses.forEach((resp) => {
expect(resp.success).to.be.false;
expect(resp.messageId).to.be.undefined;
expect(resp.error).to.have.property('code', 'messaging/invalid-argument');
});
});
});

it('sendEachForMulticast() with mixed tokens and fids', () => {
const multicastMessage: MulticastMessage = {
data: message.data,
android: message.android,
tokens: ['not-a-token'],
fids: ['not-a-fid'],
};
return getMessaging().sendEachForMulticast(multicastMessage, true)
.then((response) => {
expect(response.responses.length).to.equal(2);
expect(response.successCount).to.equal(0);
expect(response.failureCount).to.equal(2);
response.responses.forEach((resp) => {
expect(resp.success).to.be.false;
expect(resp.messageId).to.be.undefined;
expect(resp.error).to.have.property('code', 'messaging/invalid-argument');
});
});
});

it('subscribeToTopic() returns a response with success count', () => {
return getMessaging().subscribeToTopic(registrationToken, topic)
.then((response) => {
Expand Down
Loading
Loading