@backstage/integration
Advanced tools
Comparing version 0.0.0-nightly-20211322526 to 0.0.0-nightly-202121022744
# @backstage/integration | ||
## 0.0.0-nightly-20211322526 | ||
## 0.0.0-nightly-202121022744 | ||
### Patch Changes | ||
- 2132233: Properly forward errors that occur when looking up GitLab project IDs. | ||
- 277644e09: Include missing fields in GitLab config schema. This sometimes prevented loading config on the frontend specifically, when using self-hosted GitLab. | ||
- 52f613030: Support GitHub `tree` URLs in `getGitHubFileFetchUrl`. | ||
- 905cbfc96: Add `resolveEditUrl` to integrations to resolve a URL that can be used to edit | ||
a file in the web interfaces of an SCM. | ||
## 0.5.0 | ||
### Minor Changes | ||
- 491f3a0ec: Make `ScmIntegration.resolveUrl` mandatory. | ||
## 0.4.0 | ||
### Minor Changes | ||
- ffffea8e6: Update the `GitLabIntegrationConfig` to require the fields `apiBaseUrl` and `baseUrl`. The `readGitLabIntegrationConfig` function is now more strict and has better error reporting. This change mirrors actual reality in code more properly - the fields are actually necessary for many parts of code to actually function, so they should no longer be optional. | ||
Some checks that used to happen deep inside code that consumed config, now happen upfront at startup. This means that you may encounter new errors at backend startup, if you had actual mistakes in config but didn't happen to exercise the code paths that actually would break. But for most users, no change will be necessary. | ||
An example minimal GitLab config block that just adds a token to public GitLab would look similar to this: | ||
```yaml | ||
integrations: | ||
gitlab: | ||
- host: gitlab.com | ||
token: | ||
$env: GITLAB_TOKEN | ||
``` | ||
A full fledged config that points to a locally hosted GitLab could look like this: | ||
```yaml | ||
integrations: | ||
gitlab: | ||
- host: gitlab.my-company.com | ||
apiBaseUrl: https://gitlab.my-company.com/api/v4 | ||
baseUrl: https://gitlab.my-company.com | ||
token: | ||
$env: OUR_GITLAB_TOKEN | ||
``` | ||
In this case, the only optional field is `baseUrl` which is formed from the `host` if needed. | ||
## 0.3.2 | ||
### Patch Changes | ||
- c4abcdb60: Fix GitLab handling of paths with spaces | ||
- 064c513e1: Properly forward errors that occur when looking up GitLab project IDs. | ||
- 3149bfe63: Add a `resolveUrl` method to integrations, that works like the two-argument URL | ||
constructor. The reason for using this is that Azure have their paths in a | ||
query parameter, rather than the pathname of the URL. | ||
The implementation is optional (when not present, the URL constructor is used), | ||
so this does not imply a breaking change. | ||
- 2e62aea6f: #4322 Bitbucket own hosted v5.11.1 branchUrl fix and enabled error tracing… #4347 | ||
## 0.3.1 | ||
@@ -10,0 +66,0 @@ |
@@ -120,3 +120,4 @@ /* | ||
/** | ||
* The hostname of the given GitLab instance | ||
* The host of the target that this matches on, e.g. "gitlab.com". | ||
* | ||
* @visibility frontend | ||
@@ -126,8 +127,29 @@ */ | ||
/** | ||
* Token used to authenticate requests. | ||
* The base URL of the API of this provider, e.g. | ||
* "https://gitlab.com/api/v4", with no trailing slash. | ||
* | ||
* May be omitted specifically for public GitLab; then it will be deduced. | ||
* | ||
* @visibility frontend | ||
*/ | ||
apiBaseUrl?: string; | ||
/** | ||
* The authorization token to use for requests to this provider. | ||
* | ||
* If no token is specified, anonymous access is used. | ||
* | ||
* @visibility secret | ||
*/ | ||
token?: string; | ||
/** | ||
* The baseUrl of this provider, e.g. "https://gitlab.com", which is | ||
* passed into the GitLab client. | ||
* | ||
* If no baseUrl is provided, it will default to https://${host}. | ||
* | ||
* @visibility frontend | ||
*/ | ||
baseUrl?: string; | ||
}>; | ||
}; | ||
} |
@@ -16,7 +16,15 @@ 'use strict'; | ||
function isValidHost(url) { | ||
function isValidHost(host) { | ||
const check = new URL("http://example.com"); | ||
check.host = url; | ||
return check.host === url; | ||
check.host = host; | ||
return check.host === host; | ||
} | ||
function isValidUrl(url) { | ||
try { | ||
new URL(url); | ||
return true; | ||
} catch { | ||
return false; | ||
} | ||
} | ||
function basicIntegrations(integrations, getHost) { | ||
@@ -36,8 +44,27 @@ return { | ||
} | ||
function defaultScmResolveUrl(options) { | ||
const {url, base} = options; | ||
try { | ||
new URL(url); | ||
return url; | ||
} catch { | ||
} | ||
let updated; | ||
if (url.startsWith("/")) { | ||
const {filepath} = parseGitUrl__default['default'](base); | ||
updated = new URL(base); | ||
const repoRootPath = updated.pathname.substring(0, updated.pathname.length - filepath.length).replace(/\/+$/, ""); | ||
updated.pathname = `${repoRootPath}${url}`; | ||
} else { | ||
updated = new URL(url, base); | ||
} | ||
updated.search = new URL(base).search; | ||
return updated.toString(); | ||
} | ||
const AZURE_HOST = "dev.azure.com"; | ||
function readAzureIntegrationConfig(config2) { | ||
function readAzureIntegrationConfig(config) { | ||
var _a; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : AZURE_HOST; | ||
const token = config2.getOptionalString("token"); | ||
const host = (_a = config.getOptionalString("host")) != null ? _a : AZURE_HOST; | ||
const token = config.getOptionalString("token"); | ||
if (!isValidHost(host)) { | ||
@@ -56,2 +83,43 @@ throw new Error(`Invalid Azure integration config, '${host}' is not a valid host`); | ||
const _AzureIntegration = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "azure"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
resolveUrl(options) { | ||
const {url, base} = options; | ||
if (isValidUrl(url)) { | ||
return url; | ||
} | ||
const parsed = parseGitUrl__default['default'](base); | ||
const {organization, owner, name, filepath} = parsed; | ||
if (!organization || !owner || !name) { | ||
return new URL(url, base).toString(); | ||
} | ||
const path = (filepath == null ? void 0 : filepath.replace(/^\//, "")) || ""; | ||
const mockBaseUrl = new URL(`https://a.com/${path}`); | ||
const updatedPath = new URL(url, mockBaseUrl).pathname; | ||
const newUrl = new URL(base); | ||
newUrl.searchParams.set("path", updatedPath); | ||
return newUrl.toString(); | ||
} | ||
resolveEditUrl(url) { | ||
return url; | ||
} | ||
}; | ||
let AzureIntegration = _AzureIntegration; | ||
AzureIntegration.factory = ({config}) => { | ||
var _a; | ||
const configs = readAzureIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.azure")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _AzureIntegration(c)), (i) => i.config.host); | ||
}; | ||
function getAzureFileFetchUrl(url) { | ||
@@ -142,6 +210,6 @@ var _a; | ||
} | ||
function getAzureRequestOptions(config2, additionalHeaders) { | ||
function getAzureRequestOptions(config, additionalHeaders) { | ||
const headers = additionalHeaders ? {...additionalHeaders} : {}; | ||
if (config2.token) { | ||
const buffer = Buffer.from(`:${config2.token}`, "utf8"); | ||
if (config.token) { | ||
const buffer = Buffer.from(`:${config.token}`, "utf8"); | ||
headers.Authorization = `Basic ${buffer.toString("base64")}`; | ||
@@ -154,9 +222,9 @@ } | ||
const BITBUCKET_API_BASE_URL = "https://api.bitbucket.org/2.0"; | ||
function readBitbucketIntegrationConfig(config2) { | ||
function readBitbucketIntegrationConfig(config) { | ||
var _a; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : BITBUCKET_HOST; | ||
let apiBaseUrl = config2.getOptionalString("apiBaseUrl"); | ||
const token = config2.getOptionalString("token"); | ||
const username = config2.getOptionalString("username"); | ||
const appPassword = config2.getOptionalString("appPassword"); | ||
const host = (_a = config.getOptionalString("host")) != null ? _a : BITBUCKET_HOST; | ||
let apiBaseUrl = config.getOptionalString("apiBaseUrl"); | ||
const token = config.getOptionalString("token"); | ||
const username = config.getOptionalString("username"); | ||
const appPassword = config.getOptionalString("appPassword"); | ||
if (!isValidHost(host)) { | ||
@@ -189,7 +257,45 @@ throw new Error(`Invalid Bitbucket integration config, '${host}' is not a valid host`); | ||
async function getBitbucketDefaultBranch(url, config2) { | ||
const _BitbucketIntegration = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "bitbucket"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
resolveUrl(options) { | ||
return defaultScmResolveUrl(options); | ||
} | ||
resolveEditUrl(url) { | ||
const urlData = parseGitUrl__default['default'](url); | ||
const editUrl = new URL(url); | ||
editUrl.searchParams.set("mode", "edit"); | ||
editUrl.searchParams.set("spa", "0"); | ||
editUrl.searchParams.set("at", urlData.ref); | ||
return editUrl.toString(); | ||
} | ||
}; | ||
let BitbucketIntegration = _BitbucketIntegration; | ||
BitbucketIntegration.factory = ({ | ||
config | ||
}) => { | ||
var _a; | ||
const configs = readBitbucketIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.bitbucket")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _BitbucketIntegration(c)), (i) => i.config.host); | ||
}; | ||
async function getBitbucketDefaultBranch(url, config) { | ||
const {name: repoName, owner: project, resource} = parseGitUrl__default['default'](url); | ||
const isHosted = resource === "bitbucket.org"; | ||
const branchUrl = isHosted ? `${config2.apiBaseUrl}/repositories/${project}/${repoName}` : `${config2.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`; | ||
const response = await fetch__default['default'](branchUrl, getBitbucketRequestOptions(config2)); | ||
let branchUrl = isHosted ? `${config.apiBaseUrl}/repositories/${project}/${repoName}` : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`; | ||
let response = await fetch__default['default'](branchUrl, getBitbucketRequestOptions(config)); | ||
if (response.status === 404 && !isHosted) { | ||
branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/branches/default`; | ||
response = await fetch__default['default'](branchUrl, getBitbucketRequestOptions(config)); | ||
} | ||
if (!response.ok) { | ||
@@ -212,3 +318,3 @@ const message = `Failed to retrieve default branch from ${branchUrl}, ${response.status} ${response.statusText}`; | ||
} | ||
async function getBitbucketDownloadUrl(url, config2) { | ||
async function getBitbucketDownloadUrl(url, config) { | ||
const { | ||
@@ -225,9 +331,9 @@ name: repoName, | ||
if (!branch) { | ||
branch = await getBitbucketDefaultBranch(url, config2); | ||
branch = await getBitbucketDefaultBranch(url, config); | ||
} | ||
const path = filepath ? `&path=${encodeURIComponent(filepath)}` : ""; | ||
const archiveUrl = isHosted ? `${protocol}://${resource}/${project}/${repoName}/get/${branch}.zip` : `${config2.apiBaseUrl}/projects/${project}/repos/${repoName}/archive?format=zip&at=${branch}&prefix=${project}-${repoName}${path}`; | ||
const archiveUrl = isHosted ? `${protocol}://${resource}/${project}/${repoName}/get/${branch}.zip` : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/archive?format=zip&at=${branch}&prefix=${project}-${repoName}${path}`; | ||
return archiveUrl; | ||
} | ||
function getBitbucketFileFetchUrl(url, config2) { | ||
function getBitbucketFileFetchUrl(url, config) { | ||
try { | ||
@@ -239,9 +345,9 @@ const {owner, name, ref, filepathtype, filepath} = parseGitUrl__default['default'](url); | ||
const pathWithoutSlash = filepath.replace(/^\//, ""); | ||
if (config2.host === "bitbucket.org") { | ||
if (config.host === "bitbucket.org") { | ||
if (!ref) { | ||
throw new Error("Invalid Bitbucket URL or file path"); | ||
} | ||
return `${config2.apiBaseUrl}/repositories/${owner}/${name}/src/${ref}/${pathWithoutSlash}`; | ||
return `${config.apiBaseUrl}/repositories/${owner}/${name}/src/${ref}/${pathWithoutSlash}`; | ||
} | ||
return `${config2.apiBaseUrl}/projects/${owner}/repos/${name}/raw/${pathWithoutSlash}?at=${ref}`; | ||
return `${config.apiBaseUrl}/projects/${owner}/repos/${name}/raw/${pathWithoutSlash}?at=${ref}`; | ||
} catch (e) { | ||
@@ -251,8 +357,8 @@ throw new Error(`Incorrect URL: ${url}, ${e}`); | ||
} | ||
function getBitbucketRequestOptions(config2) { | ||
function getBitbucketRequestOptions(config) { | ||
const headers = {}; | ||
if (config2.token) { | ||
headers.Authorization = `Bearer ${config2.token}`; | ||
} else if (config2.username && config2.appPassword) { | ||
const buffer = Buffer.from(`${config2.username}:${config2.appPassword}`, "utf8"); | ||
if (config.token) { | ||
headers.Authorization = `Bearer ${config.token}`; | ||
} else if (config.username && config.appPassword) { | ||
const buffer = Buffer.from(`${config.username}:${config.appPassword}`, "utf8"); | ||
headers.Authorization = `Basic ${buffer.toString("base64")}`; | ||
@@ -268,9 +374,9 @@ } | ||
const GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com"; | ||
function readGitHubIntegrationConfig(config2) { | ||
function readGitHubIntegrationConfig(config) { | ||
var _a, _b; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : GITHUB_HOST; | ||
let apiBaseUrl = config2.getOptionalString("apiBaseUrl"); | ||
let rawBaseUrl = config2.getOptionalString("rawBaseUrl"); | ||
const token = config2.getOptionalString("token"); | ||
const apps = (_b = config2.getOptionalConfigArray("apps")) == null ? void 0 : _b.map((c) => ({ | ||
const host = (_a = config.getOptionalString("host")) != null ? _a : GITHUB_HOST; | ||
let apiBaseUrl = config.getOptionalString("apiBaseUrl"); | ||
let rawBaseUrl = config.getOptionalString("rawBaseUrl"); | ||
const token = config.getOptionalString("token"); | ||
const apps = (_b = config.getOptionalConfigArray("apps")) == null ? void 0 : _b.map((c) => ({ | ||
appId: c.getNumber("appId"), | ||
@@ -309,13 +415,13 @@ clientId: c.getString("clientId"), | ||
function getGitHubFileFetchUrl(url, config2) { | ||
function getGitHubFileFetchUrl(url, config) { | ||
try { | ||
const {owner, name, ref, filepathtype, filepath} = parseGitUrl__default['default'](url); | ||
if (!owner || !name || !ref || filepathtype !== "blob" && filepathtype !== "raw") { | ||
if (!owner || !name || !ref || filepathtype !== "blob" && filepathtype !== "raw" && filepathtype !== "tree") { | ||
throw new Error("Invalid GitHub URL or file path"); | ||
} | ||
const pathWithoutSlash = filepath.replace(/^\//, ""); | ||
if (chooseEndpoint(config2) === "api") { | ||
return `${config2.apiBaseUrl}/repos/${owner}/${name}/contents/${pathWithoutSlash}?ref=${ref}`; | ||
if (chooseEndpoint(config) === "api") { | ||
return `${config.apiBaseUrl}/repos/${owner}/${name}/contents/${pathWithoutSlash}?ref=${ref}`; | ||
} | ||
return `${config2.rawBaseUrl}/${owner}/${name}/${ref}/${pathWithoutSlash}`; | ||
return `${config.rawBaseUrl}/${owner}/${name}/${ref}/${pathWithoutSlash}`; | ||
} catch (e) { | ||
@@ -325,14 +431,14 @@ throw new Error(`Incorrect URL: ${url}, ${e}`); | ||
} | ||
function getGitHubRequestOptions(config2) { | ||
function getGitHubRequestOptions(config) { | ||
const headers = {}; | ||
if (chooseEndpoint(config2) === "api") { | ||
if (chooseEndpoint(config) === "api") { | ||
headers.Accept = "application/vnd.github.v3.raw"; | ||
} | ||
if (config2.token) { | ||
headers.Authorization = `token ${config2.token}`; | ||
if (config.token) { | ||
headers.Authorization = `token ${config.token}`; | ||
} | ||
return {headers}; | ||
} | ||
function chooseEndpoint(config2) { | ||
if (config2.apiBaseUrl && (config2.token || !config2.rawBaseUrl)) { | ||
function chooseEndpoint(config) { | ||
if (config.apiBaseUrl && (config.token || !config.rawBaseUrl)) { | ||
return "api"; | ||
@@ -362,7 +468,7 @@ } | ||
class GithubAppManager { | ||
constructor(config2, baseUrl) { | ||
constructor(config, baseUrl) { | ||
this.cache = new Cache(); | ||
this.baseAuthConfig = { | ||
appId: config2.appId, | ||
privateKey: config2.privateKey | ||
appId: config.appId, | ||
privateKey: config.privateKey | ||
}; | ||
@@ -433,5 +539,5 @@ this.appClient = new rest.Octokit({ | ||
class GithubAppCredentialsMux { | ||
constructor(config2) { | ||
constructor(config) { | ||
var _a, _b; | ||
this.apps = (_b = (_a = config2.apps) == null ? void 0 : _a.map((ac) => new GithubAppManager(ac, config2.apiBaseUrl))) != null ? _b : []; | ||
this.apps = (_b = (_a = config.apps) == null ? void 0 : _a.map((ac) => new GithubAppManager(ac, config.apiBaseUrl))) != null ? _b : []; | ||
} | ||
@@ -443,3 +549,3 @@ async getAppToken(owner, repo) { | ||
const results = await Promise.all(this.apps.map((app) => app.getInstallationCredentials(owner, repo).then((credentials) => ({credentials, error: void 0}), (error) => ({credentials: void 0, error})))); | ||
const result = results.find((result2) => result2.credentials); | ||
const result = results.find((resultItem) => resultItem.credentials); | ||
if (result) { | ||
@@ -461,4 +567,4 @@ return result.credentials.accessToken; | ||
} | ||
static create(config2) { | ||
return new GithubCredentialsProvider(new GithubAppCredentialsMux(config2), config2.token); | ||
static create(config) { | ||
return new GithubCredentialsProvider(new GithubAppCredentialsMux(config), config.token); | ||
} | ||
@@ -482,13 +588,41 @@ async getCredentials(opts) { | ||
const _GitHubIntegration = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "github"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
resolveUrl(options) { | ||
return replaceUrlType(defaultScmResolveUrl(options), "tree"); | ||
} | ||
resolveEditUrl(url) { | ||
return replaceUrlType(url, "edit"); | ||
} | ||
}; | ||
let GitHubIntegration = _GitHubIntegration; | ||
GitHubIntegration.factory = ({config}) => { | ||
var _a; | ||
const configs = readGitHubIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.github")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _GitHubIntegration(c)), (i) => i.config.host); | ||
}; | ||
function replaceUrlType(url, type) { | ||
return url.replace(/\/\/([^/]+)\/([^/]+)\/([^/]+)\/(blob|tree|edit)\//, (_, host, owner, repo) => { | ||
return `//${host}/${owner}/${repo}/${type}/`; | ||
}); | ||
} | ||
const GITLAB_HOST = "gitlab.com"; | ||
const GITLAB_API_BASE_URL = "https://gitlab.com/api/v4"; | ||
function readGitLabIntegrationConfig(config2) { | ||
var _a, _b; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : GITLAB_HOST; | ||
let apiBaseUrl = config2.getOptionalString("apiBaseUrl"); | ||
const token = config2.getOptionalString("token"); | ||
const baseUrl = (_b = config2.getOptionalString("baseUrl")) != null ? _b : `https://${host}`; | ||
if (!isValidHost(host)) { | ||
throw new Error(`Invalid GitLab integration config, '${host}' is not a valid host`); | ||
} | ||
function readGitLabIntegrationConfig(config) { | ||
const host = config.getString("host"); | ||
let apiBaseUrl = config.getOptionalString("apiBaseUrl"); | ||
const token = config.getOptionalString("token"); | ||
let baseUrl = config.getOptionalString("baseUrl"); | ||
if (apiBaseUrl) { | ||
@@ -499,2 +633,16 @@ apiBaseUrl = apiBaseUrl.replace(/\/+$/, ""); | ||
} | ||
if (baseUrl) { | ||
baseUrl = baseUrl.replace(/\/+$/, ""); | ||
} else { | ||
baseUrl = `https://${host}`; | ||
} | ||
if (host.includes(":")) { | ||
throw new Error(`Invalid GitLab integration config, host '${host}' should just be the host name (e.g. "github.com"), not a URL`); | ||
} else if (!isValidHost(host)) { | ||
throw new Error(`Invalid GitLab integration config, '${host}' is not a valid host`); | ||
} else if (!apiBaseUrl || !isValidUrl(apiBaseUrl)) { | ||
throw new Error(`Invalid GitLab integration config, '${apiBaseUrl}' is not a valid apiBaseUrl`); | ||
} else if (!isValidUrl(baseUrl)) { | ||
throw new Error(`Invalid GitLab integration config, '${baseUrl}' is not a valid baseUrl`); | ||
} | ||
return {host, token, apiBaseUrl, baseUrl}; | ||
@@ -505,3 +653,7 @@ } | ||
if (!result.some((c) => c.host === GITLAB_HOST)) { | ||
result.push({host: GITLAB_HOST, apiBaseUrl: GITLAB_API_BASE_URL}); | ||
result.push({ | ||
host: GITLAB_HOST, | ||
apiBaseUrl: GITLAB_API_BASE_URL, | ||
baseUrl: `https://${GITLAB_HOST}` | ||
}); | ||
} | ||
@@ -511,5 +663,5 @@ return result; | ||
async function getGitLabFileFetchUrl(url, config2) { | ||
async function getGitLabFileFetchUrl(url, config) { | ||
if (url.includes("/-/blob/")) { | ||
const projectID = await getProjectId(url, config2); | ||
const projectID = await getProjectId(url, config); | ||
return buildProjectUrl(url, projectID).toString(); | ||
@@ -519,4 +671,4 @@ } | ||
} | ||
function getGitLabRequestOptions(config2) { | ||
const {token = ""} = config2; | ||
function getGitLabRequestOptions(config) { | ||
const {token = ""} = config; | ||
return { | ||
@@ -556,3 +708,3 @@ headers: { | ||
"repository/files", | ||
encodeURIComponent(filePath.join("/")), | ||
encodeURIComponent(decodeURIComponent(filePath.join("/"))), | ||
"raw" | ||
@@ -566,3 +718,3 @@ ].join("/"); | ||
} | ||
async function getProjectId(target, config2) { | ||
async function getProjectId(target, config) { | ||
const url = new URL(target); | ||
@@ -575,3 +727,3 @@ if (!url.pathname.includes("/-/blob/")) { | ||
const repoIDLookup = new URL(`${url.protocol + url.hostname}/api/v4/projects/${encodeURIComponent(repo.replace(/^\//, ""))}`); | ||
const response = await fetch__default['default'](repoIDLookup.toString(), getGitLabRequestOptions(config2)); | ||
const response = await fetch__default['default'](repoIDLookup.toString(), getGitLabRequestOptions(config)); | ||
const data = await response.json(); | ||
@@ -587,3 +739,3 @@ if (!response.ok) { | ||
const AzureIntegration2 = class { | ||
const _GitLabIntegration = class { | ||
constructor(integrationConfig) { | ||
@@ -593,3 +745,3 @@ this.integrationConfig = integrationConfig; | ||
get type() { | ||
return "azure"; | ||
return "gitlab"; | ||
} | ||
@@ -602,82 +754,26 @@ get title() { | ||
} | ||
}; | ||
let AzureIntegration = AzureIntegration2; | ||
AzureIntegration.factory = ({config: config2}) => { | ||
var _a; | ||
const configs = readAzureIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.azure")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new AzureIntegration2(c)), (i) => i.config.host); | ||
}; | ||
const BitbucketIntegration2 = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
resolveUrl(options) { | ||
return defaultScmResolveUrl(options); | ||
} | ||
get type() { | ||
return "bitbucket"; | ||
resolveEditUrl(url) { | ||
return replaceUrlType$1(url, "edit"); | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
}; | ||
let BitbucketIntegration = BitbucketIntegration2; | ||
BitbucketIntegration.factory = ({ | ||
config: config2 | ||
}) => { | ||
let GitLabIntegration = _GitLabIntegration; | ||
GitLabIntegration.factory = ({config}) => { | ||
var _a; | ||
const configs = readBitbucketIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.bitbucket")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new BitbucketIntegration2(c)), (i) => i.config.host); | ||
const configs = readGitLabIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.gitlab")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _GitLabIntegration(c)), (i) => i.config.host); | ||
}; | ||
function replaceUrlType$1(url, type) { | ||
return url.replace(/\/\-\/(blob|tree|edit)\//, `/-/${type}/`); | ||
} | ||
const GitHubIntegration2 = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "github"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
}; | ||
let GitHubIntegration = GitHubIntegration2; | ||
GitHubIntegration.factory = ({config: config2}) => { | ||
var _a; | ||
const configs = readGitHubIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.github")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new GitHubIntegration2(c)), (i) => i.config.host); | ||
}; | ||
const GitLabIntegration2 = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "gitlab"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
}; | ||
let GitLabIntegration = GitLabIntegration2; | ||
GitLabIntegration.factory = ({config: config2}) => { | ||
var _a; | ||
const configs = readGitLabIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.gitlab")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new GitLabIntegration2(c)), (i) => i.config.host); | ||
}; | ||
class ScmIntegrations { | ||
static fromConfig(config2) { | ||
static fromConfig(config) { | ||
return new ScmIntegrations({ | ||
azure: AzureIntegration.factory({config: config2}), | ||
bitbucket: BitbucketIntegration.factory({config: config2}), | ||
github: GitHubIntegration.factory({config: config2}), | ||
gitlab: GitLabIntegration.factory({config: config2}) | ||
azure: AzureIntegration.factory({config}), | ||
bitbucket: BitbucketIntegration.factory({config}), | ||
github: GitHubIntegration.factory({config}), | ||
gitlab: GitLabIntegration.factory({config}) | ||
}); | ||
@@ -709,6 +805,25 @@ } | ||
} | ||
resolveUrl(options) { | ||
const integration = this.byUrl(options.base); | ||
if (!integration) { | ||
return defaultScmResolveUrl(options); | ||
} | ||
return integration.resolveUrl(options); | ||
} | ||
resolveEditUrl(url) { | ||
const integration = this.byUrl(url); | ||
if (!integration) { | ||
return url; | ||
} | ||
return integration.resolveEditUrl(url); | ||
} | ||
} | ||
exports.AzureIntegration = AzureIntegration; | ||
exports.BitbucketIntegration = BitbucketIntegration; | ||
exports.GitHubIntegration = GitHubIntegration; | ||
exports.GitLabIntegration = GitLabIntegration; | ||
exports.GithubCredentialsProvider = GithubCredentialsProvider; | ||
exports.ScmIntegrations = ScmIntegrations; | ||
exports.defaultScmResolveUrl = defaultScmResolveUrl; | ||
exports.getAzureCommitsUrl = getAzureCommitsUrl; | ||
@@ -715,0 +830,0 @@ exports.getAzureDownloadUrl = getAzureDownloadUrl; |
import { Config } from '@backstage/config'; | ||
/** | ||
* The configuration parameters for a single Azure provider. | ||
*/ | ||
declare type AzureIntegrationConfig = { | ||
/** | ||
* The host of the target that this matches on, e.g. "dev.azure.com". | ||
* | ||
* Currently only "dev.azure.com" is supported. | ||
*/ | ||
host: string; | ||
/** | ||
* The authorization token to use for requests. | ||
* | ||
* If no token is specified, anonymous access is used. | ||
*/ | ||
token?: string; | ||
}; | ||
/** | ||
* Reads a single Azure integration config. | ||
* | ||
* @param config The config object of a single integration | ||
*/ | ||
declare function readAzureIntegrationConfig(config: Config): AzureIntegrationConfig; | ||
/** | ||
* Reads a set of Azure integration configs, and inserts some defaults for | ||
* public Azure if not specified. | ||
* | ||
* @param configs All of the integration config objects | ||
*/ | ||
declare function readAzureIntegrationConfigs(configs: Config[]): AzureIntegrationConfig[]; | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://dev.azure.com/{organization}/{project}/_git/reponame?path={path}&version=GB{commitOrBranch}&_a=contents | ||
* to: https://dev.azure.com/{organization}/{project}/_apis/git/repositories/reponame/items?path={path}&version={commitOrBranch} | ||
* | ||
* @param url A URL pointing to a file | ||
*/ | ||
declare function getAzureFileFetchUrl(url: string): string; | ||
/** | ||
* Given a URL pointing to a path on a provider, returns a URL that is suitable | ||
* for downloading the subtree. | ||
* | ||
* @param url A URL pointing to a path | ||
*/ | ||
declare function getAzureDownloadUrl(url: string): string; | ||
/** | ||
* Given a URL, return the API URL to fetch commits on the branch. | ||
* | ||
* @param url A URL pointing to a repository or a sub-path | ||
*/ | ||
declare function getAzureCommitsUrl(url: string): string; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getAzureRequestOptions(config: AzureIntegrationConfig, additionalHeaders?: Record<string, string>): RequestInit; | ||
/** | ||
* The configuration parameters for a single Bitbucket API provider. | ||
@@ -116,35 +54,15 @@ */ | ||
/** | ||
* Given a URL pointing to a path on a provider, returns the default branch. | ||
* | ||
* @param url A URL pointing to a path | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketDefaultBranch(url: string, config: BitbucketIntegrationConfig): Promise<string>; | ||
/** | ||
* Given a URL pointing to a path on a provider, returns a URL that is suitable | ||
* for downloading the subtree. | ||
* | ||
* @param url A URL pointing to a path | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketDownloadUrl(url: string, config: BitbucketIntegrationConfig): Promise<string>; | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://bitbucket.org/orgname/reponame/src/master/file.yaml | ||
* to: https://api.bitbucket.org/2.0/repositories/orgname/reponame/src/master/file.yaml | ||
* | ||
* @param url A URL pointing to a file | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketFileFetchUrl(url: string, config: BitbucketIntegrationConfig): string; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketRequestOptions(config: BitbucketIntegrationConfig): RequestInit; | ||
declare class BitbucketIntegration implements ScmIntegration { | ||
private readonly integrationConfig; | ||
static factory: ScmIntegrationsFactory<BitbucketIntegration>; | ||
constructor(integrationConfig: BitbucketIntegrationConfig); | ||
get type(): string; | ||
get title(): string; | ||
get config(): BitbucketIntegrationConfig; | ||
resolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
resolveEditUrl(url: string): string; | ||
} | ||
@@ -233,45 +151,14 @@ /** | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://github.com/a/b/blob/branchname/path/to/c.yaml | ||
* to: https://api.github.com/repos/a/b/contents/path/to/c.yaml?ref=branchname | ||
* or: https://raw.githubusercontent.com/a/b/branchname/c.yaml | ||
* | ||
* @param url A URL pointing to a file | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitHubFileFetchUrl(url: string, config: GitHubIntegrationConfig): string; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitHubRequestOptions(config: GitHubIntegrationConfig): RequestInit; | ||
declare type GithubCredentials = { | ||
headers?: { | ||
[name: string]: string; | ||
}; | ||
token?: string; | ||
}; | ||
declare class GithubCredentialsProvider { | ||
private readonly githubAppCredentialsMux; | ||
private readonly token?; | ||
static create(config: GitHubIntegrationConfig): GithubCredentialsProvider; | ||
private constructor(); | ||
/** | ||
* Returns GithubCredentials for requested url. | ||
* Consecutive calls to this method with the same url will return cached credentials. | ||
* The shortest lifetime for a token returned is 10 minutes. | ||
* @param opts containing the organization or repository url | ||
* @returns {Promise} of @type {GithubCredentials}. | ||
* @example | ||
* const { token, headers } = await getCredentials({url: 'github.com/backstage/foobar'}) | ||
*/ | ||
getCredentials(opts: { | ||
declare class GitHubIntegration implements ScmIntegration { | ||
private readonly integrationConfig; | ||
static factory: ScmIntegrationsFactory<GitHubIntegration>; | ||
constructor(integrationConfig: GitHubIntegrationConfig); | ||
get type(): string; | ||
get title(): string; | ||
get config(): GitHubIntegrationConfig; | ||
resolveUrl(options: { | ||
url: string; | ||
}): Promise<GithubCredentials>; | ||
base: string; | ||
}): string; | ||
resolveEditUrl(url: string): string; | ||
} | ||
@@ -284,17 +171,14 @@ | ||
/** | ||
* The host of the target that this matches on, e.g. "gitlab.com" | ||
* The host of the target that this matches on, e.g. "gitlab.com". | ||
*/ | ||
host: string; | ||
/** | ||
* The base URL of the API of this provider, e.g. "https://gitlab.com/api/v4", | ||
* with no trailing slash. | ||
* The base URL of the API of this provider, e.g. | ||
* "https://gitlab.com/api/v4", with no trailing slash. | ||
* | ||
* May be omitted specifically for GitLab; then it will be deduced. | ||
* | ||
* The API will always be preferred if both its base URL and a token are | ||
* present. | ||
* May be omitted specifically for public GitLab; then it will be deduced. | ||
*/ | ||
apiBaseUrl?: string; | ||
apiBaseUrl: string; | ||
/** | ||
* The authorization token to use for requests this provider. | ||
* The authorization token to use for requests to this provider. | ||
* | ||
@@ -305,8 +189,8 @@ * If no token is specified, anonymous access is used. | ||
/** | ||
* The baseUrl of this provider, e.g "https://gitlab.com", | ||
* which is passed into the gitlab client. | ||
* The baseUrl of this provider, e.g. "https://gitlab.com", which is passed | ||
* into the GitLab client. | ||
* | ||
* If no baseUrl is provided, it will default to https://${host} | ||
*/ | ||
baseUrl?: string; | ||
baseUrl: string; | ||
}; | ||
@@ -327,42 +211,2 @@ /** | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://gitlab.example.com/a/b/blob/master/c.yaml | ||
* to: https://gitlab.example.com/a/b/raw/master/c.yaml | ||
* -or- | ||
* from: https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/filepath | ||
* to: https://gitlab.com/api/v4/projects/projectId/repository/files/filepath?ref=branch | ||
* | ||
* @param url A URL pointing to a file | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitLabFileFetchUrl(url: string, config: GitLabIntegrationConfig): Promise<string>; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitLabRequestOptions(config: GitLabIntegrationConfig): RequestInit; | ||
declare class BitbucketIntegration implements ScmIntegration { | ||
private readonly integrationConfig; | ||
static factory: ScmIntegrationsFactory<BitbucketIntegration>; | ||
constructor(integrationConfig: BitbucketIntegrationConfig); | ||
get type(): string; | ||
get title(): string; | ||
get config(): BitbucketIntegrationConfig; | ||
} | ||
declare class GitHubIntegration implements ScmIntegration { | ||
private readonly integrationConfig; | ||
static factory: ScmIntegrationsFactory<GitHubIntegration>; | ||
constructor(integrationConfig: GitHubIntegrationConfig); | ||
get type(): string; | ||
get title(): string; | ||
get config(): GitHubIntegrationConfig; | ||
} | ||
declare class GitLabIntegration implements ScmIntegration { | ||
@@ -375,2 +219,7 @@ private readonly integrationConfig; | ||
get config(): GitLabIntegrationConfig; | ||
resolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
resolveEditUrl(url: string): string; | ||
} | ||
@@ -391,2 +240,33 @@ | ||
title: string; | ||
/** | ||
* Resolves an absolute or relative URL in relation to a base URL. | ||
* | ||
* This method is adapted for use within SCM systems, so relative URLs are | ||
* within the context of the root of the hierarchy pointed to by the base | ||
* URL. | ||
* | ||
* For example, if the base URL is `<repo root url>/folder/a.yaml`, i.e. | ||
* within the file tree of a certain repo, an absolute path of `/b.yaml` does | ||
* not resolve to `https://hostname/b.yaml` but rather to | ||
* `<repo root url>/b.yaml` inside the file tree of that same repo. | ||
* | ||
* @param options.url The (absolute or relative) URL or path to resolve | ||
* @param options.base The base URL onto which this resolution happens | ||
*/ | ||
resolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
/** | ||
* Resolves the edit URL for a file within the SCM system. | ||
* | ||
* Most SCM systems have a web interface that allows viewing and editing files | ||
* in the repository. The returned URL directly jumps into the edit mode for | ||
* the file. | ||
* If this is not possible, the integration can fall back to a URL to view | ||
* the file in the web interface. | ||
* | ||
* @param url The absolute URL to the file that should be edited. | ||
*/ | ||
resolveEditUrl(url: string): string; | ||
} | ||
@@ -422,2 +302,33 @@ /** | ||
gitlab: ScmIntegrationsGroup<GitLabIntegration>; | ||
/** | ||
* Resolves an absolute or relative URL in relation to a base URL. | ||
* | ||
* This method is adapted for use within SCM systems, so relative URLs are | ||
* within the context of the root of the hierarchy pointed to by the base | ||
* URL. | ||
* | ||
* For example, if the base URL is `<repo root url>/folder/a.yaml`, i.e. | ||
* within the file tree of a certain repo, an absolute path of `/b.yaml` does | ||
* not resolve to `https://hostname/b.yaml` but rather to | ||
* `<repo root url>/b.yaml` inside the file tree of that same repo. | ||
* | ||
* @param options.url The (absolute or relative) URL or path to resolve | ||
* @param options.base The base URL onto which this resolution happens | ||
*/ | ||
resolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
/** | ||
* Resolves the edit URL for a file within the SCM system. | ||
* | ||
* Most SCM systems have a web interface that allows viewing and editing files | ||
* in the repository. The returned URL directly jumps into the edit mode for | ||
* the file. | ||
* If this is not possible, the integration can fall back to a URL to view | ||
* the file in the web interface. | ||
* | ||
* @param url The absolute URL to the file that should be edited. | ||
*/ | ||
resolveEditUrl(url: string): string; | ||
} | ||
@@ -428,2 +339,33 @@ declare type ScmIntegrationsFactory<T extends ScmIntegration> = (options: { | ||
/** | ||
* The configuration parameters for a single Azure provider. | ||
*/ | ||
declare type AzureIntegrationConfig = { | ||
/** | ||
* The host of the target that this matches on, e.g. "dev.azure.com". | ||
* | ||
* Currently only "dev.azure.com" is supported. | ||
*/ | ||
host: string; | ||
/** | ||
* The authorization token to use for requests. | ||
* | ||
* If no token is specified, anonymous access is used. | ||
*/ | ||
token?: string; | ||
}; | ||
/** | ||
* Reads a single Azure integration config. | ||
* | ||
* @param config The config object of a single integration | ||
*/ | ||
declare function readAzureIntegrationConfig(config: Config): AzureIntegrationConfig; | ||
/** | ||
* Reads a set of Azure integration configs, and inserts some defaults for | ||
* public Azure if not specified. | ||
* | ||
* @param configs All of the integration config objects | ||
*/ | ||
declare function readAzureIntegrationConfigs(configs: Config[]): AzureIntegrationConfig[]; | ||
declare class AzureIntegration implements ScmIntegration { | ||
@@ -436,4 +378,150 @@ private readonly integrationConfig; | ||
get config(): AzureIntegrationConfig; | ||
resolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
resolveEditUrl(url: string): string; | ||
} | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://dev.azure.com/{organization}/{project}/_git/reponame?path={path}&version=GB{commitOrBranch}&_a=contents | ||
* to: https://dev.azure.com/{organization}/{project}/_apis/git/repositories/reponame/items?path={path}&version={commitOrBranch} | ||
* | ||
* @param url A URL pointing to a file | ||
*/ | ||
declare function getAzureFileFetchUrl(url: string): string; | ||
/** | ||
* Given a URL pointing to a path on a provider, returns a URL that is suitable | ||
* for downloading the subtree. | ||
* | ||
* @param url A URL pointing to a path | ||
*/ | ||
declare function getAzureDownloadUrl(url: string): string; | ||
/** | ||
* Given a URL, return the API URL to fetch commits on the branch. | ||
* | ||
* @param url A URL pointing to a repository or a sub-path | ||
*/ | ||
declare function getAzureCommitsUrl(url: string): string; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getAzureRequestOptions(config: AzureIntegrationConfig, additionalHeaders?: Record<string, string>): RequestInit; | ||
/** | ||
* Given a URL pointing to a path on a provider, returns the default branch. | ||
* | ||
* @param url A URL pointing to a path | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketDefaultBranch(url: string, config: BitbucketIntegrationConfig): Promise<string>; | ||
/** | ||
* Given a URL pointing to a path on a provider, returns a URL that is suitable | ||
* for downloading the subtree. | ||
* | ||
* @param url A URL pointing to a path | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketDownloadUrl(url: string, config: BitbucketIntegrationConfig): Promise<string>; | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://bitbucket.org/orgname/reponame/src/master/file.yaml | ||
* to: https://api.bitbucket.org/2.0/repositories/orgname/reponame/src/master/file.yaml | ||
* | ||
* @param url A URL pointing to a file | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketFileFetchUrl(url: string, config: BitbucketIntegrationConfig): string; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getBitbucketRequestOptions(config: BitbucketIntegrationConfig): RequestInit; | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://github.com/a/b/blob/branchname/path/to/c.yaml | ||
* to: https://api.github.com/repos/a/b/contents/path/to/c.yaml?ref=branchname | ||
* or: https://raw.githubusercontent.com/a/b/branchname/c.yaml | ||
* | ||
* @param url A URL pointing to a file | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitHubFileFetchUrl(url: string, config: GitHubIntegrationConfig): string; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitHubRequestOptions(config: GitHubIntegrationConfig): RequestInit; | ||
declare type GithubCredentials = { | ||
headers?: { | ||
[name: string]: string; | ||
}; | ||
token?: string; | ||
}; | ||
declare class GithubCredentialsProvider { | ||
private readonly githubAppCredentialsMux; | ||
private readonly token?; | ||
static create(config: GitHubIntegrationConfig): GithubCredentialsProvider; | ||
private constructor(); | ||
/** | ||
* Returns GithubCredentials for requested url. | ||
* Consecutive calls to this method with the same url will return cached credentials. | ||
* The shortest lifetime for a token returned is 10 minutes. | ||
* @param opts containing the organization or repository url | ||
* @returns {Promise} of @type {GithubCredentials}. | ||
* @example | ||
* const { token, headers } = await getCredentials({url: 'github.com/backstage/foobar'}) | ||
*/ | ||
getCredentials(opts: { | ||
url: string; | ||
}): Promise<GithubCredentials>; | ||
} | ||
/** | ||
* Given a URL pointing to a file on a provider, returns a URL that is suitable | ||
* for fetching the contents of the data. | ||
* | ||
* Converts | ||
* from: https://gitlab.example.com/a/b/blob/master/c.yaml | ||
* to: https://gitlab.example.com/a/b/raw/master/c.yaml | ||
* -or- | ||
* from: https://gitlab.com/groupA/teams/teamA/subgroupA/repoA/-/blob/branch/filepath | ||
* to: https://gitlab.com/api/v4/projects/projectId/repository/files/filepath?ref=branch | ||
* | ||
* @param url A URL pointing to a file | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitLabFileFetchUrl(url: string, config: GitLabIntegrationConfig): Promise<string>; | ||
/** | ||
* Gets the request options necessary to make requests to a given provider. | ||
* | ||
* @param config The relevant provider config | ||
*/ | ||
declare function getGitLabRequestOptions(config: GitLabIntegrationConfig): RequestInit; | ||
/** | ||
* Default implementation of ScmIntegration.resolveUrl, that only works with | ||
* URL pathname based providers. | ||
*/ | ||
declare function defaultScmResolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
declare type IntegrationsByType = { | ||
@@ -456,4 +544,9 @@ azure: ScmIntegrationsGroup<AzureIntegration>; | ||
byHost(host: string): ScmIntegration | undefined; | ||
resolveUrl(options: { | ||
url: string; | ||
base: string; | ||
}): string; | ||
resolveEditUrl(url: string): string; | ||
} | ||
export { AzureIntegrationConfig, BitbucketIntegrationConfig, GitHubIntegrationConfig, GitLabIntegrationConfig, GithubCredentialsProvider, ScmIntegration, ScmIntegrationRegistry, ScmIntegrations, getAzureCommitsUrl, getAzureDownloadUrl, getAzureFileFetchUrl, getAzureRequestOptions, getBitbucketDefaultBranch, getBitbucketDownloadUrl, getBitbucketFileFetchUrl, getBitbucketRequestOptions, getGitHubFileFetchUrl, getGitHubRequestOptions, getGitLabFileFetchUrl, getGitLabRequestOptions, readAzureIntegrationConfig, readAzureIntegrationConfigs, readBitbucketIntegrationConfig, readBitbucketIntegrationConfigs, readGitHubIntegrationConfig, readGitHubIntegrationConfigs, readGitLabIntegrationConfig, readGitLabIntegrationConfigs }; | ||
export { AzureIntegration, AzureIntegrationConfig, BitbucketIntegration, BitbucketIntegrationConfig, GitHubIntegration, GitHubIntegrationConfig, GitLabIntegration, GitLabIntegrationConfig, GithubCredentialsProvider, ScmIntegration, ScmIntegrationRegistry, ScmIntegrations, defaultScmResolveUrl, getAzureCommitsUrl, getAzureDownloadUrl, getAzureFileFetchUrl, getAzureRequestOptions, getBitbucketDefaultBranch, getBitbucketDownloadUrl, getBitbucketFileFetchUrl, getBitbucketRequestOptions, getGitHubFileFetchUrl, getGitHubRequestOptions, getGitLabFileFetchUrl, getGitLabRequestOptions, readAzureIntegrationConfig, readAzureIntegrationConfigs, readBitbucketIntegrationConfig, readBitbucketIntegrationConfigs, readGitHubIntegrationConfig, readGitHubIntegrationConfigs, readGitLabIntegrationConfig, readGitLabIntegrationConfigs }; |
@@ -7,7 +7,15 @@ import parseGitUrl from 'git-url-parse'; | ||
function isValidHost(url) { | ||
function isValidHost(host) { | ||
const check = new URL("http://example.com"); | ||
check.host = url; | ||
return check.host === url; | ||
check.host = host; | ||
return check.host === host; | ||
} | ||
function isValidUrl(url) { | ||
try { | ||
new URL(url); | ||
return true; | ||
} catch { | ||
return false; | ||
} | ||
} | ||
function basicIntegrations(integrations, getHost) { | ||
@@ -27,8 +35,27 @@ return { | ||
} | ||
function defaultScmResolveUrl(options) { | ||
const {url, base} = options; | ||
try { | ||
new URL(url); | ||
return url; | ||
} catch { | ||
} | ||
let updated; | ||
if (url.startsWith("/")) { | ||
const {filepath} = parseGitUrl(base); | ||
updated = new URL(base); | ||
const repoRootPath = updated.pathname.substring(0, updated.pathname.length - filepath.length).replace(/\/+$/, ""); | ||
updated.pathname = `${repoRootPath}${url}`; | ||
} else { | ||
updated = new URL(url, base); | ||
} | ||
updated.search = new URL(base).search; | ||
return updated.toString(); | ||
} | ||
const AZURE_HOST = "dev.azure.com"; | ||
function readAzureIntegrationConfig(config2) { | ||
function readAzureIntegrationConfig(config) { | ||
var _a; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : AZURE_HOST; | ||
const token = config2.getOptionalString("token"); | ||
const host = (_a = config.getOptionalString("host")) != null ? _a : AZURE_HOST; | ||
const token = config.getOptionalString("token"); | ||
if (!isValidHost(host)) { | ||
@@ -47,2 +74,43 @@ throw new Error(`Invalid Azure integration config, '${host}' is not a valid host`); | ||
const _AzureIntegration = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "azure"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
resolveUrl(options) { | ||
const {url, base} = options; | ||
if (isValidUrl(url)) { | ||
return url; | ||
} | ||
const parsed = parseGitUrl(base); | ||
const {organization, owner, name, filepath} = parsed; | ||
if (!organization || !owner || !name) { | ||
return new URL(url, base).toString(); | ||
} | ||
const path = (filepath == null ? void 0 : filepath.replace(/^\//, "")) || ""; | ||
const mockBaseUrl = new URL(`https://a.com/${path}`); | ||
const updatedPath = new URL(url, mockBaseUrl).pathname; | ||
const newUrl = new URL(base); | ||
newUrl.searchParams.set("path", updatedPath); | ||
return newUrl.toString(); | ||
} | ||
resolveEditUrl(url) { | ||
return url; | ||
} | ||
}; | ||
let AzureIntegration = _AzureIntegration; | ||
AzureIntegration.factory = ({config}) => { | ||
var _a; | ||
const configs = readAzureIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.azure")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _AzureIntegration(c)), (i) => i.config.host); | ||
}; | ||
function getAzureFileFetchUrl(url) { | ||
@@ -133,6 +201,6 @@ var _a; | ||
} | ||
function getAzureRequestOptions(config2, additionalHeaders) { | ||
function getAzureRequestOptions(config, additionalHeaders) { | ||
const headers = additionalHeaders ? {...additionalHeaders} : {}; | ||
if (config2.token) { | ||
const buffer = Buffer.from(`:${config2.token}`, "utf8"); | ||
if (config.token) { | ||
const buffer = Buffer.from(`:${config.token}`, "utf8"); | ||
headers.Authorization = `Basic ${buffer.toString("base64")}`; | ||
@@ -145,9 +213,9 @@ } | ||
const BITBUCKET_API_BASE_URL = "https://api.bitbucket.org/2.0"; | ||
function readBitbucketIntegrationConfig(config2) { | ||
function readBitbucketIntegrationConfig(config) { | ||
var _a; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : BITBUCKET_HOST; | ||
let apiBaseUrl = config2.getOptionalString("apiBaseUrl"); | ||
const token = config2.getOptionalString("token"); | ||
const username = config2.getOptionalString("username"); | ||
const appPassword = config2.getOptionalString("appPassword"); | ||
const host = (_a = config.getOptionalString("host")) != null ? _a : BITBUCKET_HOST; | ||
let apiBaseUrl = config.getOptionalString("apiBaseUrl"); | ||
const token = config.getOptionalString("token"); | ||
const username = config.getOptionalString("username"); | ||
const appPassword = config.getOptionalString("appPassword"); | ||
if (!isValidHost(host)) { | ||
@@ -180,7 +248,45 @@ throw new Error(`Invalid Bitbucket integration config, '${host}' is not a valid host`); | ||
async function getBitbucketDefaultBranch(url, config2) { | ||
const _BitbucketIntegration = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "bitbucket"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
resolveUrl(options) { | ||
return defaultScmResolveUrl(options); | ||
} | ||
resolveEditUrl(url) { | ||
const urlData = parseGitUrl(url); | ||
const editUrl = new URL(url); | ||
editUrl.searchParams.set("mode", "edit"); | ||
editUrl.searchParams.set("spa", "0"); | ||
editUrl.searchParams.set("at", urlData.ref); | ||
return editUrl.toString(); | ||
} | ||
}; | ||
let BitbucketIntegration = _BitbucketIntegration; | ||
BitbucketIntegration.factory = ({ | ||
config | ||
}) => { | ||
var _a; | ||
const configs = readBitbucketIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.bitbucket")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _BitbucketIntegration(c)), (i) => i.config.host); | ||
}; | ||
async function getBitbucketDefaultBranch(url, config) { | ||
const {name: repoName, owner: project, resource} = parseGitUrl(url); | ||
const isHosted = resource === "bitbucket.org"; | ||
const branchUrl = isHosted ? `${config2.apiBaseUrl}/repositories/${project}/${repoName}` : `${config2.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`; | ||
const response = await fetch(branchUrl, getBitbucketRequestOptions(config2)); | ||
let branchUrl = isHosted ? `${config.apiBaseUrl}/repositories/${project}/${repoName}` : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/default-branch`; | ||
let response = await fetch(branchUrl, getBitbucketRequestOptions(config)); | ||
if (response.status === 404 && !isHosted) { | ||
branchUrl = `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/branches/default`; | ||
response = await fetch(branchUrl, getBitbucketRequestOptions(config)); | ||
} | ||
if (!response.ok) { | ||
@@ -203,3 +309,3 @@ const message = `Failed to retrieve default branch from ${branchUrl}, ${response.status} ${response.statusText}`; | ||
} | ||
async function getBitbucketDownloadUrl(url, config2) { | ||
async function getBitbucketDownloadUrl(url, config) { | ||
const { | ||
@@ -216,9 +322,9 @@ name: repoName, | ||
if (!branch) { | ||
branch = await getBitbucketDefaultBranch(url, config2); | ||
branch = await getBitbucketDefaultBranch(url, config); | ||
} | ||
const path = filepath ? `&path=${encodeURIComponent(filepath)}` : ""; | ||
const archiveUrl = isHosted ? `${protocol}://${resource}/${project}/${repoName}/get/${branch}.zip` : `${config2.apiBaseUrl}/projects/${project}/repos/${repoName}/archive?format=zip&at=${branch}&prefix=${project}-${repoName}${path}`; | ||
const archiveUrl = isHosted ? `${protocol}://${resource}/${project}/${repoName}/get/${branch}.zip` : `${config.apiBaseUrl}/projects/${project}/repos/${repoName}/archive?format=zip&at=${branch}&prefix=${project}-${repoName}${path}`; | ||
return archiveUrl; | ||
} | ||
function getBitbucketFileFetchUrl(url, config2) { | ||
function getBitbucketFileFetchUrl(url, config) { | ||
try { | ||
@@ -230,9 +336,9 @@ const {owner, name, ref, filepathtype, filepath} = parseGitUrl(url); | ||
const pathWithoutSlash = filepath.replace(/^\//, ""); | ||
if (config2.host === "bitbucket.org") { | ||
if (config.host === "bitbucket.org") { | ||
if (!ref) { | ||
throw new Error("Invalid Bitbucket URL or file path"); | ||
} | ||
return `${config2.apiBaseUrl}/repositories/${owner}/${name}/src/${ref}/${pathWithoutSlash}`; | ||
return `${config.apiBaseUrl}/repositories/${owner}/${name}/src/${ref}/${pathWithoutSlash}`; | ||
} | ||
return `${config2.apiBaseUrl}/projects/${owner}/repos/${name}/raw/${pathWithoutSlash}?at=${ref}`; | ||
return `${config.apiBaseUrl}/projects/${owner}/repos/${name}/raw/${pathWithoutSlash}?at=${ref}`; | ||
} catch (e) { | ||
@@ -242,8 +348,8 @@ throw new Error(`Incorrect URL: ${url}, ${e}`); | ||
} | ||
function getBitbucketRequestOptions(config2) { | ||
function getBitbucketRequestOptions(config) { | ||
const headers = {}; | ||
if (config2.token) { | ||
headers.Authorization = `Bearer ${config2.token}`; | ||
} else if (config2.username && config2.appPassword) { | ||
const buffer = Buffer.from(`${config2.username}:${config2.appPassword}`, "utf8"); | ||
if (config.token) { | ||
headers.Authorization = `Bearer ${config.token}`; | ||
} else if (config.username && config.appPassword) { | ||
const buffer = Buffer.from(`${config.username}:${config.appPassword}`, "utf8"); | ||
headers.Authorization = `Basic ${buffer.toString("base64")}`; | ||
@@ -259,9 +365,9 @@ } | ||
const GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com"; | ||
function readGitHubIntegrationConfig(config2) { | ||
function readGitHubIntegrationConfig(config) { | ||
var _a, _b; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : GITHUB_HOST; | ||
let apiBaseUrl = config2.getOptionalString("apiBaseUrl"); | ||
let rawBaseUrl = config2.getOptionalString("rawBaseUrl"); | ||
const token = config2.getOptionalString("token"); | ||
const apps = (_b = config2.getOptionalConfigArray("apps")) == null ? void 0 : _b.map((c) => ({ | ||
const host = (_a = config.getOptionalString("host")) != null ? _a : GITHUB_HOST; | ||
let apiBaseUrl = config.getOptionalString("apiBaseUrl"); | ||
let rawBaseUrl = config.getOptionalString("rawBaseUrl"); | ||
const token = config.getOptionalString("token"); | ||
const apps = (_b = config.getOptionalConfigArray("apps")) == null ? void 0 : _b.map((c) => ({ | ||
appId: c.getNumber("appId"), | ||
@@ -300,13 +406,13 @@ clientId: c.getString("clientId"), | ||
function getGitHubFileFetchUrl(url, config2) { | ||
function getGitHubFileFetchUrl(url, config) { | ||
try { | ||
const {owner, name, ref, filepathtype, filepath} = parseGitUrl(url); | ||
if (!owner || !name || !ref || filepathtype !== "blob" && filepathtype !== "raw") { | ||
if (!owner || !name || !ref || filepathtype !== "blob" && filepathtype !== "raw" && filepathtype !== "tree") { | ||
throw new Error("Invalid GitHub URL or file path"); | ||
} | ||
const pathWithoutSlash = filepath.replace(/^\//, ""); | ||
if (chooseEndpoint(config2) === "api") { | ||
return `${config2.apiBaseUrl}/repos/${owner}/${name}/contents/${pathWithoutSlash}?ref=${ref}`; | ||
if (chooseEndpoint(config) === "api") { | ||
return `${config.apiBaseUrl}/repos/${owner}/${name}/contents/${pathWithoutSlash}?ref=${ref}`; | ||
} | ||
return `${config2.rawBaseUrl}/${owner}/${name}/${ref}/${pathWithoutSlash}`; | ||
return `${config.rawBaseUrl}/${owner}/${name}/${ref}/${pathWithoutSlash}`; | ||
} catch (e) { | ||
@@ -316,14 +422,14 @@ throw new Error(`Incorrect URL: ${url}, ${e}`); | ||
} | ||
function getGitHubRequestOptions(config2) { | ||
function getGitHubRequestOptions(config) { | ||
const headers = {}; | ||
if (chooseEndpoint(config2) === "api") { | ||
if (chooseEndpoint(config) === "api") { | ||
headers.Accept = "application/vnd.github.v3.raw"; | ||
} | ||
if (config2.token) { | ||
headers.Authorization = `token ${config2.token}`; | ||
if (config.token) { | ||
headers.Authorization = `token ${config.token}`; | ||
} | ||
return {headers}; | ||
} | ||
function chooseEndpoint(config2) { | ||
if (config2.apiBaseUrl && (config2.token || !config2.rawBaseUrl)) { | ||
function chooseEndpoint(config) { | ||
if (config.apiBaseUrl && (config.token || !config.rawBaseUrl)) { | ||
return "api"; | ||
@@ -353,7 +459,7 @@ } | ||
class GithubAppManager { | ||
constructor(config2, baseUrl) { | ||
constructor(config, baseUrl) { | ||
this.cache = new Cache(); | ||
this.baseAuthConfig = { | ||
appId: config2.appId, | ||
privateKey: config2.privateKey | ||
appId: config.appId, | ||
privateKey: config.privateKey | ||
}; | ||
@@ -424,5 +530,5 @@ this.appClient = new Octokit({ | ||
class GithubAppCredentialsMux { | ||
constructor(config2) { | ||
constructor(config) { | ||
var _a, _b; | ||
this.apps = (_b = (_a = config2.apps) == null ? void 0 : _a.map((ac) => new GithubAppManager(ac, config2.apiBaseUrl))) != null ? _b : []; | ||
this.apps = (_b = (_a = config.apps) == null ? void 0 : _a.map((ac) => new GithubAppManager(ac, config.apiBaseUrl))) != null ? _b : []; | ||
} | ||
@@ -434,3 +540,3 @@ async getAppToken(owner, repo) { | ||
const results = await Promise.all(this.apps.map((app) => app.getInstallationCredentials(owner, repo).then((credentials) => ({credentials, error: void 0}), (error) => ({credentials: void 0, error})))); | ||
const result = results.find((result2) => result2.credentials); | ||
const result = results.find((resultItem) => resultItem.credentials); | ||
if (result) { | ||
@@ -452,4 +558,4 @@ return result.credentials.accessToken; | ||
} | ||
static create(config2) { | ||
return new GithubCredentialsProvider(new GithubAppCredentialsMux(config2), config2.token); | ||
static create(config) { | ||
return new GithubCredentialsProvider(new GithubAppCredentialsMux(config), config.token); | ||
} | ||
@@ -473,13 +579,41 @@ async getCredentials(opts) { | ||
const _GitHubIntegration = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "github"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
resolveUrl(options) { | ||
return replaceUrlType(defaultScmResolveUrl(options), "tree"); | ||
} | ||
resolveEditUrl(url) { | ||
return replaceUrlType(url, "edit"); | ||
} | ||
}; | ||
let GitHubIntegration = _GitHubIntegration; | ||
GitHubIntegration.factory = ({config}) => { | ||
var _a; | ||
const configs = readGitHubIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.github")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _GitHubIntegration(c)), (i) => i.config.host); | ||
}; | ||
function replaceUrlType(url, type) { | ||
return url.replace(/\/\/([^/]+)\/([^/]+)\/([^/]+)\/(blob|tree|edit)\//, (_, host, owner, repo) => { | ||
return `//${host}/${owner}/${repo}/${type}/`; | ||
}); | ||
} | ||
const GITLAB_HOST = "gitlab.com"; | ||
const GITLAB_API_BASE_URL = "https://gitlab.com/api/v4"; | ||
function readGitLabIntegrationConfig(config2) { | ||
var _a, _b; | ||
const host = (_a = config2.getOptionalString("host")) != null ? _a : GITLAB_HOST; | ||
let apiBaseUrl = config2.getOptionalString("apiBaseUrl"); | ||
const token = config2.getOptionalString("token"); | ||
const baseUrl = (_b = config2.getOptionalString("baseUrl")) != null ? _b : `https://${host}`; | ||
if (!isValidHost(host)) { | ||
throw new Error(`Invalid GitLab integration config, '${host}' is not a valid host`); | ||
} | ||
function readGitLabIntegrationConfig(config) { | ||
const host = config.getString("host"); | ||
let apiBaseUrl = config.getOptionalString("apiBaseUrl"); | ||
const token = config.getOptionalString("token"); | ||
let baseUrl = config.getOptionalString("baseUrl"); | ||
if (apiBaseUrl) { | ||
@@ -490,2 +624,16 @@ apiBaseUrl = apiBaseUrl.replace(/\/+$/, ""); | ||
} | ||
if (baseUrl) { | ||
baseUrl = baseUrl.replace(/\/+$/, ""); | ||
} else { | ||
baseUrl = `https://${host}`; | ||
} | ||
if (host.includes(":")) { | ||
throw new Error(`Invalid GitLab integration config, host '${host}' should just be the host name (e.g. "github.com"), not a URL`); | ||
} else if (!isValidHost(host)) { | ||
throw new Error(`Invalid GitLab integration config, '${host}' is not a valid host`); | ||
} else if (!apiBaseUrl || !isValidUrl(apiBaseUrl)) { | ||
throw new Error(`Invalid GitLab integration config, '${apiBaseUrl}' is not a valid apiBaseUrl`); | ||
} else if (!isValidUrl(baseUrl)) { | ||
throw new Error(`Invalid GitLab integration config, '${baseUrl}' is not a valid baseUrl`); | ||
} | ||
return {host, token, apiBaseUrl, baseUrl}; | ||
@@ -496,3 +644,7 @@ } | ||
if (!result.some((c) => c.host === GITLAB_HOST)) { | ||
result.push({host: GITLAB_HOST, apiBaseUrl: GITLAB_API_BASE_URL}); | ||
result.push({ | ||
host: GITLAB_HOST, | ||
apiBaseUrl: GITLAB_API_BASE_URL, | ||
baseUrl: `https://${GITLAB_HOST}` | ||
}); | ||
} | ||
@@ -502,5 +654,5 @@ return result; | ||
async function getGitLabFileFetchUrl(url, config2) { | ||
async function getGitLabFileFetchUrl(url, config) { | ||
if (url.includes("/-/blob/")) { | ||
const projectID = await getProjectId(url, config2); | ||
const projectID = await getProjectId(url, config); | ||
return buildProjectUrl(url, projectID).toString(); | ||
@@ -510,4 +662,4 @@ } | ||
} | ||
function getGitLabRequestOptions(config2) { | ||
const {token = ""} = config2; | ||
function getGitLabRequestOptions(config) { | ||
const {token = ""} = config; | ||
return { | ||
@@ -547,3 +699,3 @@ headers: { | ||
"repository/files", | ||
encodeURIComponent(filePath.join("/")), | ||
encodeURIComponent(decodeURIComponent(filePath.join("/"))), | ||
"raw" | ||
@@ -557,3 +709,3 @@ ].join("/"); | ||
} | ||
async function getProjectId(target, config2) { | ||
async function getProjectId(target, config) { | ||
const url = new URL(target); | ||
@@ -566,3 +718,3 @@ if (!url.pathname.includes("/-/blob/")) { | ||
const repoIDLookup = new URL(`${url.protocol + url.hostname}/api/v4/projects/${encodeURIComponent(repo.replace(/^\//, ""))}`); | ||
const response = await fetch(repoIDLookup.toString(), getGitLabRequestOptions(config2)); | ||
const response = await fetch(repoIDLookup.toString(), getGitLabRequestOptions(config)); | ||
const data = await response.json(); | ||
@@ -578,3 +730,3 @@ if (!response.ok) { | ||
const AzureIntegration2 = class { | ||
const _GitLabIntegration = class { | ||
constructor(integrationConfig) { | ||
@@ -584,3 +736,3 @@ this.integrationConfig = integrationConfig; | ||
get type() { | ||
return "azure"; | ||
return "gitlab"; | ||
} | ||
@@ -593,82 +745,26 @@ get title() { | ||
} | ||
}; | ||
let AzureIntegration = AzureIntegration2; | ||
AzureIntegration.factory = ({config: config2}) => { | ||
var _a; | ||
const configs = readAzureIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.azure")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new AzureIntegration2(c)), (i) => i.config.host); | ||
}; | ||
const BitbucketIntegration2 = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
resolveUrl(options) { | ||
return defaultScmResolveUrl(options); | ||
} | ||
get type() { | ||
return "bitbucket"; | ||
resolveEditUrl(url) { | ||
return replaceUrlType$1(url, "edit"); | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
}; | ||
let BitbucketIntegration = BitbucketIntegration2; | ||
BitbucketIntegration.factory = ({ | ||
config: config2 | ||
}) => { | ||
let GitLabIntegration = _GitLabIntegration; | ||
GitLabIntegration.factory = ({config}) => { | ||
var _a; | ||
const configs = readBitbucketIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.bitbucket")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new BitbucketIntegration2(c)), (i) => i.config.host); | ||
const configs = readGitLabIntegrationConfigs((_a = config.getOptionalConfigArray("integrations.gitlab")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new _GitLabIntegration(c)), (i) => i.config.host); | ||
}; | ||
function replaceUrlType$1(url, type) { | ||
return url.replace(/\/\-\/(blob|tree|edit)\//, `/-/${type}/`); | ||
} | ||
const GitHubIntegration2 = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "github"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
}; | ||
let GitHubIntegration = GitHubIntegration2; | ||
GitHubIntegration.factory = ({config: config2}) => { | ||
var _a; | ||
const configs = readGitHubIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.github")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new GitHubIntegration2(c)), (i) => i.config.host); | ||
}; | ||
const GitLabIntegration2 = class { | ||
constructor(integrationConfig) { | ||
this.integrationConfig = integrationConfig; | ||
} | ||
get type() { | ||
return "gitlab"; | ||
} | ||
get title() { | ||
return this.integrationConfig.host; | ||
} | ||
get config() { | ||
return this.integrationConfig; | ||
} | ||
}; | ||
let GitLabIntegration = GitLabIntegration2; | ||
GitLabIntegration.factory = ({config: config2}) => { | ||
var _a; | ||
const configs = readGitLabIntegrationConfigs((_a = config2.getOptionalConfigArray("integrations.gitlab")) != null ? _a : []); | ||
return basicIntegrations(configs.map((c) => new GitLabIntegration2(c)), (i) => i.config.host); | ||
}; | ||
class ScmIntegrations { | ||
static fromConfig(config2) { | ||
static fromConfig(config) { | ||
return new ScmIntegrations({ | ||
azure: AzureIntegration.factory({config: config2}), | ||
bitbucket: BitbucketIntegration.factory({config: config2}), | ||
github: GitHubIntegration.factory({config: config2}), | ||
gitlab: GitLabIntegration.factory({config: config2}) | ||
azure: AzureIntegration.factory({config}), | ||
bitbucket: BitbucketIntegration.factory({config}), | ||
github: GitHubIntegration.factory({config}), | ||
gitlab: GitLabIntegration.factory({config}) | ||
}); | ||
@@ -700,5 +796,19 @@ } | ||
} | ||
resolveUrl(options) { | ||
const integration = this.byUrl(options.base); | ||
if (!integration) { | ||
return defaultScmResolveUrl(options); | ||
} | ||
return integration.resolveUrl(options); | ||
} | ||
resolveEditUrl(url) { | ||
const integration = this.byUrl(url); | ||
if (!integration) { | ||
return url; | ||
} | ||
return integration.resolveEditUrl(url); | ||
} | ||
} | ||
export { GithubCredentialsProvider, ScmIntegrations, getAzureCommitsUrl, getAzureDownloadUrl, getAzureFileFetchUrl, getAzureRequestOptions, getBitbucketDefaultBranch, getBitbucketDownloadUrl, getBitbucketFileFetchUrl, getBitbucketRequestOptions, getGitHubFileFetchUrl, getGitHubRequestOptions, getGitLabFileFetchUrl, getGitLabRequestOptions, readAzureIntegrationConfig, readAzureIntegrationConfigs, readBitbucketIntegrationConfig, readBitbucketIntegrationConfigs, readGitHubIntegrationConfig, readGitHubIntegrationConfigs, readGitLabIntegrationConfig, readGitLabIntegrationConfigs }; | ||
export { AzureIntegration, BitbucketIntegration, GitHubIntegration, GitLabIntegration, GithubCredentialsProvider, ScmIntegrations, defaultScmResolveUrl, getAzureCommitsUrl, getAzureDownloadUrl, getAzureFileFetchUrl, getAzureRequestOptions, getBitbucketDefaultBranch, getBitbucketDownloadUrl, getBitbucketFileFetchUrl, getBitbucketRequestOptions, getGitHubFileFetchUrl, getGitHubRequestOptions, getGitLabFileFetchUrl, getGitLabRequestOptions, readAzureIntegrationConfig, readAzureIntegrationConfigs, readBitbucketIntegrationConfig, readBitbucketIntegrationConfigs, readGitHubIntegrationConfig, readGitHubIntegrationConfigs, readGitLabIntegrationConfig, readGitLabIntegrationConfigs }; | ||
//# sourceMappingURL=index.esm.js.map |
{ | ||
"name": "@backstage/integration", | ||
"version": "0.0.0-nightly-20211322526", | ||
"version": "0.0.0-nightly-202121022744", | ||
"main": "dist/index.cjs.js", | ||
@@ -40,4 +40,5 @@ "types": "dist/index.d.ts", | ||
"devDependencies": { | ||
"@backstage/cli": "^0.0.0-nightly-20211322526", | ||
"@backstage/test-utils": "^0.1.5", | ||
"@backstage/cli": "^0.0.0-nightly-202121022744", | ||
"@backstage/config-loader": "^0.5.1", | ||
"@backstage/test-utils": "^0.1.7", | ||
"@types/jest": "^26.0.7", | ||
@@ -44,0 +45,0 @@ "@types/luxon": "^1.25.0", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
226347
2246
6
4