1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Collections . Immutable ;
4
+ using System . IO ;
5
+ using System . Net . Http ;
6
+ using System . Threading ;
7
+ using System . Threading . Tasks ;
8
+ using FirebaseAdmin ;
9
+ using FirebaseAdmin . Messaging ;
10
+ using Google . Apis . Auth . OAuth2 ;
11
+ using Google . Apis . Http ;
12
+ using Google . Apis . Json ;
13
+ using Google . Apis . Util ;
14
+ using Newtonsoft . Json ;
15
+ using Newtonsoft . Json . Linq ;
16
+
17
+ /// <summary>
18
+ /// A helper class for interacting with the Firebase Instance ID service.Implements the FCM
19
+ /// topic management functionality.
20
+ /// </summary>
21
+ public sealed class InstanceIdClient
22
+ {
23
+ private readonly string iidHost = "https://iid.googleapis.com" ;
24
+
25
+ private readonly string iidSubscriberPath = "iid/v1:batchAdd" ;
26
+
27
+ private readonly string iidUnsubscribePath = "iid/v1:batchRemove" ;
28
+
29
+ private readonly ConfigurableHttpClient httpClient ;
30
+
31
+ private readonly HttpErrorHandler errorHandler ;
32
+
33
+ /// <summary>
34
+ /// Initializes a new instance of the <see cref="InstanceIdClient"/> class.
35
+ /// </summary>
36
+ /// <param name="clientFactory">A default implentation of the HTTP client factory.</param>
37
+ /// <param name="credential">A GoogleCredential.</param>
38
+ /// <param name="projectId">The Project Id for FCM Messaging.</param>
39
+ public InstanceIdClient ( HttpClientFactory clientFactory , GoogleCredential credential , string projectId )
40
+ {
41
+ if ( string . IsNullOrEmpty ( projectId ) )
42
+ {
43
+ throw new ArgumentException (
44
+ "Project ID is required to access messaging service. Use a service account "
45
+ + "credential or set the project ID explicitly via AppOptions. Alternatively "
46
+ + "you can set the project ID via the GOOGLE_CLOUD_PROJECT environment "
47
+ + "variable." ) ;
48
+ }
49
+
50
+ this . httpClient = clientFactory . ThrowIfNull ( nameof ( clientFactory ) )
51
+ . CreateAuthorizedHttpClient ( credential ) ;
52
+
53
+ this . errorHandler = new MessagingErrorHandler ( ) ;
54
+ }
55
+
56
+ /// <summary>
57
+ /// Index of the registration token to which this error is related to.
58
+ /// </summary>
59
+ /// <param name="topic">The topic name to subscribe to.</param>
60
+ /// <param name="registrationTokens">A list of registration tokens to subscribe.</param>
61
+ /// <returns>The response produced by FCM topic management operations.</returns>
62
+ public async Task < TopicManagementResponse > SubscribeToTopic ( string topic , List < string > registrationTokens )
63
+ {
64
+ try
65
+ {
66
+ return await this . SendInstanceIdRequest ( topic , registrationTokens , this . iidSubscriberPath ) . ConfigureAwait ( false ) ;
67
+ }
68
+ catch ( HttpRequestException e )
69
+ {
70
+ throw this . CreateExceptionFromResponse ( e ) ;
71
+ }
72
+ catch ( IOException )
73
+ {
74
+ throw new FirebaseMessagingException ( ErrorCode . Internal , "Error while calling IID backend service" ) ;
75
+ }
76
+ }
77
+
78
+ /// <summary>
79
+ /// Index of the registration token to which this error is related to.
80
+ /// </summary>
81
+ /// <param name="topic">The topic name to unsubscribe from.</param>
82
+ /// <param name="registrationTokens">A list of registration tokens to unsubscribe.</param>
83
+ /// <returns>The response produced by FCM topic management operations.</returns>
84
+ public async Task < TopicManagementResponse > UnsubscribeFromTopic ( string topic , List < string > registrationTokens )
85
+ {
86
+ try
87
+ {
88
+ return await this . SendInstanceIdRequest ( topic , registrationTokens , this . iidUnsubscribePath ) . ConfigureAwait ( false ) ;
89
+ }
90
+ catch ( HttpRequestException e )
91
+ {
92
+ throw this . CreateExceptionFromResponse ( e ) ;
93
+ }
94
+ catch ( IOException )
95
+ {
96
+ throw new FirebaseMessagingException ( ErrorCode . Internal , "Error while calling IID backend service" ) ;
97
+ }
98
+ }
99
+
100
+ private async Task < TopicManagementResponse > SendInstanceIdRequest ( string topic , List < string > registrationTokens , string path )
101
+ {
102
+ string url = string . Format ( "%s/%s" , this . iidHost , path ) ;
103
+ var body = new InstanceIdServiceRequest
104
+ {
105
+ Topic = this . GetPrefixedTopic ( topic ) ,
106
+ RegistrationTokens = registrationTokens ,
107
+ } ;
108
+
109
+ var request = new HttpRequestMessage ( )
110
+ {
111
+ Method = HttpMethod . Post ,
112
+ RequestUri = new Uri ( url ) ,
113
+ Content = NewtonsoftJsonSerializer . Instance . CreateJsonHttpContent ( body ) ,
114
+ } ;
115
+
116
+ request . Headers . Add ( "access_token_auth" , "true" ) ;
117
+
118
+ try
119
+ {
120
+ var response = await this . httpClient . SendAsync ( request , default ( CancellationToken ) ) . ConfigureAwait ( false ) ;
121
+ var json = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
122
+ this . errorHandler . ThrowIfError ( response , json ) ;
123
+ return JsonConvert . DeserializeObject < TopicManagementResponse > ( json ) ;
124
+ }
125
+ catch ( HttpRequestException e )
126
+ {
127
+ throw this . CreateExceptionFromResponse ( e ) ;
128
+ }
129
+ }
130
+
131
+ private FirebaseMessagingException CreateExceptionFromResponse ( HttpRequestException e )
132
+ {
133
+ var temp = e . ToFirebaseException ( ) ;
134
+ return new FirebaseMessagingException (
135
+ temp . ErrorCode ,
136
+ temp . Message ,
137
+ inner : temp . InnerException ,
138
+ response : temp . HttpResponse ) ;
139
+ }
140
+
141
+ private string GetPrefixedTopic ( string topic )
142
+ {
143
+ if ( topic . StartsWith ( "/topics/" ) )
144
+ {
145
+ return topic ;
146
+ }
147
+ else
148
+ {
149
+ return "/topics/" + topic ;
150
+ }
151
+ }
152
+
153
+ private class InstanceIdServiceRequest
154
+ {
155
+ [ JsonProperty ( "to" ) ]
156
+ public string Topic { get ; set ; }
157
+
158
+ [ JsonProperty ( "registration_tokens" ) ]
159
+ public List < string > RegistrationTokens { get ; set ; }
160
+ }
161
+
162
+ private class InstanceIdServiceErrorResponse
163
+ {
164
+ [ JsonProperty ( "error" ) ]
165
+ public string Error { get ; set ; }
166
+ }
167
+
168
+ private class InstanceIdServiceResponse
169
+ {
170
+ [ JsonProperty ( "results" ) ]
171
+ public List < JObject > Results { get ; set ; }
172
+ }
173
+ }
0 commit comments