zuul/web/src/api.js

388 lines
10 KiB
JavaScript

// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import Axios from 'axios'
function getHomepageUrl(url) {
//
// Discover serving location from href.
//
// This is only needed for sub-directory serving.
// Serving the application from '/' may simply default to '/'
//
// Note that this is not enough for sub-directory serving,
// The static files location also needs to be adapted with the 'homepage'
// settings of the package.json file.
//
// This homepage url is used for the Router and Link resolution logic
//
let baseUrl
if (url) {
baseUrl = url
} else {
baseUrl = window.location.href
}
// Get dirname of the current url
baseUrl = baseUrl.replace(/\\/g, '/').replace(/\/[^/]*$/, '/')
// Remove any query strings
if (baseUrl.includes('?')) {
baseUrl = baseUrl.slice(0, baseUrl.lastIndexOf('?'))
}
// Remove any hash anchor
if (baseUrl.includes('/#')) {
baseUrl = baseUrl.slice(0, baseUrl.lastIndexOf('/#') + 1)
}
// Remove known sub-path
const subDir = [
'/autohold/',
'/build/',
'/buildset/',
'/job/',
'/project/',
'/stream/',
'/status/',
]
subDir.forEach(path => {
if (baseUrl.includes(path)) {
baseUrl = baseUrl.slice(0, baseUrl.lastIndexOf(path) + 1)
}
})
// Remove tenant scope
if (baseUrl.includes('/t/')) {
baseUrl = baseUrl.slice(0, baseUrl.lastIndexOf('/t/') + 1)
}
if (!baseUrl.endsWith('/')) {
baseUrl = baseUrl + '/'
}
// console.log('Homepage url is ', baseUrl)
return baseUrl
}
function getZuulUrl() {
// Return the zuul root api absolute url
const ZUUL_API = process.env.REACT_APP_ZUUL_API
let apiUrl
if (ZUUL_API) {
// Api url set at build time, use it
apiUrl = ZUUL_API
} else {
// Api url is relative to homepage path
apiUrl = getHomepageUrl() + 'api/'
}
if (!apiUrl.endsWith('/')) {
apiUrl = apiUrl + '/'
}
if (!apiUrl.endsWith('/api/')) {
apiUrl = apiUrl + 'api/'
}
// console.log('Api url is ', apiUrl)
return apiUrl
}
const apiUrl = getZuulUrl()
function getStreamUrl(apiPrefix) {
const streamUrl = (apiUrl + apiPrefix)
.replace(/(http)(s)?:\/\//, 'ws$2://') + 'console-stream'
// console.log('Stream url is ', streamUrl)
return streamUrl
}
function getWithCorsHandling(url) {
// This performs a simple GET and tries to detect if CORS errors are
// due to proxy authentication errors.
const instance = Axios.create({
baseURL: apiUrl
})
// First try the request as normal
let res = instance.get(url).catch(err => {
if (err.response === undefined) {
// This is either a Network, DNS, or CORS error, but we can't tell which.
// If we're behind an authz proxy, it's possible our creds have timed out
// and the CORS error is because we're getting a redirect.
// Apache mod_auth_mellon (and possibly other authz proxies) will avoid
// issuing a redirect if X-Requested-With is set to 'XMLHttpRequest' and
// will instead issue a 403. We can use this to detect that case.
instance.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
let res2 = instance.get(url).catch(err2 => {
if (err2.response && err2.response.status === 403) {
// We might be getting a redirect or something else,
// so reload the page.
console.log('Received 403 after unknown error; reloading')
window.location.reload()
}
// If we're still getting an error, we don't know the cause,
// it could be a transient network error, so we won't reload, we'll just
// wait for it to clear.
throw (err2)
})
return res2
}
})
return res
}
// Direct APIs
function fetchInfo() {
return getWithCorsHandling('info')
}
function fetchComponents() {
return getWithCorsHandling('components')
}
function fetchTenantInfo(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'info')
}
function fetchOpenApi() {
return Axios.get(getHomepageUrl() + 'openapi.yaml')
}
function fetchTenants() {
return getWithCorsHandling(apiUrl + 'tenants')
}
function fetchConfigErrors(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'config-errors')
}
function fetchStatus(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'status')
}
function fetchChangeStatus(apiPrefix, changeId) {
return getWithCorsHandling(apiPrefix + 'status/change/' + changeId)
}
function fetchFreezeJob(apiPrefix, pipelineName, projectName, branchName, jobName) {
return getWithCorsHandling(apiPrefix +
'pipeline/' + pipelineName +
'/project/' + projectName +
'/branch/' + branchName +
'/freeze-job/' + jobName)
}
function fetchBuild(apiPrefix, buildId) {
return getWithCorsHandling(apiPrefix + 'build/' + buildId)
}
function fetchBuilds(apiPrefix, queryString) {
let path = 'builds'
if (queryString) {
path += '?' + queryString.slice(1)
}
return getWithCorsHandling(apiPrefix + path)
}
function fetchBuildset(apiPrefix, buildsetId) {
return getWithCorsHandling(apiPrefix + 'buildset/' + buildsetId)
}
function fetchBuildsets(apiPrefix, queryString) {
let path = 'buildsets'
if (queryString) {
path += '?' + queryString.slice(1)
}
return getWithCorsHandling(apiPrefix + path)
}
function fetchPipelines(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'pipelines')
}
function fetchProject(apiPrefix, projectName) {
return getWithCorsHandling(apiPrefix + 'project/' + projectName)
}
function fetchProjects(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'projects')
}
function fetchJob(apiPrefix, jobName) {
return getWithCorsHandling(apiPrefix + 'job/' + jobName)
}
function fetchJobGraph(apiPrefix, projectName, pipelineName, branchName) {
return getWithCorsHandling(apiPrefix +
'pipeline/' + pipelineName +
'/project/' + projectName +
'/branch/' + branchName +
'/freeze-jobs')
}
function fetchJobs(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'jobs')
}
function fetchLabels(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'labels')
}
function fetchNodes(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'nodes')
}
function fetchSemaphores(apiPrefix) {
return Axios.get(apiUrl + apiPrefix + 'semaphores')
}
function fetchAutoholds(apiPrefix) {
return getWithCorsHandling(apiPrefix + 'autohold')
}
function fetchAutohold(apiPrefix, requestId) {
return getWithCorsHandling(apiPrefix + 'autohold/' + requestId)
}
// token-protected API
function fetchUserAuthorizations(apiPrefix, token) {
// Axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.get(apiPrefix + 'authorizations')
.catch(err => { console.log('An error occurred', err) })
// Axios.defaults.headers.common['Authorization'] = ''
return res
}
function dequeue(apiPrefix, projectName, pipeline, change, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.post(
apiPrefix + 'project/' + projectName + '/dequeue',
{
pipeline: pipeline,
change: change,
}
)
return res
}
function dequeue_ref(apiPrefix, projectName, pipeline, ref, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.post(
apiPrefix + 'project/' + projectName + '/dequeue',
{
pipeline: pipeline,
ref: ref,
}
)
return res
}
function enqueue(apiPrefix, projectName, pipeline, change, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.post(
apiPrefix + 'project/' + projectName + '/enqueue',
{
pipeline: pipeline,
change: change,
}
)
return res
}
function enqueue_ref(apiPrefix, projectName, pipeline, ref, oldrev, newrev, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.post(
apiPrefix + 'project/' + projectName + '/enqueue',
{
pipeline: pipeline,
ref: ref,
oldrev: oldrev,
newrev: newrev,
}
)
return res
}
function autohold(apiPrefix, projectName, job, change, ref,
reason, count, node_hold_expiration, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.post(
apiPrefix + 'project/' + projectName + '/autohold',
{
change: change,
job: job,
ref: ref,
reason: reason,
count: count,
node_hold_expiration: node_hold_expiration,
}
)
return res
}
function autohold_delete(apiPrefix, requestId, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.delete(
apiPrefix + '/autohold/' + requestId
)
return res
}
function promote(apiPrefix, pipeline, changes, token) {
const instance = Axios.create({
baseURL: apiUrl
})
instance.defaults.headers.common['Authorization'] = 'Bearer ' + token
let res = instance.post(
apiPrefix + '/promote',
{
pipeline: pipeline,
changes: changes,
}
)
return res
}
export {
apiUrl,
getHomepageUrl,
getStreamUrl,
fetchChangeStatus,
fetchConfigErrors,
fetchStatus,
fetchBuild,
fetchBuilds,
fetchBuildset,
fetchBuildsets,
fetchFreezeJob,
fetchPipelines,
fetchProject,
fetchProjects,
fetchJob,
fetchJobGraph,
fetchJobs,
fetchLabels,
fetchNodes,
fetchOpenApi,
fetchSemaphores,
fetchTenants,
fetchInfo,
fetchComponents,
fetchTenantInfo,
fetchUserAuthorizations,
fetchAutoholds,
fetchAutohold,
autohold,
autohold_delete,
dequeue,
dequeue_ref,
enqueue,
enqueue_ref,
promote,
}