├── 3rd-party └── .gitignore ├── LICENSE ├── README.md ├── README_ZH.md ├── build └── .gitignore ├── dist └── .gitignore ├── doc └── .gitignore ├── src ├── examples │ ├── common │ │ └── pushcommon.go │ ├── send_apns_message │ │ └── main.go │ ├── send_condition_message │ │ └── main.go │ ├── send_data_message │ │ └── main.go │ ├── send_instance_app_message │ │ └── main.go │ ├── send_notify_message │ │ └── main.go │ ├── send_test_message │ │ └── main.go │ ├── send_topic_message │ │ └── main.go │ └── send_webpush_message │ │ └── main.go ├── httpclient │ └── httpclient.go └── push │ ├── authention │ └── auth.go │ ├── config │ └── config.go │ ├── constant │ ├── constants.go │ └── urlfmt.go │ ├── core │ ├── message.go │ └── pushclient.go │ ├── model │ ├── message.go │ ├── message_hm.go │ └── message_webpush.go │ └── verify │ ├── messaging_hm_verify.go │ ├── messaging_verify.go │ └── messaging_webpush_verify.go └── test └── .gitignore /3rd-party/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HMS Pushkit Go Severdemo 2 | English | [中文](https://github.com/HMS-Core/hms-push-serverdemo-go/blob/master/README_ZH.md) 3 | ## Table of Contents 4 | * [Introduction](#introduction) 5 | * [Installation](#installation) 6 | * [Configuration](#configuration) 7 | * [Sample Code](#sample-code) 8 | * [License](#license) 9 | 10 | 11 | ## Introduction 12 | Golang sample code encapsulates APIs of the HUAWEI Push Kit server. It provides many sample programs for your reference or usage. 13 | 14 | The following describes packages of Golang sample code. 15 | | Package | Description | 16 | | ----------- | ----------- | 17 | |examples|Sample code packages. Each package can run independently.| 18 | |httpclient|Common package for sending network requests.| 19 | |push|Package where APIs of the HUAWEI Push Kit server are encapsulated.| 20 | 21 | ## Installation 22 | Before using Golang sample code, check whether the Golang environment has been installed. Golang 1.11 or a later version is recommended. 23 | Decompress the Golang sample code package. 24 | 25 | Copy the org.huawei.com package in the decompressed folder to the project vendor directory in the path specified by GOPATH. 26 | Refresh the project and ensure that the file is successfully copied to the destination directory. 27 | 28 | ## Configuration 29 | Golang sample code uses the Client structure in the push package as the entry. Each method in the Client structure calls an API of the HUAWEI Push Kit server. 30 | The following describes methods in the Client structure. 31 | | Method | Description | 32 | | ----------- | ----------- | 33 | |SendMessage| Sends a message to a device.| 34 | 35 | To use functions provided by packages in examples, you need to set related parameters in pushcommon.go in the common package. 36 | 37 | The following describes parameters in pushcommon.go. 38 | | Parameter | Description | 39 | | ----------- | ----------- | 40 | |appId|App ID, which is obtained from app information.| 41 | |appSecret|Secret access key of an app, which is obtained from app information.| 42 | |authUrl|URL for the Huawei OAuth 2.0 service to obtain a token, please refer to [Generating an App-Level Access Token](https://developer.huawei.com/consumer/en/doc/development/parts-Guides/generating_app_level_access_token).| 43 | |pushUrl|URL for accessing HUAWEI Push Kit, please refer to [Sending Messages](https://developer.huawei.com/consumer/en/doc/development/HMS-References/push-sendapi).| 44 | 45 | The following table describes parameters in target.go. 46 | | Parameter | Description | 47 | | ----------- | ----------- | 48 | |TargetTopic|Name of a topic to be subscribed to, unsubscribed from, or queried.| 49 | |TargetCondition|Combination of condition expressions for a message.| 50 | |TargetToken|Token of a target device, which is obtained from the device.| 51 | |TargetTokenArray|Tokens of all target devices, which are obtained from the devices.| 52 | 53 | ## Sample Code 54 | Download Golang sample code in Downloading Server Sample Code. 55 | 56 | 1). Send an Android data message. 57 | You can obtain the initialized MessageRequest instance of the data message using the NewTransparentMsgRequest method in the push/model package. 58 | > Code location: examples/send_data_message/main.go 59 | 60 | 2). Send an Android notification message. 61 | You can obtain the initialized MessageRequest instance of the notification message using the NewNotificationMsgRequest method in the push/model package. 62 | > Code location: examples/send_notify_message/main.go 63 | 64 | 3). Send a message by topic. 65 | You can send a notification message or data message to a device by topic. Specify the topic after obtaining the MessageRequest instance. 66 | > Code location: examples/send_topic_message/main.go 67 | 68 | 4). Send a message by conditions. 69 | You can send a notification message or data message to a device by conditions. Specify the conditions after obtaining the MessageRequest instance. 70 | > Code location: examples/send_condition_message/main.go 71 | 72 | 5). Send a message to a Huawei quick app. 73 | You can send a message to a quick app by setting FastAppTarget. 74 | > Code location: examples/send_instance_app_message/main.go 75 | 76 | 6). Send a message through the APNs agent. 77 | You can send a message through the APNs agent by setting Apns of the message. 78 | > Code location: examples/send_apns_message/main.go 79 | 80 | 7). Send a message through the WebPush agent. 81 | You can send a message through the WebPush agent by setting WebPush of the message. 82 | > Code location: examples/send_webpush_message/main.go 83 | 84 | 8). Send a test message. 85 | > Code location: examples/send_test_message/main.go 86 | 87 | ## Question or issues 88 | If you want to evaluate more about HMS Core, 89 | [r/HMSCore on Reddit](https://www.reddit.com/r/HuaweiDevelopers/) is for you to keep up with latest news about HMS Core, and to exchange insights with other developers. 90 | 91 | If you have questions about how to use HMS samples, try the following options: 92 | - [Stack Overflow](https://stackoverflow.com/questions/tagged/huawei-mobile-services) is the best place for any programming questions. Be sure to tag your question with 93 | `huawei-mobile-services`. 94 | - [Huawei Developer Forum](https://forums.developer.huawei.com/forumPortal/en/home?fid=0101187876626530001) HMS Core Module is great for general questions, or seeking recommendations and opinions. 95 | 96 | If you run into a bug in our samples, please submit an [issue](https://github.com/HMS-Core/hms-push-serverdemo-go/issues) to the Repository. Even better you can submit a [Pull Request](https://github.com/HMS-Core/hms-push-serverdemo-go/pulls) with a fix. 97 | 98 | ## License 99 | Pushkit Go sample is licensed under the [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0). 100 | 101 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # 华为推送服务服务端Golang示例代码 2 | [English](https://github.com/HMS-Core/hms-push-serverdemo-go) | 中文 3 | ## 目录 4 | * [简介](#简介) 5 | * [安装](#安装) 6 | * [配置](#配置) 7 | * [示例代码](#示例代码) 8 | * [授权许可](#授权许可) 9 | 10 | ## 简介 11 | Golang示例代码对华为推送服务(HUAWEI Push Kit)服务端接口进行封装,包含丰富的示例程序,方便您参考或直接使用。 12 | 13 | 示例代码主要包括以下组成: 14 | | 包名 | 说明 | 15 | | ----------- | ----------- | 16 | |examples|示例代码包,每个包都可以独立运行| 17 | |httpclient|发送网络请求的公共包| 18 | |push|推送服务的服务端接口封装包| 19 | 20 | ## 安装 21 | 使用Golang示例代码前,请确认您已安装Golang开发环境(推荐使用Golang 1.11或以上版本),并解压Golang示例代码包。 22 | 23 | 将解压包中的org.huawei.com包复制到GOPATH指定的工程vendor目录下。刷新该工程,确保文件出现在对应的目录下。 24 | 25 | ## 配置 26 | Golang示例代码以push包中的Client结构体为入口。Client结构体中的每个方法都可以调用推送服务服务端的一个接口。 27 | Client结构体包含以下方法: 28 | | 方法 | 说明 | 29 | | ----------- | ----------- | 30 | |SendMessage| 向设备发送消息| 31 | 32 | 如需使用examples中的功能,请在公共包中的pushcommon.go中设置相关参数。 33 | 34 | pushcommon.go中配置如下参数: 35 | | 参数 | 说明 | 36 | | ----------- | ----------- | 37 | |appId|应用ID,从应用消息中获取| 38 | |appSecret|应用访问密钥,从应用信息中获取| 39 | |authUrl|华为OAuth 2.0获取token的地址。具体请参考[基于OAuth 2.0开放鉴权-客户端模式](https://developer.huawei.com/consumer/cn/doc/development/parts-Guides/generating_app_level_access_token).| 40 | |pushUrl|推送服务的访问地址。具体请参考[推送服务-下行消息](https://developer.huawei.com/consumer/cn/doc/development/HMS-References/push-sendapi).| 41 | 42 | target.go中配置如下参数: 43 | | 参数 | 说明 | 44 | | ----------- | ----------- | 45 | |TargetTopic|订阅、退订或查询的主题名称| 46 | |TargetCondition|消息的条件表达式组合| 47 | |TargetToken|目标设备token,从目标设备上获取| 48 | |TargetTokenArray|所有目标设备tokens,从目标设备上获取| 49 | 50 | ## 示例代码 51 | 请在[服务端示例代码](https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Examples-V5/server-sample-code-0000001050986079-V5)下载Golang示例代码. 52 | 53 | 1). 发送Android透传消息。 54 | 通过push/model包中的NewTransparentMsgRequest方法获取初始化的透传消息的MessageRequest实例。 55 | > 代码位置: examples/send_data_message/main.go 56 | 57 | 2). 发送Android通知栏消息。 58 | 通过push/model包中的NewNotificationMsgRequest方法获取初始化的透传消息的MessageRequest实例。 59 | > 代码位置: examples/send_notify_message/main.go 60 | 61 | 3). 基于主题发送消息。基于主题发送通知栏消息或透传消息。 62 | 获取MessageRequest实例后可定制主题。 63 | > 代码位置: examples/send_topic_message/main.go 64 | 65 | 4). 基于条件发送消息。 66 | 基于条件发送通知栏消息或透传消息。获取MessageRequest实例后可定制条件。 67 | > 代码位置: examples/send_condition_message/main.go 68 | 69 | 5). 向华为快应用发送消息。 70 | 通过设置FastAppTarget属性来实现。 71 | > 代码位置: examples/send_instance_app_message/main.go 72 | 73 | 6). 基于APNs代理发送消息。 74 | 通过设置消息的Apns属性来实现。 75 | > 代码位置: examples/send_apns_message/main.go 76 | 77 | 7). 基于WebPush代理发送消息。 78 | 通过设置消息的WebPush属性来实现。 79 | > 代码位置: examples/send_webpush_message/main.go 80 | 81 | 8). 发送测试消息。 82 | > 代码位置: examples/send_test_message/main.go 83 | 84 | ## 技术支持 85 | 如果您对HMS Core还处于评估阶段,可在[Reddit社区](https://www.reddit.com/r/HuaweiDevelopers/)获取关于HMS Core的最新讯息,并与其他开发者交流见解。 86 | 87 | 如果您对使用HMS示例代码有疑问,请尝试: 88 | - 开发过程遇到问题上[Stack Overflow](https://stackoverflow.com/questions/tagged/huawei-mobile-services),在`huawei-mobile-services`标签下提问,有华为研发专家在线一对一解决您的问题。 89 | - 到[华为开发者论坛](https://developer.huawei.com/consumer/cn/forum/blockdisplay?fid=18) HMS Core板块与其他开发者进行交流。 90 | 91 | 如果您在尝试示例代码中遇到问题,请向仓库提交[issue](https://github.com/HMS-Core/hms-push-serverdemo-go/issues),也欢迎您提交[Pull Request](https://github.com/HMS-Core/hms-push-serverdemo-go/pulls)。 92 | 93 | ## 授权许可 94 | 华为推送服务Golang示例代码经过[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0)授权许可。 95 | 96 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /dist/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /src/examples/common/pushcommon.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package common 18 | 19 | import ( 20 | "fmt" 21 | "pushkit-go-sample/push/config" 22 | "pushkit-go-sample/push/core" 23 | "sync" 24 | ) 25 | 26 | const ( 27 | // private data ,it's import,please don't let it out 28 | appId = "xxxxxx" 29 | appSecret = "xxxxxx" 30 | token = "xxxxxx" 31 | 32 | // below is public address 33 | // get token address 34 | authUrl = "https://login.cloud.huawei.com/oauth2/v2/token" 35 | // send push msg address 36 | pushUrl = "https://api.push.hicloud.com" 37 | ) 38 | 39 | var conf = &config.Config{ 40 | AppId: appId, 41 | AppSecret: appSecret, 42 | AuthUrl: authUrl, 43 | PushUrl: pushUrl, 44 | } 45 | 46 | var ( 47 | pushClient *core.HttpPushClient 48 | once sync.Once 49 | ) 50 | 51 | var ( 52 | //TargetToken the topic to be subscribed/unsubscribed 53 | TargetTopic = "topic" 54 | 55 | //TargetCondition the condition of the devices operated 56 | TargetCondition = "'topic' in topics && ('topic' in topics || 'TopicC' in topics)" 57 | 58 | //TargetToken the token of the device operated 59 | TargetToken = token 60 | 61 | //TargetTokenArray the collection of the tokens of th devices operated 62 | TargetTokenArray = []string{TargetToken} 63 | ) 64 | 65 | func GetPushClient() *core.HttpPushClient { 66 | once.Do(func() { 67 | client, err := core.NewHttpClient(conf) 68 | if err != nil { 69 | fmt.Printf("Failed to new common client! Error is %s\n", err.Error()) 70 | panic(err) 71 | } 72 | pushClient = client 73 | }) 74 | 75 | return pushClient 76 | } 77 | 78 | func GetPushConf() *config.Config{ 79 | return conf 80 | } 81 | -------------------------------------------------------------------------------- /src/examples/send_apns_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendApnsMessage() 37 | } 38 | 39 | func sendApnsMessage() { 40 | msgRequest, err := getApnsMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getApnsMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewNotificationMsgRequest() 64 | msgRequest.Message.Token = common.TargetTokenArray 65 | msgRequest.Message.Apns = model.GetDefaultApns() 66 | msgRequest.Message.Notification = nil 67 | msgRequest.Message.Data = "" 68 | 69 | b, err := json.Marshal(msgRequest) 70 | if err != nil { 71 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 72 | return nil, err 73 | } 74 | 75 | fmt.Printf("Default message is %s\n", string(b)) 76 | return msgRequest, nil 77 | } 78 | -------------------------------------------------------------------------------- /src/examples/send_condition_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendConditionMessage() 37 | } 38 | 39 | func sendConditionMessage() { 40 | msgRequest, err := getConditionMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getConditionMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewNotificationMsgRequest() 64 | msgRequest.Message.Android = model.GetDefaultAndroid() 65 | msgRequest.Message.Condition = common.TargetCondition 66 | msgRequest.Message.Android.Notification = model.GetDefaultAndroidNotification() 67 | 68 | b, err := json.Marshal(msgRequest) 69 | if err != nil { 70 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 71 | return nil, err 72 | } 73 | 74 | fmt.Printf("Default message is %s\n", string(b)) 75 | return msgRequest, nil 76 | } 77 | -------------------------------------------------------------------------------- /src/examples/send_data_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendDataMessage() 37 | } 38 | 39 | func sendDataMessage() { 40 | msgRequest, err := getDataMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | httpclient := common.GetPushClient() 47 | 48 | resp, err := httpclient.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getDataMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewTransparentMsgRequest() 64 | msgRequest.Message.Android = model.GetDefaultAndroid() 65 | msgRequest.Message.Token = common.TargetTokenArray 66 | 67 | b, err := json.Marshal(msgRequest) 68 | if err != nil { 69 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 70 | return nil, err 71 | } 72 | 73 | fmt.Printf("Default message is %s\n", string(b)) 74 | return msgRequest, nil 75 | } 76 | -------------------------------------------------------------------------------- /src/examples/send_instance_app_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendInstanceAppMessage() 37 | } 38 | 39 | func sendInstanceAppMessage() { 40 | msgRequest, err := getInstanceAppMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getInstanceAppMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewNotificationMsgRequest() 64 | msgRequest.Message.Data = "{\"pushtype\":0,\"pushbody\":{\"title\":\"test instance app title\",\"description\":\"test instance app body\",\"page\":\"/\",\"params\":{\"key1\":\"test1\",\"key2\":\"test2\"},\"ringtone\":{\"vibration\":\"true\",\"breathLight\":\"true\"}}}" 65 | msgRequest.Message.Notification = nil 66 | msgRequest.Message.Token = common.TargetTokenArray 67 | msgRequest.Message.Android = model.GetDefaultAndroid() 68 | msgRequest.Message.Android.FastAppTarget = constant.FastAppTargetDevelop 69 | 70 | b, err := json.Marshal(msgRequest) 71 | if err != nil { 72 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 73 | return nil, err 74 | } 75 | 76 | fmt.Printf("Default message is %s\n", string(b)) 77 | return msgRequest, nil 78 | } 79 | -------------------------------------------------------------------------------- /src/examples/send_notify_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendNotifyMessage() 37 | } 38 | 39 | func sendNotifyMessage() { 40 | msgRequest, err := getNotifyMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getNotifyMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewNotificationMsgRequest() 64 | msgRequest.Message.Token = common.TargetTokenArray 65 | msgRequest.Message.Android = model.GetDefaultAndroid() 66 | msgRequest.Message.Android.Notification = model.GetDefaultAndroidNotification() 67 | 68 | b, err := json.Marshal(msgRequest) 69 | if err != nil { 70 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 71 | return nil, err 72 | } 73 | 74 | fmt.Printf("Default message is %s\n", string(b)) 75 | return msgRequest, nil 76 | } 77 | -------------------------------------------------------------------------------- /src/examples/send_test_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendTestMessage() 37 | } 38 | 39 | func sendTestMessage() { 40 | msgRequest, err := getTestMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getTestMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewNotificationMsgRequest() 64 | msgRequest.Message.Token = common.TargetTokenArray 65 | msgRequest.Message.Android = model.GetDefaultAndroid() 66 | msgRequest.Message.Android.Notification = model.GetDefaultAndroidNotification() 67 | 68 | b, err := json.Marshal(msgRequest) 69 | if err != nil { 70 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 71 | return nil, err 72 | } 73 | 74 | fmt.Printf("Default message is %s\n", string(b)) 75 | return msgRequest, nil 76 | } 77 | -------------------------------------------------------------------------------- /src/examples/send_topic_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendTopicMessage() 37 | } 38 | 39 | func sendTopicMessage() { 40 | msgRequest, err := getTopicMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | func getTopicMsgRequest() (*model.MessageRequest, error) { 63 | msgRequest := model.NewNotificationMsgRequest() 64 | msgRequest.Message.Topic = common.TargetTopic 65 | msgRequest.Message.Android = model.GetDefaultAndroid() 66 | msgRequest.Message.Data = "" 67 | msgRequest.Message.Android.Notification = model.GetDefaultAndroidNotification() 68 | 69 | b, err := json.Marshal(msgRequest) 70 | if err != nil { 71 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 72 | return nil, err 73 | } 74 | 75 | fmt.Printf("Default message is %s\n", string(b)) 76 | return msgRequest, nil 77 | } 78 | -------------------------------------------------------------------------------- /src/examples/send_webpush_message/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "pushkit-go-sample/examples/common" 25 | "pushkit-go-sample/push/constant" 26 | "pushkit-go-sample/push/model" 27 | ) 28 | 29 | func main() { 30 | defer func() { 31 | if err := recover(); err != nil { 32 | fmt.Printf("Panic! Error is %s\n", err) 33 | } 34 | }() 35 | 36 | sendWebPushMessage() 37 | } 38 | 39 | func sendWebPushMessage() { 40 | msgRequest, err := getWebPushMsgRequest() 41 | if err != nil { 42 | fmt.Printf("Failed to get message request! Error is %s\n", err.Error()) 43 | return 44 | } 45 | 46 | client := common.GetPushClient() 47 | 48 | resp, err := client.SendMessage(context.Background(), msgRequest) 49 | if err != nil { 50 | fmt.Printf("Failed to send message! Error is %s\n", err.Error()) 51 | return 52 | } 53 | 54 | if resp.Code != constant.Success { 55 | fmt.Printf("Failed to send message! Response is %+v\n", resp) 56 | return 57 | } 58 | 59 | fmt.Printf("Succeed to send message! Response is %+v\n", resp) 60 | } 61 | 62 | 63 | 64 | func getWebPushMsgRequest() (*model.MessageRequest, error) { 65 | msgRequest := model.NewNotificationMsgRequest() 66 | msgRequest.Message.Token = common.TargetTokenArray 67 | msgRequest.Message.WebPush = model.GetDefaultWebPushConfig() 68 | msgRequest.Message.Data = "" 69 | 70 | b, err := json.Marshal(msgRequest) 71 | if err != nil { 72 | fmt.Printf("Failed to marshal the default message! Error is %s\n", err.Error()) 73 | return nil, err 74 | } 75 | 76 | fmt.Printf("Default message is %s\n", string(b)) 77 | return msgRequest, nil 78 | } 79 | -------------------------------------------------------------------------------- /src/httpclient/httpclient.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package httpclient 18 | 19 | import ( 20 | "bytes" 21 | "context" 22 | "crypto/tls" 23 | "io" 24 | "io/ioutil" 25 | "net/http" 26 | "time" 27 | ) 28 | 29 | type PushRequest struct { 30 | Method string 31 | URL string 32 | Body []byte 33 | Header []HTTPOption 34 | } 35 | 36 | type PushResponse struct { 37 | Status int 38 | Header http.Header 39 | Body []byte 40 | } 41 | 42 | type HTTPRetryConfig struct { 43 | MaxRetryTimes int 44 | RetryInterval time.Duration 45 | } 46 | 47 | type HTTPClient struct { 48 | Client *http.Client 49 | RetryConfig *HTTPRetryConfig 50 | } 51 | 52 | type HTTPOption func(r *http.Request) 53 | 54 | func SetHeader(key string, value string) HTTPOption { 55 | return func(r *http.Request) { 56 | r.Header.Set(key, value) 57 | } 58 | } 59 | 60 | func NewHTTPClient() (*HTTPClient, error) { 61 | tr := &http.Transport{ 62 | MaxIdleConns: 10, 63 | IdleConnTimeout: 30 * time.Second, 64 | DisableCompression: true, 65 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 66 | } 67 | 68 | client := &http.Client{Transport: tr} 69 | 70 | return &HTTPClient{ 71 | Client: client, 72 | RetryConfig: &HTTPRetryConfig{ 73 | MaxRetryTimes: 4, 74 | RetryInterval: 0}, 75 | }, nil 76 | } 77 | 78 | func (r *PushRequest) buildHTTPRequest() (*http.Request, error) { 79 | var body io.Reader 80 | 81 | if r.Body != nil { 82 | body = bytes.NewBuffer(r.Body) 83 | } 84 | 85 | req, err := http.NewRequest(r.Method, r.URL, body) 86 | if err != nil { 87 | return nil, err 88 | } 89 | 90 | for _, opt := range r.Header { 91 | opt(req) 92 | } 93 | 94 | return req, nil 95 | } 96 | 97 | func (c *HTTPClient) doHttpRequest(req *PushRequest) (*PushResponse, error) { 98 | request, err := req.buildHTTPRequest() 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | resp, err := c.Client.Do(request) 104 | 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | body, err := ioutil.ReadAll(resp.Body) 110 | defer resp.Body.Close() 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | return &PushResponse{ 116 | Status: resp.StatusCode, 117 | Header: resp.Header, 118 | Body: body, 119 | }, nil 120 | } 121 | 122 | func (c *HTTPClient) DoHttpRequest(ctx context.Context, req *PushRequest) (*PushResponse, error) { 123 | var ( 124 | result *PushResponse 125 | err error 126 | ) 127 | for retryTimes := 0; retryTimes < c.RetryConfig.MaxRetryTimes; retryTimes++ { 128 | result, err = c.doHttpRequest(req) 129 | 130 | if err == nil { 131 | break 132 | } 133 | 134 | if !c.pendingForRetry(ctx) { 135 | break 136 | } 137 | } 138 | return result, err 139 | } 140 | 141 | func (c *HTTPClient) pendingForRetry(ctx context.Context) bool { 142 | if c.RetryConfig.RetryInterval > 0 { 143 | select { 144 | case <-ctx.Done(): 145 | return false 146 | case <-time.After(c.RetryConfig.RetryInterval): 147 | return true 148 | } 149 | } 150 | return false 151 | } 152 | -------------------------------------------------------------------------------- /src/push/authention/auth.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package auth 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "errors" 23 | "fmt" 24 | "net/http" 25 | "net/url" 26 | "pushkit-go-sample/push/config" 27 | 28 | "pushkit-go-sample/httpclient" 29 | ) 30 | 31 | type AuthClient struct { 32 | endpoint string 33 | appId string 34 | appSecret string 35 | client *httpclient.HTTPClient 36 | } 37 | 38 | type TokenMsg struct { 39 | AccessToken string `json:"access_token"` 40 | ExpiresIn int `json:"expires_in"` 41 | Scope string `json:"scope"` 42 | Error string `json:"error"` 43 | ErrorDescription string `json:"error_description"` 44 | } 45 | 46 | // NewClient creates a instance of the huawei cloud auth client 47 | // It's contained in huawei cloud app and provides service through huawei cloud app 48 | // If AuthUrl is null using default auth url address 49 | func NewAuthClient(conf *config.Config) (*AuthClient, error) { 50 | if conf.AppId == "" || conf.AppSecret == "" { 51 | return nil, errors.New("appId or appSecret is null") 52 | } 53 | 54 | c, err := httpclient.NewHTTPClient() 55 | if err != nil { 56 | return nil, errors.New("failed to get http client") 57 | } 58 | 59 | if conf.AuthUrl == "" { 60 | return nil, errors.New("authUrl can't be empty") 61 | } 62 | 63 | return &AuthClient{ 64 | endpoint: conf.AuthUrl, 65 | appId: conf.AppId, 66 | appSecret: conf.AppSecret, 67 | client: c, 68 | }, nil 69 | } 70 | 71 | // GetAuthToken gets token from huawei cloud 72 | // the developer can access the app by using this token 73 | func (ac *AuthClient) GetAuthToken(ctx context.Context) (string, error) { 74 | u, _ := url.Parse(ac.appSecret) 75 | body := fmt.Sprintf("grant_type=client_credentials&client_secret=%s&client_id=%s", u.String(), ac.appId) 76 | 77 | request := &httpclient.PushRequest{ 78 | Method: http.MethodPost, 79 | URL: ac.endpoint, 80 | Body: []byte(body), 81 | Header: []httpclient.HTTPOption{httpclient.SetHeader("Content-Type", "application/x-www-form-urlencoded")}, 82 | } 83 | 84 | resp, err := ac.client.DoHttpRequest(ctx, request) 85 | if err != nil { 86 | return "", err 87 | } 88 | 89 | var token TokenMsg 90 | if resp.Status == 200 { 91 | err = json.Unmarshal(resp.Body, &token) 92 | if err != nil { 93 | return "", err 94 | } 95 | return token.AccessToken, nil 96 | } 97 | return "", nil 98 | } 99 | -------------------------------------------------------------------------------- /src/push/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | type Config struct { 20 | AppId string 21 | AppSecret string 22 | AuthUrl string 23 | PushUrl string 24 | } 25 | -------------------------------------------------------------------------------- /src/push/constant/constants.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package constant 18 | 19 | const ( 20 | // unspecified visibility 21 | VisibilityUnspecified = "VISIBILITY_UNSPECIFIED" 22 | // private visibility 23 | VisibilityPrivate = "PRIVATE" 24 | // public visibility 25 | VisibilityPublic = "PUBLIC" 26 | // secret visibility 27 | VisibilitySecret = "SECRET" 28 | ) 29 | 30 | const ( 31 | // high priority 32 | DeliveryPriorityHigh = "HIGH" 33 | // normal priority 34 | DeliveryPriorityNormal = "NORMAL" 35 | ) 36 | 37 | const ( 38 | // high priority 39 | NotificationPriorityHigh = "HIGH" 40 | // default priority 41 | NotificationPriorityDefault = "NORMAL" 42 | // low priority 43 | NotificationPriorityLow = "LOW" 44 | ) 45 | 46 | const ( 47 | // very low urgency 48 | UrgencyVeryLow = "very-low" 49 | // low urgency 50 | UrgencyLow = "low" 51 | // normal urgency 52 | UrgencyNormal = "normal" 53 | // high urgency 54 | UrgencyHigh = "high" 55 | ) 56 | 57 | const ( 58 | // webPush text direction auto 59 | DirAuto = "auto" 60 | // webPush text direction ltr 61 | DirLtr = "ltr" 62 | // webPush text direction rtl 63 | DirRtl = "rtl" 64 | ) 65 | 66 | const ( 67 | // success code from push server 68 | Success = "80000000" 69 | // parameter invalid code from push server 70 | ParameterError = "80100001" 71 | // token invalid code from push server 72 | TokenFailedErr = "80200001" 73 | //token timeout code from push server 74 | TokenTimeoutErr = "80200003" 75 | ) 76 | 77 | const ( 78 | StyleBigText = iota + 1 79 | ) 80 | 81 | const ( 82 | TypeIntentOrAction = iota + 1 83 | TypeUrl 84 | TypeApp 85 | TypeRichResource 86 | ) 87 | 88 | const ( 89 | FastAppTargetDevelop = iota + 1 90 | FastAppTargetProduct 91 | ) 92 | 93 | const ( 94 | // test user 95 | TargetUserTypeTest = iota + 1 96 | // formal user 97 | TargetUserTypeFormal 98 | // VoIP user 99 | TargetUserTypeVoIP 100 | ) 101 | -------------------------------------------------------------------------------- /src/push/constant/urlfmt.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package constant 18 | 19 | const ( 20 | //the parameters of the formats below are endpoint and appId 21 | SendMessageFmt = "%s/v1/%s/messages:send" 22 | ) 23 | -------------------------------------------------------------------------------- /src/push/core/message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package core 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | "net/http" 24 | 25 | "pushkit-go-sample/httpclient" 26 | "pushkit-go-sample/push/constant" 27 | "pushkit-go-sample/push/model" 28 | "pushkit-go-sample/push/verify" 29 | ) 30 | 31 | // SendMessage sends a message to huawei cloud common 32 | // One of Token, Topic and Condition fields must be invoked in message 33 | // If validationOnly is set to true, the message can be verified by not sent to users 34 | func (c *HttpPushClient) SendMessage(ctx context.Context, msgRequest *model.MessageRequest) (*model.MessageResponse, error) { 35 | result := &model.MessageResponse{} 36 | 37 | err := verify.ValidateMessage(msgRequest.Message) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | request, err := c.getSendMsgRequest(msgRequest) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | err = c.executeApiOperation(ctx, request, result) 48 | if err != nil { 49 | return result, err 50 | } 51 | return result, err 52 | } 53 | 54 | func (c *HttpPushClient) getSendMsgRequest(msgRequest *model.MessageRequest) (*httpclient.PushRequest, error) { 55 | body, err := json.Marshal(msgRequest) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | request := &httpclient.PushRequest{ 61 | Method: http.MethodPost, 62 | URL: fmt.Sprintf(constant.SendMessageFmt, c.endpoint, c.appId), 63 | Body: body, 64 | Header: []httpclient.HTTPOption{ 65 | httpclient.SetHeader("Content-Type", "application/json;charset=utf-8"), 66 | httpclient.SetHeader("Authorization", "Bearer "+c.token), 67 | }, 68 | } 69 | return request, nil 70 | } 71 | -------------------------------------------------------------------------------- /src/push/core/pushclient.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package core 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "errors" 23 | "fmt" 24 | "pushkit-go-sample/push/authention" 25 | "pushkit-go-sample/push/config" 26 | "reflect" 27 | 28 | "pushkit-go-sample/httpclient" 29 | "pushkit-go-sample/push/constant" 30 | ) 31 | 32 | type HttpPushClient struct { 33 | endpoint string 34 | appId string 35 | token string 36 | authClient *auth.AuthClient 37 | client *httpclient.HTTPClient 38 | } 39 | 40 | // NewClient creates a instance of the huawei cloud common client 41 | // It's contained in huawei cloud app and provides service through huawei cloud app 42 | func NewHttpClient(c *config.Config) (*HttpPushClient, error) { 43 | if c.AppId == "" { 44 | return nil, errors.New("appId can't be empty") 45 | } 46 | 47 | if c.PushUrl == "" { 48 | return nil, errors.New("pushUrl can't be empty") 49 | } 50 | 51 | client, err := httpclient.NewHTTPClient() 52 | if err != nil { 53 | return nil, errors.New("failed to get http client") 54 | } 55 | 56 | authClient, err := auth.NewAuthClient(c) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | token, err := authClient.GetAuthToken(context.Background()) 62 | if err != nil { 63 | return nil, errors.New("refresh token fail") 64 | } 65 | 66 | return &HttpPushClient{ 67 | endpoint: c.PushUrl, 68 | appId: c.AppId, 69 | token: token, 70 | authClient: authClient, 71 | client: client, 72 | }, nil 73 | } 74 | 75 | func (c *HttpPushClient) refreshToken() error { 76 | if c.authClient == nil { 77 | return errors.New("can't refresh token because getting auth client fail") 78 | } 79 | 80 | token, err := c.authClient.GetAuthToken(context.Background()) 81 | if err != nil { 82 | return errors.New("refresh token fail") 83 | } 84 | 85 | c.token = token 86 | return nil 87 | } 88 | 89 | func (c *HttpPushClient) executeApiOperation(ctx context.Context, request *httpclient.PushRequest, responsePointer interface{}) error { 90 | err := c.sendHttpRequest(ctx, request, responsePointer) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | // if need to retry for token timeout or other reasons 96 | retry, err := c.isNeedRetry(responsePointer) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | if retry { 102 | err = c.sendHttpRequest(ctx, request, responsePointer) 103 | return err 104 | } 105 | return err 106 | } 107 | 108 | func (c *HttpPushClient) sendHttpRequest(ctx context.Context, request *httpclient.PushRequest, responsePointer interface{}) error { 109 | resp, err := c.client.DoHttpRequest(ctx, request) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | if err = json.Unmarshal(resp.Body, responsePointer); err != nil { 115 | return err 116 | } 117 | return nil 118 | } 119 | 120 | // if token is timeout or error or other reason, need to refresh token and send again 121 | func (c *HttpPushClient) isNeedRetry(responsePointer interface{}) (bool, error) { 122 | tokenError, err := isTokenError(responsePointer) 123 | if err != nil { 124 | return false, err 125 | } 126 | 127 | if !tokenError { 128 | return false, nil 129 | } 130 | 131 | err = c.refreshToken() 132 | if err != nil { 133 | return false, err 134 | } 135 | return true, nil 136 | } 137 | 138 | // if token is timeout or error, refresh token and send again 139 | func isTokenError(responsePointer interface{}) (bool, error) { 140 | //the responsePointer must be point of struct 141 | val, _, ok := checkParamStructPtr(responsePointer) 142 | if !ok { 143 | return false, errors.New("the parameter should be pointer of the struct") 144 | } 145 | 146 | code := val.Elem().FieldByName("Code").String() 147 | if code == constant.TokenTimeoutErr || code == constant.TokenFailedErr { 148 | return true, nil 149 | } 150 | return false, nil 151 | } 152 | 153 | func checkParamStructPtr(structPtr interface{}) (reflect.Value, reflect.Type, bool) { 154 | val := reflect.ValueOf(structPtr) 155 | if val.Kind() != reflect.Ptr { 156 | fmt.Println("The Parameter should be Pointer of Struct!") 157 | return reflect.Value{}, nil, false 158 | } 159 | 160 | t := reflect.Indirect(val).Type() 161 | if t.Kind() != reflect.Struct { 162 | fmt.Println("The Parameter should be Pointer of Struct!") 163 | return reflect.Value{}, nil, false 164 | } 165 | return val, t, true 166 | } 167 | -------------------------------------------------------------------------------- /src/push/model/message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package model 18 | 19 | import ( 20 | "pushkit-go-sample/push/constant" 21 | ) 22 | 23 | type MessageRequest struct { 24 | ValidateOnly bool `json:"validate_only"` 25 | Message *Message `json:"message"` 26 | } 27 | 28 | type MessageResponse struct { 29 | Code string `json:"code"` 30 | Msg string `json:"msg"` 31 | RequestId string `json:"requestId"` 32 | } 33 | 34 | type Message struct { 35 | Data string `json:"data,omitempty"` 36 | Notification *Notification `json:"notification,omitempty"` 37 | Android *AndroidConfig `json:"android,omitempty"` 38 | Apns *Apns `json:"apns,omitempty"` 39 | WebPush *WebPushConfig `json:"webpush,omitempty"` 40 | Token []string `json:"token,omitempty"` 41 | Topic string `json:"topic,omitempty"` 42 | Condition string `json:"condition,omitempty"` 43 | } 44 | 45 | type Notification struct { 46 | Title string `json:"title,omitempty"` 47 | Body string `json:"body,omitempty"` 48 | Image string `json:"image,omitempty"` 49 | } 50 | 51 | type Apns struct { 52 | Headers *ApnsHeaders `json:"headers,omitempty"` 53 | Payload map[string]interface{} `json:"payload,omitempty"` 54 | HmsOptions *ApnsHmsOptions `json:"hms_options,omitempty"` 55 | } 56 | 57 | type ApnsHmsOptions struct { 58 | TargetUserType int `json:"target_user_type,omitempty"` 59 | } 60 | 61 | type ApnsHeaders struct { 62 | Authorization string `json:"authorization,omitempty"` 63 | ApnsId string `json:"apns-id,omitempty"` 64 | ApnsExpiration int64 `json:"apns-expiration,omitempty"` 65 | ApnsPriority string `json:"apns-priority,omitempty"` 66 | ApnsTopic string `json:"apns-topic,omitempty"` 67 | ApnsCollapseId string `json:"apns-collapse-id,omitempty"` 68 | } 69 | 70 | type Aps struct { 71 | Alert interface{} `json:"alert,omitempty"` // dictionary or string 72 | Badge int `json:"badge,omitempty"` 73 | Sound string `json:"sound,omitempty"` 74 | ContentAvailable int `json:"content-available,omitempty"` 75 | Category string `json:"category,omitempty"` 76 | ThreadId string `json:"thread-id,omitempty"` 77 | } 78 | 79 | type AlertDictionary struct { 80 | Title string `json:"title,omitempty"` 81 | Body string `json:"body,omitempty"` 82 | TitleLocKey string `json:"title-loc-key,omitempty"` 83 | TitleLocArgs []string `json:"title-loc-args,omitempty"` 84 | ActionLocKey string `json:"action-loc-key,omitempty"` 85 | LocKey string `json:"loc-key,omitempty"` 86 | LocArgs []string `json:"loc-args,omitempty"` 87 | LaunchImage string `json:"launch-image,omitempty"` 88 | } 89 | 90 | //NewTransparentMsgRequest will return a new MessageRequest instance with default value to send transparent message. 91 | //developers should set at least on of Message.Token or Message.Topic or Message.Condition 92 | func NewTransparentMsgRequest() *MessageRequest { 93 | msgRequest := getDefaultMsgRequest() 94 | return msgRequest 95 | } 96 | 97 | // NewNotificationMsgRequest will return a new MessageRequest instance with default value to send notification message. 98 | //developers should set at least on of Message.Token or Message.Topic or Message.Condition 99 | func NewNotificationMsgRequest() *MessageRequest { 100 | msgRequest := getDefaultMsgRequest() 101 | msgRequest.Message.Notification = getDefaultNotification() 102 | return msgRequest 103 | } 104 | 105 | func getDefaultMsgRequest() *MessageRequest { 106 | return &MessageRequest{ 107 | ValidateOnly: false, 108 | Message: &Message{ 109 | Data: "This is a transparent message data", 110 | }, 111 | } 112 | } 113 | 114 | func GetDefaultApns() *Apns { 115 | return &Apns{ 116 | HmsOptions: &ApnsHmsOptions{ 117 | TargetUserType: constant.TargetUserTypeTest, 118 | }, 119 | Payload: getDefaultApnsPayload(), 120 | } 121 | } 122 | 123 | func getDefaultApnsPayload() map[string]interface{} { 124 | payLoad := make(map[string]interface{}, 0) 125 | aps := &Aps{ 126 | Alert: &AlertDictionary{ 127 | Title: "title", 128 | Body: "apns body", 129 | TitleLocKey : "PLAY", 130 | }, 131 | Badge: 5, 132 | } 133 | 134 | payLoad["aps"] = aps 135 | return payLoad 136 | } 137 | 138 | func getDefaultNotification() *Notification { 139 | return &Notification{ 140 | Title: "notification tittle", 141 | Body: "This is a notification message body", 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/push/model/message_hm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package model 18 | 19 | import ( 20 | "pushkit-go-sample/push/constant" 21 | ) 22 | 23 | type AndroidConfig struct { 24 | CollapseKey int `json:"collapse_key,omitempty"` 25 | Urgency string `json:"urgency,omitempty"` 26 | Category string `json:"category,omitempty"` 27 | TTL string `json:"ttl,omitempty"` 28 | BiTag string `json:"bi_tag,omitempty"` 29 | FastAppTarget int `json:"fast_app_target,omitempty"` 30 | Data string `json:"data,omitempty"` 31 | Notification *AndroidNotification `json:"notification,omitempty"` 32 | } 33 | 34 | type AndroidNotification struct { 35 | Title string `json:"title,omitempty"` 36 | Body string `json:"body,omitempty"` 37 | Icon string `json:"icon,omitempty"` 38 | Color string `json:"color,omitempty"` 39 | Sound string `json:"sound,omitempty"` 40 | DefaultSound bool `json:"default_sound,omitempty"` 41 | Tag string `json:"tag,omitempty"` 42 | ClickAction *ClickAction `json:"click_action,omitempty"` 43 | BodyLocKey string `json:"body_loc_key,omitempty"` 44 | BodyLocArgs []string `json:"body_loc_args,omitempty"` 45 | TitleLocKey string `json:"title_loc_key,omitempty"` 46 | TitleLocArgs []string `json:"title_loc_args,omitempty"` 47 | MultiLangKey map[string]interface{} `json:"multi_lang_key,omitempty"` 48 | ChannelId string `json:"channel_id,omitempty"` 49 | NotifySummary string `json:"notify_summary,omitempty"` 50 | Image string `json:"image,omitempty"` 51 | Style int `json:"style,omitempty"` 52 | BigTitle string `json:"big_title,omitempty"` 53 | BigBody string `json:"big_body,omitempty"` 54 | 55 | AutoClear int `json:"auto_clear,omitempty"` 56 | NotifyId int `json:"notify_id,omitempty"` 57 | Group string `json:"group,omitempty"` 58 | Badge *BadgeNotification `json:"badge,omitempty,omitempty"` 59 | Ticker string `json:"ticker,omitempty"` 60 | AutoCancel bool `json:"auto_cancel,omitempty"` 61 | when string `json:"when,omitempty"` 62 | Importance string `json:"importance,omitempty"` 63 | UseDefaultVibrate bool `json:"use_default_vibrate,omitempty"` 64 | UseDefaultLight bool `json:"use_default_light,omitempty"` 65 | VibrateConfig []string `json:"vibrate_config,omitempty"` 66 | Visibility string `json:"visibility,omitempty"` 67 | LightSettings *LightSettings `json:"light_settings,omitempty"` 68 | ForegroundShow bool `json:"foreground_show,omitempty"` 69 | } 70 | 71 | type ClickAction struct { 72 | Type int `json:"type"` //when the type equals to 1, At least one of intent and action is not empty 73 | Intent string `json:"intent,omitempty"` 74 | Action string `json:"action,omitempty"` 75 | Url string `json:"url,omitempty"` 76 | RichResource string `json:"rich_resource,omitempty"` 77 | } 78 | 79 | type BadgeNotification struct { 80 | AddNum int `json:"add_num,omitempty"` 81 | SetNum int `json:"set_num,omitempty"` 82 | Class string `json:"class,omitempty"` 83 | } 84 | 85 | type LightSettings struct { 86 | Color *Color `json:"color"` 87 | LightOnDuration string `json:"light_on_duration,omitempty"` 88 | LightOffDuration string `json:"light_off_duration,omitempty"` 89 | } 90 | 91 | type Color struct { 92 | Alpha int `json:"alpha"` 93 | Red int `json:"red"` 94 | Green int `json:"green"` 95 | Blue int `json:"blue"` 96 | } 97 | 98 | func GetDefaultAndroid() *AndroidConfig { 99 | android := &AndroidConfig{ 100 | Urgency: constant.DeliveryPriorityNormal, 101 | TTL: "86400s", 102 | Notification: nil, 103 | } 104 | return android 105 | } 106 | 107 | func GetDefaultAndroidNotification() *AndroidNotification { 108 | notification := &AndroidNotification{ 109 | DefaultSound: true, 110 | Importance: constant.NotificationPriorityDefault, 111 | ClickAction: getDefaultClickAction(), 112 | } 113 | 114 | notification.UseDefaultVibrate = true 115 | notification.UseDefaultLight = true 116 | notification.Visibility = constant.VisibilityPrivate 117 | notification.ForegroundShow = true 118 | 119 | notification.AutoCancel = true 120 | 121 | return notification 122 | } 123 | 124 | func getDefaultClickAction() *ClickAction { 125 | return &ClickAction{ 126 | Type: constant.TypeIntentOrAction, 127 | Action: "Action", 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/push/model/message_webpush.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package model 18 | 19 | import ( 20 | "pushkit-go-sample/push/constant" 21 | "time" 22 | ) 23 | 24 | type WebPushConfig struct { 25 | Data string `json:"data,omitempty"` 26 | Headers *WebPushHeaders `json:"headers,omitempty"` 27 | HmsOptions *HmsWebPushOption `json:"hms_options,omitempty"` 28 | Notification *WebPushNotification `json:"notification,omitempty"` 29 | } 30 | 31 | type WebPushHeaders struct { 32 | TTL string `json:"ttl,omitempty"` 33 | Topic string `json:"topics,omitempty"` 34 | Urgency string `json:"urgency,omitempty"` 35 | } 36 | 37 | type HmsWebPushOption struct { 38 | Link string `json:"link,omitempty"` 39 | } 40 | 41 | type WebPushNotification struct { 42 | Title string `json:"title,omitempty"` 43 | Body string `json:"body,omitempty"` 44 | Actions []*WebPushAction `json:"actions,omitempty"` 45 | Badge string `json:"badge,omitempty"` 46 | Dir string `json:"dir,omitempty"` 47 | Icon string `json:"icon,omitempty"` 48 | Image string `json:"image,omitempty"` 49 | Lang string `json:"lang,omitempty"` 50 | Renotify bool `json:"renotify,omitempty"` 51 | RequireInteraction bool `json:"require_interaction,omitempty"` 52 | Silent bool `json:"silent,omitempty"` 53 | Tag string `json:"tag,omitempty"` 54 | Timestamp int64 `json:"timestamp,omitempty"` 55 | Vibrate []int `json:"vibrate,omitempty"` 56 | } 57 | 58 | type WebPushAction struct { 59 | Action string `json:"action,omitempty"` 60 | Icon string `json:"icon,omitempty"` 61 | Title string `json:"title,omitempty"` 62 | } 63 | 64 | func GetDefaultWebPushConfig() *WebPushConfig { 65 | return &WebPushConfig{ 66 | Data: "web push data", 67 | Headers: getDefaultHeaders(), 68 | HmsOptions: getDefaultHmsOptions(), 69 | } 70 | } 71 | func getDefaultHeaders() *WebPushHeaders { 72 | return &WebPushHeaders{ 73 | TTL: "990", 74 | Topic: "topic", 75 | Urgency: constant.UrgencyVeryLow, 76 | } 77 | } 78 | 79 | func getDefaultHmsOptions() *HmsWebPushOption { 80 | return &HmsWebPushOption{ 81 | Link: "https://www.huawei.com", 82 | } 83 | } 84 | 85 | func GetDefaultWebNotification() *WebPushNotification { 86 | return &WebPushNotification{ 87 | Dir: constant.DirAuto, 88 | Silent: true, 89 | Timestamp: time.Now().Unix(), 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/push/verify/messaging_hm_verify.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package verify 18 | 19 | import ( 20 | "errors" 21 | 22 | "pushkit-go-sample/push/constant" 23 | "pushkit-go-sample/push/model" 24 | ) 25 | 26 | func validateAndroidConfig(androidConfig *model.AndroidConfig) error { 27 | if androidConfig == nil { 28 | return nil 29 | } 30 | 31 | if androidConfig.CollapseKey < -1 || androidConfig.CollapseKey > 100 { 32 | return errors.New("collapse_key must be in interval [-1 - 100]") 33 | } 34 | 35 | if androidConfig.Urgency != "" && 36 | androidConfig.Urgency != constant.DeliveryPriorityHigh && 37 | androidConfig.Urgency != constant.DeliveryPriorityNormal { 38 | return errors.New("delivery_priority must be 'HIGH' or 'NORMAL'") 39 | } 40 | 41 | if androidConfig.TTL != "" && !ttlPattern.MatchString(androidConfig.TTL) { 42 | return errors.New("malformed ttl") 43 | } 44 | 45 | if androidConfig.FastAppTarget != 0 && 46 | androidConfig.FastAppTarget != constant.FastAppTargetDevelop && 47 | androidConfig.FastAppTarget != constant.FastAppTargetProduct { 48 | return errors.New("invalid fast_app_target") 49 | } 50 | 51 | // validate android notification 52 | return validateAndroidNotification(androidConfig.Notification) 53 | } 54 | 55 | func validateAndroidNotification(notification *model.AndroidNotification) error { 56 | if notification == nil { 57 | return nil 58 | } 59 | 60 | if notification.Sound == "" && notification.DefaultSound == false { 61 | return errors.New("sound must not be empty when default_sound is false") 62 | } 63 | 64 | if err := validateAndroidNotifyStyle(notification); err != nil { 65 | return err 66 | } 67 | 68 | if err := validateAndroidNotifyPriority(notification); err != nil { 69 | return err 70 | } 71 | 72 | if err := validateVibrateTimings(notification); err != nil { 73 | return err 74 | } 75 | 76 | if err := validateVisibility(notification); err != nil { 77 | return err 78 | } 79 | 80 | if err := validateLightSetting(notification); err != nil { 81 | return err 82 | } 83 | 84 | if notification.Color != "" && !colorPattern.MatchString(notification.Color) { 85 | return errors.New("color must be in the form #RRGGBB") 86 | } 87 | 88 | // validate click action 89 | return validateClickAction(notification.ClickAction) 90 | } 91 | 92 | func validateAndroidNotifyStyle(notification *model.AndroidNotification) error { 93 | switch notification.Style { 94 | case constant.StyleBigText: 95 | if notification.BigTitle == "" { 96 | return errors.New("big_title must not be empty when style is 1") 97 | } 98 | 99 | if notification.BigBody == "" { 100 | return errors.New("big_body must not be empty when style is 1") 101 | } 102 | 103 | } 104 | return nil 105 | } 106 | 107 | func validateAndroidNotifyPriority(notification *model.AndroidNotification) error { 108 | if notification.Importance != "" && 109 | notification.Importance != constant.NotificationPriorityHigh && 110 | notification.Importance != constant.NotificationPriorityDefault && 111 | notification.Importance != constant.NotificationPriorityLow { 112 | return errors.New("Importance must be 'HIGH', 'NORMAL' or 'LOW'") 113 | } 114 | return nil 115 | } 116 | 117 | func validateVibrateTimings(notification *model.AndroidNotification) error { 118 | if notification.VibrateConfig != nil { 119 | if len(notification.VibrateConfig) > 10 { 120 | return errors.New("vibrate_timings can't be more than 10 elements") 121 | } 122 | for _, vibrateTiming := range notification.VibrateConfig { 123 | if !ttlPattern.MatchString(vibrateTiming) { 124 | return errors.New("malformed vibrate_timings") 125 | } 126 | } 127 | } 128 | return nil 129 | } 130 | 131 | func validateVisibility(notification *model.AndroidNotification) error { 132 | if notification.Visibility == "" { 133 | notification.Visibility = constant.VisibilityPrivate 134 | return nil 135 | } 136 | if notification.Visibility != constant.VisibilityUnspecified && notification.Visibility != constant.VisibilityPrivate && 137 | notification.Visibility != constant.VisibilityPublic && notification.Visibility != constant.VisibilitySecret { 138 | return errors.New("visibility must be VISIBILITY_UNSPECIFIED, PRIVATE, PUBLIC or SECRET") 139 | } 140 | return nil 141 | } 142 | 143 | func validateLightSetting(notification *model.AndroidNotification) error { 144 | if notification.LightSettings == nil { 145 | return nil 146 | } 147 | 148 | if notification.LightSettings.Color == nil { 149 | return errors.New("light_settings.color can't be nil") 150 | } 151 | 152 | if notification.LightSettings.LightOnDuration == "" || 153 | !ttlPattern.MatchString(notification.LightSettings.LightOnDuration) { 154 | return errors.New("light_settings.light_on_duration is empty or malformed") 155 | } 156 | 157 | if notification.LightSettings.LightOffDuration == "" || 158 | !ttlPattern.MatchString(notification.LightSettings.LightOffDuration) { 159 | return errors.New("light_settings.light_off_duration is empty or malformed") 160 | } 161 | return nil 162 | } 163 | 164 | func validateClickAction(clickAction *model.ClickAction) error { 165 | if clickAction == nil { 166 | return errors.New("click_action object must not be null") 167 | } 168 | 169 | switch clickAction.Type { 170 | case constant.TypeIntentOrAction: 171 | if clickAction.Intent == "" && clickAction.Action == "" { 172 | return errors.New("at least one of intent and action is not empty when type is 1") 173 | } 174 | case constant.TypeUrl: 175 | if clickAction.Url == "" { 176 | return errors.New("url must not be empty when type is 2") 177 | } 178 | case constant.TypeApp: 179 | case constant.TypeRichResource: 180 | if clickAction.RichResource == "" { 181 | return errors.New("rich_resource must not be empty when type is 4") 182 | } 183 | default: 184 | return errors.New("type must be in the interval [1 - 4]") 185 | } 186 | return nil 187 | } 188 | -------------------------------------------------------------------------------- /src/push/verify/messaging_verify.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package verify 18 | 19 | import ( 20 | "errors" 21 | "regexp" 22 | 23 | "pushkit-go-sample/push/model" 24 | ) 25 | 26 | var ( 27 | ttlPattern = regexp.MustCompile("\\d+|\\d+[sS]|\\d+.\\d{1,9}|\\d+.\\d{1,9}[sS]") 28 | colorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$") 29 | ) 30 | 31 | func ValidateMessage(message *model.Message) error { 32 | if message == nil { 33 | return errors.New("message must not be null") 34 | } 35 | 36 | // validate field target, one of Token, Topic and Condition must be invoked 37 | if err := validateFieldTarget(message.Token, message.Topic, message.Condition); err != nil { 38 | return err 39 | } 40 | 41 | // validate android config 42 | if err := validateAndroidConfig(message.Android); err != nil { 43 | return err 44 | } 45 | 46 | // validate web common config 47 | if err := validateWebPushConfig(message.WebPush); err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | 53 | func validateFieldTarget(token []string, strings ...string) error { 54 | count := 0 55 | if token != nil { 56 | count++ 57 | } 58 | 59 | for _, s := range strings { 60 | if s != "" { 61 | count++ 62 | } 63 | } 64 | 65 | if count == 1 { 66 | return nil 67 | } 68 | return errors.New("token, topics or condition must be choice one") 69 | } 70 | -------------------------------------------------------------------------------- /src/push/verify/messaging_webpush_verify.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package verify 18 | 19 | import ( 20 | "errors" 21 | 22 | "pushkit-go-sample/push/constant" 23 | "pushkit-go-sample/push/model" 24 | ) 25 | 26 | func validateWebPushConfig(webPushConfig *model.WebPushConfig) error { 27 | if webPushConfig == nil { 28 | return nil 29 | } 30 | 31 | if err := validateWebPushHeaders(webPushConfig.Headers); err != nil { 32 | return err 33 | } 34 | 35 | return validateWebPushNotification(webPushConfig.Notification) 36 | } 37 | 38 | func validateWebPushHeaders(headers *model.WebPushHeaders) error { 39 | if headers == nil { 40 | return nil 41 | } 42 | 43 | if headers.TTL != "" && !ttlPattern.MatchString(headers.TTL) { 44 | return errors.New("malformed ttl") 45 | } 46 | 47 | if headers.Urgency != "" && 48 | headers.Urgency != constant.UrgencyHigh && 49 | headers.Urgency != constant.UrgencyNormal && 50 | headers.Urgency != constant.UrgencyLow && 51 | headers.Urgency != constant.UrgencyVeryLow { 52 | return errors.New("priority must be 'high', 'normal', 'low' or 'very-low'") 53 | } 54 | return nil 55 | } 56 | 57 | func validateWebPushNotification(notification *model.WebPushNotification) error { 58 | if notification == nil { 59 | return nil 60 | } 61 | 62 | if err := validateWebPushAction(notification.Actions); err != nil { 63 | return err 64 | } 65 | 66 | if err := validateWebPushDirection(notification.Dir); err != nil { 67 | return err 68 | } 69 | return nil 70 | } 71 | 72 | func validateWebPushAction(actions []*model.WebPushAction) error { 73 | if actions == nil { 74 | return nil 75 | } 76 | 77 | for _, action := range actions { 78 | if action.Action == "" { 79 | return errors.New("web common action can't be empty") 80 | } 81 | } 82 | return nil 83 | } 84 | 85 | func validateWebPushDirection(dir string) error { 86 | if dir != constant.DirAuto && dir != constant.DirLtr && dir != constant.DirRtl { 87 | return errors.New("web common dir must be 'auto', 'ltr', 'rtl'") 88 | } 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | --------------------------------------------------------------------------------