fastlane-plugin-huawei_appgallery 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9bf208e5687db71d1a23e0685fd487121118d9abcece8c5c52eb2fc17d18c3a
4
- data.tar.gz: 021d724d17b3f8ddcc11e44179851aa695ce8926ab6d045ad4a174b7491f3868
3
+ metadata.gz: e4411d2594ecd5bb0bf0f2b47de08df6d4476d7ed5d39e30551df2fcbfc1c18e
4
+ data.tar.gz: 17fd0a9bce8eb88ab33d4a4fea16c367786c0e793ded01a0628185a4ff651bea
5
5
  SHA512:
6
- metadata.gz: cbcd2452e9d6f98d021771cc9ebc4f4b2dcd15f4519f002f0c8f15bea2f5bc34970900396c6b68bd7537049366dde9aecdacb647f2d73ae90af17392069e9895
7
- data.tar.gz: 5962ed3c37e237da78ea18f1f05bbcf383235e42446fc09a0e26a93754edfe810a4522a95bffeae2e14052230befed50a3b43c735e57afc5f890f82efa5f5488
6
+ metadata.gz: 369701889c06b57182b8f76b2a1f09146b6d8a24de1e7afde7b426709fc445313fdd3c37a5f2adeb1a535f153a0d0425bcfb254ff286a45c2a501aa44a5a60b1
7
+ data.tar.gz: 4c546b7b87021b427e7e42d4d49112d559d11cdab7c2a72eddf2c1a7dba7dfe441aa7ed390bd86da3af43d864b71ffca175d633765798bb11dd8ad34a40e9770
data/README.md CHANGED
@@ -19,6 +19,10 @@ Example code: https://developer.huawei.com/consumer/en/service/hms/catalog/publi
19
19
 
20
20
  Pull requests are welcome. ;)
21
21
 
22
+ ## AppGallery Connect API (= API v2)
23
+ To create client id and access token, go to [AppGalleryConnect](https://developer.huawei.com/consumer/en/service/josp/agc/index.html).
24
+ Navigate to "Users and permissions", than click on the left on "Connect API" (in section "Api Key"). Now you create and manage your client ids and secrets.
25
+
22
26
  ## Example
23
27
 
24
28
  Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin.
@@ -1,14 +1,13 @@
1
1
  require 'fastlane/action'
2
- require_relative '../helper/huawei_appgallery_helper'
3
2
 
4
3
  module Fastlane
5
4
  module Actions
6
5
  class HuaweiAppgalleryAction < Action
7
6
  def self.run(params)
8
- cookie = Helper::HuaweiAppgalleryHelper.request_cookie(params[:client_id], params[:time], params[:signature])
9
- Helper::HuaweiAppgalleryHelper.update_release_notes(cookie, params[:app_id], params[:release_notes])
10
- Helper::HuaweiAppgalleryHelper.upload_apk(cookie, params[:app_id], params[:apk_path])
11
- Helper::HuaweiAppgalleryHelper.submit_app(cookie, params[:app_id])
7
+ access_token = Helper::HuaweiAppgalleryHelperV2.request_access_token(params[:client_id], params[:client_secret])
8
+ Helper::HuaweiAppgalleryHelperV2.update_release_notes(params[:client_id], access_token, params[:app_id], params[:release_notes])
9
+ Helper::HuaweiAppgalleryHelperV2.upload_apk(params[:client_id], access_token, params[:app_id], params[:apk_path])
10
+ Helper::HuaweiAppgalleryHelperV2.submit_app(params[:client_id], access_token, params[:app_id])
12
11
  UI.message('Finished!')
13
12
  end
14
13
 
@@ -35,14 +34,9 @@ module Fastlane
35
34
  description: "Client ID of an AppGallery Connect API client",
36
35
  optional: false,
37
36
  type: String),
