Skip to content

Commit 4c053ba

Browse files
authored
Merge pull request #41 from firebase/hkj-fcm-header
Setting X-Firebase-Client in FCM API calls
2 parents 44d3f51 + 7be7e39 commit 4c053ba

File tree

5 files changed

+122
-27
lines changed

5 files changed

+122
-27
lines changed

FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAppTest.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,17 @@ public void GetOrInitService()
210210
});
211211
}
212212

213+
[Fact]
214+
public void GetSdkVersion()
215+
{
216+
var version = FirebaseApp.GetSdkVersion();
217+
218+
var segments = version.Split(".");
219+
Assert.Equal(3, segments.Length);
220+
int result;
221+
Assert.All(segments, (segment) => int.TryParse(segment, out result));
222+
}
223+
213224
public void Dispose()
214225
{
215226
FirebaseApp.DeleteAll();

FirebaseAdmin/FirebaseAdmin.Tests/Messaging/FirebaseMessagingClientTest.cs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,44 @@ public async Task SendAsync()
7171
{
7272
Topic = "test-topic",
7373
};
74+
7475
var response = await client.SendAsync(message);
76+
7577
Assert.Equal("test-response", response);
76-
var req = JsonConvert.DeserializeObject<FirebaseMessagingClient.SendRequest>(
77-
handler.Request);
78+
var req = JsonConvert.DeserializeObject<FirebaseMessagingClient.SendRequest>(handler.Request);
7879
Assert.Equal("test-topic", req.Message.Topic);
7980
Assert.False(req.ValidateOnly);
8081
Assert.Equal(1, handler.Calls);
82+
var versionHeader = handler.RequestHeaders.GetValues("X-Firebase-Client").First();
83+
Assert.Equal(FirebaseMessagingClient.ClientVersion, versionHeader);
84+
}
85+
86+
[Fact]
87+
public async Task SendDryRunAsync()
88+
{
89+
var handler = new MockMessageHandler()
90+
{
91+
Response = new FirebaseMessagingClient.SingleMessageResponse()
92+
{
93+
Name = "test-response",
94+
},
95+
};
96+
var factory = new MockHttpClientFactory(handler);
97+
var client = new FirebaseMessagingClient(factory, MockCredential, "test-project");
98+
var message = new Message()
99+
{
100+
Topic = "test-topic",
101+
};
102+
103+
var response = await client.SendAsync(message, dryRun: true);
81104

82-
// Send in dryRun mode.
83-
response = await client.SendAsync(message, dryRun: true);
84105
Assert.Equal("test-response", response);
85-
req = JsonConvert.DeserializeObject<FirebaseMessagingClient.SendRequest>(
86-
handler.Request);
106+
var req = JsonConvert.DeserializeObject<FirebaseMessagingClient.SendRequest>(handler.Request);
87107
Assert.Equal("test-topic", req.Message.Topic);
88108
Assert.True(req.ValidateOnly);
89-
Assert.Equal(2, handler.Calls);
109+
Assert.Equal(1, handler.Calls);
110+
var versionHeader = handler.RequestHeaders.GetValues("X-Firebase-Client").First();
111+
Assert.Equal(FirebaseMessagingClient.ClientVersion, versionHeader);
90112
}
91113

92114
[Fact]
@@ -142,10 +164,15 @@ public async Task SendAllAsync()
142164
{
143165
Token = "test-token2",
144166
};
167+
145168
var response = await client.SendAllAsync(new[] { message1, message2 });
169+
146170
Assert.Equal(2, response.SuccessCount);
147171
Assert.Equal("projects/fir-adminintegrationtests/messages/8580920590356323124", response.Responses[0].MessageId);
148172
Assert.Equal("projects/fir-adminintegrationtests/messages/5903525881088369386", response.Responses[1].MessageId);
173+
Assert.Equal(1, handler.Calls);
174+
var versionHeader = $"X-Firebase-Client: {FirebaseMessagingClient.ClientVersion}";
175+
Assert.Equal(2, this.CountLinesWithPrefix(handler.Request, versionHeader));
149176
}
150177

