[Huawei Developers] Sherlock F:
On an Enterprise environment maybe you want to perform some console operations from a custom plaform, for example, to manage your app information, to automatize the download of the app finance report, or to automatically release an app allocated in your own server. If this is your case, yo may be interested on App Gallery Connect API.

Previous requirements

A developer account

At least one project on AGC

What will we do in this article?

We will generate our client credentials to obtain an app level access token which will give us access to the App Gallery Connect API. After that we wiil use the Connect API to perform the next operations.

Obtaining the App Id related to an app package name

Obtaining the basic app information

Obtaining the upload URL to upload a new version of our app

Obtaining the URL to download your App Report

All the API requests on this article will be performed by using Kotlin, so you can use it to develop your own management platform for mobile, desktop or web.

Generating the client credentials

Sign into AppGallery Connect and then go to Users and permissionfrr4qwq

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002172823.68534600776367025509289864918619:50511022025542:2800:1A6C83BD3BEF048316C1BC2AFA79DF550B5E4B9D421C7A941E3B355AB3D8310A.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002172823.68534600776367025509289864918619:50511022025542:2800:1A6C83BD3BEF048316C1BC2AFA79DF550B5E4B9D421C7A941E3B355AB3D8310A.png[/IMG]

From the left side panel, select Connect API and click on the Create button to generate your client credentials.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002173345.78151203577582294674124652107456:50511022025542:2800:AD7E5F5A90A764049224B01763280E1C0921FBE5488C1C6132ACD8D282681878.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002173345.78151203577582294674124652107456:50511022025542:2800:AD7E5F5A90A764049224B01763280E1C0921FBE5488C1C6132ACD8D282681878.png[/IMG]

In the pop up dialog, choose a name, project and applicable roles for your API client. In this example, I will use Administrator.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002173922.76281243119377990323880940500993:50511022025542:2800:4C4130C16914077B7ACD1E21502B5BD41D5A71C325BE59A6BFB8CF7FEF1F7B47.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002173922.76281243119377990323880940500993:50511022025542:2800:4C4130C16914077B7ACD1E21502B5BD41D5A71C325BE59A6BFB8CF7FEF1F7B47.png[/IMG]

Note: Some APIs require project-level authorization. Please specify the projects you want to access through these APIs. Select N/A if the APIs only require team-level authorization.

Once you have confirmed your settings, your client ID and secret key will appear on the Connect API screen.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002174507.51273987763605560159822573571269:50511022025542:2800:8178993AA1574C45375964165F7238B482E0BB87334FA8599A2F8FC99105873A.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/002/383/434/2640052000002383434.20201002174507.51273987763605560159822573571269:50511022025542:2800:8178993AA1574C45375964165F7238B482E0BB87334FA8599A2F8FC99105873A.png[/IMG]

Copy your Client ID and Key. Make sure to keep them safe.

Request Helper

I wrote a helper class to perform the REST calls to the Connect API

data class ResponseObject(val code:Int,val message: String,var responseBody:JSONObject?)

class RequestHelper {
companion object{
fun sendRequest(host:String, headers: HashMap<String,String>?, body:JSONObject?, requestType:String="GET"):ResponseObject{
try {
val conn = URL(host)
.openConnection() as HttpURLConnection
conn.apply {
requestMethod = requestType
headers?.apply {
for(key in keys)
setRequestProperty(key, get(key))
}
doOutput = requestType == "POST"
doInput = true
}
if(requestType!="GET"){
conn.outputStream.let{
body?.apply { it.write(toString().toByteArray()) }
}
}
val result = when (conn.responseCode) {
in 0..300 -> convertStreamToString(conn.inputStream)
else -> convertStreamToString(conn.errorStream)
}
//Returns the access token, or an empty String if something fails
return ResponseObject(conn.responseCode,conn.responseMessage, JSONObject(result)).also { conn.disconnect() }
} catch (e: Exception) {
return ResponseObject(400,e.toString(),null)
}
}

private fun convertStreamToString(input: InputStream): String {
return BufferedReader(InputStreamReader(input)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
response.toString()
}
}
}
}

Obtaining the App Level Access Token

This is a mandatory step, the access token contains information about the scope level provided to your client credentials.

Perform the following POST request:

Hostname: connect-api.cloud.huawei.com

Path: /api/oauth2/v1/token

Headers:

Content-Type: application/json

Body:

{
"grant_type":"client_credentials",
"client_id":"YOUR_CLIENT ID",
"client_secret":"YOUR_CLIENT_KEY"
}

