Groovy Code Example
/**
* @author Administrator
* @codeName OnlineDocWpsPersonal
* @description Create New
* @createTime 2024-08-05
*/
class OnlineDocCustom implements OnlineDocPersonalPlugin {
private static String CONTENT_TYPE = "application/json;charset=utf-8"
// Vendor domain
private static String HOST = "https://{host}"
// User authorization URL
private static String AUTH_CODE_URL = "/{auth_user_path}"
// Get token URL
private static String QUERY_ACCESS_TOKEN_URL = "/{query_token_path}?"
// Refresh token URL
private static String REFRESH_ACCESS_TOKEN_URL = "/{refresh_token_path}?"
// Get file list URL
private static String QUERY_FILE_LIST_URL = "/{query_files_path}?"
// Authorization callback URL suffix
private static String CALLBACK_URL_SUFFIX = "/custom/authRedirect"
// Save token info to local cache (WPS defaults to 24 hours)
private void saveAccessToken2Cache(String tenantId, String userId, String pluginApiName, Map<String, Object> runtimeData, int expiresSecond) {
Map tokenMap = [:]
tokenMap.putAll(runtimeData)
tokenMap.put("token_expires_millis", DateTime.now().toTimestamp() + (expiresSecond - 30) * 1000)
String key = "OnlineDocCustomAccess_token_" + userId + "_" + pluginApiName
Cache cache = Fx.cache.getDefaultCache()
String value = Fx.json.toJson(tokenMap)
cache.put(key, value, expiresSecond)
log.info("saveAccessToken2Cache key:" + key)
}
// Get token info from local cache
private String getAccessTokenFromCache(String tenantId, String userId, String pluginApiName) {
String key = "OnlineDocCustomAccess_token_" + userId + "_" + pluginApiName
Cache cache = Fx.cache.getDefaultCache()
String value = cache.get(key) as String
if (value == null) {
log.info("getAccessTokenFromCache key:" + key + " value:null")
return null
}
Map tokenMap = Fx.json.parse(value)
String token = tokenMap["access_token"] as String
Long expired = tokenMap["token_expires_millis"] as Long
if (expired <= DateTime.now().toTimestamp()) {
log.info("getAccessTokenFromCache key:" + key + " value:expired")
return null
}
log.info("getAccessTokenFromCache key:" + key)
return token
}
// Get or refresh runtimeData
private Map getOrRefreshAccessToken(String pluginApiName, Map devInfo, Map authRuntimeData, FunctionContext context) {
String accessTokenInCache = getAccessTokenFromCache(context.getTenantId(), context.getUserId(), pluginApiName)
if (accessTokenInCache != null) {
Map<String, Object> refreshMap = [:]
refreshMap.put("errorCode", 0)
refreshMap.put("accessToken", accessTokenInCache)
return refreshMap
}
// Refresh accessToken
Map<String, Object> refreshMap = refreshAccessToken(pluginApiName, devInfo, authRuntimeData, context)
return refreshMap
}
// Build header
private Map buildHeader(String appId, String appKey, String uri, Map body) {
Map header = [:]
header["Content-Type"] = CONTENT_TYPE
//...
return header
}
// Enterprise authentication (currently unused)
public EnterpriseAuth.Result enterpriseAuth(EnterpriseAuth.Arg arg, FunctionContext context) {
log.info("enterpriseAuth arg:" + arg)
String appId = arg.getDevInfo().get("appId") as String
String appKey = arg.getDevInfo().get("appKey") as String
String pluginApiName = arg.getPluginApiName()
// Get company token
Map tokenMap = queryCompanyToken(pluginApiName, appId, appKey)
String companyToken = tokenMap["companyToken"] as String
int errorCode = tokenMap["errorCode"] as Integer
String errorMessage = tokenMap["errorMessage"] as String
if (errorCode != 0) {
Fx.message.throwException(errorMessage)
}
EnterpriseAuth.Result result = new EnterpriseAuth.Result()
//result.setErrorCode(0)
//result.setErrorMessage(errorMessage)
log.info("enterpriseAuth result:" + result)
return result
}
// Refresh access token
private Map<String, Object> refreshAccessToken(String pluginApiName, Map<String, String> devInfo, Map<String, Object> authRuntimeData, FunctionContext context) {
log.info("refreshAccessToken arg:" + pluginApiName)
Map authMap = authRuntimeData
if (authMap.isEmpty()) {
// Never authorized, simulate WPS error code and return auth URL
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", 100050)
resultMap.put("errorMessage", "never to auth")
log.info("refreshAccessToken failed resultMap:" + resultMap)
return resultMap
}
String appId = devInfo.get("appId")
String appKey = devInfo.get("appKey")
String refreshToken = authMap.get("refresh_token")
String queryRefreshTokenUrl = HOST + REFRESH_ACCESS_TOKEN_URL + "appid=" + appId + "&appkey=" + appKey + "&refresh_token=" + refreshToken
Map headers = ["Content-Type": CONTENT_TYPE]
Map requestBody = [:]
log.info("refreshAccessToken request begin:" + queryRefreshTokenUrl)
def (Boolean error, HttpResult httpResult, String errorMessage) = Fx.http.post(queryRefreshTokenUrl, headers, requestBody, 10000, false, 0)
log.info("refreshAccessToken request finish:" + httpResult)
// Check request error
if (error || httpResult == null) {
log.info("refreshAccessToken request null error :" + errorMessage)
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", -1)
resultMap.put("errorMessage", "request error:" + errorMessage)
return resultMap
}
Map contentMap = null
if (httpResult.statusCode == 200) {
contentMap = httpResult.content as Map
} else {
contentMap = Fx.json.parse(httpResult.content as String)
}
Integer bizErrorCode = contentMap.get("result") as Integer
String bizErrorMessage = contentMap.get("msg") as String
// Check business error
if (bizErrorCode != 0) {
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", bizErrorCode)
resultMap.put("errorMessage", bizErrorMessage)
log.info("refreshAccessToken request biz failed error:" + contentMap)
return resultMap
}
// Add expiration timestamp
Map tokenMap = contentMap["token"] as Map
String accessToken = tokenMap["access_token"] as String
Integer expiresSecond = tokenMap["expires_in"] as Integer
// Save to local cache
saveAccessToken2Cache(context.getTenantId(), context.getUserId(), pluginApiName, tokenMap, expiresSecond)
// Return result
Map<String, Object> resultMap = [:]
resultMap.put("errorCode", 0)
resultMap.put("accessToken", accessToken)
log.info("refreshAccessToken finish success:" + resultMap.size())
return resultMap
}
// Request access token
private Map<String, Object> queryAccessToken(String pluginApiName,
String appId,
String appKey,
String code,
FunctionContext context) {
String queryTokenUrl = HOST + QUERY_ACCESS_TOKEN_URL + "appid=" + appId + "&appkey=" + appKey + "&code=" + code
Map headers = ["Content-Type": CONTENT_TYPE]
log.info("queryAccessToken request begin:" + queryTokenUrl)
def (Boolean error, HttpResult httpResult, String errorMessage) = Fx.http.get(queryTokenUrl, headers, 10000, false, 0)
log.info("queryAccessToken request finish:" + httpResult)
// Check request error
if (error || httpResult == null) {
log.info("queryAccessToken request null error :" + errorMessage)
Map resultMap = [:]
resultMap["errorCode"] = -1
resultMap["errorMessage"] = "request error:" + errorMessage
return resultMap
}
Map contentMap = null
if (httpResult.statusCode == 200) {
contentMap = httpResult.content as Map
} else {
contentMap = Fx.json.parse(httpResult.content as String)
}
Integer bizErrorCode = contentMap.get("result") as Integer
String bizErrorMessage = contentMap.get("msg") as String
// Check business error
if (bizErrorCode != 0) {
log.info("queryAccessToken request biz failed error:" + contentMap)
Map resultMap = [:]
resultMap["errorCode"] = bizErrorCode
resultMap["errorMessage"] = bizErrorMessage
return resultMap
}
// Add expiration timestamp
Map tokenMap = contentMap["token"] as Map
Integer expiresSecond = tokenMap["expires_in"] as Integer
// Save to local cache
saveAccessToken2Cache(context.getTenantId(), context.getUserId(), pluginApiName, tokenMap, expiresSecond)
// Return result
Map resultMap = [:]
resultMap["errorCode"] = 0
resultMap["runtimeData"] = tokenMap
resultMap["expiredTime"] = DateTime.now().toTimestamp() + 90 * 24 * 3600 * 1000L
return resultMap
}
// Get personal authorization URL
public GetPersonalAuthUrl.Result getPersonalAuthUrl(GetPersonalAuthUrl.Arg arg, FunctionContext context) {
// Standard log (do not remove)
log.info("getPersonalAuthUrl arg:" + arg)
/* Business logic starts here */
String appId = arg.getDevInfo().get("appId")
String callbackUrl = arg.getDevInfo().get("callbackUrl") + CALLBACK_URL_SUFFIX
// Construct Fxiaoke business parameters
Map<String, String> authMap = [:]
authMap.put("pluginApiName", arg.getPluginApiName())
authMap.put("enterpriseId", context.getTenantId())
authMap.put("employeeId", context.getUserId())
// Encode into state parameter
String state = Fx.crypto.base64.encode(Fx.json.toJson(authMap) as byte[])
String authUrl = HOST + AUTH_CODE_URL + "&appid=" + appId + "&redirect_uri=" + callbackUrl + "&state=" + state
GetPersonalAuthUrl.Result result = new GetPersonalAuthUrl.Result()
result.setErrorCode(0)
result.setAuthUrl(authUrl)
// Standard log (do not remove)
log.info("getPersonalAuthUrl result:" + result)
return result
}
// Process callback data
public ActionCallback.Result actionCallback(ActionCallback.Arg arg, FunctionContext context) {
// Standard log (do not remove)
log.info("actionCallback arg:" + arg)
/* Business logic starts here */
String pluginApiName = arg.getPluginApiName()
String code = arg.getCode()
String appId = arg.getDevInfo().get("appId")
String appKey = arg.getDevInfo().get("appKey")
// Get user authorization token
Map accessTokenResultMap = queryAccessToken(pluginApiName, appId, appKey, code, context)
Integer errorCode = accessTokenResultMap["errorCode"] as Integer
String errorMessage = accessTokenResultMap["errorMessage"] as String
if (errorCode != 0) {
ActionCallback.Result result = new ActionCallback.Result()
result.setErrorCode(errorCode)
result.setErrorMessage(errorMessage)
return result
}
ActionCallback.Result result = new ActionCallback.Result()
result.setErrorCode(errorCode)
result.setRuntimeData(accessTokenResultMap["runtimeData"] as Map)
result.setExpiredTime(accessTokenResultMap["expiredTime"] as Long)
// Standard log (do not remove)
log.info("actionCallback result:" + result)
return result
}
// Query file list
public QueryFileList.Result queryFileList(QueryFileList.Arg arg, FunctionContext context) {
// Standard log (do not remove)
log.info("queryFileList arg:" + arg)
String pluginApiName = arg.getPluginApiName()
String appId = arg.getDevInfo().get("appId") as String
String appKey = arg.getDevInfo().get("appKey") as String
Map authRuntimeData = arg.getAuthRuntimeData()
Map pluginRuntimeData = arg.getPluginRuntimeData()
Integer errorCode = 0
String errorMessage = null
// Get or refresh accessToken
Map accessTokenMap = getOrRefreshAccessToken(pluginApiName, arg.getDevInfo(), authRuntimeData, context)
errorCode = accessTokenMap["errorCode"] as Integer
errorMessage = accessTokenMap["errorMessage"] as String
String accessToken = accessTokenMap["accessToken"] as String
if (errorCode != 0) {
// Return failure result
return buildQueryFileListFailedResult(arg.getPluginApiName(), errorCode, errorMessage, arg.getDevInfo(), context)
}
// Query files
String parentId = null
Integer offset = extraInfo.getOrDefault("next_offset", 0) as Integer
Integer pageNumber = extraInfo.getOrDefault("pageNumber", 0) as Integer
if (arg.isNeedNextPage()) {
pageNumber += 1
}
// Enter selected folder
if (arg.getFolder() != null) {
parentId = arg.getFolder().getFileId()
}
// Request file list
Map fileListMap = queryFiles(arg.getPluginApiName(), appId, appKey, companyToken, accessToken, parentId, offset, pageNumber)
errorCode = fileListMap.get("errorCode") as Integer
errorMessage = fileListMap.get("errorMessage") as String
List<Map> fileList = fileListMap.get("fileList") as List
extraInfo["next_offset"] = fileListMap["next_offset"] as Integer
extraInfo["pageNumber"] = pageNumber
if (errorCode != 0) {
// Return failure result
return buildQueryFileListFailedResult(arg.getPluginApiName(), errorCode, errorMessage, arg.getDevInfo(), context)
}
// Return result
QueryFileList.Result result = new QueryFileList.Result()
result.setErrorCode(errorCode)
result.setErrorMessage(errorMessage)
result.setFileList(fileList)
result.setHasMore(fileListMap["hasMore"] as boolean)
result.setExtraInfo(extraInfo)
// Standard log (do not remove)
log.info("queryFileList result:" + result)
return result
}
private QueryFileList.Result buildQueryFileListFailedResult(String pluginApiName,
int errorCode,
String errorMessage,
Map devInfo,
FunctionContext context) {
// These errors require user re-authorization
if (errorCode == 1234567890) {
// Check if authorization is needed
GetPersonalAuthUrl.Arg authUrlArg = new GetPersonalAuthUrl.Arg()
authUrlArg.setPluginApiName(pluginApiName)
authUrlArg.setDevInfo(devInfo)
GetPersonalAuthUrl.Result authUrlResult = getPersonalAuthUrl(authUrlArg, context)
//
QueryFileList.Result result = new QueryFileList.Result()
result.setErrorCode(errorCode)
result.setErrorMessage(errorMessage)
result.setAuthUrl(authUrlResult.getAuthUrl())
return result
}
Fx.message.throwException(errorMessage)
}
// Query file list (pageNumber starts from 0)
private Map<String, Object> queryFiles(String pluginApiName, String appId, String appKey,
String companyToken, String accessToken,
String parentId, int offset, int pageNumber) {
int pageSize = 20
int tmpOffset = pageNumber > 0 ? pageNumber * pageSize + 1 : 0
String bizUrl = QUERY_FILE_LIST_URL
bizUrl += "access_token=" + accessToken + "&company_token=" + companyToken + "&offset=" + tmpOffset + "&count=" + pageSize
if (parentId != null) {
bizUrl += "&parent_id=" + parentId
}
String url = HOST + bizUrl
Map headers = buildHeader(appId, appKey, bizUrl, null)