151178
[Fact]
@@ -212,11 +239,16 @@ public async Task SendAllAsyncWithError()
212239
{
213240
Token = "test-token2",
214241
};
242+
215243
var response = await client.SendAllAsync(new[] { message1, message2 });
244+
216245
Assert.Equal(1, response.SuccessCount);
217246
Assert.Equal(1, response.FailureCount);
218247
Assert.Equal("projects/fir-adminintegrationtests/messages/8580920590356323124", response.Responses[0].MessageId);
219248
Assert.NotNull(response.Responses[1].Exception);
249+
Assert.Equal(1, handler.Calls);
250+
var versionHeader = $"X-Firebase-Client: {FirebaseMessagingClient.ClientVersion}";
251+
Assert.Equal(2, this.CountLinesWithPrefix(handler.Request, versionHeader));
220252
}
221253

222254
[Fact]
@@ -268,5 +300,10 @@ public async Task HttpErrorAsync()
268300
Assert.False(req.ValidateOnly);
269301
Assert.Equal(1, handler.Calls);
270302
}
303+
304+
private int CountLinesWithPrefix(string body, string linePrefix)
305+
{
306+
return body.Split('\n').Count((line) => line.StartsWith(linePrefix));
307+
}
271308
}
272309
}

FirebaseAdmin/FirebaseAdmin.Tests/MockMessageHandler.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public MockMessageHandler()
4343

4444
public string Request { get; private set; }
4545

46+
public HttpRequestHeaders RequestHeaders { get; private set; }
47+
4648
public HttpStatusCode StatusCode { get; set; }
4749

4850
public object Response { get; set; }
@@ -63,6 +65,8 @@ protected override async Task<HttpResponseMessage> SendAsyncCore(
6365
this.Request = null;
6466
}
6567

68+
this.RequestHeaders = request.Headers;
69+
6670
var resp = new HttpResponseMessage();
6771
string json;
6872
if (this.Response is byte[])

FirebaseAdmin/FirebaseAdmin/FirebaseApp.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using System;
1616
using System.Collections.Generic;
1717
using System.Collections.Immutable;
18+
using System.Reflection;
1819
using System.Runtime.CompilerServices;
1920
using System.Threading.Tasks;
2021
using Google;
@@ -261,6 +262,16 @@ internal static void DeleteAll()
261262
}
262263
}
263264