38
- FastlaneCore::ConfigItem.new(key: :time,
39
- env_name: "HUAWEI_APPGALLERY_TIME",
40
- description: "Time in milliseconds since 1970, which was used to create the signature",
41
- optional: false,
42
- type: String),
43
- FastlaneCore::ConfigItem.new(key: :signature,
44
- env_name: "HUAWEI_APPGALLERY_SIGNATURE",
45
- description: "Signature which needs to be created by your own. Example code: https://developer.huawei.com/consumer/en/service/hms/catalog/publishingAPI.html?page=hmssdk_appGalleryConnect_devguide",
37
+ FastlaneCore::ConfigItem.new(key: :client_secret,
38
+ env_name: "HUAWEI_APPGALLERY_CLIENT_SECRET",
39
+ description: "Client Secret of an AppGallery Connect API client",
46
40
  optional: false,
47
41
  type: String),
48
42
  FastlaneCore::ConfigItem.new(key: :app_id,
@@ -0,0 +1,205 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class HuaweiAppgalleryHelperV2
8
+
9
+
10
+ def self.request_access_token(client_id, client_secret)
11
+ UI.message("Requesting access token from Huawei AppGallery ...")
12
+
13
+ content = {'grant_type' => 'client_credentials', 'client_id' => client_id, 'client_secret' => client_secret}
14
+
15
+ result = Net::HTTP.post(
16
+ URI("https://connect-api.cloud.huawei.com/api/oauth2/v1/token"),
17
+ content.to_json.encode('utf-8'),
18
+ "Content-Type" => "application/json"
19
+ )
20
+
21
+ if result.code.to_i != 200
22
+ UI.user_error!("Could not get access token from app gallery api. (HTTP #{result.code} - #{result.message})")
23
+ end
24
+
25
+ result_json = JSON.parse(result.body)
26
+ access_token = result_json['access_token']
27
+ access_token
28
+ end
29
+
30
+
31
+ def self.update_release_notes(client_id, access_token, app_id, release_notes)
32
+ release_notes.each do |lang, notes|
33
+ UI.message("Updating release notes for language #{lang} ...")
34
+
35
+ content = {'lang' => lang, 'newFeatures' => notes}
36
+
37
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/app-language-info?appId=#{app_id}")
38
+ http = Net::HTTP.new(uri.host, uri.port)
39
+ http.use_ssl = true
40
+ request = Net::HTTP::Put.new(uri)
41
+ request.body = content.to_json.encode('utf-8')
42
+ request['Content-Type'] = "application/json"
43
+ request['Authorization'] = "Bearer #{access_token}"
44
+ request['client_id'] = client_id
45
+ result = http.request(request)
46
+
47
+ if result.code.to_i != 200
48
+ UI.user_error!("Could not update release notes for #{lang}. (HTTP #{result.code} - #{result.message})")
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ def self.upload_apk(client_id, access_token, app_id, apk_path)
55
+ # get upload url and auth code
56
+ upload_url_result = self.get_upload_url(client_id, access_token, app_id)
57
+ auth_code = upload_url_result['authCode']
58
+ upload_url = upload_url_result['uploadUrl']
59
+
60
+ # upload apk
61
+ server_apk_url = self.upload_apk_to_api(auth_code, upload_url, apk_path)
62
+
63
+ # update app file information
64
+ self.update_app_file_information(client_id, access_token, app_id, server_apk_url)
65
+ end
66
+
67
+
68
+ def self.get_upload_url(client_id, access_token, app_id)
69
+ UI.message("Obtaining upload url ...")
70
+
71
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/upload-url?appId=#{app_id}&suffix=apk")
72
+ http = Net::HTTP.new(uri.host, uri.port)
73
+ http.use_ssl = true
74
+ request = Net::HTTP::Get.new(uri)
75
+ request['Authorization'] = "Bearer #{access_token}"
76
+ request['client_id'] = client_id
77
+ result = http.request(request)
78
+
79
+ if result.code.to_i != 200
80
+ UI.user_error!("Could not apk upload url from app gallery api. (HTTP #{result.code} - #{result.message})")
81
+ end
82
+
83
+ # result json containing "authCode", "uploadUrl"
84
+ result_json = JSON.parse(result.body)
85
+ result_json
86
+ end
87
+
88
+
89
+ def self.upload_apk_to_api(auth_code, upload_url, apk_path)
90
+ UI.message("Uploading apk to #{upload_url} ...")
91
+
92
+ boundary = "-----------------755754302457647"
93
+
94
+ uri = URI(upload_url)
95
+ http = Net::HTTP.new(uri.host, uri.port)
96
+ http.use_ssl = true
97
+ request = Net::HTTP::Post.new(uri)
98
+ request['Accept'] = 'application/json'
99
+ request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
100
+
101
+ post_body = []
102
+ # add auth code
103
+ post_body << "--#{boundary}\r\n"
104
+ post_body << "Content-Disposition: form-data; name=\"authCode\"\r\n\r\n"
105
+ post_body << auth_code
106
+ post_body << "\r\n"
107
+ # add file count
108
+ post_body << "--#{boundary}\r\n"
109
+ post_body << "Content-Disposition: form-data; name=\"fileCount\"\r\n\r\n"
110
+ post_body << "1"
111
+ post_body << "\r\n"
112
+ # add parse type
113
+ post_body << "--#{boundary}\r\n"
114
+ post_body << "Content-Disposition: form-data; name=\"parseType\"\r\n\r\n"
115
+ post_body << "0"
116
+ post_body << "\r\n"
117
+ # add apk
118
+ post_body << "--#{boundary}\r\n"
119
+ post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"release.apk\"\r\n"
120
+ post_body << "Content-Type: multipart/form-data\r\n\r\n"
121
+ post_body << File.read(apk_path).encode('utf-8')
122
+ post_body << "\r\n"
123
+
124
+ post_body << "--#{boundary}--\r\n"
125
+ request.body = post_body.join
126
+ result = http.request(request)
127
+
128
+ if result.code.to_i != 200
129
+ UI.user_error!("Could not upload apk to gallery api. (HTTP #{result.code} - #{result.message})")
130
+ end
131
+
132
+ # example response
133
+ # {"result":{"UploadFileRsp":{"fileInfoList":[{"fileDestUlr":"https://developerfile7.hicloud.com/FileServer/getFile/7/appapktemp/20200727/appapk/000/000/410/0890049000000000410.20200727174607.32489040188051103716016542322538:20200727174634:2500:AD10C3C4138E988C7A1C3680440C84559E2DD6184DF5A2E1C457C23868E5F277.apk","size":86803266}],"ifSuccess":1},"resultCode":"0"}}
134
+ result_json = JSON.parse(result.body)
135
+ json_result_obj = result_json['result']
136
+ json_upload_file_rsp = json_result_obj['UploadFileRsp']
137
+ json_file_info_list = json_upload_file_rsp['fileInfoList']
138
+ json_file_info = json_file_info_list.first()
139
+ file_dest_url = json_file_info['fileDestUlr'] # ulr is correct
140
+
141
+ file_dest_url
142
+ end
143
+
144
+
145
+ def self.update_app_file_information(client_id, access_token, app_id, apk_server_path)
146
+ UI.message("Updating app file information ...")
147
+
148
+ content = {
149
+ 'fileType' => 5, # type 5 = RPK or APK
150
+ 'files' => [{
151
+ 'fileName' => 'naviki-release.apk',
152
+ 'fileDestUrl' => apk_server_path
153
+ }]
154
+ }
155
+
156
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/app-file-info?appId=#{app_id}")
157
+ http = Net::HTTP.new(uri.host, uri.port)
158
+ http.use_ssl = true
159
+ request = Net::HTTP::Put.new(uri)
160
+ request.body = content.to_json.encode('utf-8')
161
+ request['Content-Type'] = "application/json"
162
+ request['Authorization'] = "Bearer #{access_token}"
163
+ request['client_id'] = client_id
164
+ result = http.request(request)
165
+
166
+ if result.code.to_i != 200
167
+ UI.user_error!("Could not update app file information. (HTTP #{result.code} - #{result.message})")
168
+ end
169
+
170
+ result_json = JSON.parse(result.body)
171
+ json_ret = result_json['ret']
172
+ UI.message("app-file-info ret: #{json_ret}")
173
+ end
174
+
175
+
176
+ def self.submit_app(client_id, access_token, app_id)
177
+ UI.message('Submitting app for review ...')
178
+
179
+ # should be in format yyyy-MM-dd'T'HH:mm:ssZZ, must be escaped to be GET param
180
+ # https://apidock.com/ruby/DateTime/strftime
181
+ # https://www.shortcutfoo.com/app/dojos/ruby-date-format-strftime/cheatsheet
182
+ release_time = CGI::escape(Time.now.utc.strftime("%FT%T%z")) # as soon as possible
183
+ UI.message("Use release time #{release_time}")
184
+
185
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/app-submit?appId=#{app_id}&releaseTime=#{release_time}")
186
+ http = Net::HTTP.new(uri.host, uri.port)
187
+ http.use_ssl = true
188
+ request = Net::HTTP::Post.new(uri)
189
+ request['Content-Type'] = "application/json"
190
+ request['Accept'] = 'application/json'
191
+ request['Authorization'] = "Bearer #{access_token}"
192
+ request['client_id'] = client_id
193
+ result = http.request(request)
194
+
195
+ if result.code.to_i != 200
196
+ UI.user_error!("Could not submit app for review. (HTTP #{result.code} - #{result.message})")
197
+ end
198
+
199
+ result_json = JSON.parse(result.body)
200
+ json_ret = result_json['ret']
201
+ UI.message("app-submit ret: #{json_ret}")
202
+ end
203
+ end
204
+ end
205
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module HuaweiAppgallery
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-huawei_appgallery
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arne Kaiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-08 00:00:00.000000000 Z
11
+ date: 2020-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -146,7 +146,7 @@ files:
146
146
  - README.md
147
147
  - lib/fastlane/plugin/huawei_appgallery.rb
148
148
  - lib/fastlane/plugin/huawei_appgallery/actions/huawei_appgallery_action.rb
149
- - lib/fastlane/plugin/huawei_appgallery/helper/huawei_appgallery_helper.rb
149
+ - lib/fastlane/plugin/huawei_appgallery/helper/huawei_appgallery_helper_v2.rb
150
150
  - lib/fastlane/plugin/huawei_appgallery/version.rb
151
151
  homepage: https://github.com/arnekaiser/fastlane-plugin-huawei_appgallery
152
152
  licenses:
@@ -167,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
167
  - !ruby/object:Gem::Version
168
168
  version: '0'
169
169
  requirements: []
170
- rubyforge_project:
171
- rubygems_version: 2.7.6.2
170
+ rubygems_version: 3.0.3
172
171
  signing_key:
173
172
  specification_version: 4
174
173
  summary: Plugin to deploy an app to the Huawei AppGallery
@@ -1,158 +0,0 @@
1
- require 'fastlane_core/ui/ui'
2
-
3
- module Fastlane
4
- UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
-
6
- module Helper
7
- class HuaweiAppgalleryHelper
8
- def self.request_cookie(client_id, time, signature)
9
- UI.message("Requesting cookie from Huawei AppGallery ...")
10
- content = { 'key_string' => { 'clientId' => client_id, 'time' => time, 'sign' => signature } }
11
- result = Net::HTTP.post(
12
- URI('https://connect-api.cloud.huawei.com/api/common/v1/connect'),
13
- content.to_json.encode('utf-8'),
14
- "Accept" => "application/json"
15
- )
16
- cookie = result['set-cookie']
17
- if cookie.nil?
18
- UI.user_error!("Authentication failed: #{result.body}")
19
- end
20
- cookie.split('; ')[0]
21
- end
22
-
23
- def self.update_release_notes(cookie, app_id, release_notes)
24
- release_notes.each do |lang, notes|
25
- UI.message("Updating release notes for language #{lang} ...")
26
-
27
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/appInfo/#{app_id}?lang=#{lang}")
28
- http = Net::HTTP.new(uri.host, uri.port)
29
- http.use_ssl = true
30
- request = Net::HTTP::Get.new(uri)
31
- request['Cookie'] = cookie
32
- request['Accept'] = 'application/json'
33
-
34
- result = http.request(request)
35
-
36
- if result.code.to_i != 200
37
- UI.user_error!("Cannot retrieve language information for language #{lang}!")
38
- end
39
-
40
- # get values for mandatory parameters
41
- result_json = JSON.parse(result.body)
42
- app_name = result_json['languages'][0]['appName']
43
- app_desc = result_json['languages'][0]['appDesc']
44
- brief_info = result_json['languages'][0]['briefInfo']
45
-
46
- if app_name.nil? || app_desc.nil? || brief_info.nil?
47
- UI.user_error!("Cannot retrieve language information for language #{lang}!")
48
- end
49
-
50
- # set new release notes
51
- content = {
52
- 'appName' => app_name,
53
- 'appDesc' => app_desc,
54
- 'briefInfo' => brief_info,
55
- 'newFeatures' => notes
56
- }
57
-
58
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/languageInfo/#{app_id}/#{lang}")
59
- http = Net::HTTP.new(uri.host, uri.port)
60
- http.use_ssl = true
61
- request = Net::HTTP::Put.new(uri)
62
- request.body = content.to_json.encode('utf-8')
63
- request['Cookie'] = cookie
64
- request['Content-Type'] = 'application/json'
65
- result = http.request(request)
66
- if result.code.to_i != 200
67
- UI.user_error!("Cannot update language information for language #{lang}!")
68
- end
69
- end
70
- end
71
-
72
- def self.upload_apk(cookie, app_id, apk_path)
73
- unless File.file?(apk_path)
74
- UI.user_error!("Cannot read apk at: #{apk_path}")
75
- end
76
-
77
- # obtain upload url
78
- UI.message("Obtaining upload url ...")
79
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/uploadUrl?suffix=apk&appId=#{app_id}")
80
- http = Net::HTTP.new(uri.host, uri.port)
81
- http.use_ssl = true
82
- request = Net::HTTP::Get.new(uri)
83
- request['Cookie'] = cookie
84
- request['Accept'] = 'application/json'
85
- result = http.request(request)
86
- result_json = JSON.parse(result.body)
87
- upload_url = result_json['uploadUrl'] # this is the upload server
88
- auth_code = result_json['authCode']
89
- if result.code.to_i != 200 || upload_url.nil? || auth_code.nil?
90
- UI.user_error!("Cannot obtain upload url! Result: #{result_json}")
91
- end
92
-
93
- # upload apk
94
- UI.message("Uploading apk to #{upload_url} ...")
95
- boundary = "755754302457647"
96
- uri = URI("https://#{upload_url}/api/publish/v1/uploadFile")
97
- http = Net::HTTP.new(uri.host, uri.port)
98
- http.use_ssl = true
99
- request = Net::HTTP::Post.new(uri)
100
- request['Cookie'] = cookie
101
- request['Accept'] = 'application/json'
102
- request['Content-Type'] = "multipart/form-data, boundary=#{boundary}"
103
-
104
- post_body = []
105
- # add the auth code
106
- post_body << "--#{boundary}\r\n"
107
- post_body << "Content-Disposition: form-data; name=\"authCode\"\r\n\r\n"
108
- post_body << auth_code
109
- # add the apk
110
- post_body << "\r\n--#{boundary}\r\n"
111
- post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"release.apk\"\r\n"
112
- post_body << "Content-Type: multipart/form-data\r\n\r\n"
113
- post_body << File.read(apk_path).encode('utf-8')
114
- post_body << "\r\n--#{boundary}--\r\n"
115
- request.body = post_body.join
116
-
117
- result = http.request(request)
118
- result_json = JSON.parse(result.body)
119
- upload_url = result_json['uploadUrl']
120
-
121
- # update app file informnation
122
- UI.message("Updating app file information ...")
123
- content = {
124
- 'type' => 5, # type 5 = RPK or APK
125
- 'data' => upload_url
126
- }
127
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/mediaInfo/#{app_id}/en-US") # assumes that en-US is the default language
128
- http = Net::HTTP.new(uri.host, uri.port)
129
- http.use_ssl = true
130
- request = Net::HTTP::Put.new(uri)
131
- request.body = content.to_json.encode('utf-8')
132
- request['Cookie'] = cookie
133
- request['Content-Type'] = 'application/json'
134
- result = http.request(request)
135
- if result.code.to_i != 200
136
- UI.user_error!("Cannot upload apk!")
137
- end
138
- end
139
-
140
- def self.submit_app(cookie, app_id)
141
- UI.message('Submitting app for review ...')
142
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/submit/#{app_id}")
143
- http = Net::HTTP.new(uri.host, uri.port)
144
- http.use_ssl = true
145
- request = Net::HTTP::Post.new(uri)
146
- request['Cookie'] = cookie
147
- request['Accept'] = 'application/json'
148
- request['Content-Type'] = "application/json"
149
- content = { 'releaseTime' => (Time.now.to_f * 1000).to_i.to_s } # as soon as possible
150
- request.body = content.to_json.encode('utf-8')
151
- result = http.request(request)
152
- if result.code.to_i != 200
153
- UI.user_error!("Cannot submit app!")
154
- end
155
- end
156
- end
157
- end
158
- end