in src/main/kotlin/org/jetbrains/teamcity/github/action/CreateWebHookAction.kt [28:130]
fun doRun(info: GitHubRepositoryInfo, client: GitHubClientEx, user: SUser, context: ActionContext, connection: OAuthConnectionDescriptor): Pair<HookAddOperationResult, WebHookInfo> {
val repo = info.getRepositoryId()
val service = RepositoryService(client)
// Reload all hooks from GitHub
// It's ok throw GitHubAccessException upwards: if we cannot get hooks, we cannot add new one
GetAllWebHooksAction.doRun(info, client, context)
for (hook in context.storage.getHooks(info)) {
if (checkExisting(client, context, hook, info)) {
return HookAddOperationResult.AlreadyExists to hook
}
}
val authData = context.authDataStorage.create(user, info, connection)
val callbackUrl = context.getCallbackUrl(authData)
val hook = RepositoryHookEx()
.setEvents(arrayOf("push", "pull_request"))
.setActive(true)
.setName("web")
.setConfig(mapOf(
"url" to callbackUrl,
"content_type" to "json",
"secret" to authData.secret
// TODO: Investigate ssl option
))
val created: RepositoryHook
try {
created = service.createHook(repo, hook)
context.authDataStorage.store(authData)
} catch(e: RequestException) {
LOG.warnAndDebugDetails("Failed to create webhook for repository ${info.id}: ${e.status}", e)
context.handleCommonErrors(e)
when (e.status) {
403, 404 -> {
// ? No access
val pair = TokensHelper.getHooksAccessType(client) ?: throw GitHubAccessException(GitHubAccessException.Type.NoAccess)// Weird. No header?
if (pair.first <= TokensHelper.HookAccessType.READ) throw GitHubAccessException(GitHubAccessException.Type.TokenScopeMismatch)
throw GitHubAccessException(GitHubAccessException.Type.UserHaveNoAccess)
}
422 -> {
if (e.error.errors.any { it.resource.equals("hook", true) && it.message.contains("already exists") }) {
// Already exists
// Very low chance to happen since auth data and callback url is randomly generated
// TODO: Remove
return HookAddOperationResult.AlreadyExists to context.storage.getHooks(info).first { !it.status.bad }
}
}
}
throw e
} catch (e: HttpRetryException) {
val location = e.location ?: throw GitHubAccessException(GitHubAccessException.Type.InternalServerError, e.message)
LOG.warn("Received redirect (${e.responseCode()} from GitHub to location '$location'")
val repository: Repository
val id = URL(location).path.split('/').dropLast(1).last()
try {
val request = GitHubRequest()
request.uri = IGitHubConstants.SEGMENT_REPOSITORIES + "/" + id
request.type = Repository::class.java
repository = client.get(request).body as Repository
} catch(e1: Throwable) {
LOG.warn("Cannot obtain information about repository with id '$id' from server ${info.server}")
throw GitHubAccessException(GitHubAccessException.Type.InternalServerError, e.message)
}
throw GitHubAccessException(GitHubAccessException.Type.Moved, "${info.server}/${repository.generateId()}")
}
if (callbackUrl != created.callbackUrl
|| authData.public != getPubKey(created.callbackUrl)) {
// Weird
// Either callback url is null or does not contains public key part of callback
context.authDataStorage.remove(authData)
throw IllegalStateException("GitHub returned incorrect hook")
}
var hookInfo = context.addHook(created)
if (hookInfo == null) {
context.authDataStorage.remove(authData)
throw IllegalStateException("GitHub returned incorrect hook")
}
if (callbackUrl != hookInfo.callbackUrl) {
// context.addHook returned another hook.
context.storage.delete(info) { !it.status.good }
hookInfo = context.addHook(created)
if (hookInfo == null) {
context.authDataStorage.remove(authData)
throw IllegalStateException("GitHub returned incorrect hook")
}
}
// Remove missing hooks from storage as they don't exists remotely and we just created good one
context.storage.delete(info) { it.status == Status.MISSING }
LOG.info("Successfully created hook $hookInfo")
return HookAddOperationResult.Created to hookInfo
}