If everything goes fine, the request will return an access token and the validity period of the token. You can use the same access token for any of the following operations until the expiration time. If the token expires, you must apply for a new one.

Example:

data class AGCredential(val clientId: String, val key: String)

class AGConnectAPI(credential: AGCredential) {

companion object {
@JvmField
val HOST = "https://connect-api.cloud.huawei.com/api"
private val MISSING_CREDENTIALS = "must setup the client credentials first"
val MISSING_CREDENTIALS_RESPONSE=ResponseObject(403, MISSING_CREDENTIALS, null)
}

var token: String? = null
var credential: AGCredential= credential
set(value) {
field = value
getToken()
}

init {
getToken()
}

private fun getToken(): Int {
val host = "$HOST/oauth2/v1/token"

val headers = HashMap<String, String>().apply {
put("Content-Type", "application/json")
}

val body = JSONObject().apply {
put("client_id", credential.clientId)
put("client_secret", credential.key)
put("grant_type", "client_credentials")
}

val response = RequestHelper.sendRequest(host, headers, body, "POST")
val token = response.responseBody?.let {
if (it.has("access_token")) {
it.getString("access_token")
} else null

}

return if (token != null) {
this.token = token
200
} else response.code
}
}

Obtaining the App Id for a given package name

The App Id is required as a unique identifier for app management operations.

Perform the following GET request:

Hostname: connect-api.cloud.huawei.com

Path: /api/publish/v2/appid-list?packageName=$packageName

Headers:

Authorization: Bearer $token

client_id: clientId

Example

fun queryAppId(packageName: String): ResponseObject {
return if (!token.isNullOrEmpty()) {
val url = "$HOST/publish/v2/appid-list?packageName=$packageName"
RequestHelper.sendRequest(url, getClientHeaders(), null)
} else MISSING_CREDENTIALS_RESPONSE

}

private fun getClientHeaders(): HashMap<String, String> {
return HashMap<String, String>().apply {
put("Authorization", "Bearer $token")
put("client_id", credential.clientId)
}
}

Obtaining the upload URL to upload a new version of our app

The Connect API provides URLs to upload different files of your app configuration in AppGallery. For this example we will get the URL to upload the APK. Currently, files with the following extensions can be uploaded:

apk/rpk/pdf/jpg/jpeg/png/bmp/mp4/mov/aab.

Perform the following GET request:

Hostname: connect-api.cloud.huawei.com

Path: /api//publish/v2/upload-url?appId=$appId&suffix=$(apk/rpk/pdf/jpg/jpeg/png/bmp/mp4/mov/aab)

Headers:

Content-Type: application/json

Authorization: Bearer $token

client_id: clientId

Example:

fun getUploadURL(appId: String,suffix: String):ResponseObject{
return if (!token.isNullOrEmpty()){
val url="$HOST/publish/v2/upload-url?appId=$appId&suffix=$suffix"
RequestHelper.sendRequest(url,getClientHeaders(),null)
} else MISSING_CREDENTIALS_RESPONSE
}

Obtaining the URL to download your App Report

You can download detailed reports about your app downloads and installations, purchases made in your app and if your app is a paid app, you can get the Paid Download report.

For the Paid Download report the hostname is different for every region

Hostname: https://{domain}

Domain name for China: connect-api.cloud.huawei.com

Domain name for Europe: connect-api-dre.cloud.huawei.com

Domain name for Asia Pacific: connect-api-dra.cloud.huawei.com

Domain name for Russia: connect-api-drru.cloud.huawei.com

Path: /api/report/distribution-operation-quality/v1/orderDetailExport/$appId

Headers:

Authorization: Bearer $token

client_id: clientId

This API require some querys to generate the report, to se the query details, refer to the official documentation

Example:

fun getReportUrl(
appId: String,
lang: String,
startTime: String,
endTime: String,
filterConditions:HashMap<String,String>
):ResponseObject {
return if (!token.isNullOrEmpty()){
val fc = StringBuilder().apply {
for (key in filterConditions.keys) {
append("&filterCondition=")
append(key)
append("&filterConditionValue=")
append(filterConditions[key])
}
}
val url =
"$HOST/report/distribution-operation-quality/v1/orderDetailExport/$appId?language=$lang&startTime=$startTime&endTime=$endTime${fc}"
RequestHelper.sendRequest(url,getClientHeaders(),null)
} else MISSING_CREDENTIALS_RESPONSE
}

Conclusion

Now you know what is the Connect API and how you can use it to automatize some management operations or to develop yur own custom console. There are also a lot of things what you can do with the Connect API. For more information you can refer to the official documentation.

Check this and other demos: https://github.com/danms07/dummyapp