mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(secret-sync): Add Azure Devops PR suggestions
This commit is contained in:
@@ -225,7 +225,6 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
case TerraformCloudConnectionMethod.ApiToken:
|
||||
case VercelConnectionMethod.ApiToken:
|
||||
case OnePassConnectionMethod.ApiToken:
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
return "API Token";
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||
@@ -234,6 +233,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
||||
case WindmillConnectionMethod.AccessToken:
|
||||
case HCVaultConnectionMethod.AccessToken:
|
||||
case TeamCityConnectionMethod.AccessToken:
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
return "Access Token";
|
||||
case Auth0ConnectionMethod.ClientCredentials:
|
||||
return "Client Credentials";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export enum AzureDevOpsConnectionMethod {
|
||||
OAuth = "oauth",
|
||||
ApiToken = "api-token"
|
||||
AccessToken = "access-token"
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ export const getAzureDevopsConnectionListItem = () => {
|
||||
app: AppConnection.AzureDevOps as const,
|
||||
methods: Object.values(AzureDevOpsConnectionMethod) as [
|
||||
AzureDevOpsConnectionMethod.OAuth,
|
||||
AzureDevOpsConnectionMethod.ApiToken
|
||||
AzureDevOpsConnectionMethod.AccessToken
|
||||
],
|
||||
oauthClientId: INF_APP_CONNECTION_AZURE_CLIENT_ID
|
||||
};
|
||||
};
|
||||
|
||||
export const getAzureDevopsConnectionAccessToken = async (
|
||||
export const getAzureDevopsConnection = async (
|
||||
connectionId: string,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
@@ -104,12 +104,12 @@ export const getAzureDevopsConnectionAccessToken = async (
|
||||
|
||||
return data.access_token;
|
||||
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
if (!("apiKey" in credentials)) {
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
if (!("accessToken" in credentials)) {
|
||||
throw new BadRequestError({ message: "Invalid API token credentials" });
|
||||
}
|
||||
// For API token, return the basic auth token directly
|
||||
return credentials.apiKey as string;
|
||||
return credentials.accessToken;
|
||||
|
||||
default:
|
||||
throw new BadRequestError({ message: `Unsupported connection method` });
|
||||
@@ -188,17 +188,17 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
expiresAt: Date.now() + tokenResp.data.expires_in * 1000
|
||||
};
|
||||
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
const apiTokenCredentials = inputCredentials as { apiKey: string; orgName?: string };
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
const apiTokenCredentials = inputCredentials as { accessToken: string; orgName?: string };
|
||||
|
||||
try {
|
||||
if (apiTokenCredentials.orgName) {
|
||||
// Validate against specific organization
|
||||
const response = await request.get(
|
||||
`${IntegrationUrls.AZURE_DEVOPS_API_URL}/${apiTokenCredentials.orgName}/_apis/projects?api-version=7.2-preview.2&$top=1`,
|
||||
`${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(apiTokenCredentials.orgName)}/_apis/projects?api-version=7.2-preview.2&$top=1`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.apiKey}`).toString("base64")}`
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.accessToken}`).toString("base64")}`
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -210,7 +210,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: apiTokenCredentials.apiKey,
|
||||
accessToken: apiTokenCredentials.accessToken,
|
||||
orgName: apiTokenCredentials.orgName
|
||||
};
|
||||
}
|
||||
@@ -219,7 +219,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
`https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.apiKey}`).toString("base64")}`
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.accessToken}`).toString("base64")}`
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -230,7 +230,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
value: Array<{ accountId: string; accountName: string; accountUri: string }>;
|
||||
}>(`https://app.vssps.visualstudio.com/_apis/accounts?api-version=7.1`, {
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.apiKey}`).toString("base64")}`
|
||||
Authorization: `Basic ${Buffer.from(`:${apiTokenCredentials.accessToken}`).toString("base64")}`
|
||||
}
|
||||
});
|
||||
organizations = orgsResponse.data.value || [];
|
||||
@@ -239,7 +239,7 @@ export const validateAzureDevOpsConnectionCredentials = async (config: TAzureDev
|
||||
}
|
||||
|
||||
return {
|
||||
apiKey: apiTokenCredentials.apiKey,
|
||||
accessToken: apiTokenCredentials.accessToken,
|
||||
userDisplayName: profileResponse.data.displayName,
|
||||
organizations: organizations.map((org) => ({
|
||||
accountId: org.accountId,
|
||||
|
||||
@@ -29,12 +29,12 @@ export const AzureDevOpsConnectionOAuthOutputCredentialsSchema = z.object({
|
||||
});
|
||||
|
||||
export const AzureDevOpsConnectionApiTokenInputCredentialsSchema = z.object({
|
||||
apiKey: z.string().trim().min(1, "API Key required"),
|
||||
accessToken: z.string().trim().min(1, "Access Token required"),
|
||||
orgName: z.string().trim().min(1, "Organization name required")
|
||||
});
|
||||
|
||||
export const AzureDevOpsConnectionApiTokenOutputCredentialsSchema = z.object({
|
||||
apiKey: z.string(),
|
||||
accessToken: z.string(),
|
||||
orgName: z.string()
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ export const ValidateAzureDevOpsConnectionCredentialsSchema = z.discriminatedUni
|
||||
}),
|
||||
z.object({
|
||||
method: z
|
||||
.literal(AzureDevOpsConnectionMethod.ApiToken)
|
||||
.literal(AzureDevOpsConnectionMethod.AccessToken)
|
||||
.describe(AppConnections.CREATE(AppConnection.AzureDevOps).method),
|
||||
credentials: AzureDevOpsConnectionApiTokenInputCredentialsSchema.describe(
|
||||
AppConnections.CREATE(AppConnection.AzureDevOps).credentials
|
||||
@@ -82,7 +82,7 @@ export const AzureDevOpsConnectionSchema = z.intersection(
|
||||
credentials: AzureDevOpsConnectionOAuthOutputCredentialsSchema
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(AzureDevOpsConnectionMethod.ApiToken),
|
||||
method: z.literal(AzureDevOpsConnectionMethod.AccessToken),
|
||||
credentials: AzureDevOpsConnectionApiTokenOutputCredentialsSchema
|
||||
})
|
||||
])
|
||||
@@ -97,7 +97,7 @@ export const SanitizedAzureDevOpsConnectionSchema = z.discriminatedUnion("method
|
||||
})
|
||||
}),
|
||||
BaseAzureDevOpsConnectionSchema.extend({
|
||||
method: z.literal(AzureDevOpsConnectionMethod.ApiToken),
|
||||
method: z.literal(AzureDevOpsConnectionMethod.AccessToken),
|
||||
credentials: AzureDevOpsConnectionApiTokenOutputCredentialsSchema.pick({
|
||||
orgName: true
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IntegrationUrls } from "@app/services/integration-auth/integration-list
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
|
||||
import { AzureDevOpsConnectionMethod } from "./azure-devops-enums";
|
||||
import { getAzureDevopsConnectionAccessToken } from "./azure-devops-fns";
|
||||
import { getAzureDevopsConnection } from "./azure-devops-fns";
|
||||
import { TAzureDevOpsConnection } from "./azure-devops-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
@@ -45,7 +45,7 @@ const getAuthHeaders = (appConnection: TAzureDevOpsConnection, accessToken: stri
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
};
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
// For API token, create Basic auth header
|
||||
const basicAuthToken = Buffer.from(`user:${accessToken}`).toString("base64");
|
||||
return {
|
||||
@@ -62,7 +62,7 @@ const listAzureDevOpsProjects = async (
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
): Promise<TAzureDevOpsProject[]> => {
|
||||
const accessToken = await getAzureDevopsConnectionAccessToken(appConnection.id, appConnectionDAL, kmsService);
|
||||
const accessToken = await getAzureDevopsConnection(appConnection.id, appConnectionDAL, kmsService);
|
||||
|
||||
// Both OAuth and API Token methods use organization name from credentials
|
||||
const credentials = appConnection.credentials as { orgName: string };
|
||||
|
||||
@@ -2,7 +2,7 @@ import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { AzureDevOpsConnectionMethod } from "@app/services/app-connection/azure-devops/azure-devops-enums";
|
||||
import { getAzureDevopsConnectionAccessToken } from "@app/services/app-connection/azure-devops/azure-devops-fns";
|
||||
import { getAzureDevopsConnection } from "@app/services/app-connection/azure-devops/azure-devops-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
@@ -44,7 +44,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
});
|
||||
}
|
||||
|
||||
const accessToken = await getAzureDevopsConnectionAccessToken(
|
||||
const accessToken = await getAzureDevopsConnection(
|
||||
secretSync.connectionId,
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
@@ -115,7 +115,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
|
||||
if (!groupId) {
|
||||
// Create new variable group - API endpoint is organization-level
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${orgName}/_apis/distributedtask/variablegroups?api-version=7.1`;
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(orgName)}/_apis/distributedtask/variablegroups?api-version=7.1`;
|
||||
|
||||
await request.post(
|
||||
url,
|
||||
@@ -143,7 +143,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${orgName}/_apis/distributedtask/variablegroups/${groupId}?api-version=7.1`;
|
||||
const url = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(orgName)}/_apis/distributedtask/variablegroups/${groupId}?api-version=7.1`;
|
||||
|
||||
await request.put(
|
||||
url,
|
||||
@@ -186,7 +186,7 @@ export const azureDevOpsSyncFactory = ({ kmsService, appConnectionDAL }: TAzureD
|
||||
|
||||
if (groupId) {
|
||||
// Delete the variable group entirely using the DELETE API
|
||||
const deleteUrl = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${orgName}/_apis/distributedtask/variablegroups/${groupId}?projectIds=${secretSync.destinationConfig.devopsProjectId}&api-version=7.1`;
|
||||
const deleteUrl = `${IntegrationUrls.AZURE_DEVOPS_API_URL}/${encodeURIComponent(orgName)}/_apis/distributedtask/variablegroups/${groupId}?projectIds=${secretSync.destinationConfig.devopsProjectId}&api-version=7.1`;
|
||||
|
||||
await request.delete(deleteUrl, {
|
||||
headers: {
|
||||
|
||||
@@ -3,10 +3,10 @@ title: "Azure DevOps Connection"
|
||||
description: "Learn how to configure an Azure DevOps Connection for Infisical."
|
||||
---
|
||||
|
||||
Infisical currently supports two methods for connecting to Azure DevOps, which are OAuth and Azure DevOps API Token.
|
||||
Infisical currently supports two methods for connecting to Azure DevOps, which are OAuth and Azure DevOps Personal Access Token.
|
||||
|
||||
<Accordion title="Self-Hosted Instance">
|
||||
Using the Azure DevOps OAuth connection on a self-hosted instance of Infisical requires configuring an application in Azure
|
||||
<Accordion title="Azure OAuth on a Self-Hosted Instance">
|
||||
Using the Azure DevOps <b>OAuth connection</b> on a self-hosted instance of Infisical requires configuring an application in Azure
|
||||
and registering your instance with it.
|
||||
|
||||
**Prerequisites:**
|
||||
@@ -67,7 +67,7 @@ Infisical currently supports two methods for connecting to Azure DevOps, which a
|
||||
|
||||
<Accordion title="Azure DevOps personal access token (PAT)">
|
||||
#### Create a new Azure DevOps personal access token (PAT)
|
||||
You'll need to create a new personal access token (PAT) in order to authenticate Infisical with Azure DevOps.
|
||||
When using the Azure DevOps <b>Access Token connection</b> you'll need to create a new personal access token (PAT) in order to authenticate Infisical with Azure DevOps.
|
||||
<Steps>
|
||||
<Step title="Navigate to Azure DevOps">
|
||||

|
||||
@@ -100,10 +100,10 @@ Infisical currently supports two methods for connecting to Azure DevOps, which a
|
||||
|
||||
<Step title="Create Connection">
|
||||
<Tabs>
|
||||
<Tab title="Infisical UI">
|
||||
<Tab title="OAuth">
|
||||
<Steps>
|
||||
<Step title="Fill in Connection Details">
|
||||
Fill in the **Tenant ID** field with the Directory (Tenant) ID you obtained in the previous step. Also fill in the organization name of the Azure DevOps organization you want to connect to.
|
||||
Fill in the **Tenant ID** field with the Directory (Tenant) ID you obtained in the previous [step](#azure-oauth-on-a-self-hosted-instance). Also fill in the organization name of the Azure DevOps organization you want to connect to.
|
||||

|
||||
|
||||
<Tip>
|
||||
@@ -117,10 +117,10 @@ Infisical currently supports two methods for connecting to Azure DevOps, which a
|
||||
</Step>
|
||||
</Steps>
|
||||
</Tab>
|
||||
<Tab title="API">
|
||||
<Tab title="Access Token">
|
||||
<Steps>
|
||||
<Step title="Fill in Connection Details">
|
||||
Fill in the **API Key** field with the API key you obtained in the previous step. And the organization name of the Azure DevOps organization you want to connect to.
|
||||
Fill in the **Access Token** field with the Access Token you obtained in the previous step. And the organization name of the Azure DevOps organization you want to connect to.
|
||||

|
||||
|
||||
<Tip>
|
||||
|
||||
@@ -113,28 +113,28 @@ description: "Learn how to configure a Azure DevOps Sync for Infisical."
|
||||
"lastRemoveMessage": null,
|
||||
"lastRemovedAt": null,
|
||||
"syncOptions": {
|
||||
"initialSyncBehavior": "overwrite-destination",
|
||||
"keySchema": "PIPELINE_${secretKey}",
|
||||
"disableSecretDeletion": true
|
||||
"initialSyncBehavior": "overwrite-destination",
|
||||
"keySchema": "PIPELINE_${secretKey}",
|
||||
"disableSecretDeletion": true
|
||||
},
|
||||
"connection": {
|
||||
"app": "azure-devops",
|
||||
"name": "Production DevOps Organization",
|
||||
"id": "8b92f5cc-3g77-5e80-6666-6ff57069385d"
|
||||
"app": "azure-devops",
|
||||
"name": "Production DevOps Organization",
|
||||
"id": "8b92f5cc-3g77-5e80-6666-6ff57069385d"
|
||||
},
|
||||
"environment": {
|
||||
"slug": "production",
|
||||
"name": "Production Environment",
|
||||
"id": "4f16j9gg-7k11-9i23-2222-2jj91403729h"
|
||||
"slug": "production",
|
||||
"name": "Production Environment",
|
||||
"id": "4f16j9gg-7k11-9i23-2222-2jj91403729h"
|
||||
},
|
||||
"folder": {
|
||||
"id": "5a71e8dd-2f66-4d70-7777-7cc46958274c",
|
||||
"path": "/devops/pipeline-secrets"
|
||||
"id": "5a71e8dd-2f66-4d70-7777-7cc46958274c",
|
||||
"path": "/devops/pipeline-secrets"
|
||||
},
|
||||
"destination": "azure-devops",
|
||||
"destinationConfig": {
|
||||
"devopsProjectId": "12345678-90ab-cdef-1234-567890abcdef",
|
||||
"devopsProjectName": "example-project"
|
||||
"devopsProjectId": "12345678-90ab-cdef-1234-567890abcdef",
|
||||
"devopsProjectName": "example-project"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
||||
case TerraformCloudConnectionMethod.ApiToken:
|
||||
case VercelConnectionMethod.ApiToken:
|
||||
case OnePassConnectionMethod.ApiToken:
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
return { name: "API Token", icon: faKey };
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||
@@ -113,6 +112,7 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
||||
return { name: "Username & Password", icon: faLock };
|
||||
case HCVaultConnectionMethod.AccessToken:
|
||||
case TeamCityConnectionMethod.AccessToken:
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
case WindmillConnectionMethod.AccessToken:
|
||||
return { name: "Access Token", icon: faKey };
|
||||
case Auth0ConnectionMethod.ClientCredentials:
|
||||
|
||||
@@ -3,7 +3,7 @@ import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-con
|
||||
|
||||
export enum AzureDevOpsConnectionMethod {
|
||||
OAuth = "oauth",
|
||||
ApiToken = "api-token"
|
||||
AccessToken = "access-token"
|
||||
}
|
||||
|
||||
export type TAzureDevOpsConnection = TRootAppConnection & {
|
||||
@@ -18,9 +18,9 @@ export type TAzureDevOpsConnection = TRootAppConnection & {
|
||||
};
|
||||
}
|
||||
| {
|
||||
method: AzureDevOpsConnectionMethod.ApiToken;
|
||||
method: AzureDevOpsConnectionMethod.AccessToken;
|
||||
credentials: {
|
||||
apiKey: string;
|
||||
accessToken: string;
|
||||
orgName: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ const oauthSchema = baseSchema.extend({
|
||||
});
|
||||
|
||||
const apiTokenSchema = baseSchema.extend({
|
||||
method: z.literal(AzureDevOpsConnectionMethod.ApiToken),
|
||||
method: z.literal(AzureDevOpsConnectionMethod.AccessToken),
|
||||
credentials: z.object({
|
||||
apiKey: z.string().trim().min(1, "API Key is required"),
|
||||
accessToken: z.string().trim().min(1, "Access Token is required"),
|
||||
orgName: z.string().trim().min(1, "Organization name is required")
|
||||
})
|
||||
});
|
||||
@@ -81,13 +81,13 @@ const getDefaultValues = (appConnection?: TAzureDevOpsConnection): Partial<FormD
|
||||
};
|
||||
}
|
||||
break;
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
if ("apiKey" in credentials && "orgName" in credentials) {
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
if ("accessToken" in credentials && "orgName" in credentials) {
|
||||
return {
|
||||
...base,
|
||||
method: AzureDevOpsConnectionMethod.ApiToken,
|
||||
method: AzureDevOpsConnectionMethod.AccessToken,
|
||||
credentials: {
|
||||
apiKey: credentials.apiKey,
|
||||
accessToken: credentials.accessToken,
|
||||
orgName: credentials.orgName
|
||||
}
|
||||
};
|
||||
@@ -139,7 +139,7 @@ export const AzureDevOpsConnectionForm = ({ appConnection, onSubmit }: Props) =>
|
||||
);
|
||||
break;
|
||||
|
||||
case AzureDevOpsConnectionMethod.ApiToken:
|
||||
case AzureDevOpsConnectionMethod.AccessToken:
|
||||
onSubmit(formData);
|
||||
break;
|
||||
|
||||
@@ -231,16 +231,16 @@ export const AzureDevOpsConnectionForm = ({ appConnection, onSubmit }: Props) =>
|
||||
)}
|
||||
|
||||
{/* API Token-specific fields */}
|
||||
{selectedMethod === AzureDevOpsConnectionMethod.ApiToken && (
|
||||
{selectedMethod === AzureDevOpsConnectionMethod.AccessToken && (
|
||||
<>
|
||||
<Controller
|
||||
name="credentials.apiKey"
|
||||
name="credentials.accessToken"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipText="Personal Access Token from Azure DevOps."
|
||||
isError={Boolean(error?.message)}
|
||||
label="API Key"
|
||||
label="Access Token"
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input
|
||||
|
||||
@@ -50,7 +50,7 @@ type OAuthCredentials = Extract<
|
||||
>["credentials"];
|
||||
type ApiTokenCredentials = Extract<
|
||||
TAzureDevOpsConnection,
|
||||
{ method: AzureDevOpsConnectionMethod.ApiToken }
|
||||
{ method: AzureDevOpsConnectionMethod.AccessToken }
|
||||
>["credentials"];
|
||||
|
||||
type AzureDevOpsFormData = BaseFormData &
|
||||
|
||||
Reference in New Issue
Block a user