265+
/// <summary>
266+
/// Returns the current version of the .NET assembly.
267+
/// </summary>
268+
/// <returns>A version string in major.minor.patch format.</returns>
269+
internal static string GetSdkVersion()
270+
{
271+
const int majorMinorPatch = 3;
272+
return typeof(FirebaseApp).GetTypeInfo().Assembly.GetName().Version.ToString(majorMinorPatch);
273+
}
274+
264275
internal T GetOrInit<T>(string id, ServiceFactory<T> initializer)
265276
where T : class, IFirebaseService
266277
{

FirebaseAdmin/FirebaseAdmin/Messaging/FirebaseMessagingClient.cs

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Threading.Tasks;
2121
using Google.Apis.Auth.OAuth2;
2222
using Google.Apis.Http;
23+
using Google.Apis.Json;
2324
using Google.Apis.Requests;
2425
using Google.Apis.Services;
2526
using Google.Apis.Util;
@@ -61,6 +62,14 @@ public FirebaseMessagingClient(
6162
this.fcmClientService = new FCMClientService(this.httpClient);
6263
}
6364

65+
internal static string ClientVersion
66+
{
67+
get
68+
{
69+
return $"fire-admin-dotnet/{FirebaseApp.GetSdkVersion()}";
70+
}
71+
}
72+
6473
/// <summary>
6574
/// Sends a message to the FCM service for delivery. The message gets validated both by
6675
/// the Admin SDK, and the remote FCM service. A successful return value indicates
@@ -92,8 +101,7 @@ public async Task<string> SendAsync(
92101
};
93102
try
94103
{
95-
var response = await this.httpClient.PostJsonAsync(
96-
this.sendUrl, request, cancellationToken).ConfigureAwait(false);
104+
var response = await this.SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
97105
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
98106
if (!response.IsSuccessStatusCode)
99107
{
@@ -157,12 +165,32 @@ public void Dispose()
157165
this.httpClient.Dispose();
158166
}
159167

168+
private static void AddCommonHeaders(HttpRequestMessage request)
169+
{
170+
request.Headers.Add("X-Firebase-Client", ClientVersion);
171+
}
172+
160173
private static FirebaseException CreateExceptionFor(RequestError requestError)
161174
{
162175
return new FirebaseException(requestError.ToString());
163176
}
164177

165-
private async Task<BatchResponse> SendBatchRequestAsync(IEnumerable<Message> messages, bool dryRun, CancellationToken cancellationToken)
178+
private async Task<HttpResponseMessage> SendRequestAsync(object body, CancellationToken cancellationToken)
179+
{
180+
var request = new HttpRequestMessage()
181+
{
182+
Method = HttpMethod.Post,
183+
RequestUri = new Uri(this.sendUrl),
184+
Content = NewtonsoftJsonSerializer.Instance.CreateJsonHttpContent(body),
185+
};
186+
AddCommonHeaders(request);
187+
return await this.httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
188+
}
189+
190+
private async Task<BatchResponse> SendBatchRequestAsync(
191+
IEnumerable<Message> messages,
192+
bool dryRun,
193+
CancellationToken cancellationToken)
166194
{
167195
var responses = new List<SendResponse>();
168196

@@ -181,21 +209,30 @@ private async Task<BatchResponse> SendBatchRequestAsync(IEnumerable<Message> mes
181209
}
182210
else
183211
{
184-
responses.Add(SendResponse.FromException(new FirebaseException($"Unexpected batch response. Response status code was {message.StatusCode}.")));
212+
responses.Add(SendResponse.FromException(new FirebaseException(
213+
$"Unexpected batch response. Response status code was {message.StatusCode}.")));
185214
}
186215
});
187216

188-
await batch.ExecuteAsync(cancellationToken);
217+
await batch.ExecuteAsync(cancellationToken).ConfigureAwait(false);
189218
return new BatchResponse(responses);
190219
}
191220

192-
private BatchRequest CreateBatchRequest(IEnumerable<Message> messages, bool dryRun, BatchRequest.OnResponse<SingleMessageResponse> callback)
221+
private BatchRequest CreateBatchRequest(
222+
IEnumerable<Message> messages,
223+
bool dryRun,
224+
BatchRequest.OnResponse<SingleMessageResponse> callback)
193225
{
194226
var batch = new BatchRequest(this.fcmClientService, FcmBatchUrl);
195227

196228
foreach (var message in messages)
197229
{
198-
batch.Queue(new FCMClientServiceRequest(this.fcmClientService, this.restPath, message, dryRun), callback);
230+
var body = new SendRequest()
231+
{
232+
Message = message,
233+
ValidateOnly = dryRun,
234+
};
235+
batch.Queue(new FCMClientServiceRequest(this.fcmClientService, this.restPath, body), callback);
199236
}
200237

201238
return batch;
@@ -258,16 +295,17 @@ public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args)
258295
private sealed class FCMClientServiceRequest : ClientServiceRequest<string>
259296
{
260297
private readonly string restPath;
261-
private readonly Message message;
262-
private readonly bool dryRun;
298+
private readonly SendRequest body;
263299

264-
public FCMClientServiceRequest(IClientService clientService, string restPath, Message message, bool dryRun)
300+
public FCMClientServiceRequest(FCMClientService clientService, string restPath, SendRequest body)
265301
: base(clientService)
266302
{
267303
this.restPath = restPath;
268-
this.message = message;
269-
this.dryRun = dryRun;
270-
304+
this.body = body;
305+
this.ModifyRequest = (request) =>
306+
{
307+
AddCommonHeaders(request);
308+
};
271309
this.InitParameters();
272310
}
273311

@@ -279,13 +317,7 @@ public FCMClientServiceRequest(IClientService clientService, string restPath, Me
279317

280318
protected override object GetBody()
281319
{
282-
var sendRequest = new SendRequest()
283-
{
284-
Message = this.message,
285-
ValidateOnly = this.dryRun,
286-
};
287-
288-
return sendRequest;
320+
return this.body;
289321
}
290322
}
291323
}

0 commit comments

Comments
 (0)