├── dist
└── .gitkeep
├── docs
└── .gitkeep
├── test
└── .gitkeep
├── 3rd-party
└── .gitkeep
├── .gitignore
├── src
├── AGConnectAdmin.snk
├── AGConnectAdmin.PK.snk
├── AGConnectAdmin.Examples
│ ├── .editorconfig
│ ├── AGConnectAdmin.Examples.csproj
│ ├── Example.SendDataMessage.cs
│ ├── Example.cs
│ ├── Example.SendTopicMessage.cs
│ ├── Example.SendAndroidMessage.cs
│ ├── Example.SendTestMessage.cs
│ ├── Example.SendInstanceAppMessage.cs
│ ├── Example.SendConditionMessage.cs
│ ├── Example.SendWebpushMessage.cs
│ └── Example.SendApnsMessage.cs
├── AGConnectAdmin.Tests
│ ├── Messaging
│ │ ├── MessageTest.cs
│ │ ├── ValidatorTest.cs
│ │ ├── ConvertorTest.cs
│ │ ├── MessagingTest.cs
│ │ └── MessagingClientTest.cs
│ ├── .editorconfig
│ ├── options.json
│ ├── AGConnectAdmin.Tests.csproj
│ └── TestUtils.cs
├── .editorconfig
├── AGConnectAdmin
│ ├── Messaging
│ │ ├── NotificationStyle.cs
│ │ ├── TopicListRequest.cs
│ │ ├── TopicListResponse.cs
│ │ ├── NotificationImportance.cs
│ │ ├── Direction.cs
│ │ ├── TopicEntity.cs
│ │ ├── MessagePart.cs
│ │ ├── NotificationVisibility.cs
│ │ ├── TopicManagementResponse.cs
│ │ ├── TopicManagementRequest.cs
│ │ ├── SendRequest.cs
│ │ ├── SingleMessageResponse.cs
│ │ ├── WebpushAction.cs
│ │ ├── ApnsHmsOptions.cs
│ │ ├── Notification.cs
│ │ ├── LightSettings.cs
│ │ ├── BadgeNotification.cs
│ │ ├── CriticalSound.cs
│ │ ├── WebpushConfig.cs
│ │ ├── Convertors
│ │ │ └── TimeSpanJsonConverter.cs
│ │ ├── LightColor.cs
│ │ ├── AndroidConfig.cs
│ │ ├── Message.cs
│ │ ├── ClickAction.cs
│ │ ├── ApnsConfig.cs
│ │ ├── ApsAlert.cs
│ │ ├── WebpushNotification.cs
│ │ ├── Aps.cs
│ │ ├── AGConnectMessaging.cs
│ │ ├── AndroidNotification.cs
│ │ └── AGConnectMessagingClient.cs
│ ├── IAGConnectService.cs
│ ├── AGConnectAdmin.csproj
│ ├── AGConnectException.cs
│ ├── Utils
│ │ ├── Extensions.cs
│ │ ├── Validator.cs
│ │ └── NewtonsoftJsonSerializer.cs
│ ├── Auth
│ │ └── TokenResponse.cs
│ ├── AppOptions.cs
│ └── AGConnectApp.cs
└── AGConnectAdmin.sln
├── Third Party Open Source Software Notice.docx
├── README_ZH.md
├── README.md
└── LICENSE
/dist/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/3rd-party/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | .vscode/
4 | .vs/
5 |
6 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HMS-Core/hms-push-serverdemo-csharp/HEAD/src/AGConnectAdmin.snk
--------------------------------------------------------------------------------
/src/AGConnectAdmin.PK.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HMS-Core/hms-push-serverdemo-csharp/HEAD/src/AGConnectAdmin.PK.snk
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA2007: 考虑对等待的任务调用 ConfigureAwait
4 | dotnet_diagnostic.CA2007.severity = suggestion
5 |
--------------------------------------------------------------------------------
/Third Party Open Source Software Notice.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HMS-Core/hms-push-serverdemo-csharp/HEAD/Third Party Open Source Software Notice.docx
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/Messaging/MessageTest.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HMS-Core/hms-push-serverdemo-csharp/HEAD/src/AGConnectAdmin.Tests/Messaging/MessageTest.cs
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA2007: 考虑对等待的任务调用 ConfigureAwait
4 | dotnet_diagnostic.CA2007.severity = suggestion
5 |
6 | # CA2000: 丢失范围之前释放对象
7 | dotnet_diagnostic.CA2000.severity = suggestion
8 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "login_uri": "https://login.cloud.huawei.com/oauth2/v2/token",
3 | "api_base_uri": "https://push-api.cloud.huawei.com",
4 | "dev_app_id": "YOUR_REGISTRATION_APPID",
5 | "client_secret": "YOUR_REGISTRATION_APPSECRET"
6 | }
7 |
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA1303: 请不要将文本作为本地化参数传递
4 | dotnet_diagnostic.CA1303.severity = suggestion
5 |
6 | # CA1707: 从成员名称 AGConnectAdmin.Messaging.NotificationPriority.PRIORITY_UNSPECIFIED 中删除下划线。
7 | dotnet_diagnostic.CA1707.severity = suggestion
8 |
9 | # CA1305: 指定 IFormatProvider
10 | dotnet_diagnostic.CA1305.severity = suggestion
11 |
12 | # CA2227: 集合属性应为只读
13 | dotnet_diagnostic.CA2227.severity = suggestion
14 |
15 | # CA1062: 验证公共方法的参数
16 | dotnet_diagnostic.CA1062.severity = suggestion
17 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/AGConnectAdmin.Examples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/AGConnectAdmin.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 | true
9 |
10 | ../AGConnectAdmin.snk
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | PreserveNewest
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/NotificationStyle.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin.Messaging
17 | {
18 | ///
19 | /// Style param of notification
20 | ///
21 | public enum NotificationStyle
22 | {
23 | ///
24 | /// Default style
25 | ///
26 | Default = 0,
27 |
28 | ///
29 | /// Big text style
30 | ///
31 | BigText = 1
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/TopicListRequest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 |
18 | namespace AGConnectAdmin.Messaging
19 | {
20 | ///
21 | /// Represents request parameter for retreive topic list.
22 | ///
23 | public sealed class TopicListRequest
24 | {
25 | ///
26 | /// Gets or sets the token of target device.
27 | ///
28 | [JsonProperty("token")]
29 | public string Token { get; set; }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/IAGConnectService.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin
17 | {
18 | ///
19 | /// A stateful service that can be associated with an . This
20 | /// interface enables tearing down the service when the parent app instance is deleted.
21 | ///
22 | internal interface IAGConnectService
23 | {
24 | ///
25 | /// Cleans up any state associated with this service making it unsuitable for further use.
26 | ///
27 | void Delete();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/TopicListResponse.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 | using System.Collections.Generic;
18 |
19 | namespace AGConnectAdmin.Messaging
20 | {
21 | ///
22 | /// Represents response result for retreive topic list.
23 | ///
24 | public sealed class TopicListResponse : SingleMessageResponse
25 | {
26 | ///
27 | /// Gets or sets the list of topics
28 | ///
29 | [JsonProperty("topics")]
30 | public List Topics { get; set; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/AGConnectAdmin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | netstandard2.0;net45
6 | true
7 | ../AGConnectAdmin.snk
8 | true
9 | Huawei
10 | AGConnect Admin SDK enables server-side .NET developers to integrate HCM into their
11 | services and applications
12 | Copyright (c) Huawei Technologies Co., Ltd. 2019-2019. All rights reserved.
13 | Huawei
14 | Debug;Release;Proxy
15 |
16 |
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/NotificationImportance.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin.Messaging
17 | {
18 | ///
19 | /// The enumerations of notification priority.
20 | ///
21 | public enum NotificationImportance
22 | {
23 | ///
24 | /// LOW PRIORITY
25 | ///
26 | LOW,
27 |
28 | ///
29 | /// NORMAL PRIORITY
30 | ///
31 | NORMAL,
32 |
33 | ///
34 | /// HIGH PRIORITY
35 | ///
36 | HIGH
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/Direction.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin.Messaging
17 | {
18 | ///
19 | /// Different directions a notification can be displayed in.
20 | ///
21 | public enum Direction
22 | {
23 | ///
24 | /// Direction automatically determined.
25 | ///
26 | Auto,
27 |
28 | ///
29 | /// Left to right.
30 | ///
31 | LeftToRight,
32 |
33 | ///
34 | /// Right to left.
35 | ///
36 | RightToLeft,
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/TopicEntity.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using Newtonsoft.Json;
18 |
19 | namespace AGConnectAdmin.Messaging
20 | {
21 | ///
22 | /// Represents a topic entity.
23 | ///
24 | public sealed class TopicEntity
25 | {
26 | ///
27 | /// Gets or sets the topic name.
28 | ///
29 | [JsonProperty("name")]
30 | public string Name { get; set; }
31 |
32 | ///
33 | /// Gets or sets the topic creation date.
34 | ///
35 | [JsonProperty("addDate")]
36 | public DateTime AddDate { get; set; }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/MessagePart.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin.Messaging
17 | {
18 | ///
19 | /// Represents a part of push message.
20 | ///
21 | /// The type itself.
22 | public abstract class MessagePart
23 | {
24 | ///
25 | /// Copies this message part, and validates the content of it to ensure that it can be
26 | /// serialized into the JSON format expected by the AGConnect Cloud Messaging service.
27 | ///
28 | /// A copy of this message part
29 | internal protected abstract T CopyAndValidate();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/Messaging/ValidatorTest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Utils;
17 | using Xunit;
18 |
19 | namespace AGConnectAdmin.Tests
20 | {
21 | public class ValidatorTest
22 | {
23 | [Fact]
24 | public void TestValidator()
25 | {
26 | Assert.False(Validator.IsInRange(0, 1, 5), "out of range at low");
27 | Assert.True(Validator.IsInRange(1, 1, 5), "low bound of range");
28 | Assert.True(Validator.IsInRange(2, 1, 5), "in range");
29 | Assert.True(Validator.IsInRange(5, 1, 5), "high bound of range");
30 | Assert.False(Validator.IsInRange(6, 1, 5), "out of range at high");
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/NotificationVisibility.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin.Messaging
17 | {
18 | ///
19 | /// The enumerations of notification visibility
20 | ///
21 | public enum NotificationVisibility
22 | {
23 | ///
24 | /// Visibility
25 | ///
26 | VISIBILITY_UNSPECIFIED,
27 |
28 | ///
29 | /// Private
30 | ///
31 | PRIVATE,
32 |
33 | ///
34 | /// Public
35 | ///
36 | PUBLIC,
37 |
38 | ///
39 | /// Secret
40 | ///
41 | SECRET
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/TopicManagementResponse.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 |
18 | namespace AGConnectAdmin.Messaging
19 | {
20 | ///
21 | /// Represents response result for topic management.
22 | ///
23 | public sealed class TopicManagementResponse : SingleMessageResponse
24 | {
25 | ///
26 | /// Gets or sets the count of failure.
27 | ///
28 | [JsonProperty("failureCount")]
29 | public int FailureCount { get; set; }
30 |
31 | ///
32 | /// Gets or sets the count of success.
33 | ///
34 | [JsonProperty("successCount")]
35 | public int SuccessCount { get; set; }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/TopicManagementRequest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 | using System.Collections.Generic;
18 |
19 | namespace AGConnectAdmin.Messaging
20 | {
21 | ///
22 | /// Represents request parameter for topic management.
23 | ///
24 | public sealed class TopicManagementRequest
25 | {
26 | ///
27 | /// Gets or sets the specified topic.
28 | ///
29 | [JsonProperty("topic")]
30 | public string Topic { get; set; }
31 |
32 | ///
33 | /// Gets or sets the specified tokens.
34 | ///
35 | [JsonProperty("tokenArray")]
36 | public List Tokens { get; set; }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendDataMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendDataMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Data = "{'key1':'value1', 'key2':'value2'}",
31 | Android = new AndroidConfig()
32 | {
33 | Urgency = UrgencyPriority.HIGH
34 | },
35 | Token = new List() { TOKEN_ANDROID }
36 | });
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/AGConnectException.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 |
18 | namespace AGConnectAdmin
19 | {
20 | ///
21 | /// Common error type for all exceptions raised by AGConnect APIs.
22 | ///
23 | [Serializable]
24 | public class AGConnectException : Exception
25 | {
26 | internal AGConnectException() {}
27 |
28 | internal AGConnectException(string message)
29 | : base(message) { }
30 |
31 | internal AGConnectException(string message, Exception inner)
32 | : base(message, inner) { }
33 |
34 | ///
35 | protected AGConnectException(
36 | System.Runtime.Serialization.SerializationInfo info,
37 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Xunit.Abstractions;
17 |
18 | namespace AGConnectAdmin.Examples
19 | {
20 | public partial class Example
21 | {
22 | #region Tokens
23 | private const string TOKEN_IOS = "your ios token";
24 | private const string TOKEN_ANDROID = "your android token";
25 | private const string TOKEN_WEB = "your web token";
26 | #endregion
27 |
28 | protected ITestOutputHelper Logger { get; private set; }
29 |
30 | public Example(ITestOutputHelper logger)
31 | {
32 | Logger = logger;
33 |
34 | AGConnectApp.Create(new AppOptions()
35 | {
36 | ClientId = "your client id",
37 | ClientSecret = "your cliient secret",
38 | });
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/SendRequest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 |
18 | namespace AGConnectAdmin.Messaging
19 | {
20 | ///
21 | /// Represents the envelope message accepted by the AGC backend service, including the message
22 | /// payload and other options like validate_only.
23 | ///
24 | internal class SendRequest
25 | {
26 | ///
27 | /// Gets or sets the message body.
28 | ///
29 | [JsonProperty("message")]
30 | public Message Message { get; set; }
31 |
32 | ///
33 | /// Gets or sets if it's a validation message only that it will not be sent actually.
34 | ///
35 | [JsonProperty("validate_only")]
36 | public bool ValidateOnly { get; set; }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendTopicMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Threading.Tasks;
18 | using Xunit;
19 |
20 | namespace AGConnectAdmin.Examples
21 | {
22 | partial class Example
23 | {
24 | [Fact]
25 | public async Task SendTopicMessage()
26 | {
27 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
28 | {
29 | Android = new AndroidConfig()
30 | {
31 | Notification = new AndroidNotification()
32 | {
33 | Title = "Notification from .NET",
34 | Body = "Hello world!",
35 | ClickAction = ClickAction.OpenApp()
36 | }
37 | },
38 | Topic = "News"
39 | });
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendAndroidMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendAndroidMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Android = new AndroidConfig()
31 | {
32 | Notification = new AndroidNotification()
33 | {
34 | Title = "Notification from .NET",
35 | Body = "Hello world!",
36 | ClickAction = ClickAction.OpenApp()
37 | }
38 | },
39 | Token = new List() { TOKEN_ANDROID }
40 | });
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendTestMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendTestMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Android = new AndroidConfig()
31 | {
32 | Notification = new AndroidNotification()
33 | {
34 | Title = "Notification from .NET",
35 | Body = "Hello world!",
36 | ClickAction = ClickAction.OpenApp()
37 | }
38 | },
39 | Token = new List() { TOKEN_ANDROID }
40 | }, true);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendInstanceAppMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendInstanceAppMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Android = new AndroidConfig()
31 | {
32 | Notification = new AndroidNotification()
33 | {
34 | Title = "Notification from .NET",
35 | Body = "Hello world!",
36 | ClickAction = ClickAction.OpenApp()
37 | },
38 | FastAppTarget = FastAppTarget.Development
39 | },
40 | Token = new List() { TOKEN_ANDROID }
41 | });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/SingleMessageResponse.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 |
18 | namespace AGConnectAdmin.Messaging
19 | {
20 | ///
21 | /// Represents the response messages sent by the AGC backend service when sending a single
22 | /// message. Primarily consists of the message ID (Name) that indicates success handoff to AGC.
23 | ///
24 | public class SingleMessageResponse
25 | {
26 | ///
27 | /// Gets or sets the result code.
28 | ///
29 | [JsonProperty("code")]
30 | public string Code { get; set; }
31 |
32 | ///
33 | /// Gets or set the result message.
34 | ///
35 | [JsonProperty("msg")]
36 | public string Message { get; set; }
37 |
38 | ///
39 | /// Gets or set a request ID corresponding to the request.
40 | ///
41 | [JsonProperty("requestId")]
42 | public string RequestId { get; set; }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendConditionMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendConditionMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Android = new AndroidConfig()
31 | {
32 | Notification = new AndroidNotification()
33 | {
34 | Title = "Notification from .NET",
35 | Body = "Hello world!",
36 | ClickAction = ClickAction.OpenApp()
37 | }
38 | },
39 | Condition = "'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)",
40 | Token = new List() { TOKEN_ANDROID },
41 | });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendWebpushMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendWebpushMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Webpush = new WebpushConfig()
31 | {
32 | Notification = new WebpushNotification()
33 | {
34 | Title = "Notification from .NET",
35 | Body = "Hello world!",
36 | },
37 | HmsOptions = new WebpushHmsOptions()
38 | {
39 | Link = "https://example.com"
40 | }
41 | },
42 | Token = new List() { TOKEN_WEB }
43 | });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/TestUtils.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using System.Collections.Generic;
18 | using System.IO;
19 |
20 | namespace AGConnectAdmin.Tests
21 | {
22 | internal static class TestUtils
23 | {
24 | private const string ServiceAccountFile = "/options.json";
25 |
26 | private static bool Initialized = false;
27 | public static void GlobalInit()
28 | {
29 | if (!Initialized)
30 | {
31 | Initialized = true;
32 | AGConnectApp.Create(TestUtils.ReadOptionsFromDisk());
33 | }
34 | }
35 |
36 | public static AppOptions ReadOptionsFromDisk()
37 | {
38 | var content = File.ReadAllText(Environment.CurrentDirectory + ServiceAccountFile);
39 | IDictionary keyValuePairs = NewtonsoftJsonSerializer.Instance.Deserialize>(content);
40 | return new AppOptions()
41 | {
42 | LoginUri = keyValuePairs["login_uri"],
43 | ApiBaseUri = keyValuePairs["api_base_uri"],
44 | ClientId = keyValuePairs["dev_app_id"],
45 | ClientSecret = keyValuePairs["client_secret"]
46 | };
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Utils/Extensions.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using System.Collections.Generic;
18 |
19 | namespace AGConnectAdmin
20 | {
21 | ///
22 | /// A collection of extension methods for internal use in the SDK.
23 | ///
24 | internal static class Extensions
25 | {
26 | ///
27 | /// A utility method for throwing an System.ArgumentNullException if the object is null.
28 | ///
29 | public static T ThrowIfNull(this T obj, string paramName)
30 | {
31 | if (obj == null)
32 | {
33 | throw new ArgumentNullException(paramName);
34 | }
35 |
36 | return obj;
37 | }
38 |
39 | ///
40 | /// Creates a shallow copy of a collection of key-value pairs.
41 | ///
42 | public static IReadOnlyDictionary Copy(
43 | this IEnumerable> source)
44 | {
45 | var copy = new Dictionary();
46 | foreach (var entry in source)
47 | {
48 | copy[entry.Key] = entry.Value;
49 | }
50 |
51 | return copy;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Examples/Example.SendApnsMessage.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading.Tasks;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Examples
22 | {
23 | partial class Example
24 | {
25 | [Fact]
26 | public async Task SendApnsMessage()
27 | {
28 | await AGConnectMessaging.DefaultInstance.SendAsync(new Message()
29 | {
30 | Apns = new ApnsConfig()
31 | {
32 | Aps = new Aps()
33 | {
34 | Alert = new ApsAlert()
35 | {
36 | Title = "Notification from .NET",
37 | Body = "Hello world!"
38 | }
39 | },
40 | HmsOptions = new ApnsHmsOptions()
41 | {
42 | TargetUserType = ApnsTargetUserType.Test
43 | },
44 | CustomData = new Dictionary()
45 | {
46 | ["key1"] = "value1"
47 | }
48 | },
49 | Token = new List() { TOKEN_IOS }
50 | });
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/WebpushAction.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using Newtonsoft.Json;
19 |
20 | namespace AGConnectAdmin.Messaging
21 | {
22 | ///
23 | /// Represents an action available to users when the notification is presented.
24 | ///
25 | public class WebpushAction
26 | {
27 | ///
28 | /// Initializes a new instance of the class.
29 | ///
30 | public WebpushAction() { }
31 |
32 | internal WebpushAction(WebpushAction action)
33 | {
34 | this.Action = action.Action;
35 | this.Title = action.Title;
36 | this.Icon = action.Icon;
37 | }
38 |
39 | ///
40 | /// Gets or sets the name of the Action.
41 | ///
42 | [JsonProperty("action")]
43 | public string Action { get; set; }
44 |
45 | ///
46 | /// Gets or sets the title text.
47 | ///
48 | [JsonProperty("title")]
49 | public string Title { get; set; }
50 |
51 | ///
52 | /// Gets or sets the icon URL.
53 | ///
54 | [JsonProperty("icon")]
55 | public string Icon { get; set; }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/ApnsHmsOptions.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 |
18 | namespace AGConnectAdmin.Messaging
19 | {
20 | ///
21 | /// Represents Apple Push Notification Service HMS options.
22 | ///
23 | public class ApnsHmsOptions : MessagePart
24 | {
25 | ///
26 | /// Gets or sets target user type.
27 | ///
28 | [JsonProperty("target_user_type")]
29 | public ApnsTargetUserType TargetUserType { get; set; } = ApnsTargetUserType.Test;
30 |
31 | ///
32 | internal protected override ApnsHmsOptions CopyAndValidate()
33 | {
34 | var copy = new ApnsHmsOptions()
35 | {
36 | TargetUserType = this.TargetUserType,
37 | };
38 |
39 | return copy;
40 | }
41 | }
42 |
43 | ///
44 | /// Represents the target user type.
45 | ///
46 | public enum ApnsTargetUserType
47 | {
48 | ///
49 | /// Test user.
50 | ///
51 | Test = 1,
52 |
53 | ///
54 | /// Normal user.
55 | ///
56 | Normal = 2,
57 |
58 | ///
59 | /// VoIP user.
60 | ///
61 | VoIP = 3
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Auth/TokenResponse.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using Newtonsoft.Json;
18 |
19 | namespace AGConnectAdmin.Auth
20 | {
21 | internal sealed class TokenResponse
22 | {
23 | [JsonProperty("access_token")]
24 | public string AccessToken { get; set; }
25 |
26 | [JsonProperty("expires_in")]
27 | public long? Expires { get; set; }
28 |
29 | [JsonProperty("scope")]
30 | public string Scope { get; set; }
31 |
32 | [JsonProperty("error")]
33 | public long? Error { get; set; }
34 |
35 | [JsonProperty("error_description")]
36 | public string Description { get; set; }
37 |
38 | [JsonIgnore]
39 | private DateTime CreateTime;
40 |
41 | TokenResponse()
42 | {
43 | CreateTime = DateTime.UtcNow;
44 | }
45 |
46 | private static TimeSpan TimeInAdvance = TimeSpan.FromMinutes(5);
47 |
48 | internal string GetValidAccessToken()
49 | {
50 | if (string.IsNullOrEmpty(AccessToken))
51 | {
52 | return null;
53 | }
54 |
55 | TimeSpan leftDuration = CreateTime.AddSeconds(Expires ?? 0) - DateTime.UtcNow;
56 | if (leftDuration < TimeInAdvance)
57 | {
58 | return null;
59 | }
60 |
61 | return AccessToken;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/Notification.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using AGConnectAdmin.Utils;
19 | using Newtonsoft.Json;
20 | using System;
21 |
22 | namespace AGConnectAdmin.Messaging
23 | {
24 | ///
25 | /// Represents the notification parameters that can be included in a .
26 | ///
27 | public class Notification : MessagePart
28 | {
29 | ///
30 | /// Gets or sets the title of the notification.
31 | ///
32 | [JsonProperty("title")]
33 | public string Title { get; set; }
34 |
35 | ///
36 | /// Gets or sets the body of the notification.
37 | ///
38 | [JsonProperty("body")]
39 | public string Body { get; set; }
40 |
41 | ///
42 | /// Gets or sets the image url of the notification.
43 | ///
44 | [JsonProperty("image")]
45 | public string Image { get; set; }
46 |
47 | ///
48 | internal protected override Notification CopyAndValidate()
49 | {
50 | if (!string.IsNullOrEmpty(Image) && !Validator.IsHttpsUrl(Image))
51 | {
52 | throw new ArgumentException("Image must be a https url.");
53 | }
54 |
55 | return new Notification()
56 | {
57 | Title = Title,
58 | Body = Body,
59 | Image = Image
60 | };
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/LightSettings.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging.Convertors;
17 | using Newtonsoft.Json;
18 | using System;
19 |
20 | namespace AGConnectAdmin.Messaging
21 | {
22 | ///
23 | /// Represents the light settings.
24 | ///
25 | public class LightSettings : MessagePart
26 | {
27 | ///
28 | /// Gets or sets the light color.
29 | ///
30 | [JsonProperty("color")]
31 | public LightColor Color { get; set; }
32 |
33 | ///
34 | /// Gets or sets the duration of turning on the light.
35 | ///
36 | [JsonProperty("light_on_duration")]
37 | [JsonConverter(typeof(TimeSpanJsonConverter))]
38 | public TimeSpan? LightOnDuration { get; set; }
39 |
40 | ///
41 | /// Gets or set the duration of turning off the light.
42 | ///
43 | [JsonProperty("light_off_duration")]
44 | [JsonConverter(typeof(TimeSpanJsonConverter))]
45 | public TimeSpan? LightOffDuration { get; set; }
46 |
47 | ///
48 | internal protected override LightSettings CopyAndValidate()
49 | {
50 | //copy
51 | var copy = new LightSettings()
52 | {
53 | Color = Color?.CopyAndValidate(),
54 | LightOffDuration = LightOffDuration,
55 | LightOnDuration = LightOnDuration
56 | };
57 |
58 | return copy;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Utils/Validator.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Text.RegularExpressions;
19 | using System.Linq;
20 |
21 | namespace AGConnectAdmin.Utils
22 | {
23 | internal static class Validator
24 | {
25 | private static Regex COLOR_PATTERN = new Regex("^#[0-9a-f]{6}$", RegexOptions.IgnoreCase);
26 |
27 | internal static bool IsUrl(string url)
28 | {
29 | Uri uri;
30 | if (Uri.TryCreate(url, UriKind.Absolute, out uri))
31 | {
32 | return uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps;
33 | }
34 | return false;
35 | }
36 |
37 | internal static bool IsHttpsUrl(string url)
38 | {
39 | Uri uri;
40 | if (Uri.TryCreate(url, UriKind.Absolute, out uri))
41 | {
42 | return uri.Scheme == Uri.UriSchemeHttps;
43 | }
44 | return false;
45 | }
46 |
47 | internal static bool IsColor(string color)
48 | {
49 | return COLOR_PATTERN.Match(color).Success;
50 | }
51 |
52 | internal static bool IsInRange(T value, T min, T max) where T : IComparable
53 | {
54 | if (min.CompareTo(max) == 1)
55 | {
56 | throw new ArgumentException("The specified range is not correct, maximum must greater than or equal to minimum.");
57 | }
58 | return min.CompareTo(value) <= 0 && max.CompareTo(value) >= 0;
59 | }
60 |
61 | internal static bool IsAllInRange(IEnumerable values, T min, T max) where T : IComparable
62 | {
63 | return values.All(i => IsInRange(i, min, max));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/AppOptions.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | namespace AGConnectAdmin
17 | {
18 | ///
19 | /// Configurable options that can be specified when creating a .
20 | ///
21 | public sealed class AppOptions
22 | {
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | public AppOptions()
27 | {
28 | LoginUri = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
29 | ApiBaseUri = "https://push-api.cloud.huawei.com/v1";
30 | }
31 |
32 | internal AppOptions(AppOptions options)
33 | {
34 | this.LoginUri = options.LoginUri;
35 | this.ApiBaseUri = options.ApiBaseUri;
36 | this.ClientId = options.ClientId;
37 | this.ClientSecret = options.ClientSecret;
38 | }
39 |
40 | internal string GetApiUri()
41 | {
42 | return ApiBaseUri + string.Format("/{0}/messages:send", ClientId) ;
43 | }
44 |
45 | ///
46 | /// Gets or sets the login url that use to request access token, it's optional.
47 | ///
48 | public string LoginUri { get; set; }
49 |
50 | ///
51 | /// Gets or sets the API base path, it's optional.
52 | /// This property is optional.
53 | ///
54 | public string ApiBaseUri { get; set; }
55 |
56 | ///
57 | /// Gets or sets the APP ID from AGC.
58 | ///
59 | public string ClientId { get; set; }
60 |
61 | ///
62 | /// Gets orset the App Secret from AGC.
63 | ///
64 | public string ClientSecret { get; set; }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/BadgeNotification.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class
16 | */
17 |
18 | using AGConnectAdmin.Utils;
19 | using Newtonsoft.Json;
20 | using System;
21 |
22 | namespace AGConnectAdmin.Messaging
23 | {
24 | ///
25 | /// Represents the badge notification options that can be included in a
26 | /// .
27 | ///
28 | public class BadgeNotification : MessagePart
29 | {
30 | ///
31 | /// Gets or sets the increase number of the click badge notification.
32 | ///
33 | /// The number must between 0 to 100
34 | [JsonProperty("add_num")]
35 | public int? AddNum { get; set; }
36 |
37 | ///
38 | /// Gets or sets the number of the click badge notification.
39 | ///
40 | /// The number must between 0 to 100
41 | [JsonProperty("set_num")]
42 | public int? SetNum { get; set; }
43 |
44 | ///
45 | /// Gets or sets the class of the click badge notification.
46 | ///
47 | [JsonProperty("class")]
48 | public string Class { get; set; }
49 |
50 | ///
51 | internal protected override BadgeNotification CopyAndValidate()
52 | {
53 | var copy = new BadgeNotification()
54 | {
55 | AddNum = this.AddNum,
56 | SetNum = this.SetNum,
57 | Class = this.Class,
58 | };
59 |
60 | if(AddNum != null && !Validator.IsInRange(AddNum.Value, 0, 100))
61 | {
62 | throw new ArgumentException("AddNum must be within [0, 100].");
63 | }
64 |
65 | if (SetNum != null && !Validator.IsInRange(SetNum.Value, 0, 100))
66 | {
67 | throw new ArgumentException("SetNum must be within [0, 100].");
68 | }
69 |
70 | return copy;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29025.244
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AGConnectAdmin", "AGConnectAdmin\AGConnectAdmin.csproj", "{B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AGConnectAdmin.Tests", "AGConnectAdmin.Tests\AGConnectAdmin.Tests.csproj", "{87F67C2E-1F28-4371-9894-E09D819EB04B}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AGConnectAdmin.Examples", "AGConnectAdmin.Examples\AGConnectAdmin.Examples.csproj", "{72BE72BF-0248-4AE1-A662-0F326DE9F567}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{196D2272-7E6C-488A-A8B9-6D05FD9CF3EC}"
13 | ProjectSection(SolutionItems) = preProject
14 | .editorconfig = .editorconfig
15 | EndProjectSection
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|Any CPU = Debug|Any CPU
20 | Proxy|Any CPU = Proxy|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}.Proxy|Any CPU.ActiveCfg = Proxy|Any CPU
27 | {B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}.Proxy|Any CPU.Build.0 = Proxy|Any CPU
28 | {B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {B4BD90E7-D50F-4962-AEAD-9E067C38ECE1}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {87F67C2E-1F28-4371-9894-E09D819EB04B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {87F67C2E-1F28-4371-9894-E09D819EB04B}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {87F67C2E-1F28-4371-9894-E09D819EB04B}.Proxy|Any CPU.ActiveCfg = Debug|Any CPU
33 | {87F67C2E-1F28-4371-9894-E09D819EB04B}.Proxy|Any CPU.Build.0 = Debug|Any CPU
34 | {87F67C2E-1F28-4371-9894-E09D819EB04B}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {87F67C2E-1F28-4371-9894-E09D819EB04B}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {72BE72BF-0248-4AE1-A662-0F326DE9F567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {72BE72BF-0248-4AE1-A662-0F326DE9F567}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {72BE72BF-0248-4AE1-A662-0F326DE9F567}.Proxy|Any CPU.ActiveCfg = Debug|Any CPU
39 | {72BE72BF-0248-4AE1-A662-0F326DE9F567}.Proxy|Any CPU.Build.0 = Debug|Any CPU
40 | {72BE72BF-0248-4AE1-A662-0F326DE9F567}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {72BE72BF-0248-4AE1-A662-0F326DE9F567}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(ExtensibilityGlobals) = postSolution
47 | SolutionGuid = {ABC1FD25-D921-4EB8-90DF-67FCBF0FC1F9}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/README_ZH.md:
--------------------------------------------------------------------------------
1 | # 华为推送服务服务端C#示例代码
2 | [English](README.md) | 中文
3 |
4 | ## 目录
5 | * [简介](#简介)
6 | * [安装](#安装)
7 | * [配置](#配置)
8 | * [环境要求](#环境要求)
9 | * [示例代码](#示例代码)
10 | * [技术支持](#技术支持)
11 | * [授权许可](#授权许可)
12 |
13 | ## 简介
14 |
15 | C#示例代码对华为推送服务(HUAWEI Push Kit)服务端接口进行封装,供您参考使用。
16 |
17 | 示例代码目录结构如下:
18 | | 文件夹 | 说明 |
19 | | ------------ | ----------- |
20 | |AGConnectAdmin|封装服务端接口的类库|
21 | |AGConnectAdmin.Examples|类库使用示例|
22 |
23 | 示例代码中的主要的类定义如下:
24 | | 类名 | 说明 |
25 | | ----------- | ----------- |
26 | |AppOptions|应用相关配置|
27 | |AGConnectApp|应用|
28 | |AGConnectMessaging|推送服务相关接口的调用方法|
29 | |Message|消息体|
30 |
31 | ## 安装
32 |
33 | 1. 解压示例代码。
34 | 2. 将解压后的AGConnectAdmin复制到你的Visual Studio Solution中适当的位置,在你的应用工程中引用对应的程序集即可。
35 | 3. 参考示例代码来使用AGConnectAdmin中的类。
36 |
37 | ## 配置
38 |
39 | 以下描述了AppOptions类的相关参数。
40 |
41 | | 参数 | 说明 |
42 | | ----------- | ----------- |
43 | |ClientId|应用ID,从应用信息中获取|
44 | |ClientSecret|应用访问密钥,从应用信息中获取|
45 | |LoginUri|华为OAuth 2.0获取token的地址。详情请参见[基于OAuth 2.0开放鉴权-客户端模式](https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/oauth2-0000001212610981#section128682386159?ha_source=hms1)。|
46 | |ApiBaseUri|推送服务的访问地址。详情请参见[推送服务-下行消息](https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/android-server-dev-0000001050040110?ha_source=hms1)。|
47 |
48 | ## 环境要求
49 |
50 | 示例代码工程需要使用Visual Studio 2017或以上版本的开发工具打开,类库提供以下种框架版本:
51 |
52 | - .NET Framework 4.5以上
53 | - .NET Standard 2.0以上
54 |
55 | ## 示例代码
56 |
57 | AGConnectAdmin.Examples提供所有示例代码及相应功能。
58 |
59 | 1. 发送Android透传消息。文件目录:[SendDataMessage.cs](src/AGConnectAdmin.Examples/Example.SendDataMessage.cs)
60 |
61 | 2. 发送Android通知栏消息。文件目录:[SendAndroidMessage.cs](src/AGConnectAdmin.Examples/Example.SendAndroidMessage.cs)
62 |
63 | 3. 基于主题发送消息。文件目录:[SendTopicMessage.cs](src/AGConnectAdmin.Examples/Example.SendTopicMessage.cs)
64 |
65 | 4. 基于条件发送消息。文件目录:[SendConditionMessage.cs](src/AGConnectAdmin.Examples/Example.SendConditionMessage.cs)
66 |
67 | 5. 向华为快应用发送消息。文件目录:[SendInstanceAppMessage.cs](src/AGConnectAdmin.Examples/Example.SendInstanceAppMessage.cs)
68 |
69 | 6. 基于WebPush代理发送消息。文件目录:[SendWebpushMessage.cs](src/AGConnectAdmin.Examples/Example.SendWebpushMessage.cs)
70 |
71 | 7. 基于APNs代理发送消息。文件目录:[SendApnsMessage.cs](src/AGConnectAdmin.Examples/Example.SendApnsMessage.cs)
72 |
73 | 8. 发送测试消息。文件目录:[SendTestMessage.cs](src/AGConnectAdmin.Examples/Example.SendTestMessage.cs)
74 |
75 |
76 | ## 技术支持
77 | 如果您对HMS Core还处于评估阶段,可在[Reddit社区](https://www.reddit.com/r/HuaweiDevelopers/)获取关于HMS Core的最新讯息,并与其他开发者交流见解。
78 |
79 | 如果您对使用HMS示例代码有疑问,请尝试:
80 | - 开发过程遇到问题上[Stack Overflow](https://stackoverflow.com/questions/tagged/huawei-mobile-services?tab=Votes),在`huawei-mobile-services`标签下提问,有华为研发专家在线一对一解决您的问题。
81 | - 到[华为开发者论坛](https://developer.huawei.com/consumer/cn/forum/blockdisplay?fid=18?ha_source=hms1) HMS Core板块与其他开发者进行交流。
82 |
83 | 如果您在尝试示例代码中遇到问题,请向仓库提交[issue](https://github.com/HMS-Core/hms-push-serverdemo-csharp/issues),也欢迎您提交[Pull Request](https://github.com/HMS-Core/hms-push-serverdemo-csharp/pulls)。
84 |
85 | ## 授权许可
86 | 华为推送服务C#示例代码经过[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0)授权许可.
87 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/CriticalSound.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class
16 | */
17 |
18 | using Newtonsoft.Json;
19 | using System;
20 |
21 | namespace AGConnectAdmin.Messaging
22 | {
23 | ///
24 | /// The sound configuration for APNs critical alerts.
25 | ///
26 | public class CriticalSound : MessagePart
27 | {
28 | ///
29 | /// Gets or sets a value indicating whether to set the critical alert flag on the sound
30 | /// configuration.
31 | ///
32 | [JsonIgnore]
33 | public bool Critical { get; set; }
34 |
35 | ///
36 | /// Gets or sets the name of the sound to be played. This should be a sound file in your
37 | /// app's main bundle or in the Library/Sounds folder of your app's container
38 | /// directory. Specify the string default to play the system sound.
39 | ///
40 | [JsonProperty("name")]
41 | public string Name { get; set; }
42 |
43 | ///
44 | /// Gets or sets the volume for the critical alert's sound. Must be a value between 0.0
45 | /// (silent) and 1.0 (full volume).
46 | ///
47 | [JsonProperty("volume")]
48 | public double? Volume { get; set; }
49 |
50 | ///
51 | /// Gets or sets the integer representation of the property, which
52 | /// is how APNs expects it.
53 | ///
54 | [JsonProperty("critical")]
55 | private int? CriticalInt
56 | {
57 | get
58 | {
59 | if (this.Critical)
60 | {
61 | return 1;
62 | }
63 |
64 | return null;
65 | }
66 |
67 | set
68 | {
69 | this.Critical = value == 1;
70 | }
71 | }
72 |
73 | ///
74 | internal protected override CriticalSound CopyAndValidate()
75 | {
76 | var copy = new CriticalSound()
77 | {
78 | Critical = this.Critical,
79 | Name = this.Name,
80 | Volume = this.Volume,
81 | };
82 | if (string.IsNullOrEmpty(copy.Name))
83 | {
84 | throw new ArgumentException("Name must be specified for CriticalSound");
85 | }
86 |
87 | if (copy.Volume < 0 || copy.Volume > 1)
88 | {
89 | throw new ArgumentException("Volume of CriticalSound must be in the interval [0, 1]");
90 | }
91 |
92 | return copy;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/WebpushConfig.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using AGConnectAdmin.Utils;
19 | using Newtonsoft.Json;
20 | using System;
21 | using System.Collections.Generic;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents Web push configuration of notification.
27 | ///
28 | public class WebpushConfig : MessagePart
29 | {
30 | ///
31 | /// Gets or sets the Webpush options included in the message.
32 | ///
33 | [JsonProperty("hms_options")]
34 | public WebpushHmsOptions HmsOptions { get; set; }
35 |
36 | ///
37 | /// Gets or sets the Webpush HTTP headers. Refer
38 | ///
39 | /// Webpush specification for supported headers.
40 | ///
41 | [JsonProperty("headers")]
42 | public IReadOnlyDictionary Headers { get; set; }
43 |
44 | ///
45 | /// Gets or sets the Webpush notification included in the message.
46 | ///
47 | [JsonProperty("notification")]
48 | public WebpushNotification Notification { get; set; }
49 |
50 | ///
51 | internal protected override WebpushConfig CopyAndValidate()
52 | {
53 | return new WebpushConfig()
54 | {
55 | Headers = this.Headers?.Copy(),
56 | Notification = this.Notification?.CopyAndValidate(),
57 | HmsOptions = this.HmsOptions?.CopyAndValidate(),
58 | };
59 | }
60 | }
61 |
62 | ///
63 | /// Represents the Webpush-specific notification options.
64 | ///
65 | public sealed class WebpushHmsOptions : MessagePart
66 | {
67 | ///
68 | /// Gets or sets the link to open when the user clicks on the notification.
69 | ///
70 | [JsonProperty("link")]
71 | public string Link { get; set; }
72 |
73 | ///
74 | internal protected override WebpushHmsOptions CopyAndValidate()
75 | {
76 | var copy = new WebpushHmsOptions()
77 | {
78 | Link = this.Link,
79 | };
80 |
81 | if (copy.Link != null)
82 | {
83 | if (!Validator.IsHttpsUrl(copy.Link))
84 | {
85 | throw new ArgumentException("The link options should be a valid https url.");
86 | }
87 | }
88 |
89 | return copy;
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/Convertors/TimeSpanJsonConverter.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 | using System;
18 |
19 | namespace AGConnectAdmin.Messaging.Convertors
20 | {
21 | ///
22 | /// Convert TimeSpan to a string.
23 | ///
24 | public class TimeSpanJsonConverter : JsonConverter
25 | {
26 | ///
27 | public override bool CanConvert(Type objectType)
28 | {
29 | return typeof(TimeSpan).Equals(objectType) || typeof(TimeSpan?).Equals(objectType);
30 | }
31 |
32 | ///
33 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
34 | {
35 | if (reader.TokenType == JsonToken.Null)
36 | {
37 | return null;
38 | }
39 | if (reader.TokenType != JsonToken.String)
40 | {
41 | throw new JsonSerializationException("Only string value can be convert to TimeSpan.");
42 | }
43 | try
44 | {
45 | var value = reader.Value.ToString();
46 | var segments = value.TrimEnd('s').Split('.');
47 | var seconds = long.Parse(segments[0]);
48 | var timeSpan = TimeSpan.FromSeconds(seconds);
49 | if (segments.Length == 2)
50 | {
51 | var subsecondNanos = long.Parse(segments[1].TrimStart('0'));
52 | timeSpan = timeSpan.Add(TimeSpan.FromMilliseconds(subsecondNanos / 1e6));
53 | }
54 | return timeSpan;
55 | }
56 | catch (Exception e)
57 | {
58 | throw new JsonSerializationException($"Convert value \"{reader.Value}\" to TimeSpan failed.", e);
59 | }
60 | }
61 |
62 | ///
63 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
64 | {
65 | if (value == null)
66 | {
67 | if (serializer.NullValueHandling == NullValueHandling.Include)
68 | {
69 | writer.WriteNull();
70 | }
71 | return;
72 | }
73 |
74 | var timeSpan = value as TimeSpan?;
75 | var totalSeconds = timeSpan.Value.TotalSeconds;
76 | var seconds = (long)Math.Floor(totalSeconds);
77 | var subsecondNanos = (long)((totalSeconds - seconds) * 1e9);
78 | if (subsecondNanos > 0)
79 | {
80 | writer.WriteValue(string.Format("{0}.{1:D9}s", seconds, subsecondNanos));
81 | }
82 |
83 | writer.WriteValue(string.Format("{0}s", seconds));
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/Messaging/ConvertorTest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using AGConnectAdmin.Messaging.Convertors;
18 | using Newtonsoft.Json;
19 | using Xunit;
20 |
21 | namespace AGConnectAdmin.Tests
22 | {
23 | public class ConvertorTest
24 | {
25 | [Fact]
26 | public void SerializeTimeSpan()
27 | {
28 | string json = JsonConvert.SerializeObject(new TestObject1()
29 | {
30 | TTL = TimeSpan.FromSeconds(5)
31 | });
32 |
33 | Assert.Equal("{\"TTL\":\"5s\"}", json);
34 | }
35 |
36 | [Fact]
37 | public void SerializeNullableTimeSpan()
38 | {
39 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
40 | {
41 | NullValueHandling = NullValueHandling.Ignore
42 | };
43 |
44 | string json1 = JsonConvert.SerializeObject(new TestObject2()
45 | {
46 | });
47 | string json2 = JsonConvert.SerializeObject(new TestObject2()
48 | {
49 | TTL = TimeSpan.FromSeconds(5),
50 | TTLS = new TimeSpan[] { TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5) }
51 | });
52 |
53 | Assert.Equal("{\"TTL\":\"5s\"}", json2);
54 | }
55 |
56 | [Fact]
57 | public void NoConvertor()
58 | {
59 | string json = JsonConvert.SerializeObject(new TestObject3()
60 | {
61 | TTL = TimeSpan.FromSeconds(5)
62 | });
63 |
64 | Assert.NotEqual("{\"TTL\":\"5s\"}", json);
65 | }
66 |
67 | [Fact]
68 | public void DeserializeTimeSpan()
69 | {
70 | TestObject1 to = JsonConvert.DeserializeObject("{\"TTL\":\"5s\"}");
71 | Assert.Equal(5, to.TTL.TotalSeconds);
72 | }
73 |
74 | [Fact]
75 | public void DeserializeNullableTimeSpan()
76 | {
77 | var to1 = JsonConvert.DeserializeObject("{}");
78 | var to2 = JsonConvert.DeserializeObject("{\"TTL\":\"5s\"}");
79 |
80 | Assert.Null(to1.TTL);
81 | Assert.NotNull(to2.TTL);
82 | Assert.Equal(5, to2.TTL.Value.TotalSeconds);
83 | }
84 |
85 | class TestObject1
86 | {
87 | [JsonConverter(typeof(TimeSpanJsonConverter))]
88 | public TimeSpan TTL { get; set; }
89 | }
90 |
91 | class TestObject2
92 | {
93 | [JsonConverter(typeof(TimeSpanJsonConverter))]
94 | public TimeSpan? TTL { get; set; }
95 |
96 | [JsonConverter(typeof(TimeSpanJsonConverter))]
97 | public TimeSpan[] TTLS { get; set; }
98 | }
99 |
100 | class TestObject3
101 | {
102 | public TimeSpan? TTL { get; set; }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/LightColor.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Utils;
17 | using Newtonsoft.Json;
18 | using System;
19 |
20 | namespace AGConnectAdmin.Messaging
21 | {
22 | ///
23 | /// Represents an ARGB (alpha, red, green, blue) color.
24 | ///
25 | public sealed class LightColor : MessagePart
26 | {
27 | ///
28 | /// Gets the alpha component value of this Color structure.
29 | ///
30 | [JsonProperty("alpha")]
31 | public double Alpha { get; private set; }
32 |
33 | ///
34 | /// Gets the red component value of this Color structure.
35 | ///
36 | [JsonProperty("red")]
37 | public double Red { get; private set; }
38 |
39 | ///
40 | /// Gets the green component value of this Color structure.
41 | ///
42 | [JsonProperty("green")]
43 | public double Green { get; private set; }
44 |
45 | ///
46 | /// Gets the blue component value of this Color structure.
47 | ///
48 | [JsonProperty("blue")]
49 | public double Blue { get; private set; }
50 |
51 | ///
52 | /// Creates a Color structure from the four ARGB component (alpha, red, green, and blue) values.
53 | ///
54 | /// The alpha value (from 0 to 1)
55 | /// The red value (from 0 to 1)
56 | /// The green value (from 0 to 1)
57 | /// The blue value (from 0 to 1)
58 | ///
59 | public static LightColor FromArbg(double alpha, double red, double green, double blue)
60 | {
61 | return new LightColor()
62 | {
63 | Alpha = alpha,
64 | Red = red,
65 | Green = green,
66 | Blue = blue
67 | };
68 | }
69 |
70 | ///
71 | /// Creates a Color structure from the specified 8-bit color values (red, green, and blue). The alpha value is implicitly 1.0 (fully opaque).
72 | ///
73 | /// The red value (from 0 to 1)
74 | /// The green value (from 0 to 1)
75 | /// The blue value (from 0 to 1)
76 | ///
77 | public static LightColor FromArbg(double red, double green, double blue)
78 | {
79 | return FromArbg(1.0f, red, green, blue);
80 | }
81 |
82 | ///
83 | internal protected override LightColor CopyAndValidate()
84 | {
85 | if (!Validator.IsInRange(this.Red, 0, 1) ||
86 | !Validator.IsInRange(this.Green, 0, 1) ||
87 | !Validator.IsInRange(this.Blue, 0, 1))
88 | {
89 | throw new ArgumentException("The value of red/blue/green color must be within [0,1].");
90 | }
91 |
92 | return FromArbg(Alpha, Red, Green, Blue);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HMS Core Push Kit Sample Code (C#)
2 | English | [中文](README_ZH.md)
3 | ## Contents
4 | * [Introduction](#Introduction)
5 | * [Installation](#Installation)
6 | * [Configuration](#Configuration)
7 | * [Environment Requirements](#Environment-Requirements)
8 | * [Sample Code](#Sample-Code)
9 | * [Technical Support](#technical-support)
10 | * [License](#License)
11 |
12 | ## Introduction
13 |
14 | The sample code for C# encapsulates the server-side APIs of Push Kit, for your reference or direct use.
15 |
16 | The following table describes folders of C# sample code.
17 | | Folder| Description|
18 | | ------------ | ----------- |
19 | |AGConnectAdmin|Class library where Push Kit server APIs are encapsulated.|
20 | |AGConnectAdmin.Examples|Class library usage examples.|
21 |
22 | The following table describes main classes used in the sample code.
23 | | Class Name| Description|
24 | | ----------- | ----------- |
25 | |AppOptions|App-related configuration.|
26 | |AGConnectApp|App.|
27 | |AGConnectMessaging|Push Kit API calling methods.|
28 | |Message|Message body.|
29 |
30 | ## Installation
31 |
32 | 1. Decompress the sample code.
33 | 2. Copy **AGConnectAdmin** to a proper position in your Visual Studio solution and reference the corresponding assembly in your project.
34 | 3. Use the classes in **AGConnectAdmin** by referring to the sample code.
35 |
36 | ## Configuration
37 |
38 | The following table describes the parameters related to the **AppOptions** class.
39 |
40 | | Parameter| Description|
41 | | ----------- | ----------- |
42 | |ClientId|App ID, which is obtained from the app information.|
43 | |ClientSecret|App secret, which is obtained from the app information.|
44 | |LoginUri|URL for Huawei OAuth 2.0 to obtain a token. For details, please refer to [OAuth 2.0-based Authentication](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/oauth2-0000001212610981).|
45 | |ApiBaseUri|Access address of Push Kit. For details, please refer to [Downlink Message Sending](https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-server-dev-0000001050040110?ha_source=hms1).|
46 |
47 | ## Environment Requirements
48 |
49 | The demo projects need to be opened using Visual Studio 2017 or a later version. The following framework versions are supported:
50 |
51 | - .NET Framework 4.5 or later
52 | - .NET Standard 2.0 or later
53 |
54 | ## Sample Code
55 |
56 | **AGConnectAdmin.Examples** provides all sample code and corresponding functions.
57 |
58 | 1. Send an Android data message. Code location: [SendDataMessage.cs](src/AGConnectAdmin.Examples/Example.SendDataMessage.cs)
59 |
60 | 2. Send an Android notification message. Code location: [SendAndroidMessage.cs](src/AGConnectAdmin.Examples/Example.SendAndroidMessage.cs)
61 |
62 | 3. Send a message by topic.Code location: [SendTopicMessage.cs](src/AGConnectAdmin.Examples/Example.SendTopicMessage.cs)
63 |
64 | 4. Send a message by conditions. Code location: [SendConditionMessage.cs](src/AGConnectAdmin.Examples/Example.SendConditionMessage.cs)
65 |
66 | 5. Send a message to a Huawei quick app. Code location: [SendInstanceAppMessage.cs](src/AGConnectAdmin.Examples/Example.SendInstanceAppMessage.cs)
67 |
68 | 6. Send a message through the WebPush agent. Code location: [SendWebpushMessage.cs](src/AGConnectAdmin.Examples/Example.SendWebpushMessage.cs)
69 |
70 | 7. Send a message through the APNs agent. Code location: [SendApnsMessage.cs](src/AGConnectAdmin.Examples/Example.SendApnsMessage.cs)
71 |
72 | 8. Send a test message. Code location: [SendTestMessage.cs](src/AGConnectAdmin.Examples/Example.SendTestMessage.cs)
73 |
74 |
75 | ## Technical Support
76 | You can visit the [Reddit community](https://www.reddit.com/r/HuaweiDevelopers/) to obtain the latest information about HMS Core and communicate with other developers.
77 |
78 | If you have any questions about the sample code, try the following:
79 | - Visit [Stack Overflow](https://stackoverflow.com/questions/tagged/huawei-mobile-services?tab=Votes), submit your questions, and tag them with `huawei-mobile-services`. Huawei experts will answer your questions.
80 | - Visit the HMS Core section in the [HUAWEI Developer Forum](https://forums.developer.huawei.com/forumPortal/en/home?fid=0101187876626530001?ha_source=hms1) and communicate with other developers.
81 |
82 | If you encounter any issues when using the sample code, submit your [issues](https://github.com/HMS-Core/hms-push-serverdemo-csharp/issues) or submit a [pull request](https://github.com/HMS-Core/hms-push-serverdemo-csharp/pulls).
83 |
84 | ## License
85 | The sample code is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
86 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/AndroidConfig.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using System;
19 | using AGConnectAdmin.Messaging.Convertors;
20 | using Newtonsoft.Json;
21 | using Newtonsoft.Json.Converters;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents the Android-specific options that can be included in a .
27 | ///
28 | public class AndroidConfig : MessagePart
29 | {
30 | ///
31 | /// Gets or sets a collapse key for the message. Collapse key serves as an identifier for a
32 | /// group of messages that can be collapsed, so that only the last message gets sent when
33 | /// delivery can be resumed. A maximum of 4 different collapse keys may be active at any
34 | /// given time.
35 | ///
36 | [JsonProperty("collapse_key")]
37 | public int? CollapseKey { get; set; }
38 |
39 | ///
40 | /// Gets or sets the priority of the message.
41 | ///
42 | [JsonProperty("urgency")]
43 | [JsonConverter(typeof(StringEnumConverter))]
44 | public UrgencyPriority? Urgency { get; set; }
45 |
46 | ///
47 | /// Gets or sets the Category, eg. "PLAY_VOICE"
48 | ///
49 | [JsonProperty("category")]
50 | public string Category { get; set; }
51 |
52 | ///
53 | /// Gets or sets the time-to-live duration of the message.
54 | ///
55 | [JsonProperty("ttl")]
56 | [JsonConverter(typeof(TimeSpanJsonConverter))]
57 | public TimeSpan? TTL { get; set; }
58 |
59 | ///
60 | /// Gets or sets the BI-tag of the message.
61 | ///
62 | [JsonProperty("bi_tag")]
63 | public string BITag { get; set; }
64 |
65 | ///
66 | /// Gets or sets the fast app target of the message.
67 | ///
68 | [JsonProperty("fast_app_target")]
69 | public FastAppTarget? FastAppTarget { get; set; }
70 |
71 | ///
72 | /// Gets or sets the custom data of the message.
73 | ///
74 | [JsonProperty("data")]
75 | public string Data { get; set; }
76 |
77 | ///
78 | /// Gets or sets the Android notification to be included in the message.
79 | ///
80 | [JsonProperty("notification")]
81 | public AndroidNotification Notification { get; set; }
82 |
83 | ///
84 | internal protected override AndroidConfig CopyAndValidate()
85 | {
86 | // copy
87 | var copy = new AndroidConfig()
88 | {
89 | CollapseKey = this.CollapseKey,
90 | Urgency = this.Urgency,
91 | TTL = this.TTL,
92 | Category = this.Category,
93 | BITag = this.BITag,
94 | FastAppTarget = this.FastAppTarget,
95 | Data = this.Data,
96 | Notification = this.Notification?.CopyAndValidate()
97 | };
98 |
99 | // validate
100 | var totalSeconds = copy.TTL?.TotalSeconds ?? 0;
101 | if (totalSeconds < 0)
102 | {
103 | throw new ArgumentException("TTL must not be negative.");
104 | }
105 |
106 | return copy;
107 | }
108 | }
109 |
110 | ///
111 | /// Priority that can be set on an .
112 | ///
113 | public enum UrgencyPriority
114 | {
115 | ///
116 | /// High priority message.
117 | ///
118 | HIGH,
119 |
120 | ///
121 | /// Normal priority message.
122 | ///
123 | NORMAL
124 | }
125 |
126 | ///
127 | /// Fast app target of .
128 | ///
129 | public enum FastAppTarget
130 | {
131 | ///
132 | /// Development target
133 | ///
134 | Development = 1,
135 |
136 | ///
137 | /// Production target
138 | ///
139 | Production = 2,
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/Message.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Linq;
21 | using Newtonsoft.Json;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents a message that can be sent via AGConnect Cloud Messaging. Contains payload
27 | /// information as well as the recipient information. The recipient information must be
28 | /// specified by setting exactly one of the , or
29 | /// fields.
30 | ///
31 | public class Message : MessagePart
32 | {
33 | ///
34 | /// Gets or sets the data to be included in the message.
35 | ///
36 | [JsonProperty("data")]
37 | public string Data { get; set; }
38 |
39 | ///
40 | /// Gets or sets the notification information to be included in the message.
41 | ///
42 | [JsonProperty("notification")]
43 | public Notification Notification { get; set; }
44 |
45 | ///
46 | /// Gets or sets the Android-specific information to be included in the message.
47 | ///
48 | [JsonProperty("android")]
49 | public AndroidConfig Android { get; set; }
50 |
51 | ///
52 | /// Gets or sets the APNs-specific information to be included in the message.
53 | ///
54 | [JsonProperty("apns")]
55 | public ApnsConfig Apns { get; set; }
56 |
57 | ///
58 | /// Gets or sets the Webpush-specific information to be included in the message.
59 | ///
60 | [JsonProperty("webpush")]
61 | public WebpushConfig Webpush { get; set; }
62 |
63 | ///
64 | /// Gets or sets the registration tokens of the devices to which the message should be sent.
65 | ///
66 | [JsonProperty("token")]
67 | public IReadOnlyList Token { get; set; }
68 |
69 | ///
70 | /// Gets or sets the name of the AGC messaging topic to which the message should be sent.
71 | ///
72 | [JsonProperty("topic")]
73 | public string Topic { get; set; }
74 |
75 | ///
76 | /// Gets or sets the condition to which the message should be sent. Must be a valid
77 | /// condition string such as "'foo' in topics".
78 | ///
79 | [JsonProperty("condition")]
80 | public string Condition { get; set; }
81 |
82 | ///
83 | internal protected override Message CopyAndValidate()
84 | {
85 | // copy
86 | var copy = new Message()
87 | {
88 | Data = Data,
89 | Notification = Notification?.CopyAndValidate(),
90 | Android = Android?.CopyAndValidate(),
91 | Apns = Apns?.CopyAndValidate(),
92 | Webpush = Webpush?.CopyAndValidate(),
93 | Token = Token?.ToList(),
94 | Topic = Topic,
95 | Condition = Condition,
96 | };
97 |
98 | // validate
99 | var tokenList = Token != null ? new List(Token) : new List();
100 | var nonnullTokens = tokenList.FindAll((target) => !string.IsNullOrEmpty(target));
101 |
102 | var fieldList = new List()
103 | {
104 | copy.Topic, copy.Condition,
105 | };
106 | var nonnullFields = fieldList.FindAll((target) => !string.IsNullOrEmpty(target));
107 |
108 | if (nonnullFields.Count > 1)
109 | {
110 | throw new ArgumentException("Exactly one of Token, Topic or Condition is required.");
111 | }
112 |
113 | if (nonnullFields.Count == 1 && nonnullTokens.Count > 0)
114 | {
115 | throw new ArgumentException("Exactly one of Token, Topic or Condition is required.");
116 | }
117 |
118 | if (nonnullFields.Count == 0 && nonnullTokens.Count == 0)
119 | {
120 | throw new ArgumentException("Exactly one of Token, Topic or Condition is required.");
121 | }
122 |
123 | return copy;
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/Messaging/MessagingTest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using AGConnectAdmin.Messaging;
17 | using System.Collections.Generic;
18 | using System.Threading;
19 | using System.Threading.Tasks;
20 | using Xunit;
21 |
22 | namespace AGConnectAdmin.Tests
23 | {
24 | public class MessagingTest
25 | {
26 | static MessagingTest()
27 | {
28 | TestUtils.GlobalInit();
29 | }
30 |
31 | [Fact]
32 | public async Task SendMessageCancel()
33 | {
34 | var app = AGConnectApp.DefaultInstance;
35 | var messaging = AGConnectMessaging.DefaultInstance;
36 | var canceller = new CancellationTokenSource();
37 | canceller.Cancel();
38 | await Assert.ThrowsAsync(
39 | async () => await messaging.SendAsync(
40 | new Message() { Topic = "test-topic" }, canceller.Token));
41 | }
42 |
43 | [Fact]
44 | public async Task SendTopicMessage()
45 | {
46 | var app = AGConnectApp.Create(new AppOptions()
47 | {
48 | ClientId = "11111111",
49 | ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
50 | });
51 |
52 | var msg = AGConnectMessaging.GetMessaging(app);
53 |
54 | await msg.SendAsync(new Message()
55 | {
56 | Notification = new Notification()
57 | {
58 | Title = "Test Message",
59 | Body = "Detail Message"
60 | },
61 | Topic = "News",
62 | Token = new string[] { "yyyyyyyyyyyyyyyyyy" }
63 | });
64 | }
65 |
66 | [Fact]
67 | public async Task SendConditionMessage()
68 | {
69 | var app = AGConnectApp.Create(new AppOptions()
70 | {
71 | ClientId = "11111111",
72 | ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
73 | });
74 |
75 | var msg = AGConnectMessaging.GetMessaging(app);
76 |
77 | await msg.SendAsync(new Message()
78 | {
79 | Notification = new Notification()
80 | {
81 | Title = "Test Message",
82 | Body = "Detail Message",
83 | },
84 | Android = new AndroidConfig()
85 | {
86 | Notification = new AndroidNotification()
87 | {
88 | ClickAction = ClickAction.OpenUrl("http://example.com")
89 | }
90 | },
91 | Token = new string[] { "yyyyyyyyyyyyyyyyyy" }
92 | });
93 | }
94 |
95 | [Fact]
96 | public async Task SubscribeTopic()
97 | {
98 | var app = AGConnectApp.Create(new AppOptions()
99 | {
100 | ClientId = "11111111",
101 | ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
102 | });
103 |
104 | var msg = AGConnectMessaging.GetMessaging(app);
105 | var tokens = new List() { "sdjlfjiwekfnskdjfksdfjskdfjsdf" };
106 | var topic = "News";
107 | await msg.SubscribeToTopicAsync(tokens.AsReadOnly(), topic);
108 | }
109 |
110 | [Fact]
111 | public async Task UnsubscribeTopic()
112 | {
113 | var app = AGConnectApp.Create(new AppOptions()
114 | {
115 | ClientId = "11111111",
116 | ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
117 | });
118 |
119 | var msg = AGConnectMessaging.GetMessaging(app);
120 | var tokens = new List() { "sdjlfjiwekfnskdjfksdfjskdfjsdf" };
121 | var topic = "News";
122 | await msg.UnsubscribeFromTopicAsync(tokens.AsReadOnly(), topic);
123 | }
124 |
125 | [Fact]
126 | public async Task GetTopicList()
127 | {
128 | var app = AGConnectApp.Create(new AppOptions()
129 | {
130 | ClientId = "11111111",
131 | ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
132 | });
133 |
134 | var msg = AGConnectMessaging.GetMessaging(app);
135 | var token = "sdjlfjiwekfnskdjfksdfjskdfjsdf";
136 | TopicListResponse resp = await msg.GetTopicListAsync(token);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/ClickAction.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class
16 | */
17 |
18 | using AGConnectAdmin.Utils;
19 | using Newtonsoft.Json;
20 | using System;
21 |
22 | namespace AGConnectAdmin.Messaging
23 | {
24 | ///
25 | /// Represents the click action options that can be included in a
26 | /// .
27 | ///
28 | public class ClickAction : MessagePart
29 | {
30 | ///
31 | /// Gets or sets the type of the click action.
32 | ///
33 | [JsonProperty("type")]
34 | private int Type { get; set; }
35 |
36 | ///
37 | /// Gets or sets the intent of the click action.
38 | ///
39 | [JsonProperty("intent")]
40 | private string Intent { get; set; }
41 |
42 | ///
43 | /// Gets or sets the URL of the click action.
44 | ///
45 | [JsonProperty("url")]
46 | private string URL { get; set; }
47 |
48 | ///
49 | /// Gets or sets the rich resource of the click action.
50 | ///
51 | [JsonProperty("rich_resource")]
52 | private string RichResource { get; set; }
53 |
54 | ///
55 | /// Gets or sets the action of the click event.
56 | ///
57 | [JsonProperty("action")]
58 | private string Action { get; set; }
59 |
60 | internal ClickAction() { }
61 |
62 | ///
63 | /// Custom intent
64 | ///
65 | ///
66 | ///
67 | public static ClickAction CustomIntent(string intent)
68 | {
69 | if (string.IsNullOrEmpty(intent))
70 | {
71 | return OpenApp();
72 | }
73 |
74 | return new ClickAction()
75 | {
76 | Type = 1,
77 | Intent = intent,
78 | };
79 | }
80 |
81 | ///
82 | /// Custom action
83 | ///
84 | ///
85 | ///
86 | public static ClickAction CustomAction(string action)
87 | {
88 | if (string.IsNullOrEmpty(action))
89 | {
90 | return OpenApp();
91 | }
92 |
93 | return new ClickAction()
94 | {
95 | Type = 1,
96 | Action = action,
97 | };
98 | }
99 |
100 | ///
101 | /// Open specified url
102 | ///
103 | /// Url to be opened, must be https
104 | ///
105 | public static ClickAction OpenUrl(string url)
106 | {
107 | return new ClickAction()
108 | {
109 | Type = 2,
110 | URL = url,
111 | };
112 | }
113 |
114 |
115 | ///
116 | /// Open app
117 | ///
118 | public static ClickAction OpenApp()
119 | {
120 | return new ClickAction()
121 | {
122 | Type = 3
123 | };
124 | }
125 |
126 | ///
127 | /// Open rich resource
128 | ///
129 | /// Url of zip file of rich resource, must be https
130 | ///
131 | public static ClickAction OpenRichResource(string resourceUrl)
132 | {
133 | return new ClickAction()
134 | {
135 | Type = 4,
136 | RichResource = resourceUrl,
137 | };
138 | }
139 |
140 | ///
141 | internal protected override ClickAction CopyAndValidate()
142 | {
143 | var copy = new ClickAction()
144 | {
145 | Type = this.Type,
146 | Intent = this.Intent,
147 | URL = this.URL,
148 | RichResource = this.RichResource,
149 | Action = this.Action
150 | };
151 |
152 | if (copy.URL != null && !Validator.IsHttpsUrl(copy.URL))
153 | {
154 | throw new ArgumentException("Click action url must be a https url.");
155 | }
156 |
157 | if (copy.RichResource != null && !Validator.IsHttpsUrl(copy.RichResource))
158 | {
159 | throw new ArgumentException("Rich resource url must be a https url.");
160 | }
161 |
162 | return copy;
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/ApnsConfig.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using Newtonsoft.Json;
19 | using System;
20 | using System.Collections.Generic;
21 | using System.Linq;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents the APNS-specific options that can be included in a . Refer
27 | /// to
28 | /// APNs documentation for various headers and payload fields supported by APNS.
29 | ///
30 | public class ApnsConfig : MessagePart
31 | {
32 | private ApnsPayload payload = new ApnsPayload();
33 |
34 | ///
35 | /// Gets or sets the FCM options to be included in the message.
36 | ///
37 | [JsonProperty("hms_options")]
38 | public ApnsHmsOptions HmsOptions { get; set; } = new ApnsHmsOptions();
39 |
40 | ///
41 | /// Gets or sets the APNs headers. Refer to
42 | /// https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html
43 | ///
44 | [JsonProperty("headers")]
45 | public IReadOnlyDictionary Headers { get; set; }
46 |
47 | ///
48 | /// Gets or sets the aps dictionary to be included in the APNs payload.
49 | ///
50 | [JsonIgnore]
51 | public Aps Aps
52 | {
53 | get
54 | {
55 | return this.Payload.Aps;
56 | }
57 |
58 | set
59 | {
60 | this.Payload.Aps = value;
61 | }
62 | }
63 |
64 | ///
65 | /// Gets or sets a collection of arbitrary key-value data that will be included in the APNs
66 | /// payload.
67 | ///
68 | [JsonIgnore]
69 | public IDictionary CustomData
70 | {
71 | get
72 | {
73 | return this.Payload.CustomData;
74 | }
75 |
76 | set
77 | {
78 | this.Payload.CustomData = value;
79 | }
80 | }
81 |
82 | ///
83 | /// Gets or sets the APNs payload as accepted by the FCM backend servers.
84 | ///
85 | [JsonProperty("payload")]
86 | private ApnsPayload Payload
87 | {
88 | get
89 | {
90 | if (this.payload.Aps != null && this.payload.CustomData?.ContainsKey("aps") == true)
91 | {
92 | throw new ArgumentException("Multiple specifications for ApnsConfig key: aps");
93 | }
94 |
95 | return this.payload;
96 | }
97 |
98 | set
99 | {
100 | this.payload = value;
101 | }
102 | }
103 |
104 | ///
105 | internal protected override ApnsConfig CopyAndValidate()
106 | {
107 | var copy = new ApnsConfig()
108 | {
109 | Headers = this.Headers?.Copy(),
110 | Payload = this.Payload.CopyAndValidate(),
111 | HmsOptions = this.HmsOptions.CopyAndValidate(),
112 | };
113 | return copy;
114 | }
115 |
116 | ///
117 | /// The APNs payload object as expected by the FCM backend service.
118 | ///
119 | private class ApnsPayload
120 | {
121 | [JsonProperty("aps")]
122 | internal Aps Aps { get; set; }
123 |
124 | [JsonExtensionData]
125 | internal IDictionary CustomData { get; set; }
126 |
127 | ///
128 | /// Copies this APNs payload, and validates the content of it to ensure that it can be
129 | /// serialized into the JSON format expected by the FCM service.
130 | ///
131 | internal ApnsPayload CopyAndValidate()
132 | {
133 | var copy = new ApnsPayload()
134 | {
135 | CustomData = this.CustomData?.ToDictionary(e => e.Key, e => e.Value),
136 | };
137 | var aps = this.Aps;
138 | if (aps == null && copy.CustomData?.ContainsKey("aps") == false)
139 | {
140 | throw new ArgumentException("Aps dictionary is required in ApnsConfig");
141 | }
142 |
143 | copy.Aps = aps?.CopyAndValidate();
144 | return copy;
145 | }
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/ApsAlert.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class
16 | */
17 |
18 | using Newtonsoft.Json;
19 | using System;
20 | using System.Collections.Generic;
21 | using System.Linq;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents the
27 | /// alert property that can be included in the aps dictionary of an APNs
28 | /// payload.
29 | ///
30 | public class ApsAlert : MessagePart
31 | {
32 | ///
33 | /// Gets or sets the title of the alert. When provided, overrides the title set via
34 | /// .
35 | ///
36 | [JsonProperty("title")]
37 | public string Title { get; set; }
38 |
39 | ///
40 | /// Gets or sets the subtitle of the alert.
41 | ///
42 | [JsonProperty("subtitle")]
43 | public string Subtitle { get; set; }
44 |
45 | ///
46 | /// Gets or sets the body of the alert. When provided, overrides the body set via
47 | /// .
48 | ///
49 | [JsonProperty("body")]
50 | public string Body { get; set; }
51 |
52 | ///
53 | /// Gets or sets the launch image for the notification action.
54 | ///
55 | [JsonProperty("launch-image")]
56 | public string LaunchImage { get; set; }
57 |
58 | ///
59 | /// Gets or sets the key of the title string in the app's string resources to use to
60 | /// localize the title text.
61 | ///
62 | [JsonProperty("title-loc-key")]
63 | public string TitleLocKey { get; set; }
64 |
65 | ///
66 | /// Gets or sets the resource key strings that will be used in place of the format
67 | /// specifiers in .
68 | ///
69 | [JsonProperty("title-loc-args")]
70 | public IEnumerable TitleLocArgs { get; set; }
71 |
72 | ///
73 | /// Gets or sets the key of the subtitle string in the app's string resources to use to
74 | /// localize the subtitle text.
75 | ///
76 | [JsonProperty("subtitle-loc-key")]
77 | public string SubtitleLocKey { get; set; }
78 |
79 | ///
80 | /// Gets or sets the resource key strings that will be used in place of the format
81 | /// specifiers in .
82 | ///
83 | [JsonProperty("subtitle-loc-args")]
84 | public IEnumerable SubtitleLocArgs { get; set; }
85 |
86 | ///
87 | /// Gets or sets the key of the body string in the app's string resources to use to
88 | /// localize the body text.
89 | ///
90 | [JsonProperty("loc-key")]
91 | public string LocKey { get; set; }
92 |
93 | ///
94 | /// Gets or sets the resource key strings that will be used in place of the format
95 | /// specifiers in .
96 | ///
97 | [JsonProperty("loc-args")]
98 | public IEnumerable LocArgs { get; set; }
99 |
100 | ///
101 | internal protected override ApsAlert CopyAndValidate()
102 | {
103 | var copy = new ApsAlert()
104 | {
105 | Title = this.Title,
106 | Subtitle = this.Subtitle,
107 | Body = this.Body,
108 | LocKey = this.LocKey,
109 | LocArgs = this.LocArgs?.ToList(),
110 | TitleLocKey = this.TitleLocKey,
111 | TitleLocArgs = this.TitleLocArgs?.ToList(),
112 | SubtitleLocKey = this.SubtitleLocKey,
113 | SubtitleLocArgs = this.SubtitleLocArgs?.ToList(),
114 | LaunchImage = this.LaunchImage,
115 | };
116 | if (copy.TitleLocArgs?.Any() == true && string.IsNullOrEmpty(copy.TitleLocKey))
117 | {
118 | throw new ArgumentException("TitleLocKey is required when specifying TitleLocArgs.");
119 | }
120 |
121 | if (copy.SubtitleLocArgs?.Any() == true && string.IsNullOrEmpty(copy.SubtitleLocKey))
122 | {
123 | throw new ArgumentException("SubtitleLocKey is required when specifying SubtitleLocArgs.");
124 | }
125 |
126 | if (copy.LocArgs?.Any() == true && string.IsNullOrEmpty(copy.LocKey))
127 | {
128 | throw new ArgumentException("LocKey is required when specifying LocArgs.");
129 | }
130 |
131 | return copy;
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin.Tests/Messaging/MessagingClientTest.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Threading;
19 | using System.Threading.Tasks;
20 | using AGConnectAdmin.Messaging;
21 | using Xunit;
22 |
23 | namespace AGConnectAdmin.Tests
24 | {
25 | public class MessagingClientTest
26 | {
27 | static MessagingClientTest()
28 | {
29 | TestUtils.GlobalInit();
30 | }
31 |
32 |
33 | private Message GetMessage(string content)
34 | {
35 | var message = new Message()
36 | {
37 | Notification = new Notification()
38 | {
39 | Title = content,
40 | Body = "Body",
41 | },
42 | Android = new AndroidConfig()
43 | {
44 | CollapseKey = -1,
45 | Urgency = UrgencyPriority.HIGH,
46 | TTL = TimeSpan.FromHours(1),
47 | BITag = "tag",
48 | FastAppTarget = FastAppTarget.Development,
49 | Notification = new AndroidNotification()
50 | {
51 | Title = "title",
52 | Body = "body",
53 | Icon = "icon",
54 | Color = "#AACCDD",
55 | Sound = "sound",
56 | Tag = "tag",
57 | ClickAction = ClickAction.CustomIntent("intent"),
58 | BodyLocKey = "key",
59 | BodyLocArgs = new List { "arg1", "arg2" },
60 | TitleLocKey = "key",
61 | TitleLocArgs = new List { "arg1", "arg2" },
62 | ChannelId = "channel",
63 | NotifySummary = "summary",
64 | Style = NotificationStyle.Default,
65 | AutoClear = 3000,
66 | NotifyId = 123,
67 | Group = "group",
68 | Badge = new BadgeNotification()
69 | {
70 | AddNum = 32,
71 | Class = "class"
72 | }
73 | },
74 | },
75 | Token = new List { "YOUR_REGISTRATION_TOKEN" }
76 |
77 | };
78 | return message;
79 | }
80 |
81 | [Fact]
82 | public async Task SendSingle()
83 | {
84 | var requestId = await AGConnectMessaging.DefaultInstance.SendAsync(GetMessage("content"), dryRun: true);
85 | Assert.True(!string.IsNullOrEmpty(requestId));
86 | }
87 |
88 | [Fact]
89 | public void SendMany()
90 | {
91 | List> tasks = new List>();
92 | for (int i = 0; i < 100; i++)
93 | {
94 | var task = AGConnectMessaging.DefaultInstance.SendAsync(GetMessage("content " + i), dryRun: true);
95 | tasks.Add(task);
96 | }
97 |
98 | Task.WaitAll(tasks.ToArray());
99 |
100 | foreach (var task in tasks)
101 | {
102 | Assert.True(!string.IsNullOrEmpty(task.Result));
103 | }
104 | }
105 |
106 | [Fact]
107 | public void SendManyConcurrent()
108 | {
109 | List> tasks = new List>();
110 |
111 | for (int i = 0; i < 100; i++)
112 | {
113 | var task = Task.Run(() =>
114 | {
115 | return AGConnectMessaging.DefaultInstance.SendAsync(GetMessage("content " + i), dryRun: true);
116 | });
117 | tasks.Add(task);
118 | }
119 |
120 | Task.WaitAll(tasks.ToArray());
121 |
122 | foreach (var task in tasks)
123 | {
124 | Assert.True(!string.IsNullOrEmpty(task.Result));
125 | }
126 | }
127 |
128 | [Fact]
129 | public void SendManyConcurrentAndDelayed()
130 | {
131 | List> tasks = new List>();
132 |
133 | for (int i = 0; i < 100; i++)
134 | {
135 | var task = Task.Run(() =>
136 | {
137 | Thread.Sleep(i);
138 | return AGConnectMessaging.DefaultInstance.SendAsync(GetMessage("content " + i), dryRun: true);
139 | });
140 | tasks.Add(task);
141 | }
142 |
143 | Task.WaitAll(tasks.ToArray());
144 |
145 | foreach (var task in tasks)
146 | {
147 | Assert.True(!string.IsNullOrEmpty(task.Result));
148 | }
149 | }
150 |
151 | [Fact]
152 | public void SendManyConcurrentAndDelayed2()
153 | {
154 | List> tasks = new List>();
155 |
156 |
157 | for (int i = 0; i < 100; i++)
158 | {
159 | Thread.Sleep(i);
160 | var task = Task.Run(() =>
161 | {
162 | return AGConnectMessaging.DefaultInstance.SendAsync(GetMessage("content " + i), dryRun: true);
163 | });
164 | tasks.Add(task);
165 | }
166 |
167 | Task.WaitAll(tasks.ToArray());
168 |
169 | foreach (var task in tasks)
170 | {
171 | Assert.True(!string.IsNullOrEmpty(task.Result));
172 | }
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/WebpushNotification.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using Newtonsoft.Json;
19 | using System;
20 | using System.Collections.Generic;
21 | using System.Linq;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents the Webpush-specific notification options that can be included in a
27 | /// . Supports most standard options defined in the
28 | ///
29 | /// Web Notification specification.
30 | ///
31 | public class WebpushNotification: MessagePart
32 | {
33 | ///
34 | /// Gets or sets the title text of the notification.
35 | ///
36 | [JsonProperty("title")]
37 | public string Title { get; set; }
38 |
39 | ///
40 | /// Gets or sets the body text of the notification.
41 | ///
42 | [JsonProperty("body")]
43 | public string Body { get; set; }
44 |
45 | ///
46 | /// Gets or sets the URL to the icon of the notification.
47 | ///
48 | [JsonProperty("icon")]
49 | public string Icon { get; set; }
50 |
51 | ///
52 | /// Gets or sets the URL of an image to be displayed in the notification.
53 | ///
54 | [JsonProperty("image")]
55 | public string Image { get; set; }
56 |
57 | ///
58 | /// Gets or sets the URL of the image used to represent the notification when there is not
59 | /// enough space to display the notification itself.
60 | ///
61 | [JsonProperty("badge")]
62 | public string Badge { get; set; }
63 |
64 | ///
65 | /// Gets or sets the direction in which to display the notification.
66 | ///
67 | [JsonIgnore]
68 | public Direction? Direction { get; set; }
69 |
70 | ///
71 | /// Gets or sets the language of the notification.
72 | ///
73 | [JsonProperty("lang")]
74 | public string Language { get; set; }
75 |
76 | ///
77 | /// Gets or sets whether the user should be notified after a new notification replaces an
78 | /// old one.
79 | ///
80 | [JsonProperty("renotify")]
81 | public bool? Renotify { get; set; }
82 |
83 | ///
84 | /// Gets or sets whether the notification should remain active until the user clicks or
85 | /// dismisses it, rather than closing it automatically.
86 | ///
87 | [JsonProperty("requireInteraction")]
88 | public bool? RequireInteraction { get; set; }
89 |
90 | ///
91 | /// Gets or sets whether the notification should be silent.
92 | ///
93 | [JsonProperty("silent")]
94 | public bool? Silent { get; set; }
95 |
96 | ///
97 | /// Gets or sets an identifying tag for the notification.
98 | ///
99 | [JsonProperty("tag")]
100 | public string Tag { get; set; }
101 |
102 | ///
103 | /// Gets or sets the notification's timestamp value in milliseconds.
104 | ///
105 | [JsonProperty("timestamp")]
106 | public long? Timestamp { get; set; }
107 |
108 | ///
109 | /// Gets or sets a vibration pattern for the receiving device's vibration hardware.
110 | ///
111 | [JsonProperty("vibrate")]
112 | public IEnumerable Vibrate { get; set; }
113 |
114 | ///
115 | /// Gets or sets a collection of Webpush notification actions.
116 | ///
117 | [JsonProperty("actions")]
118 | public IEnumerable Actions { get; set; }
119 |
120 | ///
121 | /// Gets or sets some arbitrary data that will be included in the notification.
122 | ///
123 | [JsonProperty("data")]
124 | public object Data { get; set; }
125 |
126 | ///
127 | /// Gets or sets the custom key-value pairs that will be included in the
128 | /// notification. This is exposed as an to support
129 | /// correct deserialization of custom properties.
130 | ///
131 | [JsonExtensionData]
132 | public IDictionary CustomData { get; set; }
133 |
134 | ///
135 | /// Gets or sets the string representation of the property.
136 | ///
137 | [JsonProperty("dir")]
138 | private string DirectionString
139 | {
140 | get
141 | {
142 | switch (this.Direction)
143 | {
144 | case Messaging.Direction.Auto:
145 | return "auto";
146 | case Messaging.Direction.LeftToRight:
147 | return "ltr";
148 | case Messaging.Direction.RightToLeft:
149 | return "rtl";
150 | default:
151 | return null;
152 | }
153 | }
154 |
155 | set
156 | {
157 | switch (value)
158 | {
159 | case "auto":
160 | this.Direction = Messaging.Direction.Auto;
161 | return;
162 | case "ltr":
163 | this.Direction = Messaging.Direction.LeftToRight;
164 | return;
165 | case "rtl":
166 | this.Direction = Messaging.Direction.RightToLeft;
167 | return;
168 | default:
169 | throw new ArgumentException(
170 | $"Invalid direction value: {value}. Only 'auto', 'rtl' and 'ltr' "
171 | + "are allowed.");
172 | }
173 | }
174 | }
175 |
176 | ///
177 | internal protected override WebpushNotification CopyAndValidate()
178 | {
179 | var copy = new WebpushNotification()
180 | {
181 | Title = this.Title,
182 | Body = this.Body,
183 | Icon = this.Icon,
184 | Image = this.Image,
185 | Language = this.Language,
186 | Tag = this.Tag,
187 | Direction = this.Direction,
188 | Badge = this.Badge,
189 | Renotify = this.Renotify,
190 | RequireInteraction = this.RequireInteraction,
191 | Silent = this.Silent,
192 | Actions = this.Actions?.Select(item => new WebpushAction(item)).ToList(),
193 | Vibrate = this.Vibrate,
194 | Timestamp = this.Timestamp,
195 | Data = this.Data,
196 | };
197 |
198 | var customData = this.CustomData?.ToDictionary(e => e.Key, e => e.Value);
199 | if (customData?.Count > 0)
200 | {
201 | var serializer = NewtonsoftJsonSerializer.Instance;
202 | var json = serializer.Serialize(copy);
203 | var standardProperties = serializer.Deserialize>(json);
204 | var duplicates = customData.Keys
205 | .Where(customKey => standardProperties.ContainsKey(customKey))
206 | .ToList();
207 | if (duplicates.Any())
208 | {
209 | throw new ArgumentException(
210 | "Multiple specifications for WebpushNotification keys: "
211 | + string.Join(",", duplicates));
212 | }
213 |
214 | copy.CustomData = customData;
215 | }
216 |
217 | return copy;
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 |
3 | Version 2.0, January 2004
4 |
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
16 |
17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
18 |
19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
20 |
21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
22 |
23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
24 |
25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
26 |
27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
28 |
29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
30 |
31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
32 |
33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
34 |
35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
36 |
37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and
38 | You must cause any modified files to carry prominent notices stating that You changed the files; and
39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
41 |
42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
44 |
45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
46 |
47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
48 |
49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
50 |
51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
52 |
53 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Utils/NewtonsoftJsonSerializer.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using Newtonsoft.Json;
17 | using Newtonsoft.Json.Serialization;
18 | using System;
19 | using System.IO;
20 | using System.Reflection;
21 | using System.Linq;
22 |
23 | namespace AGConnectAdmin
24 | {
25 | ///
26 | /// All values of a type with this attribute are represented as a literal null in JSON.
27 | ///
28 | [AttributeUsage(AttributeTargets.Class)]
29 | internal class JsonExplicitNullAttribute : Attribute { }
30 |
31 | ///
32 | /// A JSON converter which honers RFC 3339 and the serialized date is accepted by Huawei services.
33 | ///
34 | internal class RFC3339DateTimeConverter : JsonConverter
35 | {
36 | ///
37 | public override bool CanRead => false;
38 |
39 | ///
40 | public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue,
41 | JsonSerializer serializer)
42 | {
43 | throw new NotImplementedException("Unnecessary because CanRead is false.");
44 | }
45 |
46 | ///
47 | public override bool CanConvert(Type objectType) =>
48 | // Convert DateTime only.
49 | objectType == typeof(DateTime) || objectType == typeof(Nullable);
50 |
51 | ///
52 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
53 | {
54 | if (value != null)
55 | {
56 | DateTime date = (DateTime)value;
57 | serializer.Serialize(writer, ConvertToRFC3339(date));
58 | }
59 | }
60 |
61 | internal static string ConvertToRFC3339(DateTime date)
62 | {
63 | if (date.Kind == DateTimeKind.Unspecified)
64 | {
65 | date = date.ToUniversalTime();
66 | }
67 | return date.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", System.Globalization.DateTimeFormatInfo.InvariantInfo);
68 | }
69 | }
70 |
71 | ///
72 | /// A JSON converter to write null literals into JSON when explicitly requested.
73 | ///
74 | internal class ExplicitNullConverter : JsonConverter
75 | {
76 | ///
77 | public override bool CanRead => false;
78 |
79 | ///
80 | public override bool CanConvert(Type objectType) => objectType.GetTypeInfo().GetCustomAttributes(typeof(JsonExplicitNullAttribute), false).Any();
81 |
82 | ///
83 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
84 | {
85 | throw new NotImplementedException("Unnecessary because CanRead is false.");
86 | }
87 |
88 | ///
89 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => writer.WriteNull();
90 | }
91 |
92 | ///
93 | /// A JSON contract resolver to apply and as necessary.
94 | ///
95 | ///
96 | /// Using a contract resolver is recommended in the Json.NET performance tips: https://www.newtonsoft.com/json/help/html/Performance.htm#JsonConverters
97 | ///
98 | internal class NewtonsoftJsonContractResolver : DefaultContractResolver
99 | {
100 | private static readonly JsonConverter DateTimeConverter = new RFC3339DateTimeConverter();
101 | private static readonly JsonConverter ExplicitNullConverter = new ExplicitNullConverter();
102 |
103 | ///
104 | protected override JsonContract CreateContract(Type objectType)
105 | {
106 | JsonContract contract = base.CreateContract(objectType);
107 |
108 | if (DateTimeConverter.CanConvert(objectType))
109 | {
110 | contract.Converter = DateTimeConverter;
111 | }
112 | else if (ExplicitNullConverter.CanConvert(objectType))
113 | {
114 | contract.Converter = ExplicitNullConverter;
115 | }
116 |
117 | return contract;
118 | }
119 | }
120 |
121 | /// Class for serialization and deserialization of JSON documents using the Newtonsoft Library.
122 | internal class NewtonsoftJsonSerializer
123 | {
124 | private readonly JsonSerializerSettings settings;
125 | private readonly JsonSerializer serializer;
126 |
127 | /// The default instance of the Newtonsoft JSON Serializer, with default settings.
128 | public static NewtonsoftJsonSerializer Instance { get; } = new NewtonsoftJsonSerializer();
129 |
130 | ///
131 | /// Constructs a new instance with the default serialization settings, equivalent to .
132 | ///
133 | public NewtonsoftJsonSerializer() : this(CreateDefaultSettings())
134 | {
135 | }
136 |
137 | ///
138 | /// Constructs a new instance with the given settings.
139 | ///
140 | /// The settings to apply when serializing and deserializing. Must not be null.
141 | public NewtonsoftJsonSerializer(JsonSerializerSettings settings)
142 | {
143 | if (settings == null)
144 | {
145 | throw new ArgumentNullException(nameof(settings));
146 | }
147 | this.settings = settings;
148 | serializer = JsonSerializer.Create(settings);
149 | }
150 |
151 | ///
152 | /// Creates a new instance of with the same behavior
153 | /// as the ones used in . This method is expected to be used to construct
154 | /// settings which are then passed to .
155 | ///
156 | /// A new set of default settings.
157 | public static JsonSerializerSettings CreateDefaultSettings() =>
158 | new JsonSerializerSettings
159 | {
160 | NullValueHandling = NullValueHandling.Ignore,
161 | MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
162 | ContractResolver = new NewtonsoftJsonContractResolver()
163 | };
164 |
165 | ///
166 | public string Format => "json";
167 |
168 | ///
169 | public void Serialize(object obj, Stream target)
170 | {
171 | using (var writer = new StreamWriter(target))
172 | {
173 | if (obj == null)
174 | {
175 | obj = string.Empty;
176 | }
177 | serializer.Serialize(writer, obj);
178 | }
179 | }
180 |
181 | ///
182 | public string Serialize(object obj)
183 | {
184 | using (TextWriter tw = new StringWriter())
185 | {
186 | if (obj == null)
187 | {
188 | obj = string.Empty;
189 | }
190 | serializer.Serialize(tw, obj);
191 | return tw.ToString();
192 | }
193 | }
194 |
195 | ///
196 | public T Deserialize(string input)
197 | {
198 | if (string.IsNullOrEmpty(input))
199 | {
200 | return default(T);
201 | }
202 | return JsonConvert.DeserializeObject(input, settings);
203 | }
204 |
205 | ///
206 | public object Deserialize(string input, Type type)
207 | {
208 | if (string.IsNullOrEmpty(input))
209 | {
210 | return null;
211 | }
212 | return JsonConvert.DeserializeObject(input, type, settings);
213 | }
214 |
215 | ///
216 | public T Deserialize(Stream input)
217 | {
218 | // Convert the JSON document into an object.
219 | using (StreamReader streamReader = new StreamReader(input))
220 | {
221 | return (T)serializer.Deserialize(streamReader, typeof(T));
222 | }
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/AGConnectApp.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Reflection;
19 | using System.Runtime.CompilerServices;
20 |
21 | [assembly: InternalsVisibleTo("AGConnectAdmin.Tests,PublicKey=" +
22 | "002400000480000094000000060200000024000052534131000400000100010081328559eaab41" +
23 | "055b84af73469863499d81625dcbba8d8decb298b69e0f783a0958cf471fd4f76327b85a7d4b02" +
24 | "3003684e85e61cf15f13150008c81f0b75a252673028e530ea95d0c581378da8c6846526ab9597" +
25 | "4c6d0bc66d2462b51af69968a0e25114bde8811e0d6ee1dc22d4a59eee6a8bba4712cba839652f" +
26 | "badddb9c")]
27 | namespace AGConnectAdmin
28 | {
29 | internal delegate TResult ServiceFactory()
30 | where TResult : IAGConnectService;
31 |
32 | ///
33 | /// This is the entry point to the AGConnect Admin SDK. It holds configuration and state common
34 | /// to all APIs exposed from the SDK.
35 | /// Use one of the provided Create() methods to obtain a new instance.
36 | ///
37 | public sealed class AGConnectApp
38 | {
39 | private const string DefaultAppName = "[DEFAULT]";
40 |
41 | private static readonly Dictionary Apps = new Dictionary();
42 |
43 | // Guards the mutable state local to an app instance.
44 | private readonly object appLock = new object();
45 | private readonly AppOptions options;
46 |
47 | // A collection of stateful services initialized using this app instance (e.g.
48 | // AGConnectAuth). Services are tracked here so they can be cleaned up when the app is
49 | // deleted.
50 | private readonly Dictionary services = new Dictionary();
51 | private bool deleted = false;
52 |
53 | ///
54 | /// Gets the default app instance. This property is null if the default app instance
55 | /// doesn't yet exist.
56 | ///
57 | public static AGConnectApp DefaultInstance
58 | {
59 | get
60 | {
61 | return GetInstance(DefaultAppName);
62 | }
63 | }
64 |
65 | private AGConnectApp(AppOptions options, string name)
66 | {
67 | this.options = new AppOptions(options);
68 | this.Name = name;
69 | }
70 |
71 | ///
72 | /// Gets a copy of the this app was created with.
73 | ///
74 | public AppOptions Options
75 | {
76 | get
77 | {
78 | return new AppOptions(this.options);
79 | }
80 | }
81 |
82 | ///
83 | /// Gets the name of this app.
84 | ///
85 | private string Name { get; }
86 |
87 | ///
88 | /// Returns the app instance identified by the given name.
89 | ///
90 | /// The instance with the specified name or null if it
91 | /// doesn't exist.
92 | /// If the name argument is null or empty.
93 | /// Name of the app to retrieve.
94 | private static AGConnectApp GetInstance(string name)
95 | {
96 | if (string.IsNullOrEmpty(name))
97 | {
98 | throw new ArgumentException("App name to lookup must not be null or empty");
99 | }
100 |
101 | lock (Apps)
102 | {
103 | AGConnectApp app;
104 | if (Apps.TryGetValue(name, out app))
105 | {
106 | return app;
107 | }
108 | }
109 |
110 | return null;
111 | }
112 |
113 | ///
114 | /// Creates the default app instance with the specified options.
115 | ///
116 | /// The newly created instance.
117 | /// If the default app instance already
118 | /// exists.
119 | /// Options to create the app with. Must at least contain the
120 | /// Credential.
121 | public static AGConnectApp Create(AppOptions options)
122 | {
123 | return Create(options, DefaultAppName);
124 | }
125 |
126 | ///
127 | /// Creates an app with the specified name and options.
128 | ///
129 | /// The newly created instance.
130 | /// If the default app instance already
131 | /// exists.
132 | /// Options to create the app with. Must at least contain the
133 | /// Credential.
134 | /// Name of the app.
135 | private static AGConnectApp Create(AppOptions options, string name)
136 | {
137 | if (string.IsNullOrEmpty(name))
138 | {
139 | throw new ArgumentException("App name must not be null or empty");
140 | }
141 |
142 | if (options == null)
143 | {
144 | throw new ArgumentException("options must not be null");
145 | }
146 |
147 | lock (Apps)
148 | {
149 | if (Apps.ContainsKey(name))
150 | {
151 | throw new ArgumentException($"AGConnectApp named {name} already exists.");
152 | }
153 |
154 | var app = new AGConnectApp(options, name);
155 | Apps.Add(name, app);
156 | return app;
157 | }
158 | }
159 |
160 | ///
161 | /// Deletes this app instance and cleans up any state associated with it. Once an app has
162 | /// been deleted, accessing any services related to it will result in an exception.
163 | /// If the app is already deleted, this method is a no-op.
164 | ///
165 | public void Delete()
166 | {
167 | // Clean up local state
168 | lock (this.appLock)
169 | {
170 | this.deleted = true;
171 | foreach (var entry in this.services)
172 | {
173 | try
174 | {
175 | entry.Value.Delete();
176 | }
177 | catch (Exception e)
178 | {
179 | Console.WriteLine(e);
180 | }
181 | }
182 |
183 | this.services.Clear();
184 | }
185 |
186 | // Clean up global state
187 | lock (Apps)
188 | {
189 | Apps.Remove(this.Name);
190 | }
191 | }
192 |
193 | ///
194 | /// Deleted all the apps created so far. Used for unit testing.
195 | ///
196 | internal static void DeleteAll()
197 | {
198 | lock (Apps)
199 | {
200 | var copy = new Dictionary(Apps);
201 | foreach (var entry in copy)
202 | {
203 | entry.Value.Delete();
204 | }
205 |
206 | if (Apps.Count > 0)
207 | {
208 | throw new InvalidOperationException("Failed to delete all apps");
209 | }
210 | }
211 | }
212 |
213 | ///
214 | /// Returns the current version of the .NET assembly.
215 | ///
216 | /// A version string in major.minor.patch format.
217 | internal static string GetSdkVersion()
218 | {
219 | const int majorMinorPatch = 3;
220 | return typeof(AGConnectApp).GetTypeInfo().Assembly.GetName().Version.ToString(majorMinorPatch);
221 | }
222 |
223 | internal T GetOrInit(string id, ServiceFactory initializer)
224 | where T : class, IAGConnectService
225 | {
226 | lock (this.appLock)
227 | {
228 | if (this.deleted)
229 | {
230 | throw new InvalidOperationException("Cannot use an app after it has been deleted");
231 | }
232 |
233 | IAGConnectService service;
234 | if (!this.services.TryGetValue(id, out service))
235 | {
236 | service = initializer();
237 | this.services.Add(id, service);
238 | }
239 |
240 | return (T)service;
241 | }
242 | }
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/Aps.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class
16 | */
17 |
18 | using Newtonsoft.Json;
19 | using System;
20 | using System.Collections.Generic;
21 | using System.Linq;
22 |
23 | namespace AGConnectAdmin.Messaging
24 | {
25 | ///
26 | /// Represents the
27 | /// aps dictionary that is part of every APNs message.
28 | ///
29 | public class Aps : MessagePart
30 | {
31 | private static readonly NewtonsoftJsonSerializer Serializer = NewtonsoftJsonSerializer.Instance;
32 |
33 | ///
34 | /// Gets or sets the alert configuration of the aps dictionary. Read from either
35 | /// or property.
36 | ///
37 | [JsonProperty("alert")]
38 | private object AlertObject
39 | {
40 | get
41 | {
42 | object alert = this.AlertString;
43 | if (string.IsNullOrEmpty(alert as string))
44 | {
45 | alert = this.Alert;
46 | }
47 | else if (this.Alert != null)
48 | {
49 | throw new ArgumentException(
50 | "Multiple specifications for alert (Alert and AlertString");
51 | }
52 |
53 | return alert;
54 | }
55 |
56 | set
57 | {
58 | if (value == null)
59 | {
60 | return;
61 | }
62 | else if (value.GetType() == typeof(string))
63 | {
64 | this.AlertString = (string)value;
65 | }
66 | else if (value.GetType() == typeof(ApsAlert))
67 | {
68 | this.Alert = (ApsAlert)value;
69 | }
70 | else
71 | {
72 | var json = Serializer.Serialize(value);
73 | this.Alert = Serializer.Deserialize(json);
74 | }
75 | }
76 | }
77 |
78 | ///
79 | /// Gets or sets an advanced alert configuration to be included in the message. It is an
80 | /// error to set both and properties
81 | /// together.
82 | ///
83 | [JsonIgnore]
84 | public ApsAlert Alert { get; set; }
85 |
86 | ///
87 | /// Gets or sets the alert text to be included in the message. To specify a more advanced
88 | /// alert configuration, use the property instead. It is an error to
89 | /// set both and properties together.
90 | ///
91 | [JsonIgnore]
92 | public string AlertString { get; set; }
93 |
94 | ///
95 | /// Gets or sets the badge to be displayed with the message. Set to 0 to remove the badge.
96 | /// When not specified, the badge will remain unchanged.
97 | ///
98 | [JsonProperty("badge")]
99 | public int? Badge { get; set; }
100 |
101 | ///
102 | /// Gets or sets the name of a sound file in your app's main bundle or in the
103 | /// Library/Sounds folder of your app's container directory. Specify the
104 | /// string default to play the system sound. It is an error to set both
105 | /// and properties together.
106 | ///
107 | [JsonIgnore]
108 | public string Sound { get; set; }
109 |
110 | ///
111 | /// Gets or sets the critical alert sound to be played with the message. It is an error to
112 | /// set both and properties together.
113 | ///
114 | [JsonIgnore]
115 | public CriticalSound CriticalSound { get; set; }
116 |
117 | ///
118 | /// Gets or sets a value indicating whether to configure a background update notification.
119 | ///
120 | [JsonIgnore]
121 | public bool ContentAvailable { get; set; }
122 |
123 | ///
124 | /// Gets or sets a value indicating whether to include the mutable-content property
125 | /// in the message. When set, this property allows clients to modify the notification via
126 | /// app extensions.
127 | ///
128 | [JsonIgnore]
129 | public bool MutableContent { get; set; }
130 |
131 | ///
132 | /// Gets or sets the type of the notification.
133 | ///
134 | [JsonProperty("category")]
135 | public string Category { get; set; }
136 |
137 | ///
138 | /// Gets or sets the app-specific identifier for grouping notifications.
139 | ///
140 | [JsonProperty("thread-id")]
141 | public string ThreadId { get; set; }
142 |
143 | ///
144 | /// Gets or sets the identifier of the window brought forward.
145 | ///
146 | [JsonProperty("target-content-id")]
147 | public string TargetContentId { get; set; }
148 |
149 | ///
150 | /// Gets or sets the sound configuration of the aps dictionary. Read from either
151 | /// or property.
152 | ///
153 | [JsonProperty("sound")]
154 | private object SoundObject
155 | {
156 | get
157 | {
158 | object sound = this.Sound;
159 | if (string.IsNullOrEmpty(sound as string))
160 | {
161 | sound = this.CriticalSound;
162 | }
163 | else if (this.CriticalSound != null)
164 | {
165 | throw new ArgumentException(
166 | "Multiple specifications for sound (CriticalSound and Sound");
167 | }
168 |
169 | return sound;
170 | }
171 |
172 | set
173 | {
174 | if (value == null)
175 | {
176 | return;
177 | }
178 | else if (value.GetType() == typeof(string))
179 | {
180 | this.Sound = (string)value;
181 | }
182 | else if (value.GetType() == typeof(CriticalSound))
183 | {
184 | this.CriticalSound = (CriticalSound)value;
185 | }
186 | else
187 | {
188 | var json = Serializer.Serialize(value);
189 | this.CriticalSound = Serializer.Deserialize(json);
190 | }
191 | }
192 | }
193 |
194 | ///
195 | /// Gets or sets the integer representation of the property,
196 | /// which is how APNs expects it.
197 | ///
198 | [JsonProperty("content-available")]
199 | private int? ContentAvailableInt
200 | {
201 | get
202 | {
203 | return this.ContentAvailable ? 1 : (int?)null;
204 | }
205 |
206 | set
207 | {
208 | this.ContentAvailable = value == 1;
209 | }
210 | }
211 |
212 | ///
213 | /// Gets or sets the integer representation of the property,
214 | /// which is how APNs expects it.
215 | ///
216 | [JsonProperty("mutable-content")]
217 | private int? MutableContentInt
218 | {
219 | get
220 | {
221 | return this.MutableContent ? 1 : (int?)null;
222 | }
223 |
224 | set
225 | {
226 | this.MutableContent = value == 1;
227 | }
228 | }
229 |
230 | ///
231 | /// Gets or sets a collection of arbitrary key-value data to be included in the aps
232 | /// dictionary. This is exposed as an to support
233 | /// correct deserialization of custom properties.
234 | ///
235 | [JsonExtensionData]
236 | public IDictionary CustomData { get; set; }
237 |
238 | ///
239 | internal protected override Aps CopyAndValidate()
240 | {
241 | var copy = new Aps
242 | {
243 | AlertObject = this.AlertObject,
244 | Badge = this.Badge,
245 | ContentAvailable = this.ContentAvailable,
246 | MutableContent = this.MutableContent,
247 | Category = this.Category,
248 | SoundObject = this.SoundObject,
249 | ThreadId = this.ThreadId,
250 | TargetContentId = this.TargetContentId
251 | };
252 |
253 | var customData = this.CustomData?.ToDictionary(e => e.Key, e => e.Value);
254 | if (customData?.Count > 0)
255 | {
256 | var serializer = NewtonsoftJsonSerializer.Instance;
257 | var json = serializer.Serialize(copy);
258 | var standardProperties = serializer.Deserialize>(json);
259 | var duplicates = customData.Keys
260 | .Where(customKey => standardProperties.ContainsKey(customKey))
261 | .ToList();
262 | if (duplicates.Any())
263 | {
264 | throw new ArgumentException(
265 | $"Multiple specifications for Aps keys: {string.Join(",", duplicates)}");
266 | }
267 |
268 | copy.CustomData = customData;
269 | }
270 |
271 | copy.Alert = copy.Alert?.CopyAndValidate();
272 | copy.CriticalSound = copy.CriticalSound?.CopyAndValidate();
273 | return copy;
274 | }
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/AGConnectMessaging.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed implement
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Runtime.CompilerServices;
21 | using System.Threading;
22 | using System.Threading.Tasks;
23 |
24 | [assembly: InternalsVisibleTo("AGConnectAdmin.Tests,PublicKey=" +
25 | "0024000004800000940000000602000000240000525341310004000001000100cd7e49c156e0f4" +
26 | "abc12b5177925ac69822032c9fad95fb82c8a79d2ff277e3c541311856b65c2e1b67d8b3964a65" +
27 | "bfb1c09bbca8475e8234c6ba56004f568db317c65fcd067fcda1972a97d623c20eb4c19cc6d450" +
28 | "f2233fd5bde42943f6d08dec916414f76b3187439603176223bda09146c1836a64b6b5bd36e9cd" +
29 | "435643ba")]
30 | namespace AGConnectAdmin.Messaging
31 | {
32 | ///
33 | /// This is the entry point to all server-side AGConnect Cloud Messaging (HCM) operations. You
34 | /// can get an instance of this class via AGConnectMessaging.DefaultInstance.
35 | ///
36 | public sealed class AGConnectMessaging : IAGConnectService
37 | {
38 | private readonly AGConnectMessagingClient messagingClient;
39 |
40 | private AGConnectMessaging(AGConnectApp app)
41 | {
42 | this.messagingClient = new AGConnectMessagingClient(app.Options);
43 | }
44 |
45 | ///
46 | /// Gets the messaging instance associated with the default AGConnect app. This property is
47 | /// null if the default app doesn't yet exist.
48 | ///
49 | public static AGConnectMessaging DefaultInstance
50 | {
51 | get
52 | {
53 | var app = AGConnectApp.DefaultInstance;
54 | if (app == null)
55 | {
56 | return null;
57 | }
58 |
59 | return GetMessaging(app);
60 | }
61 | }
62 |
63 | ///
64 | /// Returns the messaging instance for the specified app.
65 | ///
66 | /// The instance associated with the specified
67 | /// app.
68 | /// If the app argument is null.
69 | /// An app instance.
70 | internal static AGConnectMessaging GetMessaging(AGConnectApp app)
71 | {
72 | if (app == null)
73 | {
74 | throw new ArgumentNullException(nameof(app));
75 | }
76 |
77 | return app.GetOrInit(typeof(AGConnectMessaging).Name, () =>
78 | {
79 | return new AGConnectMessaging(app);
80 | });
81 | }
82 |
83 | ///
84 | /// Sends a message to the HCM service for delivery. The message gets validated both by
85 | /// the Admin SDK, and the remote HCM service. A successful return value indicates
86 | /// that the message has been successfully sent to HCM, where it has been accepted by the
87 | /// AGC service.
88 | ///
89 | /// A task that completes with a message ID string, which represents
90 | /// successful handoff to HCM.
91 | /// If the message argument is null.
92 | /// If the message contains any invalid
93 | /// fields.
94 | /// If an error occurs while sending the
95 | /// message.
96 | /// The message to be sent. Must not be null.
97 | public async Task SendAsync(Message message)
98 | {
99 | return await this.SendAsync(message, false).ConfigureAwait(false);
100 | }
101 |
102 | ///
103 | /// Sends a message to the AGC service for delivery. The message gets validated both by
104 | /// the Admin SDK, and the remote AGC service. A successful return value indicates
105 | /// that the message has been successfully sent to AGC, where it has been accepted by the
106 | /// AGC service.
107 | ///
108 | /// A task that completes with a message ID string, which represents
109 | /// successful handoff to AGC.
110 | /// If the message argument is null.
111 | /// If the message contains any invalid
112 | /// fields.
113 | /// If an error occurs while sending the
114 | /// message.
115 | /// The message to be sent. Must not be null.
116 | /// A cancellation token to monitor the asynchronous
117 | /// operation.
118 | public async Task SendAsync(Message message, CancellationToken cancellationToken)
119 | {
120 | return await this.SendAsync(message, false, cancellationToken).ConfigureAwait(false);
121 | }
122 |
123 | ///
124 | /// Sends a message to the AGC service for delivery. The message gets validated both by
125 | /// the Admin SDK, and the remote AGC service. A successful return value indicates
126 | /// that the message has been successfully sent to AGC, where it has been accepted by the
127 | /// AGC service.
128 | /// If the option is set to true, the message will not be
129 | /// actually sent to the recipients. Instead, the AGC service performs all the necessary
130 | /// validations, and emulates the send operation. This is a good way to check if a
131 | /// certain message will be accepted by AGC for delivery.
132 | ///
133 | /// A task that completes with a message ID string, which represents
134 | /// successful handoff to AGC.
135 | /// If the message argument is null.
136 | /// If the message contains any invalid
137 | /// fields.
138 | /// If an error occurs while sending the
139 | /// message.
140 | /// The message to be sent. Must not be null.
141 | /// A boolean indicating whether to perform a dry run (validation
142 | /// only) of the send. If set to true, the message will be sent to the AGC backend service,
143 | /// but it will not be delivered to any actual recipients.
144 | public async Task SendAsync(Message message, bool dryRun)
145 | {
146 | return await this.SendAsync(message, dryRun, default(CancellationToken)).ConfigureAwait(false);
147 | }
148 |
149 | ///
150 | /// Sends a message to the AGC service for delivery. The message gets validated both by
151 | /// the Admin SDK, and the remote AGC service. A successful return value indicates
152 | /// that the message has been successfully sent to AGC, where it has been accepted by the
153 | /// AGC service.
154 | /// If the option is set to true, the message will not be
155 | /// actually sent to the recipients. Instead, the AGC service performs all the necessary
156 | /// validations, and emulates the send operation. This is a good way to check if a
157 | /// certain message will be accepted by AGC for delivery.
158 | ///
159 | /// A task that completes with a message ID string, which represents
160 | /// successful handoff to AGC.
161 | /// If the message argument is null.
162 | /// If the message contains any invalid
163 | /// fields.
164 | /// If an error occurs while sending the
165 | /// message.
166 | /// The message to be sent. Must not be null.
167 | /// A boolean indicating whether to perform a dry run (validation
168 | /// only) of the send. If set to true, the message will be sent to the AGC backend service,
169 | /// but it will not be delivered to any actual recipients.
170 | /// A cancellation token to monitor the asynchronous
171 | /// operation.
172 | public async Task SendAsync(
173 | Message message, bool dryRun, CancellationToken cancellationToken)
174 | {
175 | return await this.messagingClient.SendAsync(
176 | message, dryRun, cancellationToken).ConfigureAwait(false);
177 | }
178 |
179 |
180 | ///
181 | /// Subscribes a list of registration tokens to a topic.
182 | ///
183 | /// A list of registration tokens to subscribe.
184 | /// The topic name to subscribe to.
185 | /// A task that completes with a , giving details about the topic subscription operations.
186 | public async Task SubscribeToTopicAsync(
187 | IReadOnlyList registrationTokens, string topic)
188 | {
189 | return await this.messagingClient.SubscribeToTopicAsync(registrationTokens, topic).ConfigureAwait(false);
190 | }
191 |
192 | ///
193 | /// Unsubscribes a list of registration tokens from a topic.
194 | ///
195 | /// A list of registration tokens to unsubscribe.
196 | /// The topic name to unsubscribe from.
197 | /// A task that completes with a , giving details about the topic unsubscription operations.
198 | public async Task UnsubscribeFromTopicAsync(
199 | IReadOnlyList registrationTokens, string topic)
200 | {
201 | return await this.messagingClient.UnsubscribeFromTopicAsync(registrationTokens, topic).ConfigureAwait(false);
202 | }
203 |
204 | ///
205 | /// Retrieve a list of topics that the specified registration token subscribe to.
206 | ///
207 | /// The topic name to unsubscribe from.
208 | /// A task that completes with a , giving details about the topic retrieve operations.
209 | public async Task GetTopicListAsync(string registrationToken)
210 | {
211 | return await this.messagingClient.GetTopicListAsync(registrationToken).ConfigureAwait(false);
212 | }
213 |
214 | ///
215 | /// Deletes this service instance.
216 | ///
217 | void IAGConnectService.Delete()
218 | {
219 | this.messagingClient.Dispose();
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/AndroidNotification.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed base class and some properties
16 | */
17 |
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Linq;
21 | using AGConnectAdmin.Utils;
22 | using Newtonsoft.Json;
23 | using Newtonsoft.Json.Converters;
24 | using Newtonsoft.Json.Linq;
25 |
26 | namespace AGConnectAdmin.Messaging
27 | {
28 | ///
29 | /// Represents the Android-specific notification options that can be included in a
30 | /// .
31 | ///
32 | public class AndroidNotification : MessagePart
33 | {
34 | ///
35 | /// Gets or sets the title of the Android notification. When provided, overrides the title
36 | /// set via .
37 | ///
38 | [JsonProperty("title")]
39 | public string Title { get; set; }
40 |
41 | ///
42 | /// Gets or sets the title of the Android notification. When provided, overrides the title
43 | /// set via .
44 | ///
45 | [JsonProperty("body")]
46 | public string Body { get; set; }
47 |
48 | ///
49 | /// Gets or sets the icon of the Android notification.
50 | ///
51 | [JsonProperty("icon")]
52 | public string Icon { get; set; }
53 |
54 | ///
55 | /// Gets or sets the notification icon color. Must be of the form #RRGGBB.
56 | ///
57 | [JsonProperty("color")]
58 | public string Color { get; set; }
59 |
60 | ///
61 | /// Gets or sets the sound to be played when the device receives the notification.
62 | ///
63 | [JsonProperty("sound")]
64 | public string Sound { get; set; }
65 |
66 | ///
67 | /// Indicate whether the default sound should be played when the device receives the notification.
68 | ///
69 | [JsonProperty("default_sound")]
70 | public bool DefaultSound { get; set; }
71 |
72 | ///
73 | /// Gets or sets the notification tag. This is an identifier used to replace existing
74 | /// notifications in the notification drawer. If not specified, each request creates a new
75 | /// notification.
76 | ///
77 | [JsonProperty("tag")]
78 | public string Tag { get; set; }
79 |
80 | ///
81 | /// Gets or sets the action associated with a user click on the notification. If specified,
82 | /// an activity with a matching Intent Filter is launched when a user clicks on the
83 | /// notification.
84 | ///
85 | [JsonProperty("click_action")]
86 | public ClickAction ClickAction { get; set; }
87 |
88 | ///
89 | /// Gets or sets the key of the body string in the app's string resources to use to
90 | /// localize the body text.
91 | ///
92 | [JsonProperty("body_loc_key")]
93 | public string BodyLocKey { get; set; }
94 |
95 | ///
96 | /// Gets or sets the collection of resource key strings that will be used in place of the
97 | /// format specifiers in .
98 | ///
99 | [JsonProperty("body_loc_args")]
100 | public IEnumerable BodyLocArgs { get; set; }
101 |
102 | ///
103 | /// Gets or sets the key of the title string in the app's string resources to use to
104 | /// localize the title text.
105 | ///
106 | [JsonProperty("title_loc_key")]
107 | public string TitleLocKey { get; set; }
108 |
109 | ///
110 | /// Gets or sets the collection of resource key strings that will be used in place of the
111 | /// format specifiers in .
112 | ///
113 | [JsonProperty("title_loc_args")]
114 | public IEnumerable TitleLocArgs { get; set; }
115 |
116 | ///
117 | /// Gets or sets the multi localization keys.
118 | ///
119 | [JsonProperty("multi_lang_key")]
120 | public JObject MultiLangKey { get; set; }
121 |
122 | ///
123 | /// Gets or sets the Android notification channel ID (new in Android O). The app must
124 | /// create a channel with this channel ID before any notification with this channel ID is
125 | /// received. If you don't send this channel ID in the request, or if the channel ID
126 | /// provided has not yet been created by the app, AGC uses the channel ID specified in the
127 | /// app manifest.
128 | ///
129 | [JsonProperty("channel_id")]
130 | public string ChannelId { get; set; }
131 |
132 | ///
133 | /// Gets or sets the notify summary.
134 | ///
135 | [JsonProperty("notify_summary")]
136 | public string NotifySummary { get; set; }
137 |
138 | ///
139 | /// Gets or sets the image of the message.
140 | ///
141 | [JsonProperty("image")]
142 | public string Image { get; set; }
143 |
144 | ///
145 | /// Gets or set notification style.
146 | ///
147 | [JsonProperty("style")]
148 | public NotificationStyle Style { get; set; }
149 |
150 | ///
151 | /// Gets or sets big size title. It will override the
152 | /// if the is .
153 | ///
154 | [JsonProperty("big_title")]
155 | public string BigTitle { get; set; }
156 |
157 | ///
158 | /// Gets or sets big size body. It will override the
159 | /// if the is .
160 | ///
161 | [JsonProperty("big_body")]
162 | public string BigBody { get; set; }
163 |
164 | ///
165 | /// Gets or sets the auto clear time (milliseconds) of the message.
166 | ///
167 | [JsonProperty("auto_clear")]
168 | public int? AutoClear { get; set; }
169 |
170 | ///
171 | ///
172 | ///
173 | [JsonProperty("notify_id")]
174 | public int? NotifyId { get; set; }
175 |
176 | ///
177 | /// Group of notification
178 | ///
179 | [JsonProperty("group")]
180 | public string Group { get; set; }
181 |
182 | ///
183 | /// Badge notification
184 | ///
185 | [JsonProperty("badge")]
186 | public BadgeNotification Badge { get; set; }
187 |
188 | ///
189 | /// Gets or sets the ticker of the notification.
190 | ///
191 | [JsonProperty("ticker")]
192 | public string Ticker { get; set; }
193 |
194 | ///
195 | /// Indicate whether the notification should be hide automatically.
196 | ///
197 | [JsonProperty("auto_cancel")]
198 | public bool AutoCancel { get; set; }
199 |
200 | ///
201 | /// Gets or sets the event time of the notification which will affect the sorting.
202 | ///
203 | [JsonProperty("when")]
204 | public DateTime? EventTime { get; set; }
205 |
206 | ///
207 | /// Gets or set the priority of the notification.
208 | ///
209 | [JsonProperty("importance")]
210 | [JsonConverter(typeof(StringEnumConverter))]
211 | public NotificationImportance? Importance { get; set; }
212 |
213 | ///
214 | /// Indicate whether should be use default vibrate timings of device for the notification.
215 | ///
216 | [JsonProperty("use_default_vibrate")]
217 | public bool? DefaultVibrateTimings { get; set; }
218 |
219 | ///
220 | /// Gets or sets the vibrate timings of the notification.
221 | /// Each item represents seconds of vibrate timings and must be within 60.
222 | ///
223 | [JsonProperty("vibrate_config")]
224 | public IEnumerable VibrateTimings { get; set; }
225 |
226 | ///
227 | /// Gets or sets the visibility of the notification.
228 | ///
229 | [JsonProperty("visibility")]
230 | [JsonConverter(typeof(StringEnumConverter))]
231 | public NotificationVisibility? Visibility { get; set; }
232 |
233 | ///
234 | /// Indicate whether should be use default light settings of device for the notification.
235 | ///
236 | [JsonProperty("use_default_light")]
237 | public bool? DefaultLightSettings { get; set; }
238 |
239 | ///
240 | /// Gets or sets the light settings.
241 | ///
242 | [JsonProperty("light_settings")]
243 | public LightSettings LightSettings { get; set; }
244 |
245 | ///
246 | /// Indicate whether show the notification when the application is active.
247 | ///
248 | [JsonProperty("foreground_show")]
249 | public bool? ForegroundShow { get; set; }
250 |
251 | ///
252 | internal protected override AndroidNotification CopyAndValidate()
253 | {
254 | // copy
255 | var copy = new AndroidNotification()
256 | {
257 | Title = this.Title,
258 | Body = this.Body,
259 | Icon = this.Icon,
260 | Color = this.Color,
261 | Sound = this.Sound,
262 | DefaultSound = this.DefaultSound,
263 | Tag = this.Tag,
264 | ClickAction = this.ClickAction?.CopyAndValidate(),
265 | BodyLocKey = this.BodyLocKey,
266 | BodyLocArgs = this.BodyLocArgs?.ToList(),
267 | TitleLocKey = this.TitleLocKey,
268 | TitleLocArgs = this.TitleLocArgs?.ToList(),
269 | MultiLangKey = MultiLangKey == null ? null : new JObject(this.MultiLangKey),
270 | ChannelId = this.ChannelId,
271 | NotifySummary = this.NotifySummary,
272 | Image = this.Image,
273 | Style = this.Style,
274 | BigTitle = this.BigTitle,
275 | BigBody = BigBody,
276 | AutoClear = AutoClear,
277 | NotifyId = NotifyId,
278 | Group = Group,
279 | Badge = this.Badge?.CopyAndValidate(),
280 | Ticker = this.Ticker,
281 | AutoCancel = this.AutoCancel,
282 | EventTime = this.EventTime,
283 | Importance = this.Importance,
284 | DefaultVibrateTimings = this.DefaultVibrateTimings,
285 | DefaultLightSettings = this.DefaultLightSettings,
286 | VibrateTimings = this.VibrateTimings?.ToArray(),
287 | Visibility = this.Visibility,
288 | LightSettings = this.LightSettings?.CopyAndValidate(),
289 | ForegroundShow = this.ForegroundShow
290 | };
291 |
292 | // validate
293 | if (copy.Color != null && !Validator.IsColor(copy.Color))
294 | {
295 | throw new ArgumentException("Color must be in the form #RRGGBB.");
296 | }
297 |
298 | if (copy.Image != null && !Validator.IsHttpsUrl(copy.Image))
299 | {
300 | throw new ArgumentException("Image must be a https url.");
301 | }
302 |
303 | if (copy.TitleLocArgs?.Any() == true && string.IsNullOrEmpty(copy.TitleLocKey))
304 | {
305 | throw new ArgumentException($"{nameof(TitleLocKey)} is required when specifying {nameof(TitleLocArgs)}.");
306 | }
307 |
308 | if (copy.BodyLocArgs?.Any() == true && string.IsNullOrEmpty(copy.BodyLocKey))
309 | {
310 | throw new ArgumentException($"{nameof(BodyLocKey)} is required when specifying {nameof(BodyLocArgs)}.");
311 | }
312 |
313 | if (copy.VibrateTimings?.Any() == true && !Validator.IsAllInRange(copy.VibrateTimings, 0, 60))
314 | {
315 | throw new ArgumentException("Vibrate timing must be within 60 seconds");
316 | }
317 |
318 | return copy;
319 | }
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/src/AGConnectAdmin/Messaging/AGConnectMessagingClient.cs:
--------------------------------------------------------------------------------
1 | /* Copyright 2018, Google Inc. All rights reserved.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | *
15 | * 2019.12.27-Changed implement
16 | */
17 |
18 | using System;
19 | using System.Net.Http;
20 | using System.Net.Http.Headers;
21 | using System.Threading;
22 | using System.Threading.Tasks;
23 | using Newtonsoft.Json;
24 | using System.Net;
25 | using System.Text;
26 | using System.Collections.Generic;
27 | using AGConnectAdmin.Auth;
28 |
29 | namespace AGConnectAdmin.Messaging
30 | {
31 | ///
32 | /// A client for making authorized HTTP calls to the HCM backend service. Handles request
33 | /// serialization, response parsing, and HTTP error handling.
34 | ///
35 | internal sealed class AGConnectMessagingClient : IDisposable
36 | {
37 | private const string SUCCESS_CODE = "80000000";
38 | private readonly HttpClient httpClient;
39 | private string TokenUrl;
40 | private readonly string sendUrl;
41 | private AppOptions options;
42 |
43 | private TokenResponse tokenResponse;
44 | private Task accessTokenTask;
45 | private object accessTokenTaskLock = new object();
46 |
47 | public AGConnectMessagingClient(AppOptions options)
48 | {
49 | this.options = options;
50 | TokenUrl = options.LoginUri;
51 | sendUrl = options.GetApiUri();
52 | #if PROXY
53 | httpClient = new HttpClient(new HttpClientHandler()
54 | {
55 | UseProxy = true,
56 | // Use cntlm proxy setting to accesss internet.
57 | Proxy = new WebProxy("localhost", 3128),
58 | });
59 | #else
60 | httpClient = new HttpClient();
61 | #endif
62 | }
63 |
64 | public void Dispose()
65 | {
66 | httpClient.Dispose();
67 | }
68 |
69 | ///
70 | /// Sends a message to the AGC service for delivery. The message gets validated both by
71 | /// the Admin SDK, and the remote AGC service. A successful return value indicates
72 | /// that the message has been successfully sent to AGC, where it has been accepted by the
73 | /// AGC service.
74 | ///
75 | /// A task that completes with a message ID string, which represents
76 | /// successful handoff to AGC.
77 | /// If the message argument is null.
78 | /// If the message contains any invalid
79 | /// fields.
80 | /// If an error occurs while sending the
81 | /// message.
82 | /// The message to be sent. Must not be null.
83 | /// A boolean indicating whether to perform a dry run (validation
84 | /// only) of the send. If set to true, the message will be sent to the AGC backend service,
85 | /// but it will not be delivered to any actual recipients.
86 | /// A cancellation token to monitor the asynchronous
87 | /// operation.
88 | public async Task SendAsync(
89 | Message message,
90 | bool dryRun = false,
91 | CancellationToken cancellationToken = default)
92 | {
93 | try
94 | {
95 | var sendRequest = new SendRequest()
96 | {
97 | Message = (message.ThrowIfNull(nameof(message)) as MessagePart).CopyAndValidate(),
98 | ValidateOnly = dryRun,
99 | };
100 | var accessToken = await GetAccessTokenAsync().ConfigureAwait(false);
101 | var payload = NewtonsoftJsonSerializer.Instance.Serialize(sendRequest);
102 | var content = new StringContent(payload, Encoding.UTF8, "application/json");
103 | var request = new HttpRequestMessage()
104 | {
105 | Method = HttpMethod.Post,
106 | RequestUri = new Uri(sendUrl),
107 | Content = content,
108 | };
109 |
110 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
111 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
112 |
113 | var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
114 |
115 | if (response.StatusCode != HttpStatusCode.OK)
116 | {
117 | var error = $"Response status code does not indicate success: {response.StatusCode}";
118 | throw new AGConnectException(error);
119 | }
120 | var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
121 | var parsed = JsonConvert.DeserializeObject(json);
122 | if (parsed.Code != SUCCESS_CODE)
123 | {
124 | throw new AGConnectException($"Send message failed: {parsed.Code}{Environment.NewLine}{parsed.Message}");
125 | }
126 | return parsed.RequestId;
127 | }
128 | catch (HttpRequestException e)
129 | {
130 | throw new AGConnectException("Error while calling the AGC messaging service.", e);
131 | }
132 | }
133 |
134 | ///
135 | /// Subscribes a list of registration tokens to a topic.
136 | ///
137 | /// A list of registration tokens to subscribe.
138 | /// The topic name to subscribe to.
139 | /// A task that completes with a , giving details about the topic subscription operations.
140 | public async Task SubscribeToTopicAsync(
141 | IReadOnlyList registrationTokens, string topic)
142 | {
143 | if (string.IsNullOrWhiteSpace(topic))
144 | {
145 | throw new ArgumentException("Topic can not be empty");
146 | }
147 | if (registrationTokens == null)
148 | {
149 | throw new ArgumentException("Registration tokens can not be null");
150 | }
151 | if (registrationTokens.Count == 0)
152 | {
153 | throw new ArgumentException("Registration tokens can not be empty");
154 | }
155 | if (registrationTokens.Count > 1000)
156 | {
157 | throw new ArgumentException("Registration token list must not contain more than 1000 tokens");
158 | }
159 |
160 | var accessToken = await GetAccessTokenAsync().ConfigureAwait(false);
161 | var body = new TopicManagementRequest()
162 | {
163 | Topic = topic,
164 | Tokens = new List(registrationTokens)
165 | };
166 | var url = $"{options.ApiBaseUri}/v1/{options.ClientId}/topic:subscribe";
167 | var payload = NewtonsoftJsonSerializer.Instance.Serialize(body);
168 | var content = new StringContent(payload, Encoding.UTF8, "application/json");
169 | var request = new HttpRequestMessage()
170 | {
171 | Method = HttpMethod.Post,
172 | RequestUri = new Uri(url),
173 | Content = content,
174 | };
175 |
176 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
177 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
178 |
179 | var resp = await httpClient.SendAsync(request).ConfigureAwait(false);
180 | var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
181 | return NewtonsoftJsonSerializer.Instance.Deserialize(json);
182 | }
183 |
184 | ///
185 | /// Unsubscribes a list of registration tokens from a topic.
186 | ///
187 | /// A list of registration tokens to unsubscribe.
188 | /// The topic name to unsubscribe from.
189 | /// A task that completes with a , giving details about the topic unsubscription operations.
190 | public async Task UnsubscribeFromTopicAsync(
191 | IReadOnlyList registrationTokens, string topic)
192 | {
193 | if (string.IsNullOrWhiteSpace(topic))
194 | {
195 | throw new ArgumentException("Topic can not be empty");
196 | }
197 | if (registrationTokens == null)
198 | {
199 | throw new ArgumentException("Registration tokens can not be null");
200 | }
201 | if (registrationTokens.Count == 0)
202 | {
203 | throw new ArgumentException("Registration tokens can not be empty");
204 | }
205 | if (registrationTokens.Count > 1000)
206 | {
207 | throw new ArgumentException("Registration token list must not contain more than 1000 tokens");
208 | }
209 |
210 | var accessToken = await GetAccessTokenAsync().ConfigureAwait(false);
211 | var body = new TopicManagementRequest()
212 | {
213 | Topic = topic,
214 | Tokens = new List(registrationTokens)
215 | };
216 | var url = $"{options.ApiBaseUri}/v1/{options.ClientId}/topic:unsubscribe";
217 | var payload = NewtonsoftJsonSerializer.Instance.Serialize(body);
218 | var content = new StringContent(payload, Encoding.UTF8, "application/json");
219 | var request = new HttpRequestMessage()
220 | {
221 | Method = HttpMethod.Post,
222 | RequestUri = new Uri(url),
223 | Content = content,
224 | };
225 |
226 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
227 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
228 |
229 | var resp = await httpClient.SendAsync(request).ConfigureAwait(false);
230 | var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
231 | return NewtonsoftJsonSerializer.Instance.Deserialize(json);
232 | }
233 |
234 | ///
235 | /// Retrieve a list of topics that the specified registration token subscribe to.
236 | ///
237 | /// The topic name to unsubscribe from.
238 | /// A task that completes with a , giving details about the topic retrieve operations.
239 | public async Task GetTopicListAsync(string registrationToken)
240 | {
241 | if (string.IsNullOrWhiteSpace(registrationToken))
242 | {
243 | throw new ArgumentException("Registration token can not be empty");
244 | }
245 | var accessToken = await GetAccessTokenAsync().ConfigureAwait(false);
246 | var body = new TopicListRequest()
247 | {
248 | Token = registrationToken
249 | };
250 | var url = $"{options.ApiBaseUri}/v1/{options.ClientId}/topic:list";
251 | var payload = NewtonsoftJsonSerializer.Instance.Serialize(body);
252 | var content = new StringContent(payload, Encoding.UTF8, "application/json");
253 | var request = new HttpRequestMessage()
254 | {
255 | Method = HttpMethod.Post,
256 | RequestUri = new Uri(url),
257 | Content = content,
258 | };
259 |
260 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
261 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
262 |
263 | var resp = await httpClient.SendAsync(request).ConfigureAwait(false);
264 | var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
265 | return NewtonsoftJsonSerializer.Instance.Deserialize(json);
266 | }
267 |
268 | private async Task GetAccessTokenAsync()
269 | {
270 | string accessToken = tokenResponse?.GetValidAccessToken();
271 | if (string.IsNullOrEmpty(accessToken))
272 | {
273 | lock (accessTokenTaskLock)
274 | {
275 | if (accessTokenTask == null)
276 | {
277 | // access token task may be shared by multiple messaging task, thus should not pass cancellationToken to it
278 | accessTokenTask = RequestAccessTokenAsync(default);
279 | }
280 | }
281 |
282 | accessToken = await accessTokenTask.ConfigureAwait(false);
283 |
284 | lock (accessTokenTaskLock)
285 | {
286 | accessTokenTask = null;
287 | }
288 | }
289 |
290 | if (string.IsNullOrEmpty(accessToken))
291 | {
292 | var error = "AccessToken is null or empty";
293 | throw new AGConnectException(error);
294 | }
295 |
296 | return accessToken;
297 | }
298 |
299 | private async Task RequestAccessTokenAsync(CancellationToken cancellationToken)
300 | {
301 | string content = string.Format("grant_type=client_credentials&client_secret={0}&client_id={1}", options.ClientSecret, options.ClientId);
302 | var request = new HttpRequestMessage()
303 | {
304 | Method = HttpMethod.Post,
305 | RequestUri = new Uri(TokenUrl),
306 | Content = new StringContent(content),
307 | };
308 | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
309 | var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
310 | if (response.StatusCode != HttpStatusCode.OK)
311 | {
312 | var error = $"Response status code does not indicate success: {response.StatusCode}";
313 | throw new AGConnectException(error);
314 | }
315 |
316 | var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
317 | var parsed = JsonConvert.DeserializeObject(json);
318 | switch (parsed.Error)
319 | {
320 | case 1101:
321 | throw new AGConnectException("Get access token failed: invalid request");
322 | case 1102:
323 | throw new AGConnectException("Get access token failed: missing required param");
324 | case 1104:
325 | throw new AGConnectException("Get access token failed: unsupported response type");
326 | case 1105:
327 | throw new AGConnectException("Get access token failed: unsupported grant type");
328 | case 1107:
329 | throw new AGConnectException("Get access token failed: access denied");
330 | case 1201:
331 | throw new AGConnectException("Get access token failed: invalid ticket");
332 | case 1202:
333 | throw new AGConnectException("Get access token failed: invalid sso_st");
334 | default:
335 | break;
336 | }
337 |
338 | var token = parsed.GetValidAccessToken();
339 | if (string.IsNullOrEmpty(token))
340 | {
341 | var error = "AccessToken return by AGC is null or empty";
342 | throw new AGConnectException(error);
343 | }
344 |
345 | tokenResponse = parsed;
346 | return token;
347 | }
348 | }
349 | }
350 |
--------------------------------------------------------------------------------