mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-02 16:25:52 +00:00
Merge pull request #909 from Infisical/team-city-branch-config
Add support for build-configuration environment variable sync for TeamCity integration
This commit is contained in:
@@ -547,6 +547,57 @@ export const getIntegrationAuthNorthflankSecretGroups = async (req: Request, res
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of build configs for TeamCity project with id [appId]
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getIntegrationAuthTeamCityBuildConfigs = async (req: Request, res: Response) => {
|
||||||
|
const appId = req.query.appId as string;
|
||||||
|
|
||||||
|
interface TeamCityBuildConfig {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
projectName: string;
|
||||||
|
projectId: string;
|
||||||
|
href: string;
|
||||||
|
webUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetTeamCityBuildConfigsRes {
|
||||||
|
count: number;
|
||||||
|
href: string;
|
||||||
|
buildType: TeamCityBuildConfig[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (appId && appId !== "") {
|
||||||
|
const { data: { buildType } } = (
|
||||||
|
await standardRequest.get<GetTeamCityBuildConfigsRes>(`${req.integrationAuth.url}/app/rest/buildTypes`, {
|
||||||
|
params: {
|
||||||
|
locator: `project:${appId}`
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${req.accessToken}`,
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
buildConfigs: buildType.map((buildConfig) => ({
|
||||||
|
name: buildConfig.name,
|
||||||
|
buildConfigId: buildConfig.id
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
buildConfigs: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete integration authorization with id [integrationAuthId]
|
* Delete integration authorization with id [integrationAuthId]
|
||||||
* @param req
|
* @param req
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
version: 1
|
version: 1
|
||||||
},
|
},
|
||||||
$unset: {
|
$unset: {
|
||||||
'metadata.source': true as true
|
"metadata.source": true as const
|
||||||
},
|
},
|
||||||
...u,
|
...u,
|
||||||
_id: new Types.ObjectId(u._id)
|
_id: new Types.ObjectId(u._id)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
INTEGRATION_VERCEL
|
INTEGRATION_VERCEL
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { UnauthorizedRequestError } from "../utils/errors";
|
import { UnauthorizedRequestError } from "../utils/errors";
|
||||||
import { syncSecretsToActiveIntegrationsQueue } from "../queues/integrations/syncSecretsToThirdPartyServices"
|
|
||||||
|
|
||||||
interface Update {
|
interface Update {
|
||||||
workspace: string;
|
workspace: string;
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ const getAppsTeamCity = async ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
).data.project.slice(1);
|
).data.project.slice(1);
|
||||||
|
|
||||||
const apps = res.map((a: any) => {
|
const apps = res.map((a: any) => {
|
||||||
return {
|
return {
|
||||||
name: a.name,
|
name: a.name,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import {
|
|||||||
INTEGRATION_AZURE_TOKEN_URL,
|
INTEGRATION_AZURE_TOKEN_URL,
|
||||||
INTEGRATION_BITBUCKET,
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_BITBUCKET_TOKEN_URL,
|
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||||
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
|
INTEGRATION_GCP_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
@@ -13,21 +15,19 @@ import {
|
|||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL
|
||||||
INTEGRATION_GCP_SECRET_MANAGER,
|
|
||||||
INTEGRATION_GCP_TOKEN_URL
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import {
|
import {
|
||||||
getClientIdGCPSecretManager,
|
|
||||||
getClientSecretGCPSecretManager,
|
|
||||||
getClientIdAzure,
|
getClientIdAzure,
|
||||||
getClientIdBitBucket,
|
getClientIdBitBucket,
|
||||||
|
getClientIdGCPSecretManager,
|
||||||
getClientIdGitHub,
|
getClientIdGitHub,
|
||||||
getClientIdGitLab,
|
getClientIdGitLab,
|
||||||
getClientIdNetlify,
|
getClientIdNetlify,
|
||||||
getClientIdVercel,
|
getClientIdVercel,
|
||||||
getClientSecretAzure,
|
getClientSecretAzure,
|
||||||
getClientSecretBitBucket,
|
getClientSecretBitBucket,
|
||||||
|
getClientSecretGCPSecretManager,
|
||||||
getClientSecretGitHub,
|
getClientSecretGitHub,
|
||||||
getClientSecretGitLab,
|
getClientSecretGitLab,
|
||||||
getClientSecretHeroku,
|
getClientSecretHeroku,
|
||||||
|
|||||||
@@ -2185,7 +2185,7 @@ const syncSecretsTerraformCloud = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to TeamCity project
|
* Sync/push [secrets] to TeamCity project (and optionally build config)
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {IIntegration} obj.integration - integration details
|
* @param {IIntegration} obj.integration - integration details
|
||||||
* @param {Object} obj.secrets - secrets to push to integration
|
* @param {Object} obj.secrets - secrets to push to integration
|
||||||
@@ -2207,57 +2207,124 @@ const syncSecretsTeamCity = async ({
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get secrets from Teamcity
|
interface TeamCityBuildConfigParameter {
|
||||||
const res = (
|
name: string;
|
||||||
await standardRequest.get(
|
value: string;
|
||||||
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
|
inherited: boolean;
|
||||||
|
}
|
||||||
|
interface GetTeamCityBuildConfigParametersRes {
|
||||||
|
href: string;
|
||||||
|
count: number;
|
||||||
|
property: TeamCityBuildConfigParameter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integration.targetEnvironment && integration.targetEnvironmentId) {
|
||||||
|
// case: sync to specific build-config in TeamCity project
|
||||||
|
const res = (await standardRequest.get<GetTeamCityBuildConfigParametersRes>(
|
||||||
|
`${integrationAuth.url}/app/rest/buildTypes/${integration.targetEnvironmentId}/parameters`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Accept: "application/json"
|
Accept: "application/json",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
).data.property.reduce((obj: any, secret: TeamCitySecret) => {
|
.data
|
||||||
const secretName = secret.name.replace(/^env\./, "");
|
.property
|
||||||
return {
|
.filter((parameter) => !parameter.inherited)
|
||||||
...obj,
|
.reduce((obj: any, secret: TeamCitySecret) => {
|
||||||
[secretName]: secret.value
|
const secretName = secret.name.replace(/^env\./, "");
|
||||||
};
|
return {
|
||||||
}, {});
|
...obj,
|
||||||
|
[secretName]: secret.value
|
||||||
for await (const key of Object.keys(secrets)) {
|
};
|
||||||
if (!(key in res) || (key in res && secrets[key] !== res[key])) {
|
}, {});
|
||||||
// case: secret does not exist in TeamCity or secret value has changed
|
|
||||||
// -> create/update secret
|
for await (const key of Object.keys(secrets)) {
|
||||||
await standardRequest.post(
|
if (!(key in res) || (key in res && secrets[key].value !== res[key])) {
|
||||||
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
|
// case: secret does not exist in TeamCity or secret value has changed
|
||||||
|
// -> create/update secret
|
||||||
|
await standardRequest.post(`${integrationAuth.url}/app/rest/buildTypes/${integration.targetEnvironmentId}/parameters`,
|
||||||
{
|
{
|
||||||
name: `env.${key}`,
|
name:`env.${key}`,
|
||||||
value: secrets[key].value
|
value: secrets[key].value
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Accept: "application/json"
|
Accept: "application/json",
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for await (const key of Object.keys(res)) {
|
for await (const key of Object.keys(res)) {
|
||||||
if (!(key in secrets)) {
|
if (!(key in secrets)) {
|
||||||
// delete secret
|
// delete secret
|
||||||
await standardRequest.delete(
|
await standardRequest.delete(
|
||||||
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`,
|
`${integrationAuth.url}/app/rest/buildTypes/${integration.targetEnvironmentId}/parameters/env.${key}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// case: sync to TeamCity project
|
||||||
|
const res = (
|
||||||
|
await standardRequest.get(
|
||||||
|
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Accept: "application/json"
|
Accept: "application/json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
).data.property.reduce((obj: any, secret: TeamCitySecret) => {
|
||||||
|
const secretName = secret.name.replace(/^env\./, "");
|
||||||
|
return {
|
||||||
|
...obj,
|
||||||
|
[secretName]: secret.value
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
for await (const key of Object.keys(secrets)) {
|
||||||
|
if (!(key in res) || (key in res && secrets[key] !== res[key])) {
|
||||||
|
// case: secret does not exist in TeamCity or secret value has changed
|
||||||
|
// -> create/update secret
|
||||||
|
await standardRequest.post(
|
||||||
|
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`,
|
||||||
|
{
|
||||||
|
name: `env.${key}`,
|
||||||
|
value: secrets[key].value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const key of Object.keys(res)) {
|
||||||
|
if (!(key in secrets)) {
|
||||||
|
// delete secret
|
||||||
|
await standardRequest.delete(
|
||||||
|
`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
INTEGRATION_CODEFRESH,
|
INTEGRATION_CODEFRESH,
|
||||||
INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM,
|
INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_HASHICORP_VAULT,
|
INTEGRATION_HASHICORP_VAULT,
|
||||||
@@ -24,8 +25,7 @@ import {
|
|||||||
INTEGRATION_TERRAFORM_CLOUD,
|
INTEGRATION_TERRAFORM_CLOUD,
|
||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_WINDMILL,
|
INTEGRATION_WINDMILL
|
||||||
INTEGRATION_GCP_SECRET_MANAGER
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { Schema, Types, model } from "mongoose";
|
import { Schema, Types, model } from "mongoose";
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
INTEGRATION_CODEFRESH,
|
INTEGRATION_CODEFRESH,
|
||||||
INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM,
|
INTEGRATION_DIGITAL_OCEAN_APP_PLATFORM,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_HASHICORP_VAULT,
|
INTEGRATION_HASHICORP_VAULT,
|
||||||
@@ -26,8 +27,7 @@ import {
|
|||||||
INTEGRATION_TERRAFORM_CLOUD,
|
INTEGRATION_TERRAFORM_CLOUD,
|
||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_WINDMILL,
|
INTEGRATION_WINDMILL
|
||||||
INTEGRATION_GCP_SECRET_MANAGER
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { Document, Schema, Types, model } from "mongoose";
|
import { Document, Schema, Types, model } from "mongoose";
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import Queue, { Job } from "bull";
|
import Queue, { Job } from "bull";
|
||||||
import { ProbotOctokit } from "probot"
|
import { ProbotOctokit } from "probot"
|
||||||
import { Commit, Committer, Repository } from "@octokit/webhooks-types";
|
import { Commit } from "@octokit/webhooks-types";
|
||||||
import TelemetryService from "../../services/TelemetryService";
|
import TelemetryService from "../../services/TelemetryService";
|
||||||
import { sendMail } from "../../helpers";
|
import { sendMail } from "../../helpers";
|
||||||
import GitRisks from "../../ee/models/gitRisks";
|
import GitRisks from "../../ee/models/gitRisks";
|
||||||
import { MembershipOrg, User } from "../../models";
|
import { MembershipOrg, User } from "../../models";
|
||||||
import { OWNER, ADMIN } from "../../variables";
|
import { ADMIN, OWNER } from "../../variables";
|
||||||
import { convertKeysToLowercase, scanContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper";
|
import { convertKeysToLowercase, scanContentAndGetFindings } from "../../ee/services/GithubSecretScanning/helper";
|
||||||
import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config";
|
import { getSecretScanningGitAppId, getSecretScanningPrivateKey } from "../../config";
|
||||||
import { SecretMatch } from "../../ee/services/GithubSecretScanning/types";
|
import { SecretMatch } from "../../ee/services/GithubSecretScanning/types";
|
||||||
|
|
||||||
export const githubPushEventSecretScan = new Queue('github-push-event-secret-scanning', 'redis://redis:6379');
|
export const githubPushEventSecretScan = new Queue("github-push-event-secret-scanning", "redis://redis:6379");
|
||||||
|
|
||||||
type TScanPushEventQueueDetails = {
|
type TScanPushEventQueueDetails = {
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
|
|||||||
@@ -89,4 +89,4 @@ router.post(
|
|||||||
integrationController.manualSync
|
integrationController.manualSync
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -168,6 +168,20 @@ router.get(
|
|||||||
integrationAuthController.getIntegrationAuthNorthflankSecretGroups
|
integrationAuthController.getIntegrationAuthNorthflankSecretGroups
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:integrationAuthId/teamcity/build-configs",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AuthMode.JWT],
|
||||||
|
}),
|
||||||
|
requireIntegrationAuthorizationAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
}),
|
||||||
|
param("integrationAuthId").exists().isString(),
|
||||||
|
query("appId").exists().isString(),
|
||||||
|
validateRequest,
|
||||||
|
integrationAuthController.getIntegrationAuthTeamCityBuildConfigs
|
||||||
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:integrationAuthId",
|
"/:integrationAuthId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import express, { Request, Response } from "express";
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
import { requireAuth, requireWorkspaceAuth, validateRequest } from "../../middleware";
|
||||||
import { body, param, query } from "express-validator";
|
import { body, param, query } from "express-validator";
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 936 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 942 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -11,21 +11,18 @@ Prerequisites:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Enter your TeamCity API Token and Server URL
|
## Enter your TeamCity Access Token and Server URL
|
||||||
|
|
||||||
Obtain a TeamCity API Token in Profile > Access Tokens
|
Obtain a TeamCity Access Token in Profile > Access Tokens
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
Obtain your TeamCity Server URL in Administration > Cloud Server Settings > Server URL
|
<Note>
|
||||||
|
For this integration to work, the TeamCity Access Token must either have the
|
||||||

|
**Same as current user** account-wide permission enabled or, if **Limit per project**
|
||||||

|
is selected, then it must at minimum have the **View build configuration settings** and **Edit project** permissions enabled.
|
||||||
|
</Note>
|
||||||
Press on the TeamCity tile and input your TeamCity API Token and Server URL to grant Infisical access to your TeamCity account.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<Info>
|
<Info>
|
||||||
If this is your project's first cloud integration, then you'll have to grant
|
If this is your project's first cloud integration, then you'll have to grant
|
||||||
@@ -34,9 +31,20 @@ Press on the TeamCity tile and input your TeamCity API Token and Server URL to g
|
|||||||
the cloud platform.
|
the cloud platform.
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
|
Press on the TeamCity tile and input your TeamCity Access Token and Server URL to grant Infisical access to your TeamCity account.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Start integration
|
## Start integration
|
||||||
|
|
||||||
Select which Infisical environment secrets, you want to sync to which TeamCity project and press create integration to start syncing secrets to TeamCity.
|
Select which Infisical environment secrets you want to sync to which TeamCity project (and optionally build configuration) and press create integration to start syncing secrets to TeamCity.
|
||||||
|
|
||||||

|

|
||||||

|
|
||||||
|
<Note>
|
||||||
|
Infisical integrates with both TeamCity's project-level and build configuration-level environment variables.
|
||||||
|
|
||||||
|
To sync secrets to a specific build configuration in a TeamCity project, you can select a build configuration from the **TeamCity Build Config** dropdown; otherwise, leaving it empty will sync secrets to TeamCity at the project-level.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export {
|
|||||||
useGetIntegrationAuthNorthflankSecretGroups,
|
useGetIntegrationAuthNorthflankSecretGroups,
|
||||||
useGetIntegrationAuthRailwayEnvironments,
|
useGetIntegrationAuthRailwayEnvironments,
|
||||||
useGetIntegrationAuthRailwayServices,
|
useGetIntegrationAuthRailwayServices,
|
||||||
|
useGetIntegrationAuthTeamCityBuildConfigs,
|
||||||
useGetIntegrationAuthTeams,
|
useGetIntegrationAuthTeams,
|
||||||
useGetIntegrationAuthVercelBranches,
|
useGetIntegrationAuthVercelBranches,
|
||||||
useSaveIntegrationAccessToken
|
useSaveIntegrationAccessToken
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {
|
|||||||
IntegrationAuth,
|
IntegrationAuth,
|
||||||
NorthflankSecretGroup,
|
NorthflankSecretGroup,
|
||||||
Service,
|
Service,
|
||||||
Team
|
Team,
|
||||||
} from "./types";
|
TeamCityBuildConfig} from "./types";
|
||||||
|
|
||||||
const integrationAuthKeys = {
|
const integrationAuthKeys = {
|
||||||
getIntegrationAuthById: (integrationAuthId: string) =>
|
getIntegrationAuthById: (integrationAuthId: string) =>
|
||||||
@@ -49,7 +49,14 @@ const integrationAuthKeys = {
|
|||||||
}: {
|
}: {
|
||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
}) => [{ integrationAuthId, appId }, "integrationAuthNorthflankSecretGroups"] as const,
|
}) => [{ integrationAuthId, appId }, "integrationAuthNorthflankSecretGroups"] as const,
|
||||||
|
getIntegrationAuthTeamCityBuildConfigs: ({
|
||||||
|
integrationAuthId,
|
||||||
|
appId
|
||||||
|
}: {
|
||||||
|
integrationAuthId: string;
|
||||||
|
appId: string;
|
||||||
|
}) => [{ integrationAuthId, appId }, "integrationAuthTeamCityBranchConfigs"] as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchIntegrationAuthById = async (integrationAuthId: string) => {
|
const fetchIntegrationAuthById = async (integrationAuthId: string) => {
|
||||||
@@ -183,6 +190,27 @@ const fetchIntegrationAuthNorthflankSecretGroups = async ({
|
|||||||
return secretGroups;
|
return secretGroups;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchIntegrationAuthTeamCityBuildConfigs = async ({
|
||||||
|
integrationAuthId,
|
||||||
|
appId
|
||||||
|
}: {
|
||||||
|
integrationAuthId: string;
|
||||||
|
appId: string;
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
data: { buildConfigs }
|
||||||
|
} = await apiRequest.get<{ buildConfigs: TeamCityBuildConfig[] }>(
|
||||||
|
`/api/v1/integration-auth/${integrationAuthId}/teamcity/build-configs`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
appId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return buildConfigs;
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetIntegrationAuthById = (integrationAuthId: string) => {
|
export const useGetIntegrationAuthById = (integrationAuthId: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthById(integrationAuthId),
|
queryKey: integrationAuthKeys.getIntegrationAuthById(integrationAuthId),
|
||||||
@@ -312,6 +340,26 @@ export const useGetIntegrationAuthNorthflankSecretGroups = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetIntegrationAuthTeamCityBuildConfigs = ({
|
||||||
|
integrationAuthId,
|
||||||
|
appId
|
||||||
|
}: {
|
||||||
|
integrationAuthId: string;
|
||||||
|
appId: string;
|
||||||
|
}) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: integrationAuthKeys.getIntegrationAuthTeamCityBuildConfigs({
|
||||||
|
integrationAuthId,
|
||||||
|
appId
|
||||||
|
}),
|
||||||
|
queryFn: () => fetchIntegrationAuthTeamCityBuildConfigs({
|
||||||
|
integrationAuthId,
|
||||||
|
appId
|
||||||
|
}),
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useAuthorizeIntegration = () => {
|
export const useAuthorizeIntegration = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@@ -40,4 +40,9 @@ export type BitBucketWorkspace = {
|
|||||||
export type NorthflankSecretGroup = {
|
export type NorthflankSecretGroup = {
|
||||||
name: string;
|
name: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TeamCityBuildConfig = {
|
||||||
|
name: string;
|
||||||
|
buildConfigId: string;
|
||||||
}
|
}
|
||||||
@@ -17,13 +17,20 @@ import {
|
|||||||
} from "../../../components/v2";
|
} from "../../../components/v2";
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthById
|
useGetIntegrationAuthById,
|
||||||
|
useGetIntegrationAuthTeamCityBuildConfigs
|
||||||
} from "../../../hooks/api/integrationAuth";
|
} from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
|
||||||
export default function TeamCityCreateIntegrationPage() {
|
export default function TeamCityCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
|
|
||||||
|
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
||||||
|
const [targetAppId, setTargetAppId] = useState("");
|
||||||
|
const [targetBuildConfigId, setTargetBuildConfigId] = useState<string>("");
|
||||||
|
const [secretPath, setSecretPath] = useState("/");
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
|
|
||||||
@@ -32,11 +39,11 @@ export default function TeamCityCreateIntegrationPage() {
|
|||||||
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
|
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
|
||||||
integrationAuthId: (integrationAuthId as string) ?? ""
|
integrationAuthId: (integrationAuthId as string) ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
const { data: targetBuildConfigs } = useGetIntegrationAuthTeamCityBuildConfigs({
|
||||||
const [targetApp, setTargetApp] = useState("");
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
const [secretPath, setSecretPath] = useState("/");
|
appId: targetAppId
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
@@ -47,29 +54,31 @@ export default function TeamCityCreateIntegrationPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (integrationAuthApps) {
|
if (integrationAuthApps) {
|
||||||
if (integrationAuthApps.length > 0) {
|
if (integrationAuthApps.length > 0) {
|
||||||
setTargetApp(integrationAuthApps[0].name);
|
setTargetAppId(integrationAuthApps[0].appId as string);
|
||||||
} else {
|
} else {
|
||||||
setTargetApp("none");
|
setTargetAppId("none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [integrationAuthApps]);
|
}, [integrationAuthApps]);
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
const handleButtonClick = async () => {
|
||||||
try {
|
try {
|
||||||
if (!integrationAuth?._id) return;
|
if (!integrationAuth?._id) return;
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const targetEnvironment = targetBuildConfigs?.find(
|
||||||
|
(buildConfig) => buildConfig.buildConfigId === targetBuildConfigId
|
||||||
|
);
|
||||||
|
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
integrationAuthId: integrationAuth?._id,
|
integrationAuthId: integrationAuth?._id,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
app: targetApp,
|
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name ?? null,
|
||||||
appId:
|
appId: targetAppId,
|
||||||
integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp)
|
|
||||||
?.appId ?? null,
|
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
targetEnvironment: null,
|
targetEnvironment: targetEnvironment ? targetEnvironment.name : null,
|
||||||
targetEnvironmentId: null,
|
targetEnvironmentId: targetEnvironment ? targetEnvironment.buildConfigId : null,
|
||||||
targetService: null,
|
targetService: null,
|
||||||
targetServiceId: null,
|
targetServiceId: null,
|
||||||
owner: null,
|
owner: null,
|
||||||
@@ -86,12 +95,17 @@ export default function TeamCityCreateIntegrationPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const filteredBuildConfigs = targetBuildConfigs?.concat({
|
||||||
|
name: "",
|
||||||
|
buildConfigId: ""
|
||||||
|
});
|
||||||
|
|
||||||
return integrationAuth &&
|
return integrationAuth &&
|
||||||
workspace &&
|
workspace &&
|
||||||
selectedSourceEnvironment &&
|
selectedSourceEnvironment &&
|
||||||
integrationAuthApps &&
|
integrationAuthApps &&
|
||||||
targetApp ? (
|
filteredBuildConfigs &&
|
||||||
|
targetAppId ? (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Card className="max-w-md rounded-md p-8">
|
<Card className="max-w-md rounded-md p-8">
|
||||||
<CardTitle className="text-center">TeamCity Integration</CardTitle>
|
<CardTitle className="text-center">TeamCity Integration</CardTitle>
|
||||||
@@ -120,16 +134,16 @@ export default function TeamCityCreateIntegrationPage() {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl label="TeamCity Project" className="mt-4">
|
<FormControl label="TeamCity Project" className="mt-4">
|
||||||
<Select
|
<Select
|
||||||
value={targetApp}
|
value={targetAppId}
|
||||||
onValueChange={(val) => setTargetApp(val)}
|
onValueChange={(val) => setTargetAppId(val)}
|
||||||
className="w-full border border-mineshaft-500"
|
className="w-full border border-mineshaft-500"
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
isDisabled={integrationAuthApps.length === 0}
|
||||||
>
|
>
|
||||||
{integrationAuthApps.length > 0 ? (
|
{integrationAuthApps.length > 0 ? (
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
integrationAuthApps.map((integrationAuthApp) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
value={integrationAuthApp.name}
|
value={integrationAuthApp.appId as string}
|
||||||
key={`target-app-${integrationAuthApp.name}`}
|
key={`target-app-${integrationAuthApp.appId as string}`}
|
||||||
>
|
>
|
||||||
{integrationAuthApp.name}
|
{integrationAuthApp.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -141,6 +155,22 @@ export default function TeamCityCreateIntegrationPage() {
|
|||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl label="Team City Build Config (Optional)" className="mt-4">
|
||||||
|
<Select
|
||||||
|
value={targetBuildConfigId}
|
||||||
|
onValueChange={(val) => setTargetBuildConfigId(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
>
|
||||||
|
{filteredBuildConfigs.map((buildConfig: any) => (
|
||||||
|
<SelectItem
|
||||||
|
value={buildConfig.buildConfigId}
|
||||||
|
key={`target-build-config-${buildConfig.buildConfigId}`}
|
||||||
|
>
|
||||||
|
{buildConfig.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
|
|||||||
+1
@@ -106,6 +106,7 @@ export const IntegrationsSection = ({
|
|||||||
integration.integration === "netlify" ||
|
integration.integration === "netlify" ||
|
||||||
integration.integration === "railway" ||
|
integration.integration === "railway" ||
|
||||||
integration.integration === "gitlab" ||
|
integration.integration === "gitlab" ||
|
||||||
|
integration.integration === "teamcity" ||
|
||||||
integration.integration === "bitbucket") && (
|
integration.integration === "bitbucket") && (
|
||||||
<div className="ml-4 flex flex-col">
|
<div className="ml-4 flex flex-col">
|
||||||
<FormLabel label="Target Environment" />
|
<FormLabel label="Target Environment" />
|
||||||
|
|||||||
Reference in New Issue
Block a user