diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index edf9b8333..12a39a389 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -249,7 +249,7 @@ export const getIntegrationAuthVercelBranches = async (req: Request, res: Respon } /** - * Return list of available Railway environments for Railway project with + * Return list of Railway environments for Railway project with * id [appId] * @param req * @param res @@ -315,6 +315,84 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R }); } +/** + * Return list of Railway services for Railway project with id + * [appId] + * @param req + * @param res + */ +export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => { + const { integrationAuthId } = req.params; + const appId = req.query.appId as string; + + interface RailwayService { + node: { + id: string; + name: string; + } + } + + interface Service { + name: string; + serviceId: string; + } + + let services: Service[] = []; + + const query = ` + query project($id: String!) { + project(id: $id) { + createdAt + deletedAt + id + description + expiredAt + isPublic + isTempProject + isUpdatable + name + prDeploys + teamId + updatedAt + upstreamUrl + services { + edges { + node { + id + name + } + } + } + } + } + `; + + if (appId && appId !== '') { + const variables = { + id: appId + } + + const { data: { data: { project: { services: { edges } } } } } = await request.post(INTEGRATION_RAILWAY_API_URL, { + query, + variables + }, { + headers: { + 'Authorization': `Bearer ${req.accessToken}`, + 'Content-Type': 'application/json', + }, + }); + + services = edges.map((e: RailwayService) => ({ + name: e.node.name, + serviceId: e.node.id + })); + } + + return res.status(200).send({ + services + }); +} + /** * Delete integration authorization with id [integrationAuthId] * @param req diff --git a/backend/src/controllers/v1/integrationController.ts b/backend/src/controllers/v1/integrationController.ts index b4c627925..6b633fc2c 100644 --- a/backend/src/controllers/v1/integrationController.ts +++ b/backend/src/controllers/v1/integrationController.ts @@ -25,6 +25,8 @@ export const createIntegration = async (req: Request, res: Response) => { sourceEnvironment, targetEnvironment, targetEnvironmentId, + targetService, + targetServiceId, owner, path, region @@ -41,12 +43,14 @@ export const createIntegration = async (req: Request, res: Response) => { appId, targetEnvironment, targetEnvironmentId, + targetService, + targetServiceId, owner, path, region, integration: req.integrationAuth.integration, integrationAuth: new Types.ObjectId(integrationAuthId) - }).save(); + }).save(); if (integration) { // trigger event - push secrets diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts index 192e2b414..6929726e9 100644 --- a/backend/src/integrations/apps.ts +++ b/backend/src/integrations/apps.ts @@ -355,10 +355,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { } `; - const variables = { - // userId: '', // Replace with the desired user ID or remove if not needed - // teamId: '', // Replace with the desired team ID or remove if not needed - }; + const variables = {}; const { data: { data: { projects: { edges }}} } = await request.post(INTEGRATION_RAILWAY_API_URL, { query, @@ -367,6 +364,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', + 'Accept-Encoding': 'application/json' }, }); diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index 99b0825c1..9a998d6f5 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -1179,6 +1179,7 @@ const syncSecretsRailway = async ({ accessToken: string; }) => { try { + const query = ` mutation UpsertVariables($input: VariableCollectionUpsertInput!) { variableCollectionUpsert(input: $input) @@ -1188,6 +1189,7 @@ const syncSecretsRailway = async ({ const input = { projectId: integration.appId, environmentId: integration.targetEnvironmentId, + ...(integration.targetServiceId ? { serviceId: integration.targetServiceId } : {}), replace: true, variables: secrets }; @@ -1201,6 +1203,7 @@ const syncSecretsRailway = async ({ headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', + 'Accept-Encoding': 'application/json' }, }); diff --git a/backend/src/models/integration.ts b/backend/src/models/integration.ts index 13a095609..22a8713e0 100644 --- a/backend/src/models/integration.ts +++ b/backend/src/models/integration.ts @@ -21,10 +21,12 @@ export interface IIntegration { environment: string; isActive: boolean; app: string; + appId: string; owner: string; targetEnvironment: string; targetEnvironmentId: string; - appId: string; + targetService: string; + targetServiceId: string; path: string; region: string; integration: @@ -78,6 +80,16 @@ const integrationSchema = new Schema( type: String, default: null }, + targetService: { + // railway-specific service + type: String, + default: null + }, + targetServiceId: { + // railway-specific service + type: String, + default: null + }, owner: { // github-specific repo owner-login type: String, diff --git a/backend/src/routes/v1/integration.ts b/backend/src/routes/v1/integration.ts index 8a862d85a..52930ce0c 100644 --- a/backend/src/routes/v1/integration.ts +++ b/backend/src/routes/v1/integration.ts @@ -31,6 +31,8 @@ router.post( // new: add new integration for integration auth body('sourceEnvironment').trim(), body('targetEnvironment').trim(), body('targetEnvironmentId').trim(), + body('targetService').trim(), + body('targetServiceId').trim(), body('owner').trim(), body('path').trim(), body('region').trim(), diff --git a/backend/src/routes/v1/integrationAuth.ts b/backend/src/routes/v1/integrationAuth.ts index e8c9e7ad5..fa5863029 100644 --- a/backend/src/routes/v1/integrationAuth.ts +++ b/backend/src/routes/v1/integrationAuth.ts @@ -125,6 +125,20 @@ router.get( integrationAuthController.getIntegrationAuthRailwayEnvironments ); +router.get( + '/:integrationAuthId/railway/services', + requireAuth({ + acceptedAuthModes: ['jwt'] + }), + requireIntegrationAuthorizationAuth({ + acceptedRoles: [ADMIN, MEMBER] + }), + param('integrationAuthId').exists().isString(), + query('appId').exists().isString(), + validateRequest, + integrationAuthController.getIntegrationAuthRailwayServices +); + router.delete( '/:integrationAuthId', requireAuth({ diff --git a/docs/images/integrations-railway-authorization.png b/docs/images/integrations-railway-authorization.png new file mode 100644 index 000000000..8608ea6d1 Binary files /dev/null and b/docs/images/integrations-railway-authorization.png differ diff --git a/docs/images/integrations-railway-create.png b/docs/images/integrations-railway-create.png new file mode 100644 index 000000000..548c06c5e Binary files /dev/null and b/docs/images/integrations-railway-create.png differ diff --git a/docs/images/integrations-railway-dashboard.png b/docs/images/integrations-railway-dashboard.png new file mode 100644 index 000000000..0d6b66754 Binary files /dev/null and b/docs/images/integrations-railway-dashboard.png differ diff --git a/docs/images/integrations-railway-token.png b/docs/images/integrations-railway-token.png new file mode 100644 index 000000000..d414289e9 Binary files /dev/null and b/docs/images/integrations-railway-token.png differ diff --git a/docs/images/integrations-railway.png b/docs/images/integrations-railway.png new file mode 100644 index 000000000..0bde672d2 Binary files /dev/null and b/docs/images/integrations-railway.png differ diff --git a/docs/images/integrations.png b/docs/images/integrations.png index 44a8269dd..234a842b3 100644 Binary files a/docs/images/integrations.png and b/docs/images/integrations.png differ diff --git a/docs/integrations/cloud/railway.mdx b/docs/integrations/cloud/railway.mdx new file mode 100644 index 000000000..5ebf8fc1d --- /dev/null +++ b/docs/integrations/cloud/railway.mdx @@ -0,0 +1,54 @@ +--- +title: "Railway" +description: "How to automatically sync secrets from Infisical into your Railway projects and services" +--- + +Prerequisites: + +- Set up and add envars to [Infisical Cloud](https://app.infisical.com) + + +## Navigate to your project's integrations tab + +![integrations](../../images/integrations.png) + +## Enter your Railway API Token + +Obtain a Railway API Token in your Railway [Account Settings > Tokens](https://railway.app/account/tokens). + +![integrations railway dashboard](../../images/integrations-railway-dashboard.png) +![integrations railway token](../../images/integrations-railway-token.png) + + + If this is your first time creating a Railway API token, then you'll be prompted to join + Railway's Private Boarding Beta program on the Railway Account Settings > Tokens page. + + Note that Railway project tokens will not work for this integration since they don't work with + Railway's Public API. + + +Press on the Railway tile and input your Railway API Key to grant Infisical access to your Railway account. + +![integrations railway authorization](../../images/integrations-railway-authorization.png) + + + If this is your project's first cloud integration, then you'll have to grant + Infisical access to your project's environment variables. Although this step + breaks E2EE, it's necessary for Infisical to sync the environment variables to + the cloud platform. + + +## Start integration + +Select which Infisical environment secrets you want to sync to which Railway project and environment (and optionally service). Lastly, press create integration to start syncing secrets to Railway. + +![integrations create railway](../../images/integrations-railway-create.png) + + + Infisical integrates with both Railway's [shared variables](https://blog.railway.app/p/shared-variables-release) at the project environment level as well as service variables at the service level. + + To sync secrets to a specific service in a project, you can select a service from the Railway Service dropdown; otherwise, leaving it empty will sync secrets to the shared variables of that project. + + +![integrations railway](../../images/integrations-railway.png) + diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx index 15e5698c1..e6e2622cd 100644 --- a/docs/integrations/overview.mdx +++ b/docs/integrations/overview.mdx @@ -18,6 +18,7 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi | [Vercel](/integrations/cloud/vercel) | Cloud | Available | | [Netlify](/integrations/cloud/netlify) | Cloud | Available | | [Render](/integrations/cloud/render) | Cloud | Available | +| [Railway](/integrations/cloud/railway) | Cloud | Available | | [Fly.io](/integrations/cloud/flyio) | Cloud | Available | | [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available | | [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available | diff --git a/docs/mint.json b/docs/mint.json index 48dc1805c..165ce6862 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -149,6 +149,7 @@ "integrations/cloud/vercel", "integrations/cloud/netlify", "integrations/cloud/render", + "integrations/cloud/railway", "integrations/cloud/flyio", "integrations/cloud/azure-key-vault", "integrations/cicd/githubactions", diff --git a/frontend/public/data/frequentConstants.ts b/frontend/public/data/frequentConstants.ts index 8a69465ac..f5e5e5016 100644 --- a/frontend/public/data/frequentConstants.ts +++ b/frontend/public/data/frequentConstants.ts @@ -12,6 +12,7 @@ const integrationSlugNameMapping: Mapping = { 'github': 'GitHub', 'gitlab': 'GitLab', 'render': 'Render', + 'railway': 'Railway', 'flyio': 'Fly.io', 'circleci': 'CircleCI', 'travisci': 'TravisCI' diff --git a/frontend/src/components/integrations/Integration.tsx b/frontend/src/components/integrations/Integration.tsx index e01c31e2b..2467566c4 100644 --- a/frontend/src/components/integrations/Integration.tsx +++ b/frontend/src/components/integrations/Integration.tsx @@ -45,6 +45,7 @@ type Props = { handleDeleteIntegration: (args: { integration: Integration }) => void; }; +// TODO: refactor const IntegrationTile = ({ integration, integrations, @@ -55,7 +56,6 @@ const IntegrationTile = ({ handleDeleteIntegration }: Props) => { - // set initial environment. This find will only execute when component is mounting const [integrationEnvironment, setIntegrationEnvironment] = useState( environments.find(({ slug }) => slug === integration?.environment) || { name: '', @@ -176,6 +176,21 @@ const IntegrationTile = ({ /> ); + case 'railway': + return ( +
+
ENVIRONMENT
+ +
+ ); default: return
; } diff --git a/frontend/src/hooks/api/integrationAuth/index.tsx b/frontend/src/hooks/api/integrationAuth/index.tsx index cabf26f9a..367534305 100644 --- a/frontend/src/hooks/api/integrationAuth/index.tsx +++ b/frontend/src/hooks/api/integrationAuth/index.tsx @@ -1,7 +1,7 @@ export { useGetIntegrationAuthApps, useGetIntegrationAuthById, + useGetIntegrationAuthRailwayEnvironments, + useGetIntegrationAuthRailwayServices, useGetIntegrationAuthTeams, - useGetIntegrationAuthVercelBranches, - useGetRailwayEnvironments -} from './queries'; \ No newline at end of file + useGetIntegrationAuthVercelBranches} from './queries'; \ No newline at end of file diff --git a/frontend/src/hooks/api/integrationAuth/queries.tsx b/frontend/src/hooks/api/integrationAuth/queries.tsx index 50a0618f6..02a8b6c51 100644 --- a/frontend/src/hooks/api/integrationAuth/queries.tsx +++ b/frontend/src/hooks/api/integrationAuth/queries.tsx @@ -6,6 +6,7 @@ import { App, Environment, IntegrationAuth, + Service, Team } from './types'; @@ -27,6 +28,13 @@ const integrationAuthKeys = { integrationAuthId: string; appId: string; }) => [{ integrationAuthId, appId }, 'integrationAuthRailwayEnvironments'] as const, + getIntegrationAuthRailwayServices: ({ + integrationAuthId, + appId + }: { + integrationAuthId: string; + appId: string; + }) => [{ integrationAuthId, appId }, 'integrationAuthRailwayServices'] as const } const fetchIntegrationAuthById = async (integrationAuthId: string) => { @@ -86,6 +94,22 @@ const fetchIntegrationAuthRailwayEnvironments = async ({ return environments; } +const fetchIntegrationAuthRailwayServices = async ({ + integrationAuthId, + appId +}: { + integrationAuthId: string; + appId: string; +}) => { + const { data: { services } } = await apiRequest.get<{ services: Service[] }>(`/api/v1/integration-auth/${integrationAuthId}/railway/services`, { + params: { + appId + } + }); + + return services; +} + export const useGetIntegrationAuthById = (integrationAuthId: string) => { return useQuery({ queryKey: integrationAuthKeys.getIntegrationAuthById(integrationAuthId), @@ -139,7 +163,7 @@ export const useGetIntegrationAuthVercelBranches = ({ }); } -export const useGetRailwayEnvironments = ({ +export const useGetIntegrationAuthRailwayEnvironments = ({ integrationAuthId, appId }: { @@ -158,3 +182,23 @@ export const useGetRailwayEnvironments = ({ enabled: true }); } + +export const useGetIntegrationAuthRailwayServices = ({ + integrationAuthId, + appId +}: { + integrationAuthId: string; + appId: string; +}) => { + return useQuery({ + queryKey: integrationAuthKeys.getIntegrationAuthRailwayServices({ + integrationAuthId, + appId, + }), + queryFn: () => fetchIntegrationAuthRailwayServices({ + integrationAuthId, + appId, + }), + enabled: true + }); +} \ No newline at end of file diff --git a/frontend/src/hooks/api/integrationAuth/types.ts b/frontend/src/hooks/api/integrationAuth/types.ts index d13489757..7ea8799a5 100644 --- a/frontend/src/hooks/api/integrationAuth/types.ts +++ b/frontend/src/hooks/api/integrationAuth/types.ts @@ -20,4 +20,9 @@ export type Team = { export type Environment = { name: string; environmentId: string; +} + +export type Service = { + name: string; + serviceId: string; } \ No newline at end of file diff --git a/frontend/src/pages/api/integrations/createIntegration.ts b/frontend/src/pages/api/integrations/createIntegration.ts index 393c40afd..e3e7c010a 100644 --- a/frontend/src/pages/api/integrations/createIntegration.ts +++ b/frontend/src/pages/api/integrations/createIntegration.ts @@ -8,6 +8,8 @@ interface Props { sourceEnvironment: string; targetEnvironment: string | null; targetEnvironmentId: string | null; + targetService: string | null; + targetServiceId: string | null; owner: string | null; path: string | null; region: string | null; @@ -26,6 +28,8 @@ const createIntegration = ({ sourceEnvironment, targetEnvironment, targetEnvironmentId, + targetService, + targetServiceId, owner, path, region @@ -43,6 +47,8 @@ const createIntegration = ({ sourceEnvironment, targetEnvironment, targetEnvironmentId, + targetService, + targetServiceId, owner, path, region diff --git a/frontend/src/pages/integrations/aws-parameter-store/create.tsx b/frontend/src/pages/integrations/aws-parameter-store/create.tsx index 01112eeff..200628921 100644 --- a/frontend/src/pages/integrations/aws-parameter-store/create.tsx +++ b/frontend/src/pages/integrations/aws-parameter-store/create.tsx @@ -99,6 +99,8 @@ export default function AWSParameterStoreCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path, region: selectedAWSRegion diff --git a/frontend/src/pages/integrations/aws-secret-manager/create.tsx b/frontend/src/pages/integrations/aws-secret-manager/create.tsx index c464564d6..7f253e119 100644 --- a/frontend/src/pages/integrations/aws-secret-manager/create.tsx +++ b/frontend/src/pages/integrations/aws-secret-manager/create.tsx @@ -98,6 +98,8 @@ export default function AWSSecretManagerCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: selectedAWSRegion diff --git a/frontend/src/pages/integrations/azure-key-vault/create.tsx b/frontend/src/pages/integrations/azure-key-vault/create.tsx index 779ec1798..8f506b6d4 100644 --- a/frontend/src/pages/integrations/azure-key-vault/create.tsx +++ b/frontend/src/pages/integrations/azure-key-vault/create.tsx @@ -63,6 +63,8 @@ export default function AzureKeyVaultCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: null diff --git a/frontend/src/pages/integrations/circleci/create.tsx b/frontend/src/pages/integrations/circleci/create.tsx index cf0a8db69..05a53e609 100644 --- a/frontend/src/pages/integrations/circleci/create.tsx +++ b/frontend/src/pages/integrations/circleci/create.tsx @@ -61,6 +61,8 @@ export default function CircleCICreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: null, diff --git a/frontend/src/pages/integrations/flyio/create.tsx b/frontend/src/pages/integrations/flyio/create.tsx index 4783c64a9..13dbc05d1 100644 --- a/frontend/src/pages/integrations/flyio/create.tsx +++ b/frontend/src/pages/integrations/flyio/create.tsx @@ -62,6 +62,8 @@ export default function FlyioCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: null diff --git a/frontend/src/pages/integrations/github/create.tsx b/frontend/src/pages/integrations/github/create.tsx index 7a9825138..eee9f8a71 100644 --- a/frontend/src/pages/integrations/github/create.tsx +++ b/frontend/src/pages/integrations/github/create.tsx @@ -65,6 +65,8 @@ export default function GitHubCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: targetApp.owner, path: null, region: null diff --git a/frontend/src/pages/integrations/gitlab/create.tsx b/frontend/src/pages/integrations/gitlab/create.tsx index 6d2e3aada..c8a25e78b 100644 --- a/frontend/src/pages/integrations/gitlab/create.tsx +++ b/frontend/src/pages/integrations/gitlab/create.tsx @@ -90,6 +90,8 @@ export default function GitLabCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: null diff --git a/frontend/src/pages/integrations/heroku/create.tsx b/frontend/src/pages/integrations/heroku/create.tsx index 591b56646..beb52c4d4 100644 --- a/frontend/src/pages/integrations/heroku/create.tsx +++ b/frontend/src/pages/integrations/heroku/create.tsx @@ -61,6 +61,8 @@ export default function HerokuCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: null, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: null diff --git a/frontend/src/pages/integrations/netlify/create.tsx b/frontend/src/pages/integrations/netlify/create.tsx index 88ef46f21..cf3a23038 100644 --- a/frontend/src/pages/integrations/netlify/create.tsx +++ b/frontend/src/pages/integrations/netlify/create.tsx @@ -70,6 +70,8 @@ export default function NetlifyCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment, targetEnvironmentId: null, + targetService: null, + targetServiceId: null, owner: null, path: null, region: null diff --git a/frontend/src/pages/integrations/railway/create.tsx b/frontend/src/pages/integrations/railway/create.tsx index 2e09ea262..95eb90c6f 100644 --- a/frontend/src/pages/integrations/railway/create.tsx +++ b/frontend/src/pages/integrations/railway/create.tsx @@ -14,7 +14,8 @@ import { import { useGetIntegrationAuthApps, useGetIntegrationAuthById, - useGetRailwayEnvironments + useGetIntegrationAuthRailwayEnvironments, + useGetIntegrationAuthRailwayServices } from '../../../hooks/api/integrationAuth'; import { useGetWorkspaceById } from '../../../hooks/api/workspace'; import createIntegration from "../../api/integrations/createIntegration"; @@ -24,6 +25,8 @@ export default function RailwayCreateIntegrationPage() { const [targetAppId, setTargetAppId] = useState(''); const [targetEnvironmentId, setTargetEnvironmentId] = useState(''); + const [targetServiceId, setTargetServiceId] = useState(''); + const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -33,8 +36,11 @@ export default function RailwayCreateIntegrationPage() { const { data: integrationAuthApps } = useGetIntegrationAuthApps({ integrationAuthId: integrationAuthId as string ?? '' }); - - const { data: targetEnvironments } = useGetRailwayEnvironments({ + const { data: targetEnvironments } = useGetIntegrationAuthRailwayEnvironments({ + integrationAuthId: integrationAuthId as string ?? '', + appId: targetAppId + }); + const { data: targetServices } = useGetIntegrationAuthRailwayServices({ integrationAuthId: integrationAuthId as string ?? '', appId: targetAppId }); @@ -64,6 +70,12 @@ export default function RailwayCreateIntegrationPage() { } } }, [targetEnvironments]); + + const filteredServices = targetServices + ?.concat({ + name: '', + serviceId: '' + }); const handleButtonClick = async () => { try { @@ -75,6 +87,8 @@ export default function RailwayCreateIntegrationPage() { const targetEnvironment = targetEnvironments?.find((environment) => environment.environmentId === targetEnvironmentId); if (!targetApp || !targetApp.appId || !targetEnvironment) return; + + const targetService = targetServices?.find((service) => service.serviceId === targetServiceId); await createIntegration({ integrationAuthId: integrationAuth?._id, @@ -84,6 +98,8 @@ export default function RailwayCreateIntegrationPage() { sourceEnvironment: selectedSourceEnvironment, targetEnvironment: targetEnvironment.name, targetEnvironmentId: targetEnvironment.environmentId, + targetService: targetService ? targetService.name : null, + targetServiceId: targetService ? targetService.serviceId : null, owner: null, path: null, region: null @@ -99,7 +115,7 @@ export default function RailwayCreateIntegrationPage() { } } - return workspace && selectedSourceEnvironment && integrationAuthApps && targetEnvironments ? ( + return workspace && selectedSourceEnvironment && integrationAuthApps && targetEnvironments && filteredServices ? (
Railway Integration @@ -158,6 +174,19 @@ export default function RailwayCreateIntegrationPage() { )} + + +