You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by ti...@apache.org on 2025/05/29 18:01:42 UTC
(incubator-kie-tools) branch main updated: kie-issues#11: GitLab support on KIE Sandbox (#3132)
This is an automated email from the ASF dual-hosted git repository.
tiagobento pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 0351d2e094e kie-issues#11: GitLab support on KIE Sandbox (#3132)
0351d2e094e is described below
commit 0351d2e094e3d1df60051d2acfdb713f850a88e6
Author: Adarsh vk <33...@users.noreply.github.com>
AuthorDate: Thu May 29 23:31:39 2025 +0530
kie-issues#11: GitLab support on KIE Sandbox (#3132)
---
packages/cors-proxy-api/src/index.ts | 1 +
packages/cors-proxy/src/proxy/ExpressCorsProxy.ts | 5 +
packages/i18n-common-dictionary/src/names.ts | 1 +
packages/online-editor/build/defaultEnvJson.ts | 3 +-
.../online-editor/src/accounts/AccountsContext.tsx | 21 ++
.../online-editor/src/accounts/AccountsIcon.tsx | 29 +++
.../src/accounts/git/ConnectToGitSection.tsx | 82 +++++++-
.../src/authProviders/AuthProvidersApi.ts | 13 +-
.../src/authProviders/AuthProvidersGallery.tsx | 10 +
.../src/authSessions/AuthSessionsContext.tsx | 21 +-
packages/online-editor/src/bitbucket/Hooks.tsx | 6 +
.../GitIntegration/CreateGistOrSnippetModal.tsx | 55 ++++-
.../GitIntegration/CreateGitRepositoryModal.tsx | 50 ++++-
.../GitIntegration/GitIntegrationAlerts.tsx | 20 +-
.../GitIntegrationContextProvider.tsx | 73 ++++++-
.../GitIntegration/LoadOrganizationsSelect.tsx | 103 +++++++---
.../src/editor/Toolbar/NewFileDropdownMenu.tsx | 13 +-
.../src/editor/Toolbar/Share/GitDropdownGroup.tsx | 8 +-
.../src/editor/Toolbar/SyncDropdownMenu.tsx | 6 +
.../src/editor/Toolbar/Workspace/Hooks.tsx | 2 +
packages/online-editor/src/github/Hooks.tsx | 8 +
packages/online-editor/src/gitlab/GitlabClient.ts | 222 +++++++++++++++++++++
.../online-editor/src/gitlab/ParseGitLabUrl.ts | 181 +++++++++++++++++
.../online-editor/src/gitlab/useGitlabClient.ts | 81 ++++++++
packages/online-editor/src/i18n/locales/de.ts | 59 ++++++
packages/online-editor/src/i18n/locales/en.ts | 59 ++++++
.../importFromUrl/AdvancedImportModalContent.tsx | 18 +-
.../importFromUrl/ImportFromUrlHomePageCard.tsx | 40 +++-
.../importFromUrl/ImportSingleFileFromUrlForm.tsx | 2 +-
.../src/importFromUrl/ImportableUrlHooks.tsx | 129 ++++++++++--
.../src/importFromUrl/NewWorkspaceFromUrlPage.tsx | 38 +++-
.../src/importFromUrl/fetchSingleFileContent.ts | 38 +++-
packages/online-editor/src/navigation/Routes.ts | 2 +
.../src/workspace/components/WorkspaceLabel.tsx | 13 ++
.../src/context/WorkspacesContext.tsx | 28 ++-
.../src/context/WorkspacesContextProvider.tsx | 46 ++++-
.../workspaces-git-fs/src/services/GitService.tsx | 31 ++-
.../src/services/WorkspaceDescriptorService.tsx | 49 ++++-
.../src/services/WorkspaceService.tsx | 2 +
.../src/worker/WorkspacesWorkerApiImpl.ts | 53 ++++-
.../src/worker/api/WorkspaceDescriptor.tsx | 1 +
.../src/worker/api/WorkspaceOrigin.tsx | 37 +++-
.../src/worker/api/WorkspacesWorkerGitApi.ts | 28 ++-
43 files changed, 1558 insertions(+), 129 deletions(-)
diff --git a/packages/cors-proxy-api/src/index.ts b/packages/cors-proxy-api/src/index.ts
index ebcfb9288f5..b623f62e559 100644
--- a/packages/cors-proxy-api/src/index.ts
+++ b/packages/cors-proxy-api/src/index.ts
@@ -20,6 +20,7 @@
export enum CorsProxyHeaderKeys {
TARGET_URL = "target-url",
INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION = "insecurely-disable-tls-certificate-validation",
+ DISABLE_ENCODING = "disable-encoding",
}
export type CorsConfig = {
diff --git a/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts b/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
index 55627402fe8..b8a78f313c5 100644
--- a/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
+++ b/packages/cors-proxy/src/proxy/ExpressCorsProxy.ts
@@ -29,6 +29,7 @@ const BANNED_PROXY_HEADERS = [
"host",
CorsProxyHeaderKeys.TARGET_URL,
CorsProxyHeaderKeys.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION,
+ CorsProxyHeaderKeys.DISABLE_ENCODING,
];
export class ExpressCorsProxy implements CorsProxy<Request, Response> {
@@ -73,6 +74,10 @@ export class ExpressCorsProxy implements CorsProxy<Request, Response> {
if (req.headers[CorsProxyHeaderKeys.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION] === "true") {
outHeaders["accept-encoding"] = "identity";
}
+ // Force uncompressed response if encoding is disabled via header
+ if (req.headers[CorsProxyHeaderKeys.DISABLE_ENCODING] === "true") {
+ outHeaders["accept-encoding"] = "identity";
+ }
this.logger.log("Proxying to: ", info.proxyUrl.toString());
this.logger.debug("Proxy Method: ", req.method);
diff --git a/packages/i18n-common-dictionary/src/names.ts b/packages/i18n-common-dictionary/src/names.ts
index 43b4e121a5f..0f95031d44b 100644
--- a/packages/i18n-common-dictionary/src/names.ts
+++ b/packages/i18n-common-dictionary/src/names.ts
@@ -36,6 +36,7 @@ export const names = {
dropbox: "Dropbox",
git: "Git",
github: "GitHub",
+ gitlab: "GitLab",
kogito: "Kogito",
linux: "Linux",
macos: "macOS",
diff --git a/packages/online-editor/build/defaultEnvJson.ts b/packages/online-editor/build/defaultEnvJson.ts
index 46bd281196c..5608b7d9c3d 100644
--- a/packages/online-editor/build/defaultEnvJson.ts
+++ b/packages/online-editor/build/defaultEnvJson.ts
@@ -74,9 +74,10 @@ export const defaultEnvJson: EnvJson = {
supportedGitRemoteDomains: ["gitlab.com"],
type: AuthProviderType.gitlab,
name: "GitLab",
- enabled: false,
+ enabled: true,
iconPath: routes.static.images.gitlabLogo.path({}),
group: AuthProviderGroup.GIT,
+ disableEncoding: true,
},
{
id: "bitbucket_dot_org",
diff --git a/packages/online-editor/src/accounts/AccountsContext.tsx b/packages/online-editor/src/accounts/AccountsContext.tsx
index e025095c261..57ba3b132b5 100644
--- a/packages/online-editor/src/accounts/AccountsContext.tsx
+++ b/packages/online-editor/src/accounts/AccountsContext.tsx
@@ -36,6 +36,7 @@ export enum AccountsSection {
CONNECT_TO_AN_ACCOUNT = "CONNECT_TO_AN_ACCOUNT",
CONNECT_TO_GITHUB = "CONNECT_TO_GITHUB",
CONNECT_TO_BITBUCKET = "CONNECT_TO_BITBUCKET",
+ CONNECT_TO_GITLAB = "CONNECT_TO_GITLAB",
CONNECT_TO_OPENSHIFT = "CONNECT_TO_OPENSHIFT",
CONNECT_TO_KUBERNETES = "CONNECT_TO_KUBERNETES",
}
@@ -67,6 +68,12 @@ export type AccountsState =
backActionKind: AccountsDispatchActionKind.SELECT_AUTH_PROVIDER | AccountsDispatchActionKind.GO_HOME;
onNewAuthSession?: (newAuthSession: AuthSession) => any;
}
+ | {
+ section: AccountsSection.CONNECT_TO_GITLAB;
+ selectedAuthProvider: GitAuthProvider;
+ backActionKind: AccountsDispatchActionKind.SELECT_AUTH_PROVIDER | AccountsDispatchActionKind.GO_HOME;
+ onNewAuthSession?: (newAuthSession: AuthSession) => any;
+ }
| {
section: AccountsSection.CONNECT_TO_OPENSHIFT;
selectedAuthProvider: OpenShiftAuthProvider;
@@ -88,6 +95,7 @@ export enum AccountsDispatchActionKind {
SELECT_AUTH_PROVIDER = "SELECT_AUTH_PROVIDER",
SETUP_GITHUB_AUTH = "SETUP_GITHUB_AUTH",
SETUP_BITBUCKET_AUTH = "SETUP_BITBUCKET_AUTH",
+ SETUP_GITLAB_AUTH = "SETUP_GITLAB_AUTH",
SETUP_OPENSHIFT_AUTH = "SETUP_OPENSHIFT_AUTH",
SETUP_KUBERNETES_AUTH = "SETUP_KUBERNETES_AUTH",
}
@@ -116,6 +124,12 @@ export type AccountsDispatchAction =
backActionKind: AccountsDispatchActionKind.SELECT_AUTH_PROVIDER | AccountsDispatchActionKind.GO_HOME;
onNewAuthSession?: (newAuthSession: AuthSession) => any;
}
+ | {
+ kind: AccountsDispatchActionKind.SETUP_GITLAB_AUTH;
+ selectedAuthProvider: GitAuthProvider;
+ backActionKind: AccountsDispatchActionKind.SELECT_AUTH_PROVIDER | AccountsDispatchActionKind.GO_HOME;
+ onNewAuthSession?: (newAuthSession: AuthSession) => any;
+ }
| {
kind: AccountsDispatchActionKind.SETUP_OPENSHIFT_AUTH;
selectedAuthProvider: OpenShiftAuthProvider;
@@ -174,6 +188,13 @@ export function AccountsContextProvider(props: React.PropsWithChildren<{}>) {
onNewAuthSession: action.onNewAuthSession,
backActionKind: action.backActionKind,
};
+ case AccountsDispatchActionKind.SETUP_GITLAB_AUTH:
+ return {
+ section: AccountsSection.CONNECT_TO_GITLAB,
+ selectedAuthProvider: action.selectedAuthProvider,
+ onNewAuthSession: action.onNewAuthSession,
+ backActionKind: action.backActionKind,
+ };
case AccountsDispatchActionKind.SETUP_OPENSHIFT_AUTH:
return {
section: AccountsSection.CONNECT_TO_OPENSHIFT,
diff --git a/packages/online-editor/src/accounts/AccountsIcon.tsx b/packages/online-editor/src/accounts/AccountsIcon.tsx
index dfdd7eccfc9..579d2fc2bc8 100644
--- a/packages/online-editor/src/accounts/AccountsIcon.tsx
+++ b/packages/online-editor/src/accounts/AccountsIcon.tsx
@@ -168,6 +168,32 @@ export function AccountsIcon() {
</Flex>
</>
)}
+ {accounts.section === AccountsSection.CONNECT_TO_GITLAB && (
+ <>
+ <Flex justifyContent={{ default: "justifyContentSpaceBetween" }}>
+ <FlexItem>
+ <Flex
+ justifyContent={{ default: "justifyContentFlexStart" }}
+ spaceItems={{ default: "spaceItemsSm" }}
+ >
+ <TextContent>
+ <Text component={TextVariants.h2}>
+ {`Connect to`}
+
+ {accounts.selectedAuthProvider.name}
+ </Text>
+ </TextContent>
+ <TextContent>
+ <Text component={TextVariants.small}>
+ <i>{accounts.selectedAuthProvider.domain}</i>
+ </Text>
+ </TextContent>
+ </Flex>
+ </FlexItem>
+ <AuthProviderIcon authProvider={accounts.selectedAuthProvider} size={"sm"} />
+ </Flex>
+ </>
+ )}
{accounts.section === AccountsSection.CONNECT_TO_OPENSHIFT && (
<>
<TextContent>
@@ -234,6 +260,9 @@ export function AccountsIcon() {
{accounts.section === AccountsSection.CONNECT_TO_BITBUCKET && (
<ConnectToGitSection authProvider={accounts.selectedAuthProvider} />
)}
+ {accounts.section === AccountsSection.CONNECT_TO_GITLAB && (
+ <ConnectToGitSection authProvider={accounts.selectedAuthProvider} />
+ )}
{accounts.section === AccountsSection.CONNECT_TO_OPENSHIFT && <ConnectToOpenShiftSection />}
{accounts.section === AccountsSection.CONNECT_TO_KUBERNETES && <ConnectToKubernetesSection />}
</>
diff --git a/packages/online-editor/src/accounts/git/ConnectToGitSection.tsx b/packages/online-editor/src/accounts/git/ConnectToGitSection.tsx
index 57bbdfe100c..d7749b6dfec 100644
--- a/packages/online-editor/src/accounts/git/ConnectToGitSection.tsx
+++ b/packages/online-editor/src/accounts/git/ConnectToGitSection.tsx
@@ -45,14 +45,18 @@ import {
import { switchExpression } from "@kie-tools-core/switch-expression-ts";
import { AuthOptionsType, getBitbucketClient } from "../../bitbucket/Hooks";
import { useEnv } from "../../env/hooks/EnvContext";
+import { getGitlabClient } from "../../gitlab/useGitlabClient";
import { HelperText, HelperTextItem } from "@patternfly/react-core/dist/js/components/HelperText";
export const GITHUB_OAUTH_TOKEN_SIZE = 40;
export const BITBUCKET_OAUTH_TOKEN_SIZE = 40;
+export const GITLAB_OAUTH_TOKEN_SIZE = 40;
export const GITHUB_TOKENS_HOW_TO_URL =
"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token";
export const BITBUCKET_TOKENS_HOW_TO_URL = "https://support.atlassian.com/bitbucket-cloud/docs/create-an-app-password/";
+export const GITLAB_TOKENS_HOW_TO_URL =
+ "https://docs.gitlab.com/user/profile/personal_access_tokens/#create-a-personal-access-token";
export const GITHUB_OAUTH_SCOPES = ["repo", "gist"];
export const BITBUCKET_OAUTH_SCOPES = [
@@ -64,6 +68,8 @@ export const BITBUCKET_OAUTH_SCOPES = [
"snippet:write",
];
+export const GITLAB_OAUTH_SCOPES = ["api", "read_user", "read_repository", "write_repository"];
+
export type AuthenticatedUserResponse = {
headers: {
scopes: string[];
@@ -119,14 +125,25 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
tokenInput,
props.authProvider.domain,
env.KIE_SANDBOX_CORS_PROXY_URL,
- props.authProvider.insecurelyDisableTlsCertificateValidation
+ props.authProvider.insecurelyDisableTlsCertificateValidation,
+ props.authProvider.disableEncoding
),
github: () =>
fetchAuthenticatedGitHubUser(
tokenInput,
props.authProvider.domain,
env.KIE_SANDBOX_CORS_PROXY_URL,
- props.authProvider.insecurelyDisableTlsCertificateValidation
+ props.authProvider.insecurelyDisableTlsCertificateValidation,
+ props.authProvider.disableEncoding
+ ),
+ gitlab: () =>
+ fetchAuthenticatedGitlabUser(
+ env.KIE_SANDBOX_APP_NAME,
+ tokenInput,
+ props.authProvider.domain,
+ env.KIE_SANDBOX_CORS_PROXY_URL,
+ props.authProvider.insecurelyDisableTlsCertificateValidation,
+ props.authProvider.disableEncoding
),
})
)
@@ -137,6 +154,7 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
const requiredScopes = switchExpression(authProviderType, {
bitbucket: BITBUCKET_OAUTH_SCOPES,
github: GITHUB_OAUTH_SCOPES,
+ gitlab: GITLAB_OAUTH_SCOPES,
});
if (!response.headers.scopes.some((it) => requiredScopes.includes(it))) {
setNewAuthSession({
@@ -176,6 +194,7 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
props.authProvider.type,
props.authProvider.domain,
props.authProvider.insecurelyDisableTlsCertificateValidation,
+ props.authProvider.disableEncoding,
props.authProvider.id,
tokenInput,
success,
@@ -230,7 +249,8 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
const successPrimaryAction = useMemo(() => {
if (
(accounts.section !== AccountsSection.CONNECT_TO_GITHUB &&
- accounts.section !== AccountsSection.CONNECT_TO_BITBUCKET) ||
+ accounts.section !== AccountsSection.CONNECT_TO_BITBUCKET &&
+ accounts.section !== AccountsSection.CONNECT_TO_GITLAB) ||
!newAuthSession.data
) {
return;
@@ -331,6 +351,7 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
href={switchExpression(props.authProvider.type, {
bitbucket: generateNewBitbucketTokenUrl(props.authProvider.domain),
github: generateNewGitHubTokenUrl(props.authProvider.domain),
+ gitlab: generateNewGitlabTokenUrl(props.authProvider.domain),
})}
target={"_blank"}
rel={"noopener"}
@@ -351,6 +372,7 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
href={switchExpression(props.authProvider.type, {
bitbucket: BITBUCKET_TOKENS_HOW_TO_URL,
github: GITHUB_TOKENS_HOW_TO_URL,
+ gitlab: GITLAB_TOKENS_HOW_TO_URL,
})}
target={"_blank"}
rel={"noopener"}
@@ -370,6 +392,7 @@ export function ConnectToGitSection(props: { authProvider: GitAuthProvider }) {
return switchExpression(props.authProvider.type, {
bitbucket: BITBUCKET_OAUTH_TOKEN_SIZE,
github: GITHUB_OAUTH_TOKEN_SIZE,
+ gitlab: GITLAB_OAUTH_TOKEN_SIZE,
default: -1,
});
}
@@ -391,6 +414,8 @@ export const generateNewBitbucketTokenUrl = (domain: string) => {
export const generateNewGitHubTokenUrl = (domain: string) => {
return `https://${domain}/settings/tokens`;
};
+export const generateNewGitlabTokenUrl = (domain: string): string =>
+ `https://${domain}/-/user_settings/personal_access_tokens`;
function delay(ms: number) {
return new Promise((res) => setTimeout(res, ms));
@@ -404,9 +429,16 @@ export const fetchAuthenticatedGitHubUser = async (
githubToken: string,
domain?: string,
proxyUrl?: string,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) => {
- const octokit = getOctokitClient({ githubToken, domain, proxyUrl, insecurelyDisableTlsCertificateValidation });
+ const octokit = getOctokitClient({
+ githubToken,
+ domain,
+ proxyUrl,
+ insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
+ });
const response = await octokit.users.getAuthenticated();
return {
data: {
@@ -425,7 +457,8 @@ export const fetchAuthenticatedBitbucketUser = async (
bitbucketToken: string,
domain?: string,
proxyUrl?: string,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) => {
const bitbucketClient = getBitbucketClient({
appName,
@@ -437,6 +470,7 @@ export const fetchAuthenticatedBitbucketUser = async (
},
proxyUrl,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
const response = await bitbucketClient.getAuthedUser();
@@ -456,3 +490,39 @@ export const fetchAuthenticatedBitbucketUser = async (
headers: { scopes: response.headers.get("x-oauth-scopes")?.split(", ") ?? [] },
};
};
+
+export const fetchAuthenticatedGitlabUser = async (
+ appName: string,
+ gitlabToken: string,
+ domain?: string,
+ proxyUrl?: string,
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
+) => {
+ const gitlabClient = getGitlabClient({
+ appName,
+ domain,
+ token: gitlabToken,
+ proxyUrl,
+ insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
+ });
+
+ const response = await gitlabClient.getAuthedUser();
+
+ if (!response.ok) {
+ const message = await response.text();
+ throw new Error(`While authenticating : ${response.status} ${response.statusText} ${message}`);
+ }
+ const jsonResponse = await response.json();
+
+ return {
+ data: {
+ name: jsonResponse?.name,
+ login: jsonResponse?.username,
+ email: jsonResponse?.email,
+ },
+ // GitLab does not expose PAT scopes in the response
+ headers: { scopes: [] },
+ };
+};
diff --git a/packages/online-editor/src/authProviders/AuthProvidersApi.ts b/packages/online-editor/src/authProviders/AuthProvidersApi.ts
index b661b639bbf..072991507f5 100644
--- a/packages/online-editor/src/authProviders/AuthProvidersApi.ts
+++ b/packages/online-editor/src/authProviders/AuthProvidersApi.ts
@@ -31,7 +31,11 @@ export enum AuthProviderType {
}
const gitAuthProviderKeys = [AuthProviderType.bitbucket, AuthProviderType.github, AuthProviderType.gitlab] as const;
-const supportedGitAuthProvidersKeys = [AuthProviderType.bitbucket, AuthProviderType.github] as const;
+const supportedGitAuthProvidersKeys = [
+ AuthProviderType.bitbucket,
+ AuthProviderType.github,
+ AuthProviderType.gitlab,
+] as const;
export type SupportedGitAuthProviders = (typeof supportedGitAuthProvidersKeys)[number];
export const isSupportedGitAuthProviderType = (
maybeSupportedTypeKey: AuthProviderType | undefined
@@ -78,11 +82,16 @@ export type GitAuthProvider = {
supportedGitRemoteDomains: string[];
group: AuthProviderGroup.GIT;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
};
export type AuthProvider = OpenShiftAuthProvider | KubernetesAuthProvider | GitAuthProvider;
-const gistEnabledTypeConfigKeys = [AuthProviderType.github, AuthProviderType.bitbucket] as const;
+const gistEnabledTypeConfigKeys = [
+ AuthProviderType.github,
+ AuthProviderType.bitbucket,
+ AuthProviderType.gitlab,
+] as const;
export type GistEnabledAuthProviderType = (typeof gistEnabledTypeConfigKeys)[number];
export const isGistEnabledAuthProviderType = (
diff --git a/packages/online-editor/src/authProviders/AuthProvidersGallery.tsx b/packages/online-editor/src/authProviders/AuthProvidersGallery.tsx
index 1e03177d6fb..623b08f2d06 100644
--- a/packages/online-editor/src/authProviders/AuthProvidersGallery.tsx
+++ b/packages/online-editor/src/authProviders/AuthProvidersGallery.tsx
@@ -102,6 +102,16 @@ export function AuthProvidersGallery(props: {
? accounts.onNewAuthSession
: undefined,
});
+ } else if (authProvider.enabled && authProvider.type === "gitlab") {
+ accountsDispatch({
+ kind: AccountsDispatchActionKind.SETUP_GITLAB_AUTH,
+ selectedAuthProvider: authProvider,
+ backActionKind: props.backActionKind,
+ onNewAuthSession:
+ accounts.section === AccountsSection.CONNECT_TO_AN_ACCOUNT
+ ? accounts.onNewAuthSession
+ : undefined,
+ });
} else if (authProvider.enabled && authProvider.type === "openshift") {
accountsDispatch({
kind: AccountsDispatchActionKind.SETUP_OPENSHIFT_AUTH,
diff --git a/packages/online-editor/src/authSessions/AuthSessionsContext.tsx b/packages/online-editor/src/authSessions/AuthSessionsContext.tsx
index e3e3758d55b..c6c137f688d 100644
--- a/packages/online-editor/src/authSessions/AuthSessionsContext.tsx
+++ b/packages/online-editor/src/authSessions/AuthSessionsContext.tsx
@@ -23,7 +23,11 @@ import { useCancelableEffect } from "@kie-tools-core/react-hooks/dist/useCancela
import { decoder, encoder } from "@kie-tools-core/workspaces-git-fs/dist/encoderdecoder/EncoderDecoder";
import { LfsStorageFile } from "@kie-tools-core/workspaces-git-fs/dist/lfs/LfsStorageService";
import { useAuthProviders } from "../authProviders/AuthProvidersContext";
-import { fetchAuthenticatedBitbucketUser, fetchAuthenticatedGitHubUser } from "../accounts/git/ConnectToGitSection";
+import {
+ fetchAuthenticatedBitbucketUser,
+ fetchAuthenticatedGitHubUser,
+ fetchAuthenticatedGitlabUser,
+} from "../accounts/git/ConnectToGitSection";
import {
AuthSession,
AuthSessionStatus,
@@ -166,14 +170,25 @@ export function AuthSessionsContextProvider(props: PropsWithChildren<{}>) {
authSession.token,
authProvider?.domain,
env.KIE_SANDBOX_CORS_PROXY_URL,
- authProvider?.insecurelyDisableTlsCertificateValidation
+ authProvider?.insecurelyDisableTlsCertificateValidation,
+ authProvider?.disableEncoding
),
github: () =>
fetchAuthenticatedGitHubUser(
authSession.token,
authProvider?.domain,
env.KIE_SANDBOX_CORS_PROXY_URL,
- authProvider?.insecurelyDisableTlsCertificateValidation
+ authProvider?.insecurelyDisableTlsCertificateValidation,
+ authProvider?.disableEncoding
+ ),
+ gitlab: () =>
+ fetchAuthenticatedGitlabUser(
+ env.KIE_SANDBOX_APP_NAME,
+ authSession.token,
+ authProvider?.domain,
+ env.KIE_SANDBOX_CORS_PROXY_URL,
+ authProvider?.insecurelyDisableTlsCertificateValidation,
+ authProvider?.disableEncoding
),
});
await fetchUser();
diff --git a/packages/online-editor/src/bitbucket/Hooks.tsx b/packages/online-editor/src/bitbucket/Hooks.tsx
index fc252de5ea6..f812d20fcdf 100644
--- a/packages/online-editor/src/bitbucket/Hooks.tsx
+++ b/packages/online-editor/src/bitbucket/Hooks.tsx
@@ -227,6 +227,7 @@ export function getBitbucketClient(args: {
domain?: string;
proxyUrl?: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) {
return new BitbucketClient({
appName: args.appName,
@@ -241,6 +242,10 @@ export function getBitbucketClient(args: {
).toString(),
}
: {}),
+ // If disableEncoding is true, force proxy/server to skip compression
+ ...(args.disableEncoding
+ ? { [CorsProxyHeaderKeys.DISABLE_ENCODING]: Boolean(args.disableEncoding).toString() }
+ : {}),
},
});
}
@@ -269,6 +274,7 @@ export function useBitbucketClient(authSession: AuthSession | undefined): Bitbuc
},
proxyUrl: env.KIE_SANDBOX_CORS_PROXY_URL,
insecurelyDisableTlsCertificateValidation: authProvider.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: authProvider.disableEncoding,
});
}, [authProviders, authSession, env.KIE_SANDBOX_APP_NAME, env.KIE_SANDBOX_CORS_PROXY_URL]);
}
diff --git a/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGistOrSnippetModal.tsx b/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGistOrSnippetModal.tsx
index b557f539a95..3302ecd6bd9 100644
--- a/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGistOrSnippetModal.tsx
+++ b/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGistOrSnippetModal.tsx
@@ -27,6 +27,7 @@ import { Radio } from "@patternfly/react-core/dist/js/components/Radio";
import { UsersIcon } from "@patternfly/react-icons/dist/js/icons/users-icon";
import { LockIcon } from "@patternfly/react-icons/dist/js/icons/lock-icon";
+import { GitlabIcon } from "@patternfly/react-icons/dist/js/icons/gitlab-icon";
import { Button } from "@patternfly/react-core/dist/js/components/Button";
import { GIST_ORIGIN_REMOTE_NAME } from "@kie-tools-core/workspaces-git-fs/dist/constants/GitConstants";
@@ -49,8 +50,8 @@ import { useOnlineI18n } from "../../../i18n";
import { LoadOrganizationsSelect, SelectOptionObjectType } from "./LoadOrganizationsSelect";
import { useGitIntegration } from "./GitIntegrationContextProvider";
import { useEnv } from "../../../env/hooks/EnvContext";
+import { useGitlabClient } from "../../../gitlab/useGitlabClient";
import { HelperText, HelperTextItem } from "@patternfly/react-core/dist/js/components/HelperText";
-
export interface CreateGistOrSnippetResponse {
cloneUrl: string;
htmlUrl: string;
@@ -68,6 +69,7 @@ export const CreateGistOrSnippetModal = (props: {
const authProvider = useAuthProvider(authSession);
const bitbucketClient = useBitbucketClient(authSession);
const gitHubClient = useGitHubClient(authSession);
+ const gitlabClient = useGitlabClient(authSession);
const [isPrivate, setPrivate] = useState(false);
const [error, setError] = useState<string | undefined>(undefined);
@@ -107,17 +109,17 @@ If you are, it means that creating this Gist failed and it can safely be deleted
if (selectedOrganization?.kind !== "organization") {
throw new Error("No workspace was selected for Bitbucket Snippet.");
}
+ const bitbucketWorkspace = (selectedOrganization as SelectOptionObjectType<"organization">).selectedOption;
const response = await bitbucketClient.createSnippet({
- workspace: selectedOrganization.value,
+ workspace: bitbucketWorkspace.name,
title: props.workspace.name ?? `${env.KIE_SANDBOX_APP_NAME} Snippet`,
files: {
"README.md": {
content: `
-This Snippet was created from ${env.KIE_SANDBOX_APP_NAME}.
+ This Snippet was created from ${env.KIE_SANDBOX_APP_NAME}.
-This file is temporary and you should not be seeing it.
-If you are, it means that creating this Snippet failed and it can safely be deleted.
-`,
+ This file is temporary and you should not be seeing it.
+ If you are, it means that creating this Snippet failed and it can safely be deleted.`,
},
},
isPrivate,
@@ -136,6 +138,35 @@ If you are, it means that creating this Snippet failed and it can safely be dele
return { cloneUrl, htmlUrl: json.links.html.href };
}, [bitbucketClient, env.KIE_SANDBOX_APP_NAME, isPrivate, props.workspace.name, selectedOrganization]);
+ const createGitlabSnippet: () => Promise<CreateGistOrSnippetResponse> = useCallback(async () => {
+ const gitlabGroupOrProject = (selectedOrganization as SelectOptionObjectType<"organization">).selectedOption;
+ const snippetResponse = await gitlabClient.createSnippet({
+ title: props?.workspace?.name ?? `${env.KIE_SANDBOX_APP_NAME} Snippet`,
+ visibility: isPrivate ? "private" : "public",
+ files: [
+ {
+ file_path: "README.md",
+ content: `
+ This Snippet was created from ${env.KIE_SANDBOX_APP_NAME}.
+
+ This file is temporary and you should not be seeing it.
+ If you are, it means that creating this Snippet failed and it can safely be deleted.`,
+ },
+ ],
+ id: gitlabGroupOrProject.value,
+ });
+ if (!(snippetResponse.status === 201)) {
+ throw new Error(
+ `Gitlab snippet creation request failed with: ${snippetResponse.status} ${snippetResponse.statusText}`
+ );
+ }
+ const repo = await snippetResponse.json();
+ if (!repo?.http_url_to_repo || !repo?.web_url) {
+ throw new Error("Unexpected contents of the Gitlab snippet creation response.");
+ }
+ return { cloneUrl: repo?.http_url_to_repo, htmlUrl: repo?.web_url };
+ }, [gitlabClient, env.KIE_SANDBOX_APP_NAME, isPrivate, props.workspace.name, selectedOrganization]);
+
const createGistOrSnippet = useCallback(async () => {
try {
if (!authInfo || !isGistEnabledAuthProviderType(authProvider?.type)) {
@@ -147,11 +178,14 @@ If you are, it means that creating this Snippet failed and it can safely be dele
const insecurelyDisableTlsCertificateValidation =
authProvider?.group === AuthProviderGroup.GIT && authProvider.insecurelyDisableTlsCertificateValidation;
+ const disableEncoding = authProvider?.group === AuthProviderGroup.GIT && authProvider.disableEncoding;
+
const createGistOrSnippetCommand: () => Promise<CreateGistOrSnippetResponse> = switchExpression(
gistEnabledAuthProvider,
{
github: createGitHubGist,
bitbucket: createBitbucketSnippet,
+ gitlab: createGitlabSnippet,
}
);
const gistOrSnippet = await createGistOrSnippetCommand();
@@ -161,6 +195,7 @@ If you are, it means that creating this Snippet failed and it can safely be dele
url: new URL(gistOrSnippet.cloneUrl).toString(),
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
})
)
.find((serverRef) => serverRef.ref === "HEAD")!
@@ -171,15 +206,18 @@ If you are, it means that creating this Snippet failed and it can safely be dele
remoteUrl: URL;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) => Promise<void> = switchExpression(gistEnabledAuthProvider, {
github: workspaces.initGistOnWorkspace,
bitbucket: workspaces.initSnippetOnWorkspace,
+ gitlab: workspaces.initGilabSnippetOnWorkspace,
});
await initWorkspaceCommand({
workspaceId: props.workspace.workspaceId,
remoteUrl: new URL(gistOrSnippet.cloneUrl),
branch: gistOrSnippetDefaultBranch,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
await workspaces.addRemote({
@@ -207,11 +245,13 @@ If you are, it means that creating this Snippet failed and it can safely be dele
force: true,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
await workspaces.pull({
workspaceId: props.workspace.workspaceId,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
props.onClose();
@@ -231,6 +271,7 @@ If you are, it means that creating this Snippet failed and it can safely be dele
authProvider,
createGitHubGist,
createBitbucketSnippet,
+ createGitlabSnippet,
workspaces,
props,
gitConfig,
@@ -254,6 +295,7 @@ If you are, it means that creating this Snippet failed and it can safely be dele
titleIconVariant={switchExpression(authProvider.type, {
bitbucket: BitbucketIcon,
github: GithubIcon,
+ gitlab: GitlabIcon,
})}
description={i18n.createGistOrSnippetModal[authProvider.type].description(props.workspace.name)}
actions={[
@@ -293,6 +335,7 @@ If you are, it means that creating this Snippet failed and it can safely be dele
workspace={props.workspace}
onSelect={setSelectedOrganization}
readonly={authProvider.type === AuthProviderType.github}
+ actionType="snippet"
/>
<FormHelperText>
<HelperText>
diff --git a/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGitRepositoryModal.tsx b/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGitRepositoryModal.tsx
index 31e41c27e8d..deb3fb99a35 100644
--- a/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGitRepositoryModal.tsx
+++ b/packages/online-editor/src/editor/Toolbar/GitIntegration/CreateGitRepositoryModal.tsx
@@ -38,6 +38,7 @@ import { useAuthSession } from "../../../authSessions/AuthSessionsContext";
import { useBitbucketClient } from "../../../bitbucket/Hooks";
import { BitbucketIcon } from "@patternfly/react-icons/dist/js/icons/bitbucket-icon";
import { GithubIcon } from "@patternfly/react-icons/dist/js/icons/github-icon";
+import { GitlabIcon } from "@patternfly/react-icons/dist/js/icons/gitlab-icon";
import { useGitHubClient } from "../../../github/Hooks";
import { AuthProviderGroup, isSupportedGitAuthProviderType } from "../../../authProviders/AuthProvidersApi";
import { useAuthProvider } from "../../../authProviders/AuthProvidersContext";
@@ -45,6 +46,7 @@ import { switchExpression } from "@kie-tools-core/switch-expression-ts";
import { useOnlineI18n } from "../../../i18n";
import { LoadOrganizationsSelect, SelectOptionObjectType } from "./LoadOrganizationsSelect";
import { useGitIntegration } from "./GitIntegrationContextProvider";
+import { useGitlabClient } from "../../../gitlab/useGitlabClient";
import { HelperText, HelperTextItem } from "@patternfly/react-core/dist/js/components/HelperText";
export interface CreateRepositoryResponse {
@@ -69,6 +71,7 @@ export function CreateGitRepositoryModal(props: {
const authProvider = useAuthProvider(authSession);
const bitbucketClient = useBitbucketClient(authSession);
const gitHubClient = useGitHubClient(authSession);
+ const gitlabClient = useGitlabClient(authSession);
const [isPrivate, setPrivate] = useState(false);
const [isLoading, setLoading] = useState(false);
@@ -89,7 +92,12 @@ export function CreateGitRepositoryModal(props: {
if (selectedOrganization?.kind !== "organization") {
throw new Error("No workspace was selected for Bitbucket Repository.");
}
- const repoResponse = await bitbucketClient.createRepo({ name, workspace: selectedOrganization.value, isPrivate });
+ const bitbucketWorkspace = (selectedOrganization as SelectOptionObjectType<"organization">).selectedOption;
+ const repoResponse = await bitbucketClient.createRepo({
+ name,
+ workspace: bitbucketWorkspace.name,
+ isPrivate,
+ });
if (!repoResponse.ok) {
throw new Error(
`Bitbucket repository creation request failed with: ${repoResponse.status} ${repoResponse.statusText}`
@@ -108,13 +116,33 @@ export function CreateGitRepositoryModal(props: {
return { cloneUrl, htmlUrl: repo.links.html.href };
}, [bitbucketClient, isPrivate, name, selectedOrganization]);
+ const createGitlabRepository = useCallback(async (): Promise<CreateRepositoryResponse> => {
+ const gitlabGroup = (selectedOrganization as SelectOptionObjectType<"organization">).selectedOption;
+ const repoResponse = await gitlabClient.createRepository({
+ name,
+ groupId: gitlabGroup.value,
+ visibility: isPrivate ? "private" : "public",
+ });
+ if (!(repoResponse.status === 201)) {
+ throw new Error(
+ `Gitlab repository creation request failed with: ${repoResponse.status} ${repoResponse.statusText}`
+ );
+ }
+ const repo = await repoResponse.json();
+ if (!repo?.http_url_to_repo || !repo?.web_url) {
+ throw new Error("Unexpected contents of the Gitlab repository creation response.");
+ }
+ return { cloneUrl: repo?.http_url_to_repo, htmlUrl: repo?.web_url };
+ }, [gitlabClient, isPrivate, name, selectedOrganization]);
+
const createGitHubRepository = useCallback(async (): Promise<CreateRepositoryResponse> => {
+ const githubOrg = (selectedOrganization as SelectOptionObjectType<"organization">).selectedOption;
const repo =
selectedOrganization?.kind === "organization"
? await gitHubClient.repos.createInOrg({
name,
private: isPrivate,
- org: selectedOrganization.value,
+ org: githubOrg.name,
})
: await gitHubClient.repos.createForAuthenticatedUser({ name, private: isPrivate });
@@ -134,11 +162,12 @@ export function CreateGitRepositoryModal(props: {
if (selectedOrganization?.kind !== "organization") {
throw new Error("No workspace was selected for Bitbucket Repository.");
}
+ const org = (selectedOrganization as SelectOptionObjectType<"organization">).selectedOption;
// need an empty commit push through REST API first
await bitbucketClient
.pushEmptyCommit({
repository: name,
- workspace: selectedOrganization.value,
+ workspace: org.name,
branch: props.workspace.origin.branch,
})
.then((response) => {
@@ -157,12 +186,15 @@ export function CreateGitRepositoryModal(props: {
const insecurelyDisableTlsCertificateValidation =
authProvider?.group === AuthProviderGroup.GIT && authProvider.insecurelyDisableTlsCertificateValidation;
+ const disableEncoding = authProvider?.group === AuthProviderGroup.GIT && authProvider.disableEncoding;
+
setError(undefined);
setLoading(true);
const createRepositoryCommand: () => Promise<CreateRepositoryResponse> = switchExpression(authProvider?.type, {
bitbucket: createBitbucketRepository,
github: createGitHubRepository,
+ gitlab: createGitlabRepository,
});
if (!createRepositoryCommand) {
@@ -195,9 +227,11 @@ export function CreateGitRepositoryModal(props: {
force: switchExpression(authProvider?.type, {
github: false,
bitbucket: true,
+ gitlab: true,
}),
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
await workspaces.initGitOnWorkspace({
@@ -205,6 +239,7 @@ export function CreateGitRepositoryModal(props: {
remoteUrl: new URL(cloneUrl),
branch: props.workspace.origin.branch,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
await workspaces.renameWorkspace({
@@ -228,6 +263,7 @@ export function CreateGitRepositoryModal(props: {
authProvider,
createBitbucketRepository,
createGitHubRepository,
+ createGitlabRepository,
pushEmptyCommitIntoBitbucket,
workspaces,
props,
@@ -264,6 +300,7 @@ export function CreateGitRepositoryModal(props: {
titleIconVariant={switchExpression(authProvider.type, {
bitbucket: BitbucketIcon,
github: GithubIcon,
+ gitlab: GitlabIcon,
})}
description={i18n.createGitRepositoryModal[authProvider.type].description(props.workspace.name)}
actions={[
@@ -275,6 +312,7 @@ export function CreateGitRepositoryModal(props: {
isDisabled={switchExpression(authProvider.type, {
bitbucket: !isNameValid || selectedOrganization === undefined,
github: !isNameValid,
+ gitlab: !isNameValid,
})}
>
{i18n.createGitRepositoryModal.form.buttonCreate}
@@ -302,7 +340,11 @@ export function CreateGitRepositoryModal(props: {
</FormAlert>
)}
<FormGroup label={i18n.createGitRepositoryModal[authProvider.type].form.select.label} fieldId="organization">
- <LoadOrganizationsSelect workspace={props.workspace} onSelect={setSelectedOrganization} />
+ <LoadOrganizationsSelect
+ workspace={props.workspace}
+ onSelect={setSelectedOrganization}
+ actionType="repository"
+ />
<FormHelperText>
<HelperText>
<HelperTextItem variant="default">
diff --git a/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationAlerts.tsx b/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationAlerts.tsx
index 68a15e004a7..185038bdb40 100644
--- a/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationAlerts.tsx
+++ b/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationAlerts.tsx
@@ -237,6 +237,7 @@ export function useGitIntegrationAlerts(workspace: ActiveWorkspace) {
title={switchExpression(workspace.descriptor.origin.kind, {
GITHUB_GIST: i18n.editorPage.alerts.createGist,
BITBUCKET_SNIPPET: i18n.editorPage.alerts.createSnippet,
+ GITLAB_SNIPPET: i18n.editorPage.alerts.createSnippet,
})}
actionClose={<AlertActionCloseButton onClose={close} />}
actionLinks={
@@ -264,7 +265,13 @@ export function useGitIntegrationAlerts(workspace: ActiveWorkspace) {
return <></>;
}
- const gistOrSnippetUrl = workspace.descriptor.origin.url;
+ const gistOrSnippetUrl = switchExpression(workspace.descriptor.origin.kind, {
+ // Remove the `.git` extension from GitLab snippet URLs.
+ // If a snippet URL ends with `.git`, GitLab may return a 404 error when accessed.
+ // This ensures only the extension is removed, not other parts of the URL.
+ GITLAB_SNIPPET: workspace.descriptor.origin.url?.replace?.(/\.git$/, ""),
+ default: workspace.descriptor.origin.url,
+ });
return (
<Alert
variant="info"
@@ -275,6 +282,7 @@ export function useGitIntegrationAlerts(workspace: ActiveWorkspace) {
{switchExpression(workspace.descriptor.origin.kind, {
BITBUCKET_SNIPPET: "Snippet",
GITHUB_GIST: "Gist",
+ GITLAB_SNIPPET: "Snippet",
})}
...
</>
@@ -299,13 +307,20 @@ export function useGitIntegrationAlerts(workspace: ActiveWorkspace) {
return <></>;
}
- const gistOrSnippetUrl = workspace.descriptor.origin.url;
+ const gistOrSnippetUrl = switchExpression(workspace.descriptor.origin.kind, {
+ // Remove the `.git` extension from GitLab snippet URLs.
+ // If a snippet URL ends with `.git`, GitLab may return a 404 error when accessed.
+ // This ensures only the extension is removed, not other parts of the URL.
+ GITLAB_SNIPPET: workspace.descriptor.origin.url?.replace?.(/\.git$/, ""),
+ default: workspace.descriptor.origin.url,
+ });
return (
<Alert
variant="success"
title={switchExpression(workspace.descriptor.origin.kind, {
GITHUB_GIST: i18n.editorPage.alerts.updateGist,
BITBUCKET_SNIPPET: i18n.editorPage.alerts.updateSnippet,
+ GITLAB_SNIPPET: i18n.editorPage.alerts.updateSnippet,
})}
actionClose={<AlertActionCloseButton onClose={close} />}
actionLinks={
@@ -335,6 +350,7 @@ export function useGitIntegrationAlerts(workspace: ActiveWorkspace) {
title={switchExpression(workspace.descriptor.origin.kind as WorkspaceKindGistLike, {
GITHUB_GIST: i18n.editorPage.alerts.errorPushingGist,
BITBUCKET_SNIPPET: i18n.editorPage.alerts.errorPushingSnippet,
+ GITLAB_SNIPPET: i18n.editorPage.alerts.errorPushingSnippet,
})}
actionLinks={[
<AlertActionLink
diff --git a/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationContextProvider.tsx b/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationContextProvider.tsx
index 4017e5456e7..ca4541e529a 100644
--- a/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationContextProvider.tsx
+++ b/packages/online-editor/src/editor/Toolbar/GitIntegration/GitIntegrationContextProvider.tsx
@@ -55,6 +55,7 @@ import { useNavigate } from "react-router-dom";
import { useGitIntegrationAlerts } from "./GitIntegrationAlerts";
import { AuthProviderGroup, isGistEnabledAuthProviderType } from "../../../authProviders/AuthProvidersApi";
import { useEditorToolbarDispatchContext } from "../EditorToolbarContextProvider";
+import { useGitlabClient } from "../../../gitlab/useGitlabClient";
export type GitIntegrationContextType = {
workspaceImportableUrl: ImportableUrl;
@@ -103,12 +104,14 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
const gitHubClient = useGitHubClient(authSession);
const bitbucketClient = useBitbucketClient(authSession);
+ const gitlabClient = useGitlabClient(authSession);
const [isGistOrSnippetLoading, setGistOrSnippetLoading] = useState(false);
const [gitHubGist, setGitHubGist] = useState<
OctokitRestEndpointMethodTypes["gists"]["get"]["response"]["data"] | undefined
>(undefined);
const [bitbucketSnippet, setBitbucketSnippet] = useState<any>(undefined);
+ const [gitlabSnippet, setGitlabSnippet] = useState<any>(undefined);
const insecurelyDisableTlsCertificateValidation = useMemo(() => {
if (authProvider?.group === AuthProviderGroup.GIT) {
@@ -117,6 +120,13 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
return props.workspace.descriptor.gitInsecurelyDisableTlsCertificateValidation;
}, [authProvider, props.workspace]);
+ const disableEncoding = useMemo(() => {
+ if (authProvider?.group === AuthProviderGroup.GIT) {
+ return authProvider.disableEncoding;
+ }
+ return props.workspace.descriptor.gitDisableEncoding;
+ }, [authProvider, props.workspace]);
+
useCancelableEffect(
useCallback(
({ canceled }) => {
@@ -169,6 +179,31 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
)
);
+ useCancelableEffect(
+ useCallback(
+ ({ canceled }) => {
+ if (gitlabSnippet || workspaceImportableUrl.type !== UrlType.GITLAB_DOT_COM_SNIPPET) {
+ return;
+ }
+
+ const { snippetId, group, project } = workspaceImportableUrl;
+
+ if (!snippetId) {
+ return;
+ }
+
+ gitlabClient.getSnippet({ snippetId, group, project }).then(async (response) => {
+ if (canceled.get()) {
+ return;
+ }
+ const json = await response.json();
+ setGitlabSnippet(json);
+ });
+ },
+ [gitlabSnippet, workspaceImportableUrl, gitlabClient]
+ )
+ );
+
const authSessionSelectFilter = useMemo(() => {
if (!props.workspace) {
return gitAuthSessionSelectFilter();
@@ -196,8 +231,15 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
);
}
+ if (props.workspace.descriptor.origin.kind === WorkspaceKind.GITLAB_SNIPPET) {
+ return authSessionsSelectFilterCompatibleWithGistOrSnippetUrlDomain(
+ new URL(props.workspace.descriptor.origin.url).host,
+ gitlabSnippet?.author?.username
+ );
+ }
+
return gitAuthSessionSelectFilter();
- }, [bitbucketSnippet?.owner?.login, gitHubGist?.owner?.login, props.workspace]);
+ }, [bitbucketSnippet?.owner?.login, gitHubGist?.owner?.login, gitlabSnippet?.author?.username, props.workspace]);
const changeGitAuthSessionId = useCallback(
(newGitAuthSessionId: React.SetStateAction<string | undefined>, lastAuthSessionId: string | undefined) => {
@@ -206,9 +248,10 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
gitAuthSessionId:
typeof newGitAuthSessionId === "function" ? newGitAuthSessionId(lastAuthSessionId) : newGitAuthSessionId,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
},
- [props.workspace.descriptor.workspaceId, workspaces, insecurelyDisableTlsCertificateValidation]
+ [props.workspace.descriptor.workspaceId, workspaces, insecurelyDisableTlsCertificateValidation, disableEncoding]
);
const workspaceHasNestedDirectories = useMemo(
@@ -220,9 +263,11 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
const canCreateBitbucketRepository = useMemo(() => authProvider?.type === "bitbucket", [authProvider?.type]);
+ const canCreateGitlabRepository = useMemo(() => authProvider?.type === "gitlab", [authProvider?.type]);
+
const canCreateGitRepository = useMemo(
- () => canCreateGitHubRepository || canCreateBitbucketRepository,
- [canCreateGitHubRepository, canCreateBitbucketRepository]
+ () => canCreateGitHubRepository || canCreateBitbucketRepository || canCreateGitlabRepository,
+ [canCreateGitHubRepository, canCreateBitbucketRepository, canCreateGitlabRepository]
);
const canPushToGitRepository = useMemo(
@@ -238,7 +283,11 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
return authInfo?.uuid && bitbucketSnippet?.creator.uuid === authInfo.uuid;
}, [authInfo, bitbucketSnippet]);
- const isGistOrSnippetOwner = isGitHubGistOwner || isBitbucketSnippetOwner;
+ const isGitlabSnippetOwner = useMemo(() => {
+ return authInfo?.username && gitlabSnippet?.author?.username === authInfo.username;
+ }, [authInfo, gitlabSnippet]);
+
+ const isGistOrSnippetOwner = isGitHubGistOwner || isBitbucketSnippetOwner || isGitlabSnippetOwner;
const canCreateGistOrSnippet = useMemo(
() =>
@@ -301,6 +350,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
force: false,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
navigate({
@@ -327,6 +377,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
navigate,
routes.import,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
]
);
@@ -349,6 +400,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
workspaceId: props.workspace.descriptor.workspaceId,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
if (args.showAlerts) {
@@ -379,6 +431,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
canPushToGitRepository,
pushNewBranch,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
]
);
@@ -408,6 +461,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
force: false,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
await pullFromGitRepository({ showAlerts: false });
alerts.pushSuccessAlert.show();
@@ -427,6 +481,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
gitConfig,
pullFromGitRepository,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
]);
const forceUpdateGistOrSnippet = useCallback(async () => {
@@ -444,12 +499,14 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
remoteRef: `refs/heads/${props.workspace.descriptor.origin.branch}`,
force: true,
authInfo,
+ disableEncoding,
});
await workspaces.pull({
workspaceId: props.workspace.descriptor.workspaceId,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
alerts.successfullyUpdatedGistOrSnippetAlert.show();
} catch (e) {
@@ -466,6 +523,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
setSyncGistOrSnippetDropdownOpen,
workspaces,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
]);
const updateGistOrSnippet = useCallback(async () => {
@@ -493,11 +551,13 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
force: false,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
await workspaces.pull({
workspaceId: props.workspace.descriptor.workspaceId,
authInfo,
+ disableEncoding,
});
} catch (e) {
alerts.errorPushingGistOrSnippet.show({ forceUpdateGistOrSnippet });
@@ -519,6 +579,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
forceUpdateGistOrSnippet,
setSyncGistOrSnippetDropdownOpen,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
]);
const forkGitHubGist = useCallback(async () => {
@@ -559,6 +620,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
force: true,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
// Redirect to import workspace
@@ -590,6 +652,7 @@ export function GitIntegrationContextProvider(props: GitIntegrationContextProvid
routes.import,
alerts.errorAlert,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
]);
useEffect(() => {
diff --git a/packages/online-editor/src/editor/Toolbar/GitIntegration/LoadOrganizationsSelect.tsx b/packages/online-editor/src/editor/Toolbar/GitIntegration/LoadOrganizationsSelect.tsx
index 06fcd2c980a..6212c4df66e 100644
--- a/packages/online-editor/src/editor/Toolbar/GitIntegration/LoadOrganizationsSelect.tsx
+++ b/packages/online-editor/src/editor/Toolbar/GitIntegration/LoadOrganizationsSelect.tsx
@@ -36,31 +36,43 @@ import { SyncAltIcon } from "@patternfly/react-icons/dist/js/icons/sync-alt-icon
import { UserIcon } from "@patternfly/react-icons/dist/js/icons/user-icon";
import { UsersIcon } from "@patternfly/react-icons/dist/js/icons/users-icon";
import { useOnlineI18n } from "../../../i18n";
+import GitlabIcon from "@patternfly/react-icons/dist/js/icons/gitlab-icon";
+import { useGitlabClient } from "../../../gitlab/useGitlabClient";
+export type Organization = {
+ name: string;
+ value: string;
+};
export interface LoadOrganizationsReponse {
- organizations: {
- name: string;
- }[];
+ organizations: Organization[];
}
+type KindToValueMap = {
+ user: string;
+ organization: Organization;
+};
export type SelectOptionKindType = "user" | "organization";
-export type SelectOptionObjectType = {
- value: string;
- kind: SelectOptionKindType;
+export type SelectOptionObjectType<K extends SelectOptionKindType = SelectOptionKindType> = {
+ selectedOption: KindToValueMap[K];
+ kind: K;
toString(): string;
compareTo(selectOption: any): boolean;
};
-const getSelectOptionValue = (value: string, kind: SelectOptionKindType): SelectOptionObjectType => {
+const getSelectOptionValue = <K extends SelectOptionKindType>(
+ selectedOption: KindToValueMap[K],
+ kind: K
+): SelectOptionObjectType<K> => {
return {
- value,
+ selectedOption,
kind,
toString: () => {
- return value;
+ return kind === "user" ? (selectedOption as string) : (selectedOption as Organization).name;
},
compareTo: (selectOption: any) => {
- return value.toLowerCase() === selectOption.toString().toLowerCase();
+ const val = kind === "user" ? (selectedOption as string) : (selectedOption as Organization).name;
+ return val.toLowerCase() === selectOption.toString().toLowerCase();
},
};
};
@@ -70,18 +82,20 @@ export interface Props {
onSelect: (organization?: SelectOptionObjectType) => void;
readonly?: boolean;
hideUser?: boolean;
+ actionType?: "repository" | "snippet";
}
-export const LoadOrganizationsSelect = (props: Props) => {
- const { authSession, authInfo } = useAuthSession(props.workspace.gitAuthSessionId);
+export const LoadOrganizationsSelect = ({ onSelect, workspace, readonly, actionType }: Props) => {
+ const { authSession, authInfo } = useAuthSession(workspace.gitAuthSessionId);
const authProvider = useAuthProvider(authSession);
const gitHubClient = useGitHubClient(authSession);
const bitbucketClient = useBitbucketClient(authSession);
+ const gitlabClient = useGitlabClient(authSession);
const { i18n } = useOnlineI18n();
const [isOrganizationDropdownOpen, setOrganizationDropdownOpen] = useState(false);
- const [organizations, setOrganizations] = useState<{ name: string }[]>();
+ const [organizations, setOrganizations] = useState<Organization[]>();
const [isLoadingOrganizations, setLoadingOrganizations] = useState(false);
const [internalSelectedOption, setInternalSelectedOption] = useState<SelectOptionObjectType>();
@@ -89,9 +103,14 @@ export const LoadOrganizationsSelect = (props: Props) => {
return authProvider?.type === AuthProviderType.bitbucket;
}, [authProvider?.type]);
+ const isGitlabSnippet = useMemo(
+ () => authProvider?.type === AuthProviderType.gitlab && actionType === "snippet",
+ [authProvider?.type, actionType]
+ );
+
const getGitHubOrganizationsForUser = useCallback(async (): Promise<LoadOrganizationsReponse> => {
const orgs = await gitHubClient.orgs.listForAuthenticatedUser();
- return { organizations: orgs.data.map((it) => ({ name: it.login })) };
+ return { organizations: orgs.data.map((it) => ({ name: it.login, value: it.login })) };
}, [gitHubClient.orgs]);
const getBitbucketWorkspacesForUser = useCallback(async (): Promise<LoadOrganizationsReponse> => {
@@ -102,10 +121,34 @@ export const LoadOrganizationsSelect = (props: Props) => {
return emptyResponse;
}
const values: any[] = json.values;
- return { organizations: values.map((it) => ({ name: it.workspace.slug })) };
+ return { organizations: values.map((it) => ({ name: it.workspace.slug, value: it.workspace.slug })) };
}, [bitbucketClient]);
- const { onSelect } = props;
+ const getGitlabGroupsForUser = useCallback(async (): Promise<LoadOrganizationsReponse> => {
+ if (actionType && actionType === "snippet") {
+ const projectsResponse = await gitlabClient.listProjects();
+ const projectsResponseJson: any[] = await projectsResponse.json();
+ return {
+ organizations: (projectsResponseJson ?? [])
+ .map((project) => ({
+ name: project?.name_with_namespace,
+ value: project?.id,
+ }))
+ .sort((a, b) => a?.name?.localeCompare?.(b?.name)),
+ };
+ }
+ const groupsResponse = await gitlabClient.listGroups();
+ const groupsResponseJson: any[] = await groupsResponse.json();
+ return {
+ organizations: (groupsResponseJson ?? [])
+ .map((group) => ({
+ name: group?.full_name,
+ value: group?.id,
+ }))
+ .sort((a, b) => a?.name?.localeCompare?.(b?.name)),
+ };
+ }, [gitlabClient, actionType]);
+
const setSelectedOption = useCallback(
(option?: SelectOptionObjectType) => {
onSelect(option);
@@ -119,10 +162,13 @@ export const LoadOrganizationsSelect = (props: Props) => {
if (!isSupportedGitAuthProviderType(authProvider?.type)) {
return;
}
+ const defaultSelectedOptionValue = orgs?.organizations?.[0] ?? authInfo?.username;
+ const defaultSelectedOptionKind = orgs?.organizations?.[0] ? "organization" : "user";
setSelectedOption(
switchExpression(authProvider?.type, {
- bitbucket: getSelectOptionValue(orgs.organizations[0].name, "organization"),
+ bitbucket: getSelectOptionValue(orgs.organizations[0], "organization"),
github: getSelectOptionValue(authInfo!.username, "user"),
+ gitlab: getSelectOptionValue(defaultSelectedOptionValue, defaultSelectedOptionKind),
})
);
},
@@ -137,6 +183,7 @@ export const LoadOrganizationsSelect = (props: Props) => {
switchExpression(authProvider?.type, {
bitbucket: getBitbucketWorkspacesForUser,
github: getGitHubOrganizationsForUser,
+ gitlab: getGitlabGroupsForUser,
})()
.then((it) => {
setOrganizations(it.organizations);
@@ -145,7 +192,13 @@ export const LoadOrganizationsSelect = (props: Props) => {
.finally(() => {
setLoadingOrganizations(false);
});
- }, [authProvider?.type, getBitbucketWorkspacesForUser, getGitHubOrganizationsForUser, selectDefaultOption]);
+ }, [
+ authProvider?.type,
+ getBitbucketWorkspacesForUser,
+ getGitHubOrganizationsForUser,
+ getGitlabGroupsForUser,
+ selectDefaultOption,
+ ]);
useEffect(() => {
setOrganizations(undefined);
@@ -177,9 +230,12 @@ export const LoadOrganizationsSelect = (props: Props) => {
options.push(<Divider component="li" key={1} />);
}
options.push(
- <SelectGroup label={i18n.loadOrganizationsSelect[authProvider.type].organizations} key="group2">
+ <SelectGroup
+ label={isGitlabSnippet ? "GitLab Projects" : i18n.loadOrganizationsSelect[authProvider.type].organizations}
+ key="group2"
+ >
{organizations?.map((it, index) => (
- <SelectOption key={2 + index} value={getSelectOptionValue(it.name, "organization")}>
+ <SelectOption key={2 + index} value={getSelectOptionValue(it, "organization")}>
<Flex>
<FlexItem>
<UsersIcon />
@@ -192,7 +248,7 @@ export const LoadOrganizationsSelect = (props: Props) => {
);
}
return options;
- }, [authInfo, authProvider, hideUser, i18n.loadOrganizationsSelect, organizations]);
+ }, [authInfo, authProvider, hideUser, i18n.loadOrganizationsSelect, organizations, isGitlabSnippet]);
if (!authProvider || !isSupportedGitAuthProviderType(authProvider?.type)) {
return <></>;
@@ -211,10 +267,11 @@ export const LoadOrganizationsSelect = (props: Props) => {
toggleIcon={switchExpression(authProvider.type, {
bitbucket: <BitbucketIcon />,
github: <GithubIcon />,
+ gitlab: <GitlabIcon />,
})}
onToggle={(_event, val) => setOrganizationDropdownOpen(val)}
selections={internalSelectedOption}
- isDisabled={props.readonly}
+ isDisabled={readonly}
>
{selectOptions}
</Select>
@@ -227,7 +284,7 @@ export const LoadOrganizationsSelect = (props: Props) => {
size="sm"
isLoading={isLoadingOrganizations}
onClick={loadOrganizations}
- isDisabled={props.readonly}
+ isDisabled={readonly}
>
{isLoadingOrganizations ? "" : <SyncAltIcon />}
</Button>
diff --git a/packages/online-editor/src/editor/Toolbar/NewFileDropdownMenu.tsx b/packages/online-editor/src/editor/Toolbar/NewFileDropdownMenu.tsx
index cf08374e0f5..ac9014561dc 100644
--- a/packages/online-editor/src/editor/Toolbar/NewFileDropdownMenu.tsx
+++ b/packages/online-editor/src/editor/Toolbar/NewFileDropdownMenu.tsx
@@ -52,6 +52,7 @@ import { useBitbucketClient } from "../../bitbucket/Hooks";
import { isEditable } from "../../envelopeLocator/EditorEnvelopeLocatorFactory";
import { useEditorsConfig } from "../../envelopeLocator/hooks/EditorEnvelopeLocatorContext";
import { useEnv } from "../../env/hooks/EnvContext";
+import { useGitlabClient } from "../../gitlab/useGitlabClient";
const ROOT_MENU_ID = "addFileRootMenu";
@@ -200,6 +201,8 @@ export function NewFileDropdownMenu(props: {
UrlType.GITHUB_DOT_COM_FILE,
UrlType.BITBUCKET_DOT_ORG_FILE,
UrlType.BITBUCKET_DOT_ORG_SNIPPET_FILE,
+ UrlType.GITLAB_DOT_COM_FILE,
+ UrlType.GITLAB_DOT_COM_SNIPPET_FILE,
],
[]
)
@@ -208,6 +211,7 @@ export function NewFileDropdownMenu(props: {
const { authSession } = useAuthSession(authSessionId);
const gitHubClient = useGitHubClient(authSession);
const bitbucketClient = useBitbucketClient(authSession);
+ const gitlabClient = useGitlabClient(authSession);
// Select authSession based on the importableUrl domain (begin)
const authProviders = useAuthProviders();
@@ -240,7 +244,12 @@ export function NewFileDropdownMenu(props: {
setImportingError(undefined);
try {
- const { error, rawUrl, content } = await fetchSingleFileContent(importableUrl, gitHubClient, bitbucketClient);
+ const { error, rawUrl, content } = await fetchSingleFileContent(
+ importableUrl,
+ gitHubClient,
+ bitbucketClient,
+ gitlabClient
+ );
if (error) {
setImportingError(error);
return;
@@ -263,7 +272,7 @@ export function NewFileDropdownMenu(props: {
setImporting(false);
}
},
- [gitHubClient, bitbucketClient, workspaces, props]
+ [gitHubClient, bitbucketClient, gitlabClient, workspaces, props]
);
const sampleUrl = useCallback(
diff --git a/packages/online-editor/src/editor/Toolbar/Share/GitDropdownGroup.tsx b/packages/online-editor/src/editor/Toolbar/Share/GitDropdownGroup.tsx
index c5afcf0ae78..551dc28cb2b 100644
--- a/packages/online-editor/src/editor/Toolbar/Share/GitDropdownGroup.tsx
+++ b/packages/online-editor/src/editor/Toolbar/Share/GitDropdownGroup.tsx
@@ -38,6 +38,7 @@ import { Tooltip } from "@patternfly/react-core/dist/js/components/Tooltip";
import { switchExpression } from "@kie-tools-core/switch-expression-ts";
import BitbucketIcon from "@patternfly/react-icons/dist/js/icons/bitbucket-icon";
import GithubIcon from "@patternfly/react-icons/dist/js/icons/github-icon";
+import { GitlabIcon } from "@patternfly/react-icons/dist/js/icons/gitlab-icon";
import GitIcon from "@patternfly/react-icons/dist/js/icons/git-icon";
import { useAuthSession } from "../../../authSessions/AuthSessionsContext";
import { useAuthProvider } from "../../../authProviders/AuthProvidersContext";
@@ -95,7 +96,7 @@ export const GitDropdownGroup = (props: Props) => {
/>
}
>
- {`Manage authentication sources for '${props.workspace.descriptor.name}' to be able to create Repository, GitHub Gist or Bitbucket Snippet.`}
+ {`Manage authentication sources for '${props.workspace.descriptor.name}' to be able to create Repository, GitHub Gist, Bitbucket Snippet or GitLab Snippet.`}
</Alert>
{canCreateGitRepository && (
<Tooltip
@@ -109,6 +110,7 @@ export const GitDropdownGroup = (props: Props) => {
icon={switchExpression(authProvider?.type as GitAuthProviderType, {
bitbucket: <BitbucketIcon />,
github: <GithubIcon />,
+ gitlab: <GitlabIcon />,
default: <GitIcon />,
})}
data-testid={"create-git-repository-button"}
@@ -120,6 +122,7 @@ export const GitDropdownGroup = (props: Props) => {
{switchExpression(authProvider?.type as GitAuthProviderType, {
github: "GitHub repository",
bitbucket: "Bitbucket repository",
+ gitlab: "GitLab repository",
default: "Git repository",
})}
...
@@ -135,6 +138,7 @@ export const GitDropdownGroup = (props: Props) => {
{switchExpression(authProvider?.type as GistEnabledAuthProviderType, {
github: i18n.editorToolbar.cantCreateGistTooltip,
bitbucket: i18n.editorToolbar.cantCreateSnippetTooltip,
+ gitlab: i18n.editorToolbar.cantCreateSnippetTooltip,
})}
</div>
}
@@ -145,6 +149,7 @@ export const GitDropdownGroup = (props: Props) => {
icon={switchExpression(authProvider?.type as GistEnabledAuthProviderType, {
bitbucket: <BitbucketIcon />,
github: <GithubIcon />,
+ gitlab: <GitlabIcon />,
})}
data-testid={"create-gist-or-snippet-button"}
component="button"
@@ -154,6 +159,7 @@ export const GitDropdownGroup = (props: Props) => {
{switchExpression(authProvider?.type as GistEnabledAuthProviderType, {
github: i18n.editorToolbar.createGist,
bitbucket: i18n.editorToolbar.createSnippet,
+ gitlab: i18n.editorToolbar.createSnippet,
})}
</DropdownItem>
</Tooltip>
diff --git a/packages/online-editor/src/editor/Toolbar/SyncDropdownMenu.tsx b/packages/online-editor/src/editor/Toolbar/SyncDropdownMenu.tsx
index 0dc774d0bec..d24961eb7e5 100644
--- a/packages/online-editor/src/editor/Toolbar/SyncDropdownMenu.tsx
+++ b/packages/online-editor/src/editor/Toolbar/SyncDropdownMenu.tsx
@@ -49,6 +49,7 @@ import { useEditorToolbarContext, useEditorToolbarDispatchContext } from "./Edit
import { useGitIntegration } from "./GitIntegration/GitIntegrationContextProvider";
import { useAuthProvider } from "../../authProviders/AuthProvidersContext";
import { useAuthSession } from "../../authSessions/AuthSessionsContext";
+import GitlabIcon from "@patternfly/react-icons/dist/js/icons/gitlab-icon";
type Props = {
workspace: ActiveWorkspace;
@@ -94,6 +95,7 @@ export function SyncDropdownMenu(props: Props) {
{switchExpression(props.workspace.descriptor.origin.kind as WorkspaceKindGistLike, {
GITHUB_GIST: i18n.editorToolbar.cantUpdateGistTooltip,
BITBUCKET_SNIPPET: i18n.editorToolbar.cantUpdateSnippetTooltip,
+ GITLAB_SNIPPET: i18n.editorToolbar.cantUpdateSnippetTooltip,
})}
</div>
}
@@ -106,6 +108,7 @@ export function SyncDropdownMenu(props: Props) {
icon={switchExpression(props.workspace.descriptor.origin.kind as WorkspaceKindGistLike, {
BITBUCKET_SNIPPET: <BitbucketIcon />,
GITHUB_GIST: <GithubIcon />,
+ GITLAB_SNIPPET: <GitlabIcon />,
})}
onClick={updateGistOrSnippet}
isDisabled={!canUpdateGistOrSnippet}
@@ -114,6 +117,7 @@ export function SyncDropdownMenu(props: Props) {
{switchExpression(props.workspace.descriptor.origin.kind as WorkspaceKindGistLike, {
BITBUCKET_SNIPPET: "Bitbucket Snippet",
GITHUB_GIST: "GitHub Gist",
+ GITLAB_SNIPPET: "GitLab Snippet",
})}
</DropdownItem>
{canForkGitHubGist && (
@@ -169,6 +173,7 @@ export function SyncDropdownMenu(props: Props) {
title={`Can't Update ${switchExpression(authProvider?.type as GitAuthProviderType, {
github: "GitHub repository",
bitbucket: "Bitbucket repository",
+ gitlab: "GitLab repository",
default: "Git repository",
})} without selecting a matching authentication source`}
actionLinks={
@@ -195,6 +200,7 @@ export function SyncDropdownMenu(props: Props) {
{
GITHUB_GIST: "Github Gist",
BITBUCKET_SNIPPET: "Bitbucket Snippet",
+ GITLAB_SNIPPET: "GitLab Snippet",
}
)}.`}
</Alert>
diff --git a/packages/online-editor/src/editor/Toolbar/Workspace/Hooks.tsx b/packages/online-editor/src/editor/Toolbar/Workspace/Hooks.tsx
index c31635cb689..c31588a1fc2 100644
--- a/packages/online-editor/src/editor/Toolbar/Workspace/Hooks.tsx
+++ b/packages/online-editor/src/editor/Toolbar/Workspace/Hooks.tsx
@@ -89,6 +89,7 @@ export function useWorkspaceNavigationBlocker(workspace: ActiveWorkspace) {
canPush={switchExpression(workspace.descriptor.origin.kind as WorkspaceKindGitBased, {
BITBUCKET_SNIPPET: canUpdateGistOrSnippet,
GITHUB_GIST: canUpdateGistOrSnippet,
+ GITLAB_SNIPPET: canUpdateGistOrSnippet,
GIT: canPushToGitRepository,
})}
remoteRef={`${GIT_ORIGIN_REMOTE_NAME}/${workspace.descriptor.origin.branch}`}
@@ -98,6 +99,7 @@ export function useWorkspaceNavigationBlocker(workspace: ActiveWorkspace) {
switchExpression(workspace.descriptor.origin.kind as WorkspaceKindGitBased, {
BITBUCKET_SNIPPET: updateGistOrSnippet,
GITHUB_GIST: updateGistOrSnippet,
+ GITLAB_SNIPPET: updateGistOrSnippet,
GIT: pushToGitRepository,
})();
}}
diff --git a/packages/online-editor/src/github/Hooks.tsx b/packages/online-editor/src/github/Hooks.tsx
index 4c2afad381d..a83a0e46d96 100644
--- a/packages/online-editor/src/github/Hooks.tsx
+++ b/packages/online-editor/src/github/Hooks.tsx
@@ -29,6 +29,7 @@ export function getOctokitClient(args: {
domain?: string;
proxyUrl?: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) {
return new Octokit({
auth: args.githubToken,
@@ -48,6 +49,12 @@ export function getOctokitClient(args: {
).toString(),
}
: {}),
+ // If disableEncoding is true, force proxy/server to skip compression
+ ...(args.disableEncoding
+ ? {
+ [CorsProxyHeaderKeys.DISABLE_ENCODING]: Boolean(args.disableEncoding).toString(),
+ }
+ : {}),
...options.headers,
},
});
@@ -75,6 +82,7 @@ export function useGitHubClient(authSession: AuthSession | undefined): Octokit {
githubToken: authSession.token,
proxyUrl: authProvider.insecurelyDisableTlsCertificateValidation ? env.KIE_SANDBOX_CORS_PROXY_URL : undefined,
insecurelyDisableTlsCertificateValidation: authProvider.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: authProvider.disableEncoding,
});
}, [authProviders, env.KIE_SANDBOX_CORS_PROXY_URL, authSession]);
}
diff --git a/packages/online-editor/src/gitlab/GitlabClient.ts b/packages/online-editor/src/gitlab/GitlabClient.ts
new file mode 100644
index 00000000000..01e79843b30
--- /dev/null
+++ b/packages/online-editor/src/gitlab/GitlabClient.ts
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { isEmpty } from "lodash";
+
+export interface Options {
+ appName: string;
+ token?: string;
+ domain?: string;
+ headers?: Record<string, string>;
+ proxyUrl?: string;
+}
+
+type GitlabVisibility = "private" | "internal" | "public";
+type GitlabBasePath = "/projects" | "/groups" | "/snippets";
+
+type GetSnippetArgsType = {
+ snippetId: string;
+ group?: string;
+ project?: string;
+};
+
+type GetSnippetFileArgsType = {
+ snippetId: string;
+ group?: string;
+ project?: string;
+ branch?: string;
+ filePath: string;
+};
+
+type GetRepositoryContentsArgsType = {
+ project: string;
+ ref: string;
+ path: string;
+ group?: string;
+};
+
+type CreateRepositoryArgsType = {
+ name: string;
+ groupId?: string;
+ visibility: GitlabVisibility;
+};
+
+type SnippetsFile = {
+ file_path: string;
+ content: string;
+};
+
+type CreateSnippetArgsType = {
+ title: string;
+ files: SnippetsFile[];
+ visibility: GitlabVisibility;
+ id?: string;
+};
+
+type ProjectPath = {
+ group?: string;
+ project?: string;
+};
+
+export interface GitlabClientApi {
+ appName: string;
+ token?: string;
+ domain?: string;
+ headers?: Record<string, string>;
+ getApiUrl(): string;
+ getAuthedUser(): Promise<Response>;
+ createRepository(args: CreateRepositoryArgsType): Promise<Response>;
+ getRepositoryContents(args: GetRepositoryContentsArgsType): Promise<Response>;
+ createSnippet(args: CreateSnippetArgsType): Promise<Response>;
+ getSnippet(args: GetSnippetArgsType): Promise<Response>;
+ getSnippetFile(args: GetSnippetFileArgsType): Promise<Response>;
+ getSnippetFileRaw(args: GetSnippetFileArgsType): Promise<Response>;
+ listGroups(): Promise<Response>;
+ listProjects(): Promise<Response>;
+}
+export class GitlabClient implements GitlabClientApi {
+ constructor(options: Options) {
+ this.appName = options.appName;
+ this.token = options?.token ?? "";
+ this.domain = options?.domain ?? "gitlab.com";
+ this.headers = {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ ...options?.headers,
+ };
+ this.proxyUrl = options.proxyUrl;
+ }
+ appName: string;
+ token: string;
+ domain: string;
+ headers: Record<string, string>;
+ proxyUrl?: string;
+
+ request = async (props: {
+ urlContext: string;
+ method?: string;
+ body?: any;
+ extraHeaders?: { [key: string]: string | undefined };
+ }) => {
+ const resolvedUrl = new URL(this.getApiUrl() + props.urlContext).toString();
+ const authHeader: Record<string, string> = { Authorization: "Bearer " + this.token };
+ const requestHeaders = {
+ ...this.headers,
+ ...props.extraHeaders,
+ ...authHeader,
+ };
+
+ return fetch(resolvedUrl, {
+ method: props.method,
+ headers: JSON.parse(JSON.stringify(requestHeaders)), // allows for erasing header in extraHeaders
+ body: props.body,
+ });
+ };
+
+ getApiUrl = () => {
+ if (this.proxyUrl) {
+ return `${this.proxyUrl}/${this.domain}/api/v4`;
+ }
+ return `https://${this.domain}/api/v4`;
+ };
+
+ getAuthedUser = () => this.request({ urlContext: "/user" });
+
+ createProjectPath = ({ group, project }: ProjectPath): string =>
+ encodeURIComponent([group, project].filter(Boolean).join("/"));
+
+ createProjectBasePathWithId = ({ group, project }: ProjectPath, basePath: GitlabBasePath = "/projects") => {
+ const id = this.createProjectPath({ group, project });
+ return id ? `${basePath}/${id}` : basePath;
+ };
+
+ checkSnippetLevel = ({ group, project }: ProjectPath): string => {
+ const isProjectSnippet = !isEmpty(group) || !isEmpty(project);
+ return isProjectSnippet ? this.createProjectBasePathWithId({ group, project }) : "";
+ };
+
+ getRepositoryContents = ({ project, path, ref, group }: GetRepositoryContentsArgsType) => {
+ const baseUrl = this.createProjectBasePathWithId({ group, project });
+ return this.request({
+ urlContext: `${baseUrl}/repository/files/${path}/raw?ref=${ref}`,
+ method: "get",
+ });
+ };
+
+ createRepository = ({ groupId, visibility, name }: CreateRepositoryArgsType) =>
+ this.request({
+ urlContext: `/projects`,
+ method: "post",
+ body: JSON.stringify({
+ name,
+ visibility,
+ description: `This repository was created using ${this.appName}`,
+ ...(groupId && { namespace_id: groupId }),
+ }),
+ });
+
+ createSnippet = ({ title, visibility, id, files }: CreateSnippetArgsType) => {
+ const url = id ? "/projects/" + id + "/snippets" : "/snippets";
+ return this.request({
+ urlContext: url,
+ method: "post",
+ body: JSON.stringify({
+ title,
+ description: `This snippet was created using ${this.appName}`,
+ visibility,
+ files,
+ }),
+ });
+ };
+
+ getSnippet = ({ snippetId, group, project }: GetSnippetArgsType) => {
+ const baseUrl = this.checkSnippetLevel({ group, project });
+ return this.request({
+ urlContext: `${baseUrl}/snippets/${snippetId}`,
+ method: "get",
+ });
+ };
+
+ getSnippetFile = ({ snippetId, branch, filePath, group, project }: GetSnippetFileArgsType) => {
+ const baseUrl = this.checkSnippetLevel({ group, project });
+ return this.request({
+ urlContext: `${baseUrl}/snippets/${snippetId}/files/${branch}/${filePath}`,
+ method: "get",
+ });
+ };
+
+ getSnippetFileRaw = ({ snippetId, branch, filePath, group, project }: GetSnippetFileArgsType) => {
+ const baseUrl = this.checkSnippetLevel({ group, project });
+ return this.request({
+ urlContext: `${baseUrl}/snippets/${snippetId}/files/${branch}/${filePath}/raw`,
+ method: "get",
+ });
+ };
+
+ listGroups = () =>
+ this.request({
+ urlContext: "/groups?active=true",
+ method: "get",
+ });
+
+ listProjects = () =>
+ this.request({
+ urlContext: "/projects?membership=true&&active=true",
+ method: "get",
+ });
+}
diff --git a/packages/online-editor/src/gitlab/ParseGitLabUrl.ts b/packages/online-editor/src/gitlab/ParseGitLabUrl.ts
new file mode 100644
index 00000000000..417bde15b30
--- /dev/null
+++ b/packages/online-editor/src/gitlab/ParseGitLabUrl.ts
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { matchPath } from "react-router-dom";
+import { ImportableUrl, UrlType } from "../importFromUrl/ImportableUrlHooks";
+
+const GITLAB_NAMESPACE_REGEX = "([^\\/]+(?:\\/[^\\/]+)*)"; // group or nested group(s)
+const NAMESPACE = GITLAB_NAMESPACE_REGEX;
+const PROJECT = "([^\\/]+)"; // project name
+
+const GITLAB_PROJECT_SNIPPET_REGEX = new RegExp(`^/${NAMESPACE}/${PROJECT}/snippets/(\\d+)(?:\\.git)?/?$`);
+const GITLAB_PROJECT_SNIPPET_FILE_REGEX = new RegExp(`^/${NAMESPACE}/${PROJECT}/snippets/([^/]+)/raw/([^/]+)/(.+)$`);
+const GITLAB_REPO_TREE_REGEX = new RegExp(`^/${NAMESPACE}/${PROJECT}/tree/([^/]+)$`);
+const GITLAB_REPO_FILE_REGEX = new RegExp(`^/${NAMESPACE}/${PROJECT}/blob/([^/]+)/(.*)$`);
+const GITLAB_DEFAULT_REPO_REGEX = new RegExp(`^/${NAMESPACE}/${PROJECT}$`);
+
+function ensureGitExtension(pathname: string): string {
+ // Remove any trailing slash first
+ const cleanPath = pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
+
+ // Add `.git` if not already present
+ return cleanPath.endsWith(".git") ? cleanPath : `${cleanPath}.git`;
+}
+
+export function parseGitLabUrl(url: URL): ImportableUrl {
+ // /-/ is mostly used in GitLab web URLs, not the REST API.
+ // replace all instances of /-/ with /
+ const pathname = (url.pathname = url?.pathname?.replace(/\/-\//g, "/"));
+
+ // 1. Standalone Snippet: eg: https://gitlab.com/snippets/123
+ const standaloneSnippet = matchPath(
+ {
+ path: "/snippets/:snippetId",
+ end: true,
+ caseSensitive: true,
+ },
+ pathname
+ );
+
+ if (standaloneSnippet !== null && standaloneSnippet.params.snippetId) {
+ const customStandaloneSnippetUrl = new URL(url);
+ // Without .git on GitLab → Error (not a git repository or unexpected server response)
+ // Ensure the pathname ends with `.git`
+ customStandaloneSnippetUrl.pathname = ensureGitExtension(customStandaloneSnippetUrl.pathname);
+ return {
+ type: UrlType.GITLAB_DOT_COM_SNIPPET,
+ snippetId: standaloneSnippet.params.snippetId,
+ url: customStandaloneSnippetUrl,
+ };
+ }
+
+ // 2. Standalone Snippet File: eg: https://gitlab.com/snippets/123/raw/main/sample.dmn
+ const standaloneSnippetFile = matchPath(
+ {
+ path: "/snippets/:snippetId/raw/:tree/:fileName",
+ end: true,
+ caseSensitive: true,
+ },
+ pathname
+ );
+
+ if (
+ standaloneSnippetFile !== null &&
+ standaloneSnippetFile.params.fileName &&
+ standaloneSnippetFile.params.snippetId &&
+ standaloneSnippetFile.params.tree
+ ) {
+ const {
+ params: { fileName, snippetId, tree },
+ } = standaloneSnippetFile;
+ return { type: UrlType.GITLAB_DOT_COM_SNIPPET_FILE, snippetId, branch: tree, filePath: fileName, url };
+ }
+
+ // 3. Project Snippet: eg: https://gitlab.com/group/project/-/snippets/123
+ const projectSnippetMatch = pathname.match(GITLAB_PROJECT_SNIPPET_REGEX);
+ if (projectSnippetMatch) {
+ const [, group, project, snippetId] = projectSnippetMatch ?? [];
+ if (group && project && snippetId) {
+ const customProjectSnippetUrl = new URL(url);
+ // Without .git on GitLab → Error (not a git repository or unexpected server response)
+ // Ensure the pathname ends with `.git`
+ customProjectSnippetUrl.pathname = ensureGitExtension(customProjectSnippetUrl.pathname);
+ return {
+ type: UrlType.GITLAB_DOT_COM_SNIPPET,
+ snippetId,
+ group: group,
+ project,
+ url: customProjectSnippetUrl,
+ };
+ }
+ }
+
+ // 4. Project Snippet File: eg: https://gitlab.com/group/project/-/snippets/123/raw/main/sample.dmn
+ const projectSnippetFileMatch = pathname.match(GITLAB_PROJECT_SNIPPET_FILE_REGEX);
+ if (projectSnippetFileMatch) {
+ const [, group, project, snippetId, tree, path] = projectSnippetFileMatch ?? [];
+ if (group && project && snippetId && tree && path) {
+ return {
+ type: UrlType.GITLAB_DOT_COM_SNIPPET_FILE,
+ snippetId,
+ group,
+ project,
+ branch: tree,
+ filePath: path,
+ url,
+ };
+ }
+ }
+
+ // 5. Specific branch/ref: eg: https://gitlab.com/group/project/-/tree/main
+ const repoTreeViewMatch = pathname.match(GITLAB_REPO_TREE_REGEX);
+ if (repoTreeViewMatch) {
+ const [, group, project, tree] = repoTreeViewMatch ?? [];
+ if (group && project && tree) {
+ const customGitRefNameUrl = new URL(url);
+ customGitRefNameUrl.pathname = customGitRefNameUrl.pathname.replace(`/tree/${tree}`, "");
+ // Without .git on GitLab → Error (not a git repository or unexpected server response)
+ // Ensure the pathname ends with `.git`
+ customGitRefNameUrl.pathname = ensureGitExtension(customGitRefNameUrl.pathname);
+ return {
+ type: UrlType.GITLAB_DOT_COM,
+ group,
+ project,
+ branch: tree,
+ url: customGitRefNameUrl,
+ };
+ }
+ }
+
+ // 6. File in branch: eg: https://gitlab.com/group/project/-/blob/main/src/sample.dmn
+ const repoFileViewMatch = pathname.match(GITLAB_REPO_FILE_REGEX);
+ if (repoFileViewMatch) {
+ const [, group, project, tree, path] = repoFileViewMatch ?? [];
+ if (group && project && tree && path) {
+ return {
+ type: UrlType.GITLAB_DOT_COM_FILE,
+ group: group,
+ project,
+ branch: tree,
+ filePath: path,
+ url,
+ };
+ }
+ }
+
+ // 7. Default repo view: eg: https://gitlab.com/group/project
+ const repoDefaultViewMatch = pathname.match(GITLAB_DEFAULT_REPO_REGEX);
+ if (repoDefaultViewMatch) {
+ const [, group, project] = repoDefaultViewMatch ?? [];
+ if (group && project) {
+ const customGitRepoUrl = new URL(url);
+ // Without .git on GitLab → Error (not a git repository or unexpected server response)
+ // Ensure the pathname ends with `.git`
+ customGitRepoUrl.pathname = ensureGitExtension(customGitRepoUrl.pathname);
+ return {
+ type: UrlType.GITLAB_DOT_COM,
+ group,
+ project,
+ url: customGitRepoUrl,
+ };
+ }
+ }
+
+ return { type: UrlType.NOT_SUPPORTED, error: "Unsupported GitLab URL", url };
+}
diff --git a/packages/online-editor/src/gitlab/useGitlabClient.ts b/packages/online-editor/src/gitlab/useGitlabClient.ts
new file mode 100644
index 00000000000..8cb579b1025
--- /dev/null
+++ b/packages/online-editor/src/gitlab/useGitlabClient.ts
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useMemo } from "react";
+import { CorsProxyHeaderKeys } from "@kie-tools/cors-proxy-api";
+import { GitlabClient, GitlabClientApi } from "./GitlabClient";
+import { useAuthProviders } from "../authProviders/AuthProvidersContext";
+import { useEnv } from "../env/hooks/EnvContext";
+import { AuthSession } from "../authSessions/AuthSessionApi";
+
+export function getGitlabClient(args: {
+ appName: string;
+ token?: string;
+ domain?: string;
+ proxyUrl?: string;
+ insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
+}) {
+ return new GitlabClient({
+ appName: args.appName,
+ token: args.token,
+ domain: args.domain,
+ proxyUrl: args.insecurelyDisableTlsCertificateValidation ? args.proxyUrl : undefined,
+ headers: {
+ ...(args.insecurelyDisableTlsCertificateValidation
+ ? {
+ [CorsProxyHeaderKeys.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION]: Boolean(
+ args.insecurelyDisableTlsCertificateValidation
+ ).toString(),
+ }
+ : {}),
+ // If disableEncoding is true, force proxy/server to skip compression
+ ...(args.disableEncoding
+ ? {
+ [CorsProxyHeaderKeys.DISABLE_ENCODING]: Boolean(args.disableEncoding).toString(),
+ }
+ : {}),
+ },
+ });
+}
+
+export function useGitlabClient(authSession: AuthSession | undefined): GitlabClientApi {
+ const authProviders = useAuthProviders();
+ const { env } = useEnv();
+
+ return useMemo(() => {
+ if (authSession?.type !== "git") {
+ return getGitlabClient({ appName: env.KIE_SANDBOX_APP_NAME });
+ }
+
+ const authProvider = authProviders.find((a) => a.id === authSession.authProviderId);
+ if (authProvider?.type !== "gitlab") {
+ return getGitlabClient({ appName: env.KIE_SANDBOX_APP_NAME });
+ }
+
+ return getGitlabClient({
+ appName: env.KIE_SANDBOX_APP_NAME,
+ token: authSession?.token,
+ domain: authProvider.domain,
+ proxyUrl: env.KIE_SANDBOX_CORS_PROXY_URL,
+ insecurelyDisableTlsCertificateValidation: authProvider.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: authProvider.disableEncoding,
+ });
+ }, [authProviders, authSession, env.KIE_SANDBOX_APP_NAME, env.KIE_SANDBOX_CORS_PROXY_URL]);
+}
diff --git a/packages/online-editor/src/i18n/locales/de.ts b/packages/online-editor/src/i18n/locales/de.ts
index 6f0baf32574..42b3a544985 100644
--- a/packages/online-editor/src/i18n/locales/de.ts
+++ b/packages/online-editor/src/i18n/locales/de.ts
@@ -355,6 +355,31 @@ export const de: OnlineI18n = {
},
},
},
+ gitlab: {
+ header: {
+ title: `${de_common.names.gitlab} ${de_common.names.oauth} ${de_common.terms.token}`,
+ subtitle: `Richten Sie Ihr ${de_common.names.gitlab}-Token ein, damit Sie mit ${de_common.names.gitlab} interagieren können.`,
+ },
+ footer: {
+ createNewToken: "Neues Token generieren",
+ placeHolder: "Fügen Sie hier Ihren Token ein",
+ },
+ body: {
+ learnMore: `Erfahren Sie mehr über ${de_common.names.gitlab}-Token`,
+ note: `Sie sollten ein Token mit der Berechtigung ${"'api' 'read_user' 'read_repository' 'write_repository'"} bereitstellen.`,
+ },
+ validation: {
+ scopes: {
+ helper: "Ihr Token muss die Bereiche 'api' 'read_user' 'read_repository' 'write_repository' enthalten.",
+ },
+ },
+ form: {
+ token: {
+ label: "Persönliches Zugriffstoken",
+ placeHolder: `Fügen Sie Ihr ${de_common.names.gitlab} Token ein.`,
+ },
+ },
+ },
auth: {
disclaimer: `Das von Ihnen angegebene Token wird lokal in diesem Browser gespeichert und niemals an Dritte weitergegeben..`,
error: {
@@ -666,6 +691,21 @@ export const de: OnlineI18n = {
},
},
},
+ gitlab: {
+ repository: `${de_common.names.gitlab} repository`,
+ createRepository: `Erstelle ${de_common.names.gitlab} repository`,
+ description: (workspace: string) =>
+ `Der Inhalt von '${workspace}' wird in das neue ${de_common.names.gitlab} repository übernommen.`,
+ error: {
+ formAlert: (error: string) => `Fehler beim Erstellen des Repositorys ${de_common.names.gitlab}. ${error}`,
+ },
+ form: {
+ select: {
+ label: "Das neue Repository wird unter dem folgenden Scope erstellt",
+ description: `Wählen Sie entweder Ihr Benutzerkonto oder eine ${de_common.names.gitlab} Gruppe.`,
+ },
+ },
+ },
},
createGistOrSnippetModal: {
form: {
@@ -711,6 +751,21 @@ export const de: OnlineI18n = {
},
},
},
+ gitlab: {
+ gistOrSnippet: `${de_common.names.gitlab} Snippet`,
+ create: `Erstelle ${de_common.names.gitlab} Snippet`,
+ description: (workspace: string) =>
+ `Der Inhalt des '${workspace}' wird in dem neuen ${de_common.names.gitlab} Snippet.`,
+ error: {
+ formAlert: (error: string) => `Fehler beim Erstellen von ${de_common.names.gitlab} Snippet. ${error}`,
+ },
+ form: {
+ select: {
+ label: "Wählen Sie einen Arbeitsbereich aus, unter dem das neue Snippet erstellt wird.",
+ description: "Wählen Sie entweder ein persönliches oder ein gemeinsames Projekt aus.",
+ },
+ },
+ },
},
loadOrganizationsSelect: {
bitbucket: {
@@ -721,6 +776,10 @@ export const de: OnlineI18n = {
user: "GitHub-Benutzer",
organizations: "GitHub Organisationen",
},
+ gitlab: {
+ user: `${de_common.names.gitlab} user`,
+ organizations: `${de_common.names.gitlab} Gruppen`,
+ },
},
gitStatusIndicatorActions: {
revert: {
diff --git a/packages/online-editor/src/i18n/locales/en.ts b/packages/online-editor/src/i18n/locales/en.ts
index d738fc479b3..2f6add5d92a 100644
--- a/packages/online-editor/src/i18n/locales/en.ts
+++ b/packages/online-editor/src/i18n/locales/en.ts
@@ -344,6 +344,31 @@ export const en: OnlineI18n = {
},
},
},
+ gitlab: {
+ header: {
+ title: `${en_common.names.gitlab} ${en_common.names.oauth} ${en_common.terms.token}`,
+ subtitle: `Set up your ${en_common.names.gitlab} token so you can interact with ${en_common.names.gitlab}.`,
+ },
+ footer: {
+ createNewToken: "Generate new token",
+ placeHolder: "Paste your token here",
+ },
+ body: {
+ learnMore: `Learn more about ${en_common.names.gitlab} tokens`,
+ note: `You should provide a token with the ${"'api' 'read_user' 'read_repository' 'write_repository'"} permission.`,
+ },
+ validation: {
+ scopes: {
+ helper: `Your token must include the ${"'api' 'read_user' 'read_repository' 'write_repository'"} scopes.`,
+ },
+ },
+ form: {
+ token: {
+ label: "Personal Access Token",
+ placeHolder: `Paste your ${en_common.names.gitlab} token here`,
+ },
+ },
+ },
auth: {
disclaimer: `The token you provide is locally stored in this browser and is never shared with anyone.`,
error: {
@@ -650,6 +675,21 @@ export const en: OnlineI18n = {
},
},
},
+ gitlab: {
+ repository: `${en_common.names.gitlab} repository`,
+ createRepository: `Create ${en_common.names.gitlab} repository`,
+ description: (workspace: string) =>
+ `The contents of '${workspace}' will be all in the new ${en_common.names.gitlab} repository.`,
+ error: {
+ formAlert: (error: string) => `Error creating ${en_common.names.gitlab} repository. ${error}`,
+ },
+ form: {
+ select: {
+ label: "The new repository will be created under the following scope",
+ description: `Pick either your user account or a ${en_common.names.gitlab} group.`,
+ },
+ },
+ },
},
createGistOrSnippetModal: {
form: {
@@ -695,6 +735,21 @@ export const en: OnlineI18n = {
},
},
},
+ gitlab: {
+ gistOrSnippet: `${en_common.names.gitlab} Snippet`,
+ create: `Create ${en_common.names.gitlab} Snippet`,
+ description: (workspace: string) =>
+ `The contents of '${workspace}' will be all in the new ${en_common.names.gitlab} Snippet.`,
+ error: {
+ formAlert: (error: string) => `Error creating ${en_common.names.gitlab} Snippet. ${error}`,
+ },
+ form: {
+ select: {
+ label: "Pick a project under which the new Snippet will be created.",
+ description: "Pick either a personal or shared project.",
+ },
+ },
+ },
},
loadOrganizationsSelect: {
bitbucket: {
@@ -705,6 +760,10 @@ export const en: OnlineI18n = {
user: "GitHub user",
organizations: "GitHub organizations",
},
+ gitlab: {
+ user: `${en_common.names.gitlab} user`,
+ organizations: `${en_common.names.gitlab} groups`,
+ },
},
gitStatusIndicatorActions: {
revert: {
diff --git a/packages/online-editor/src/importFromUrl/AdvancedImportModalContent.tsx b/packages/online-editor/src/importFromUrl/AdvancedImportModalContent.tsx
index def2d146db7..a9e8c73bad7 100644
--- a/packages/online-editor/src/importFromUrl/AdvancedImportModalContent.tsx
+++ b/packages/online-editor/src/importFromUrl/AdvancedImportModalContent.tsx
@@ -171,7 +171,7 @@ export const AdvancedImportModal = React.forwardRef<AdvancedImportModalRef, Adva
aria-label="Disable TLS Certificate Validation"
tabIndex={4}
isChecked={props.insecurelyDisableTlsCertificateValidation}
- onChange={(_event, val) => props.setInsecurelyDisableTlsCertificateValidation}
+ onChange={(_event, val) => props.setInsecurelyDisableTlsCertificateValidation(val)}
/>
</FormGroup>
<FormGroup fieldId="url" label="URL" isRequired={true}>
@@ -184,16 +184,12 @@ export const AdvancedImportModal = React.forwardRef<AdvancedImportModalRef, Adva
onChange={(_event, value) => props.setUrl(value)}
/>
<HelperText>
- {props.validation.helperTextInvalid === "error" ? (
+ {props.validation.option === "error" ? (
<HelperTextItem variant="error" icon={<ExclamationCircleIcon />}>
- {props.validation.option !== ValidatedOptions.success ? (
- props.validation.helperText
- ) : (
- <FormHelperText />
- )}
+ {props.validation.helperTextInvalid}
</HelperTextItem>
) : (
- <HelperTextItem icon={props.validation.option}></HelperTextItem>
+ props.validation.option !== "success" && props.validation.helperText
)}
</HelperText>
</FormGroup>
@@ -230,11 +226,7 @@ export const AdvancedImportModal = React.forwardRef<AdvancedImportModalRef, Adva
</SelectGroup>
))}
</Select>
- <HelperText>
- <HelperTextItem variant="error" icon={ValidatedOptions.default}>
- {<FormHelperText>Loading...</FormHelperText>}
- </HelperTextItem>
- </HelperText>
+ <HelperText>{props.validation.option !== "success" && props.validation.helperText}</HelperText>
</FormGroup>
</Form>
<br />
diff --git a/packages/online-editor/src/importFromUrl/ImportFromUrlHomePageCard.tsx b/packages/online-editor/src/importFromUrl/ImportFromUrlHomePageCard.tsx
index 9d96d679901..a012eb7acea 100644
--- a/packages/online-editor/src/importFromUrl/ImportFromUrlHomePageCard.tsx
+++ b/packages/online-editor/src/importFromUrl/ImportFromUrlHomePageCard.tsx
@@ -48,6 +48,7 @@ export function ImportFromUrlCard() {
const [authSessionId, setAuthSessionId] = useState<string | undefined>(AUTH_SESSION_NONE.id);
const [url, setUrl] = useState("");
const [insecurelyDisableTlsCertificateValidation, setInsecurelyDisableTlsCertificateValidation] = useState(false);
+ const [disableEncoding, setDisableEncoding] = useState(false);
const [gitRefName, setGitRef] = useState("");
const advancedImportModalRef = useRef<AdvancedImportModalRef>(null);
@@ -56,13 +57,19 @@ export function ImportFromUrlCard() {
const importableUrl = useImportableUrl(url);
- const clonableUrl = useClonableUrl(url, authInfo, gitRefName, insecurelyDisableTlsCertificateValidation);
+ const clonableUrl = useClonableUrl(
+ url,
+ authInfo,
+ gitRefName,
+ insecurelyDisableTlsCertificateValidation,
+ disableEncoding
+ );
// Select authSession based on the importableUrl domain (begin)
const authProviders = useAuthProviders();
const { authSessions, authSessionStatus } = useAuthSessions();
- const updateInsecurelyDisableTlsCertificateValidation = useCallback(
+ const updateInsecurelyDisableTlsCertificateValidationAndDisableEncoding = useCallback(
(newAuthSession: AuthSession) => {
if (newAuthSession?.type === "git") {
const localAuthProvider = authProviders.find((provider) => provider.id === newAuthSession.authProviderId);
@@ -70,6 +77,7 @@ export function ImportFromUrlCard() {
setInsecurelyDisableTlsCertificateValidation(
localAuthProvider.insecurelyDisableTlsCertificateValidation ?? false
);
+ setDisableEncoding(localAuthProvider.disableEncoding ?? false);
}
}
},
@@ -85,8 +93,14 @@ export function ImportFromUrlCard() {
urlDomain,
});
setAuthSessionId(compatible[0]!.id);
- updateInsecurelyDisableTlsCertificateValidation(compatible[0]);
- }, [authProviders, authSessionStatus, authSessions, importableUrl, updateInsecurelyDisableTlsCertificateValidation]);
+ updateInsecurelyDisableTlsCertificateValidationAndDisableEncoding(compatible[0]);
+ }, [
+ authProviders,
+ authSessionStatus,
+ authSessions,
+ importableUrl,
+ updateInsecurelyDisableTlsCertificateValidationAndDisableEncoding,
+ ]);
// Select authSession based on the importableUrl domain (end)
useEffect(() => {
@@ -94,8 +108,8 @@ export function ImportFromUrlCard() {
}, [clonableUrl.selectedGitRefName]);
useEffect(() => {
- authSession && updateInsecurelyDisableTlsCertificateValidation(authSession);
- }, [authSession, updateInsecurelyDisableTlsCertificateValidation]);
+ authSession && updateInsecurelyDisableTlsCertificateValidationAndDisableEncoding(authSession);
+ }, [authSession, updateInsecurelyDisableTlsCertificateValidationAndDisableEncoding]);
const validation = useImportableUrlValidation(authSession, url, gitRefName, clonableUrl, advancedImportModalRef);
@@ -119,10 +133,20 @@ export function ImportFromUrlCard() {
branch: gitRefName,
authSessionId,
insecurelyDisableTlsCertificateValidation: insecurelyDisableTlsCertificateValidation.toString(),
+ disableEncoding: disableEncoding.toString(),
}),
});
},
- [authSessionId, gitRefName, navigate, isValid, routes.import, url, insecurelyDisableTlsCertificateValidation]
+ [
+ authSessionId,
+ gitRefName,
+ navigate,
+ isValid,
+ routes.import,
+ url,
+ insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
+ ]
);
const buttonLabel = useMemo(() => {
@@ -168,7 +192,7 @@ export function ImportFromUrlCard() {
<CardBody>
<TextContent>
<Text component={TextVariants.p}>
- Import a Git repository, a GitHub Gist, Bitbucket Snippet, or any other file URL.
+ Import a Git repository, a GitHub Gist, Bitbucket Snippet, GitLab Snippet or any other file URL.
</Text>
</TextContent>
<br />
diff --git a/packages/online-editor/src/importFromUrl/ImportSingleFileFromUrlForm.tsx b/packages/online-editor/src/importFromUrl/ImportSingleFileFromUrlForm.tsx
index 13e8cb7f94f..5d5b1583841 100644
--- a/packages/online-editor/src/importFromUrl/ImportSingleFileFromUrlForm.tsx
+++ b/packages/online-editor/src/importFromUrl/ImportSingleFileFromUrlForm.tsx
@@ -100,7 +100,7 @@ export function ImportSingleFileFromUrlForm(props: {
isRequired={true}
placeholder={"File URL"}
value={props.url}
- onChange={(_event, val) => props.setUrl}
+ onChange={(_event, val) => props.setUrl(val)}
/>
{validatedOption === "error" ? (
<FormHelperText>
diff --git a/packages/online-editor/src/importFromUrl/ImportableUrlHooks.tsx b/packages/online-editor/src/importFromUrl/ImportableUrlHooks.tsx
index 8b07001c383..3d00ec2a06c 100644
--- a/packages/online-editor/src/importFromUrl/ImportableUrlHooks.tsx
+++ b/packages/online-editor/src/importFromUrl/ImportableUrlHooks.tsx
@@ -28,9 +28,9 @@ import * as React from "react";
import { useCallback, useMemo } from "react";
import { matchPath } from "react-router-dom";
import { AuthProviderIcon } from "../authProviders/AuthProviderIcon";
-import { useAuthProvider } from "../authProviders/AuthProvidersContext";
+import { useAuthProvider, useAuthProviders } from "../authProviders/AuthProvidersContext";
import { AuthSession, AUTH_SESSION_NONE } from "../authSessions/AuthSessionApi";
-import { AuthInfo } from "../authSessions/AuthSessionsContext";
+import { AuthInfo, useAuthSessions } from "../authSessions/AuthSessionsContext";
import { useEditorEnvelopeLocator } from "../envelopeLocator/hooks/EditorEnvelopeLocatorContext";
import { AdvancedImportModalRef } from "./AdvancedImportModalContent";
import { getGitRefName, getGitRefType } from "../gitRefs/GitRefs";
@@ -38,8 +38,11 @@ import { PromiseStateStatus, useLivePromiseState } from "@kie-tools-core/react-h
import { useWorkspaces } from "@kie-tools-core/workspaces-git-fs/dist/context/WorkspacesContext";
import { GitServerRef } from "@kie-tools-core/workspaces-git-fs/dist/worker/api/GitServerRef";
import { GitRefTypeIcon } from "../gitRefs/GitRefTypeIcon";
+import { parseGitLabUrl } from "../gitlab/ParseGitLabUrl";
import { Icon } from "@patternfly/react-core/dist/js/components/Icon";
import { Spinner } from "@patternfly/react-core/dist/js/components/Spinner";
+import { getCompatibleAuthSessionWithUrlDomain } from "../authSessions/CompatibleAuthSessions";
+import { AuthProviderType } from "../authProviders/AuthProvidersApi";
export enum UrlType {
//git
@@ -48,12 +51,16 @@ export enum UrlType {
GIST_DOT_GITHUB_DOT_COM,
BITBUCKET_DOT_ORG,
BITBUCKET_DOT_ORG_SNIPPET,
+ GITLAB_DOT_COM,
+ GITLAB_DOT_COM_SNIPPET,
//single file
GIST_DOT_GITHUB_DOT_COM_FILE,
GITHUB_DOT_COM_FILE,
BITBUCKET_DOT_ORG_FILE,
BITBUCKET_DOT_ORG_SNIPPET_FILE,
+ GITLAB_DOT_COM_FILE,
+ GITLAB_DOT_COM_SNIPPET_FILE,
FILE,
//other
@@ -68,7 +75,9 @@ export function isCertainlyGit(urlType: UrlType): boolean {
urlType === UrlType.GITHUB_DOT_COM ||
urlType === UrlType.GIST_DOT_GITHUB_DOT_COM ||
urlType === UrlType.BITBUCKET_DOT_ORG ||
- urlType === UrlType.BITBUCKET_DOT_ORG_SNIPPET
+ urlType === UrlType.BITBUCKET_DOT_ORG_SNIPPET ||
+ urlType === UrlType.GITLAB_DOT_COM ||
+ urlType === UrlType.GITLAB_DOT_COM_SNIPPET
);
}
@@ -82,7 +91,9 @@ export function isSingleFile(urlType: UrlType) {
urlType === UrlType.GIST_DOT_GITHUB_DOT_COM_FILE ||
urlType === UrlType.GITHUB_DOT_COM_FILE ||
urlType === UrlType.BITBUCKET_DOT_ORG_FILE ||
- urlType === UrlType.BITBUCKET_DOT_ORG_SNIPPET_FILE
+ urlType === UrlType.BITBUCKET_DOT_ORG_SNIPPET_FILE ||
+ urlType === UrlType.GITLAB_DOT_COM_FILE ||
+ urlType === UrlType.GITLAB_DOT_COM_SNIPPET_FILE
);
}
@@ -157,6 +168,42 @@ export type ImportableUrl =
org: string;
filename: string;
}
+ | {
+ type: UrlType.GITLAB_DOT_COM;
+ project: string;
+ url: URL;
+ group?: string;
+ branch?: string;
+ error?: undefined;
+ }
+ | {
+ type: UrlType.GITLAB_DOT_COM_FILE;
+ error?: undefined;
+ url: URL;
+ group?: string;
+ project: string;
+ branch: string;
+ filePath: string;
+ }
+ | {
+ type: UrlType.GITLAB_DOT_COM_SNIPPET;
+ url: URL;
+ snippetId: string;
+ group?: string;
+ project?: string;
+ branch?: string;
+ error?: undefined;
+ }
+ | {
+ type: UrlType.GITLAB_DOT_COM_SNIPPET_FILE;
+ url: URL;
+ snippetId: string;
+ error?: undefined;
+ group?: string;
+ project?: string;
+ branch?: string;
+ filePath?: string;
+ }
| {
type: UrlType.NOT_SUPPORTED;
error: string;
@@ -175,6 +222,8 @@ export type ImportableUrl =
export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]): ImportableUrl {
const editorEnvelopeLocator = useEditorEnvelopeLocator();
+ const { authSessions, authSessionStatus } = useAuthSessions();
+ const authProviders = useAuthProviders();
const ifAllowed = useCallback(
(url: ImportableUrl): ImportableUrl => {
@@ -187,6 +236,40 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
[allowedUrlTypes]
);
+ const isGitRemoteDomainAllowed = useCallback(
+ (
+ url: URL,
+ hostname: string | string[],
+ authProvider: AuthProviderType.bitbucket | AuthProviderType.github | AuthProviderType.gitlab
+ ) => {
+ const hostnames = Array.isArray(hostname) ? hostname : [hostname];
+ // allow if exact match
+ if (hostnames.includes(url.host)) {
+ return true;
+ }
+ // Step 1: Determine which auth sessions are compatible with the given URL domain
+ const { compatible } = getCompatibleAuthSessionWithUrlDomain({
+ authProviders,
+ authSessions,
+ authSessionStatus,
+ urlDomain: url.host,
+ });
+ // Step 2: Find the first compatible auth provider (if any)
+ const selectedAuthProvider = authProviders?.find(
+ ({ id }) => compatible?.[0]?.type !== "none" && id === compatible?.[0]?.authProviderId
+ );
+ if (!selectedAuthProvider || selectedAuthProvider.type !== AuthProviderType[authProvider]) {
+ return false;
+ }
+ // Step 3: Validate whether the hostname is allowed for the selected provider
+ return (
+ selectedAuthProvider?.domain === url.host || // match primary domain
+ selectedAuthProvider?.supportedGitRemoteDomains?.some((supportedDomain) => supportedDomain === url.host) // match against supported domains
+ );
+ },
+ [authProviders, authSessionStatus, authSessions]
+ );
+
return useMemo(() => {
if (!urlString) {
return { type: UrlType.INVALID, error: "Empty URL" };
@@ -231,7 +314,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
const gitHubFileMatch = matchPath(
{
- path: "/:org/:repo/blob/:tree/:path*",
+ path: "/:org/:repo/blob/:tree/*",
end: true,
caseSensitive: true,
},
@@ -243,7 +326,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
gitHubFileMatch.params.org &&
gitHubFileMatch.params.repo &&
gitHubFileMatch.params.tree &&
- gitHubFileMatch.params["path*"]
+ gitHubFileMatch.params["*"]
) {
return ifAllowed({
type: UrlType.GITHUB_DOT_COM_FILE,
@@ -251,7 +334,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
org: gitHubFileMatch.params.org,
repo: gitHubFileMatch.params.repo,
branch: gitHubFileMatch.params.tree,
- filePath: gitHubFileMatch.params["path*"],
+ filePath: gitHubFileMatch.params["*"],
});
}
@@ -290,7 +373,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
const bitbucketFileMatch = matchPath(
{
- path: "/:org/:repo/src/:tree/:path*",
+ path: "/:org/:repo/src/:tree/*",
end: true,
caseSensitive: true,
},
@@ -302,7 +385,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
bitbucketFileMatch.params.org &&
bitbucketFileMatch.params.repo &&
bitbucketFileMatch.params.tree &&
- bitbucketFileMatch.params["path*"]
+ bitbucketFileMatch.params["*"]
) {
return ifAllowed({
type: UrlType.BITBUCKET_DOT_ORG_FILE,
@@ -310,7 +393,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
org: bitbucketFileMatch.params.org,
repo: bitbucketFileMatch.params.repo,
branch: bitbucketFileMatch.params.tree,
- filePath: bitbucketFileMatch.params["path*"],
+ filePath: bitbucketFileMatch.params["*"],
});
}
@@ -382,7 +465,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
if (url.host === "raw.githubusercontent.com") {
const gitHubRawFileMatch = matchPath(
{
- path: "/:org/:repo/refs/heads/:tree/:path*",
+ path: "/:org/:repo/refs/heads/:tree/*",
end: true,
caseSensitive: true,
},
@@ -394,7 +477,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
gitHubRawFileMatch.params.org &&
gitHubRawFileMatch.params.repo &&
gitHubRawFileMatch.params.tree &&
- gitHubRawFileMatch.params["path*"]
+ gitHubRawFileMatch.params["*"]
) {
return ifAllowed({
type: UrlType.GITHUB_DOT_COM_FILE,
@@ -402,7 +485,7 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
org: gitHubRawFileMatch.params.org,
repo: gitHubRawFileMatch.params.repo,
branch: gitHubRawFileMatch.params.tree,
- filePath: gitHubRawFileMatch.params["path*"],
+ filePath: gitHubRawFileMatch.params["*"],
});
}
@@ -466,6 +549,12 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
});
}
+ // Gitlab
+ if (isGitRemoteDomainAllowed(url, "gitlab.com", AuthProviderType.gitlab)) {
+ const gitlabImportUrl = parseGitLabUrl(url);
+ return ifAllowed(gitlabImportUrl);
+ }
+
const extension = extname(url.pathname).replace(".", "");
if (extension) {
if (extension === "git") {
@@ -480,21 +569,23 @@ export function useImportableUrl(urlString?: string, allowedUrlTypes?: UrlType[]
}
return { type: UrlType.UNKNOWN, url };
- }, [urlString, ifAllowed, editorEnvelopeLocator]);
+ }, [urlString, ifAllowed, editorEnvelopeLocator, isGitRemoteDomainAllowed]);
}
export function useClonableUrl(
url: string | undefined,
authInfo: AuthInfo | undefined,
gitRefName: string | undefined,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) {
const importableUrl = useImportableUrl(url);
const gitServerRefsPromise = useGitServerRefs(
isPotentiallyGit(importableUrl.type) ? importableUrl.url : undefined,
authInfo,
- insecurelyDisableTlsCertificateValidation
+ insecurelyDisableTlsCertificateValidation,
+ disableEncoding
);
const gitRefNameFromUrl = useMemo(() => {
@@ -560,7 +651,8 @@ export function useClonableUrl(
export function useGitServerRefs(
url: URL | undefined,
authInfo: AuthInfo | undefined,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) {
const workspaces = useWorkspaces();
@@ -574,6 +666,7 @@ export function useGitServerRefs(
url: url.toString(),
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding,
});
const headRef = refs.filter((f) => f.ref === "HEAD").pop()!.target!;
@@ -582,7 +675,7 @@ export function useGitServerRefs(
return { refs, defaultBranch, headRef };
};
- }, [authInfo, url, workspaces, insecurelyDisableTlsCertificateValidation])
+ }, [authInfo, url, workspaces, insecurelyDisableTlsCertificateValidation, disableEncoding])
);
return gitServerRefsPromise;
diff --git a/packages/online-editor/src/importFromUrl/NewWorkspaceFromUrlPage.tsx b/packages/online-editor/src/importFromUrl/NewWorkspaceFromUrlPage.tsx
index 107f51ae56c..3db98eea15b 100644
--- a/packages/online-editor/src/importFromUrl/NewWorkspaceFromUrlPage.tsx
+++ b/packages/online-editor/src/importFromUrl/NewWorkspaceFromUrlPage.tsx
@@ -54,6 +54,7 @@ import { PromiseStateStatus } from "@kie-tools-core/react-hooks/dist/PromiseStat
import { AUTH_SESSION_NONE } from "../authSessions/AuthSessionApi";
import { useBitbucketClient } from "../bitbucket/Hooks";
import { AuthProviderGroup } from "../authProviders/AuthProvidersApi";
+import { useGitlabClient } from "../gitlab/useGitlabClient";
export function NewWorkspaceFromUrlPage() {
const workspaces = useWorkspaces();
@@ -71,6 +72,7 @@ export function NewWorkspaceFromUrlPage() {
const queryParamInsecurelyDisableTlsCertificateValidation = useQueryParam(
QueryParams.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION
);
+ const queryParamDisableEncodingValidation = useQueryParam(QueryParams.DISABLE_ENCODING);
const queryParamConfirm = useQueryParam(QueryParams.CONFIRM);
const authProviders = useAuthProviders();
@@ -86,12 +88,20 @@ export function NewWorkspaceFromUrlPage() {
: false;
}, [queryParamInsecurelyDisableTlsCertificateValidation, authProvider]);
+ const disableEncodingValidation = useMemo(() => {
+ if (typeof queryParamDisableEncodingValidation === "string") {
+ return queryParamDisableEncodingValidation === "true";
+ }
+ return authProvider?.group === AuthProviderGroup.GIT ? authProvider.disableEncoding : false;
+ }, [queryParamDisableEncodingValidation, authProvider]);
+
const importableUrl = useImportableUrl(queryParamUrl);
const clonableUrlObject = useClonableUrl(
queryParamUrl,
authInfo,
queryParamBranch,
- insecurelyDisableTlsCertificateValidation
+ insecurelyDisableTlsCertificateValidation,
+ disableEncodingValidation
);
const { clonableUrl, selectedGitRefName, gitServerRefsPromise } = clonableUrlObject;
@@ -309,10 +319,16 @@ export function NewWorkspaceFromUrlPage() {
const gitHubClient = useGitHubClient(authSession);
const bitbucketClient = useBitbucketClient(authSession);
+ const gitlabClient = useGitlabClient(authSession);
const doImportAsSingleFile = useCallback(
async (importableUrl: ImportableUrl) => {
- const singleFileContent = await fetchSingleFileContent(importableUrl, gitHubClient, bitbucketClient);
+ const singleFileContent = await fetchSingleFileContent(
+ importableUrl,
+ gitHubClient,
+ bitbucketClient,
+ gitlabClient
+ );
if (singleFileContent.error) {
setImportingError(singleFileContent.error);
@@ -324,7 +340,7 @@ export function NewWorkspaceFromUrlPage() {
fileContents: encoder.encode(singleFileContent.content!),
});
},
- [bitbucketClient, createWorkspaceForFile, gitHubClient]
+ [bitbucketClient, createWorkspaceForFile, gitHubClient, gitlabClient]
);
const doImport = useCallback(async () => {
@@ -354,6 +370,7 @@ export function NewWorkspaceFromUrlPage() {
authInfo,
gitAuthSessionId: queryParamAuthSessionId,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding: disableEncodingValidation,
});
} else {
await doImportAsSingleFile(importableUrl);
@@ -364,6 +381,7 @@ export function NewWorkspaceFromUrlPage() {
else if (
importableUrl.type === UrlType.GITHUB_DOT_COM ||
importableUrl.type === UrlType.BITBUCKET_DOT_ORG ||
+ importableUrl.type === UrlType.GITLAB_DOT_COM ||
importableUrl.type === UrlType.GIT
) {
if (gitServerRefsPromise.data?.defaultBranch) {
@@ -377,6 +395,7 @@ export function NewWorkspaceFromUrlPage() {
gitConfig,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding: disableEncodingValidation,
});
} else {
setImportingError(`Can't clone. ${gitServerRefsPromise.error}`);
@@ -399,6 +418,7 @@ export function NewWorkspaceFromUrlPage() {
gitConfig,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding: disableEncodingValidation,
});
} else {
setImportingError(`Can't clone. ${gitServerRefsPromise.error}`);
@@ -407,12 +427,18 @@ export function NewWorkspaceFromUrlPage() {
}
// snippet
- else if (importableUrl.type === UrlType.BITBUCKET_DOT_ORG_SNIPPET) {
+ else if (
+ importableUrl.type === UrlType.BITBUCKET_DOT_ORG_SNIPPET ||
+ importableUrl.type === UrlType.GITLAB_DOT_COM_SNIPPET
+ ) {
importableUrl.url.hash = "";
if (gitServerRefsPromise.data?.defaultBranch) {
await cloneGitRepository({
origin: {
- kind: WorkspaceKind.BITBUCKET_SNIPPET,
+ kind:
+ importableUrl.type === UrlType.BITBUCKET_DOT_ORG_SNIPPET
+ ? WorkspaceKind.BITBUCKET_SNIPPET
+ : WorkspaceKind.GITLAB_SNIPPET,
url: importableUrl.url.toString(),
branch: queryParamBranch ?? selectedGitRefName ?? gitServerRefsPromise.data.defaultBranch,
},
@@ -420,6 +446,7 @@ export function NewWorkspaceFromUrlPage() {
gitConfig,
authInfo,
insecurelyDisableTlsCertificateValidation,
+ disableEncoding: disableEncodingValidation,
});
} else {
setImportingError(`Can't clone. ${gitServerRefsPromise.error}`);
@@ -453,6 +480,7 @@ export function NewWorkspaceFromUrlPage() {
doImportAsSingleFile,
queryParamBranch,
insecurelyDisableTlsCertificateValidation,
+ disableEncodingValidation,
]);
useEffect(() => {
diff --git a/packages/online-editor/src/importFromUrl/fetchSingleFileContent.ts b/packages/online-editor/src/importFromUrl/fetchSingleFileContent.ts
index 3fd104a71ef..94c998fe159 100644
--- a/packages/online-editor/src/importFromUrl/fetchSingleFileContent.ts
+++ b/packages/online-editor/src/importFromUrl/fetchSingleFileContent.ts
@@ -20,11 +20,13 @@
import { Octokit } from "@octokit/rest";
import { BitbucketClientApi } from "../bitbucket/Hooks";
import { ImportableUrl, UrlType } from "./ImportableUrlHooks";
+import { GitlabClientApi } from "../gitlab/GitlabClient";
export async function fetchSingleFileContent(
importableUrl: ImportableUrl,
gitHubClient: Octokit,
- bitbucketClient: BitbucketClientApi
+ bitbucketClient: BitbucketClientApi,
+ gitlabClient: GitlabClientApi
): Promise<{ rawUrl?: URL; content?: string; error?: string }> {
let rawUrl = importableUrl.url as URL;
if (importableUrl.type === UrlType.GITHUB_DOT_COM_FILE) {
@@ -90,6 +92,40 @@ export async function fetchSingleFileContent(
}
rawUrl = new URL(links.self.href);
}
+ if (importableUrl.type === UrlType.GITLAB_DOT_COM_FILE) {
+ const repoResponse = await gitlabClient.getRepositoryContents({
+ group: importableUrl.group,
+ project: importableUrl.project,
+ ref: importableUrl.branch,
+ path: decodeURIComponent(importableUrl.filePath),
+ });
+
+ if (!repoResponse.ok) {
+ throw new Error(`Couldn't get Gitlab repository contents: ${repoResponse.status} ${repoResponse.statusText}`);
+ }
+ const content = await repoResponse?.text();
+ if (!content) {
+ throw new Error(`Unexpected contents of Gitlab response`);
+ }
+ return { content, rawUrl: new URL(importableUrl?.url), error: undefined };
+ }
+ if (importableUrl.type === UrlType.GITLAB_DOT_COM_SNIPPET_FILE) {
+ const snippetResponse = await gitlabClient.getSnippetFileRaw({
+ group: importableUrl.group,
+ filePath: decodeURIComponent(importableUrl.filePath!),
+ snippetId: importableUrl.snippetId,
+ branch: importableUrl?.branch,
+ project: importableUrl?.project,
+ });
+ if (!snippetResponse.ok) {
+ throw new Error(`Couldn't get Gitlab snippet contents: ${snippetResponse.status} ${snippetResponse.statusText}`);
+ }
+ const content = await snippetResponse?.text();
+ if (!content) {
+ throw new Error(`Unexpected contents of Gitlab response`);
+ }
+ return { content, rawUrl: new URL(importableUrl?.url), error: undefined };
+ }
const response = await fetch(rawUrl.toString());
if (!response.ok) {
diff --git a/packages/online-editor/src/navigation/Routes.ts b/packages/online-editor/src/navigation/Routes.ts
index 2bf260c5392..23b3fa48629 100644
--- a/packages/online-editor/src/navigation/Routes.ts
+++ b/packages/online-editor/src/navigation/Routes.ts
@@ -26,6 +26,7 @@ export enum QueryParams {
EXPAND = "expand",
AUTH_SESSION_ID = "authSessionId",
INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION = "insecurelyDisableTlsCertificateValidation",
+ DISABLE_ENCODING = "disableEncoding",
CONFIRM = "confirm",
}
@@ -141,6 +142,7 @@ export const routes = {
| QueryParams.BRANCH
| QueryParams.AUTH_SESSION_ID
| QueryParams.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION
+ | QueryParams.DISABLE_ENCODING
| QueryParams.CONFIRM;
}>(() => `/import`),
diff --git a/packages/online-editor/src/workspace/components/WorkspaceLabel.tsx b/packages/online-editor/src/workspace/components/WorkspaceLabel.tsx
index c292188709c..693d489a07c 100644
--- a/packages/online-editor/src/workspace/components/WorkspaceLabel.tsx
+++ b/packages/online-editor/src/workspace/components/WorkspaceLabel.tsx
@@ -139,6 +139,19 @@ export function WorkspaceLabel(props: { descriptor?: WorkspaceDescriptor }) {
</Tooltip>
</FlexItem>
),
+ GITLAB_SNIPPET: (
+ <FlexItem>
+ <Tooltip
+ content={`'${props.descriptor?.name}' is linked to a Gitlab Snippet. ${props.descriptor?.origin.url}`}
+ position={"right"}
+ >
+ <Label>
+ <GitlabIcon />
+ Snippet
+ </Label>
+ </Tooltip>
+ </FlexItem>
+ ),
LOCAL: (
<FlexItem>
<Tooltip
diff --git a/packages/workspaces-git-fs/src/context/WorkspacesContext.tsx b/packages/workspaces-git-fs/src/context/WorkspacesContext.tsx
index ac89dc98c72..26a8071b2a4 100644
--- a/packages/workspaces-git-fs/src/context/WorkspacesContext.tsx
+++ b/packages/workspaces-git-fs/src/context/WorkspacesContext.tsx
@@ -26,7 +26,14 @@ import {
import * as React from "react";
import { createContext, useContext } from "react";
import { WorkspaceDescriptor } from "../worker/api/WorkspaceDescriptor";
-import { BitbucketOrigin, GistOrigin, GitHubOrigin, SnippetOrigin } from "../worker/api/WorkspaceOrigin";
+import {
+ BitbucketOrigin,
+ GistOrigin,
+ GitHubOrigin,
+ GitlabOrigin,
+ GitlabSnippetOrigin,
+ BitbucketSnippetOrigin,
+} from "../worker/api/WorkspaceOrigin";
import { LocalFile } from "../worker/api/LocalFile";
import { decoder } from "../encoderdecoder/EncoderDecoder";
import { parseWorkspaceFileRelativePath } from "../relativePath/WorkspaceFileRelativePathParser";
@@ -103,7 +110,7 @@ export interface WorkspacesContextType {
}>;
createWorkspaceFromGitRepository: (args: {
- origin: GistOrigin | GitHubOrigin | BitbucketOrigin | SnippetOrigin;
+ origin: GistOrigin | GitHubOrigin | BitbucketOrigin | BitbucketSnippetOrigin | GitlabOrigin | GitlabSnippetOrigin;
gitConfig?: { email: string; name: string };
gitAuthSessionId: string | undefined;
authInfo?: {
@@ -111,6 +118,7 @@ export interface WorkspacesContextType {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) => Promise<{ workspace: WorkspaceDescriptor; suggestedFirstFile?: WorkspaceFile }>;
pull(args: {
@@ -121,6 +129,7 @@ export interface WorkspacesContextType {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
push(args: {
@@ -134,6 +143,7 @@ export interface WorkspacesContextType {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
deleteBranch(args: { workspaceId: string; ref: string }): Promise<void>;
@@ -157,6 +167,7 @@ export interface WorkspacesContextType {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<GitServerRef[]>;
hasLocalChanges(args: { workspaceId: string }): Promise<boolean>;
@@ -196,6 +207,7 @@ export interface WorkspacesContextType {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<FetchResult>;
// storage
@@ -257,6 +269,7 @@ export interface WorkspacesContextType {
remoteUrl: URL;
branch?: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
initGistOnWorkspace(args: {
@@ -264,6 +277,7 @@ export interface WorkspacesContextType {
remoteUrl: URL;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
initSnippetOnWorkspace(args: {
@@ -271,12 +285,22 @@ export interface WorkspacesContextType {
remoteUrl: URL;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
+ }): Promise<void>;
+
+ initGilabSnippetOnWorkspace(args: {
+ workspaceId: string;
+ remoteUrl: URL;
+ branch: string;
+ insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
changeGitAuthSessionId(args: {
workspaceId: string;
gitAuthSessionId: string | undefined;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
initLocalOnWorkspace(args: { workspaceId: string }): Promise<void>;
diff --git a/packages/workspaces-git-fs/src/context/WorkspacesContextProvider.tsx b/packages/workspaces-git-fs/src/context/WorkspacesContextProvider.tsx
index 73b8e2078b6..84f1de1d3a0 100644
--- a/packages/workspaces-git-fs/src/context/WorkspacesContextProvider.tsx
+++ b/packages/workspaces-git-fs/src/context/WorkspacesContextProvider.tsx
@@ -22,7 +22,14 @@ import { useCallback, useMemo } from "react";
import { ResourceContentOptions } from "@kie-tools-core/workspace/dist/api";
import { WorkspaceFile, WorkspacesContext } from "./WorkspacesContext";
import { LocalFile } from "../worker/api/LocalFile";
-import { BitbucketOrigin, GistOrigin, GitHubOrigin, SnippetOrigin } from "../worker/api/WorkspaceOrigin";
+import {
+ BitbucketOrigin,
+ BitbucketSnippetOrigin,
+ GistOrigin,
+ GitHubOrigin,
+ GitlabOrigin,
+ GitlabSnippetOrigin,
+} from "../worker/api/WorkspaceOrigin";
import { WorkspaceWorkerFileDescriptor } from "../worker/api/WorkspaceWorkerFileDescriptor";
import { WorkspacesSharedWorker } from "../worker/WorkspacesSharedWorker";
@@ -105,6 +112,7 @@ export function WorkspacesContextProvider(props: Props) {
gitConfig?: { name: string; email: string };
authInfo?: { username: string; password: string };
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_pull(args)
@@ -124,6 +132,7 @@ export function WorkspacesContextProvider(props: Props) {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) => {
return workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_push(args);
@@ -170,6 +179,7 @@ export function WorkspacesContextProvider(props: Props) {
ref: string;
authInfo?: { username: string; password: string };
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_fetch(args)
@@ -261,6 +271,7 @@ export function WorkspacesContextProvider(props: Props) {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_getGitServerRefs(args)
@@ -304,7 +315,7 @@ export function WorkspacesContextProvider(props: Props) {
const createWorkspaceFromGitRepository = useCallback(
async (args: {
- origin: GistOrigin | GitHubOrigin | BitbucketOrigin | SnippetOrigin;
+ origin: GistOrigin | GitHubOrigin | BitbucketOrigin | BitbucketSnippetOrigin | GitlabOrigin | GitlabSnippetOrigin;
gitConfig?: { email: string; name: string };
gitAuthSessionId: string | undefined;
authInfo?: {
@@ -312,6 +323,7 @@ export function WorkspacesContextProvider(props: Props) {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) => {
const workspaceClone = await workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_clone(args)
@@ -519,6 +531,7 @@ export function WorkspacesContextProvider(props: Props) {
remoteUrl: URL;
branch?: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_initGitOnExistingWorkspace({
@@ -526,6 +539,7 @@ export function WorkspacesContextProvider(props: Props) {
remoteUrl: args.remoteUrl.toString(),
branch: args.branch,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
})
),
[workspacesSharedWorker]
@@ -537,6 +551,7 @@ export function WorkspacesContextProvider(props: Props) {
remoteUrl: URL;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_initGistOnExistingWorkspace({
@@ -544,6 +559,7 @@ export function WorkspacesContextProvider(props: Props) {
remoteUrl: args.remoteUrl.toString(),
branch: args.branch,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
})
),
[workspacesSharedWorker]
@@ -555,6 +571,7 @@ export function WorkspacesContextProvider(props: Props) {
remoteUrl: URL;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) =>
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_initSnippetOnExistingWorkspace({
@@ -562,6 +579,27 @@ export function WorkspacesContextProvider(props: Props) {
remoteUrl: args.remoteUrl.toString(),
branch: args.branch,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
+ })
+ ),
+ [workspacesSharedWorker]
+ );
+
+ const initGilabSnippetOnWorkspace = useCallback(
+ async (args: {
+ workspaceId: string;
+ remoteUrl: URL;
+ branch: string;
+ insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
+ }) =>
+ workspacesSharedWorker.withBus((workspacesWorkerBus) =>
+ workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_initGitlabSnippetOnExistingWorkspace({
+ workspaceId: args.workspaceId,
+ remoteUrl: args.remoteUrl.toString(),
+ branch: args.branch,
+ insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
})
),
[workspacesSharedWorker]
@@ -572,12 +610,14 @@ export function WorkspacesContextProvider(props: Props) {
workspaceId: string;
gitAuthSessionId: string | undefined;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) => {
workspacesSharedWorker.withBus((workspacesWorkerBus) =>
workspacesWorkerBus.clientApi.requests.kieSandboxWorkspacesGit_changeGitAuthSessionId({
workspaceId: args.workspaceId,
gitAuthSessionId: args.gitAuthSessionId,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
})
);
},
@@ -635,6 +675,7 @@ export function WorkspacesContextProvider(props: Props) {
initGitOnWorkspace,
initGistOnWorkspace,
initSnippetOnWorkspace,
+ initGilabSnippetOnWorkspace,
changeGitAuthSessionId,
initLocalOnWorkspace,
isFileModified,
@@ -680,6 +721,7 @@ export function WorkspacesContextProvider(props: Props) {
initGitOnWorkspace,
initGistOnWorkspace,
initSnippetOnWorkspace,
+ initGilabSnippetOnWorkspace,
changeGitAuthSessionId,
initLocalOnWorkspace,
isFileModified,
diff --git a/packages/workspaces-git-fs/src/services/GitService.tsx b/packages/workspaces-git-fs/src/services/GitService.tsx
index 31bdbd5adc7..f23dca523f1 100644
--- a/packages/workspaces-git-fs/src/services/GitService.tsx
+++ b/packages/workspaces-git-fs/src/services/GitService.tsx
@@ -37,6 +37,7 @@ export interface CloneArgs {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}
export interface CommitArgs {
@@ -62,6 +63,7 @@ export interface PushArgs {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}
export interface RemoteRefArgs {
@@ -73,6 +75,7 @@ export interface RemoteRefArgs {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}
export enum FileModificationStatus {
@@ -87,14 +90,22 @@ export type UnstagedModifiedFilesStatusEntryType = {
export class GitService {
public constructor(private readonly corsProxy: Promise<string>) {}
- private getRequestHeaders(args: { insecurelyDisableTlsCertificateValidation?: boolean }) {
- return args.insecurelyDisableTlsCertificateValidation
- ? {
- [CorsProxyHeaderKeys.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION]: Boolean(
- args.insecurelyDisableTlsCertificateValidation
- ).toString(),
- }
- : undefined;
+ private getRequestHeaders(args: { insecurelyDisableTlsCertificateValidation?: boolean; disableEncoding?: boolean }) {
+ if (!args.insecurelyDisableTlsCertificateValidation && !args.disableEncoding) {
+ return undefined;
+ }
+ const headers: Record<string, string> = {
+ ...(args.insecurelyDisableTlsCertificateValidation && {
+ [CorsProxyHeaderKeys.INSECURELY_DISABLE_TLS_CERTIFICATE_VALIDATION]: Boolean(
+ args.insecurelyDisableTlsCertificateValidation
+ ).toString(),
+ }),
+ // If disableEncoding is true, force proxy/server to skip compression
+ ...(args.disableEncoding
+ ? { [CorsProxyHeaderKeys.DISABLE_ENCODING]: Boolean(args.disableEncoding).toString() }
+ : {}),
+ };
+ return headers;
}
public async listServerRefs(args: {
@@ -104,6 +115,7 @@ export class GitService {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) {
return git.listServerRefs({
http,
@@ -175,6 +187,7 @@ export class GitService {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<FetchResult> {
return await git.fetch({
fs: args.fs,
@@ -254,6 +267,7 @@ export class GitService {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}) {
await git.pull({
fs: args.fs,
@@ -314,6 +328,7 @@ export class GitService {
remoteRef: args.remoteRef,
authInfo: args.authInfo,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
});
if (serverRemoteRef?.oid && head === serverRemoteRef.oid) return;
diff --git a/packages/workspaces-git-fs/src/services/WorkspaceDescriptorService.tsx b/packages/workspaces-git-fs/src/services/WorkspaceDescriptorService.tsx
index 97a2479734b..c69d0d8fbf2 100644
--- a/packages/workspaces-git-fs/src/services/WorkspaceDescriptorService.tsx
+++ b/packages/workspaces-git-fs/src/services/WorkspaceDescriptorService.tsx
@@ -76,6 +76,7 @@ export class WorkspaceDescriptorService {
preferredName?: string;
gitAuthSessionId: string | undefined;
gitInsecurelyDisableTlsCertificateValidation?: boolean;
+ gitDisableEncoding?: boolean;
}) {
const workspace: WorkspaceDescriptor = {
workspaceId: this.newWorkspaceId(),
@@ -85,6 +86,7 @@ export class WorkspaceDescriptorService {
lastUpdatedDateISO: new Date().toISOString(),
gitAuthSessionId: args.gitAuthSessionId,
gitInsecurelyDisableTlsCertificateValidation: args.gitInsecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: args.gitDisableEncoding,
};
await this.storageService.createOrOverwriteFile(args.fs, this.toStorageFile(workspace));
return workspace;
@@ -107,7 +109,8 @@ export class WorkspaceDescriptorService {
workspaceId: string,
gistUrl: URL,
branch: string,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) {
const file = this.toStorageFile({
...(await this.get(fs, workspaceId)),
@@ -117,6 +120,7 @@ export class WorkspaceDescriptorService {
branch,
},
gitInsecurelyDisableTlsCertificateValidation: insecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: disableEncoding,
});
await this.storageService.updateFile(fs, file.path, file.getFileContents);
@@ -135,7 +139,8 @@ export class WorkspaceDescriptorService {
workspaceId: string,
snippetUrl: URL,
branch: string,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) {
const file = this.toStorageFile({
...(await this.get(fs, workspaceId)),
@@ -145,6 +150,38 @@ export class WorkspaceDescriptorService {
branch,
},
gitInsecurelyDisableTlsCertificateValidation: insecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: disableEncoding,
+ });
+ await this.storageService.updateFile(fs, file.path, file.getFileContents);
+
+ new Broadcaster().broadcast({
+ channel: workspaceId,
+ message: async () => ({ type: "WS_UPDATE_DESCRIPTOR" }),
+ });
+
+ new Broadcaster().broadcast({
+ channel: WORKSPACES_BROADCAST_CHANNEL,
+ message: async () => ({ type: "WSS_UPDATE", workspaceId }),
+ });
+ }
+
+ public async turnIntoGitlabSnippet(
+ fs: KieSandboxWorkspacesFs,
+ workspaceId: string,
+ snippetUrl: URL,
+ branch: string,
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
+ ) {
+ const file = this.toStorageFile({
+ ...(await this.get(fs, workspaceId)),
+ origin: {
+ kind: WorkspaceKind.GITLAB_SNIPPET,
+ url: snippetUrl.toString(),
+ branch,
+ },
+ gitInsecurelyDisableTlsCertificateValidation: insecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: disableEncoding,
});
await this.storageService.updateFile(fs, file.path, file.getFileContents);
@@ -164,7 +201,8 @@ export class WorkspaceDescriptorService {
workspaceId: string,
url: URL,
branch?: string,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) {
const file = this.toStorageFile({
...(await this.get(fs, workspaceId)),
@@ -174,6 +212,7 @@ export class WorkspaceDescriptorService {
branch: branch ?? GIT_DEFAULT_BRANCH,
},
gitInsecurelyDisableTlsCertificateValidation: insecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: disableEncoding,
});
await this.storageService.updateFile(fs, file.path, file.getFileContents);
@@ -192,12 +231,14 @@ export class WorkspaceDescriptorService {
fs: KieSandboxWorkspacesFs,
workspaceId: string,
gitAuthSessionId: string | undefined,
- insecurelyDisableTlsCertificateValidation?: boolean
+ insecurelyDisableTlsCertificateValidation?: boolean,
+ disableEncoding?: boolean
) {
const file = this.toStorageFile({
...(await this.get(fs, workspaceId)),
gitAuthSessionId,
gitInsecurelyDisableTlsCertificateValidation: insecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: disableEncoding,
});
await this.storageService.updateFile(fs, file.path, file.getFileContents);
diff --git a/packages/workspaces-git-fs/src/services/WorkspaceService.tsx b/packages/workspaces-git-fs/src/services/WorkspaceService.tsx
index 796f5e4a78a..41d69fa3196 100644
--- a/packages/workspaces-git-fs/src/services/WorkspaceService.tsx
+++ b/packages/workspaces-git-fs/src/services/WorkspaceService.tsx
@@ -56,6 +56,7 @@ export class WorkspaceService {
preferredName?: string;
gitAuthSessionId: string | undefined;
gitInsecurelyDisableTlsCertificateValidation?: boolean;
+ gitDisableEncoding?: boolean;
}) {
const workspace = await this.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
return this.workspaceDescriptorService.create({
@@ -64,6 +65,7 @@ export class WorkspaceService {
preferredName: args.preferredName,
gitAuthSessionId: args.gitAuthSessionId,
gitInsecurelyDisableTlsCertificateValidation: args.gitInsecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: args.gitDisableEncoding,
});
});
diff --git a/packages/workspaces-git-fs/src/worker/WorkspacesWorkerApiImpl.ts b/packages/workspaces-git-fs/src/worker/WorkspacesWorkerApiImpl.ts
index 04d37442a65..9954bf79f3f 100644
--- a/packages/workspaces-git-fs/src/worker/WorkspacesWorkerApiImpl.ts
+++ b/packages/workspaces-git-fs/src/worker/WorkspacesWorkerApiImpl.ts
@@ -36,9 +36,11 @@ import { LocalFile } from "./api/LocalFile";
import { WorkspaceDescriptor } from "./api/WorkspaceDescriptor";
import {
BitbucketOrigin,
+ BitbucketSnippetOrigin,
GistOrigin,
GitHubOrigin,
- SnippetOrigin,
+ GitlabOrigin,
+ GitlabSnippetOrigin,
WorkspaceKind,
WorkspaceOrigin,
} from "./api/WorkspaceOrigin";
@@ -77,13 +79,15 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
workspaceId: string;
gitAuthSessionId: string | undefined;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void> {
return this.args.services.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
return this.args.services.descriptorService.changeGitAuthSessionId(
fs,
args.workspaceId,
args.gitAuthSessionId,
- args.insecurelyDisableTlsCertificateValidation
+ args.insecurelyDisableTlsCertificateValidation,
+ args.disableEncoding
);
});
}
@@ -93,6 +97,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
remoteUrl: string;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void> {
return this.args.services.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
return this.args.services.descriptorService.turnIntoGist(
@@ -100,7 +105,8 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
args.workspaceId,
new URL(args.remoteUrl),
args.branch,
- args.insecurelyDisableTlsCertificateValidation
+ args.insecurelyDisableTlsCertificateValidation,
+ args.disableEncoding
);
});
}
@@ -110,6 +116,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
remoteUrl: string;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void> {
return this.args.services.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
return this.args.services.descriptorService.turnIntoSnippet(
@@ -117,7 +124,27 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
args.workspaceId,
new URL(args.remoteUrl),
args.branch,
- args.insecurelyDisableTlsCertificateValidation
+ args.insecurelyDisableTlsCertificateValidation,
+ args.disableEncoding
+ );
+ });
+ }
+
+ public async kieSandboxWorkspacesGit_initGitlabSnippetOnExistingWorkspace(args: {
+ workspaceId: string;
+ remoteUrl: string;
+ branch: string;
+ insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
+ }): Promise<void> {
+ return this.args.services.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
+ return this.args.services.descriptorService.turnIntoGitlabSnippet(
+ fs,
+ args.workspaceId,
+ new URL(args.remoteUrl),
+ args.branch,
+ args.insecurelyDisableTlsCertificateValidation,
+ args.disableEncoding
);
});
}
@@ -127,6 +154,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
remoteUrl: string;
branch?: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void> {
return this.args.services.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
return this.args.services.descriptorService.turnIntoGit(
@@ -134,7 +162,8 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
args.workspaceId,
new URL(args.remoteUrl),
args.branch,
- args.insecurelyDisableTlsCertificateValidation
+ args.insecurelyDisableTlsCertificateValidation,
+ args.disableEncoding
);
});
}
@@ -506,16 +535,18 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<GitServerRef[]> {
return this.args.services.gitService.listServerRefs(args);
}
public async kieSandboxWorkspacesGit_clone(args: {
- origin: GistOrigin | GitHubOrigin | BitbucketOrigin | SnippetOrigin;
+ origin: GistOrigin | GitHubOrigin | BitbucketOrigin | BitbucketSnippetOrigin | GitlabOrigin | GitlabSnippetOrigin;
gitConfig?: { email: string; name: string };
authInfo?: { username: string; password: string };
gitAuthSessionId: string | undefined;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<{ workspace: WorkspaceDescriptor; suggestedFirstFile?: WorkspaceWorkerFileDescriptor }> {
console.log(args);
return this.createWorkspace({
@@ -523,6 +554,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
origin: args.origin,
gitAuthSessionId: args.gitAuthSessionId,
gitInsecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: args.disableEncoding,
storeFiles: async (fs, schema, workspace) => {
await this.args.services.gitService.clone({
fs,
@@ -532,6 +564,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
authInfo: args.authInfo,
sourceBranch: args.origin.branch,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
});
return this.args.services.workspaceService.getFilteredWorkspaceFileDescriptors(schema, workspace.workspaceId);
},
@@ -651,6 +684,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
remote: string;
ref: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<FetchResult> {
return this.args.services.workspaceFsService.withReadWriteInMemoryFs(
args.workspaceId,
@@ -670,12 +704,14 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
gitAuthSessionId: string | undefined;
gitConfig?: { email: string; name: string };
gitInsecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<{ workspace: WorkspaceDescriptor; suggestedFirstFile?: WorkspaceWorkerFileDescriptor }> {
return this.createWorkspace({
preferredName: args.preferredName,
origin: { kind: WorkspaceKind.LOCAL, branch: GIT_DEFAULT_BRANCH },
gitAuthSessionId: args.gitAuthSessionId,
gitInsecurelyDisableTlsCertificateValidation: args.gitInsecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: args.disableEncoding,
storeFiles: async (fs, schema, workspace) => {
const files = args.localFiles
.filter((file) => !file.path.startsWith(".git/"))
@@ -752,6 +788,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
gitConfig?: { email: string; name: string };
authInfo?: { username: string; password: string };
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void> {
const workspace = await this.args.services.descriptorsFsService.withReadWriteInMemoryFs(({ fs }) => {
return this.args.services.descriptorService.get(fs, args.workspaceId);
@@ -770,6 +807,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
},
authInfo: args.authInfo,
insecurelyDisableTlsCertificateValidation: args.insecurelyDisableTlsCertificateValidation,
+ disableEncoding: args.disableEncoding,
});
broadcaster.broadcast({
@@ -794,6 +832,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void> {
return this.args.services.workspaceFsService.withReadWriteInMemoryFs(
args.workspaceId,
@@ -864,6 +903,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
preferredName?: string;
gitAuthSessionId: string | undefined;
gitInsecurelyDisableTlsCertificateValidation?: boolean;
+ gitDisableEncoding?: boolean;
}) {
const { workspace, files } = await this.args.services.workspaceService.create({
storeFiles: args.storeFiles,
@@ -871,6 +911,7 @@ export class WorkspacesWorkerApiImpl implements WorkspacesWorkerApi {
preferredName: args.preferredName,
gitAuthSessionId: args.gitAuthSessionId,
gitInsecurelyDisableTlsCertificateValidation: args.gitInsecurelyDisableTlsCertificateValidation,
+ gitDisableEncoding: args.gitDisableEncoding,
});
if (files.length <= 0) {
diff --git a/packages/workspaces-git-fs/src/worker/api/WorkspaceDescriptor.tsx b/packages/workspaces-git-fs/src/worker/api/WorkspaceDescriptor.tsx
index d729fc5aa5c..51c72b7f7cf 100644
--- a/packages/workspaces-git-fs/src/worker/api/WorkspaceDescriptor.tsx
+++ b/packages/workspaces-git-fs/src/worker/api/WorkspaceDescriptor.tsx
@@ -29,4 +29,5 @@ export interface WorkspaceDescriptor {
lastUpdatedDateISO: string;
gitAuthSessionId: string | undefined;
gitInsecurelyDisableTlsCertificateValidation?: boolean;
+ gitDisableEncoding?: boolean;
}
diff --git a/packages/workspaces-git-fs/src/worker/api/WorkspaceOrigin.tsx b/packages/workspaces-git-fs/src/worker/api/WorkspaceOrigin.tsx
index 5e5d3e7286a..4a9dc1f80c2 100644
--- a/packages/workspaces-git-fs/src/worker/api/WorkspaceOrigin.tsx
+++ b/packages/workspaces-git-fs/src/worker/api/WorkspaceOrigin.tsx
@@ -24,9 +24,15 @@ export enum WorkspaceKind {
GIT = "GIT",
LOCAL = "LOCAL",
BITBUCKET_SNIPPET = "BITBUCKET_SNIPPET",
+ GITLAB_SNIPPET = "GITLAB_SNIPPET",
}
-const gitBasedTypeKeys = [WorkspaceKind.GIT, WorkspaceKind.BITBUCKET_SNIPPET, WorkspaceKind.GITHUB_GIST] as const;
+const gitBasedTypeKeys = [
+ WorkspaceKind.GIT,
+ WorkspaceKind.BITBUCKET_SNIPPET,
+ WorkspaceKind.GITHUB_GIST,
+ WorkspaceKind.GITLAB_SNIPPET,
+] as const;
export type WorkspaceKindGitBased = (typeof gitBasedTypeKeys)[number];
export const isGitBasedWorkspaceKind = (
@@ -35,7 +41,11 @@ export const isGitBasedWorkspaceKind = (
return gitBasedTypeKeys.some((k) => k === maybeGitBasedType);
};
-const gistLikeTypeKeys = [WorkspaceKind.GITHUB_GIST, WorkspaceKind.BITBUCKET_SNIPPET] as const;
+const gistLikeTypeKeys = [
+ WorkspaceKind.GITHUB_GIST,
+ WorkspaceKind.BITBUCKET_SNIPPET,
+ WorkspaceKind.GITLAB_SNIPPET,
+] as const;
export type WorkspaceKindGistLike = (typeof gistLikeTypeKeys)[number];
export const isGistLikeWorkspaceKind = (
@@ -44,7 +54,14 @@ export const isGistLikeWorkspaceKind = (
return gistLikeTypeKeys.some((k) => k === maybeGistLikeType);
};
-export type WorkspaceOrigin = LocalOrigin | GistOrigin | GitHubOrigin | BitbucketOrigin | SnippetOrigin;
+export type WorkspaceOrigin =
+ | LocalOrigin
+ | GistOrigin
+ | GitHubOrigin
+ | BitbucketOrigin
+ | BitbucketSnippetOrigin
+ | GitlabOrigin
+ | GitlabSnippetOrigin;
export interface LocalOrigin {
kind: WorkspaceKind.LOCAL;
@@ -70,8 +87,20 @@ export interface GistOrigin {
branch: string;
}
-export interface SnippetOrigin {
+export interface BitbucketSnippetOrigin {
kind: WorkspaceKind.BITBUCKET_SNIPPET;
url: string;
branch: string;
}
+
+export interface GitlabOrigin {
+ kind: WorkspaceKind.GIT;
+ url: string;
+ branch: string;
+}
+
+export interface GitlabSnippetOrigin {
+ kind: WorkspaceKind.GITLAB_SNIPPET;
+ url: string;
+ branch: string;
+}
diff --git a/packages/workspaces-git-fs/src/worker/api/WorkspacesWorkerGitApi.ts b/packages/workspaces-git-fs/src/worker/api/WorkspacesWorkerGitApi.ts
index c240208ef1c..31b5ed4450c 100644
--- a/packages/workspaces-git-fs/src/worker/api/WorkspacesWorkerGitApi.ts
+++ b/packages/workspaces-git-fs/src/worker/api/WorkspacesWorkerGitApi.ts
@@ -18,7 +18,14 @@
*/
import { WorkspaceDescriptor } from "./WorkspaceDescriptor";
-import { BitbucketOrigin, GistOrigin, GitHubOrigin, SnippetOrigin } from "./WorkspaceOrigin";
+import {
+ BitbucketOrigin,
+ BitbucketSnippetOrigin,
+ GistOrigin,
+ GitHubOrigin,
+ GitlabOrigin,
+ GitlabSnippetOrigin,
+} from "./WorkspaceOrigin";
import { LocalFile } from "./LocalFile";
import { WorkspaceWorkerFileDescriptor } from "./WorkspaceWorkerFileDescriptor";
import { GitServerRef } from "./GitServerRef";
@@ -33,6 +40,7 @@ export interface WorkspacesWorkerGitApi {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<GitServerRef[]>;
kieSandboxWorkspacesGit_hasLocalChanges(args: { workspaceId: string }): Promise<boolean>;
@@ -56,7 +64,7 @@ export interface WorkspacesWorkerGitApi {
}>;
kieSandboxWorkspacesGit_clone(args: {
- origin: GistOrigin | GitHubOrigin | BitbucketOrigin | SnippetOrigin;
+ origin: GistOrigin | GitHubOrigin | BitbucketOrigin | BitbucketSnippetOrigin | GitlabOrigin | GitlabSnippetOrigin;
gitAuthSessionId: string | undefined;
gitConfig?: {
email: string;
@@ -67,6 +75,7 @@ export interface WorkspacesWorkerGitApi {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<{
workspace: WorkspaceDescriptor;
suggestedFirstFile?: WorkspaceWorkerFileDescriptor;
@@ -83,6 +92,7 @@ export interface WorkspacesWorkerGitApi {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
kieSandboxWorkspacesGit_push(args: {
@@ -96,6 +106,7 @@ export interface WorkspacesWorkerGitApi {
password: string;
};
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
kieSandboxWorkspacesGit_deleteBranch(args: { workspaceId: string; ref: string }): Promise<void>;
@@ -150,6 +161,7 @@ export interface WorkspacesWorkerGitApi {
};
ref: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<FetchResult>;
kieSandboxWorkspacesGit_initGitOnExistingWorkspace(args: {
@@ -157,6 +169,7 @@ export interface WorkspacesWorkerGitApi {
remoteUrl: string;
branch?: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
kieSandboxWorkspacesGit_initGistOnExistingWorkspace(args: {
@@ -164,6 +177,7 @@ export interface WorkspacesWorkerGitApi {
remoteUrl: string;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
kieSandboxWorkspacesGit_initSnippetOnExistingWorkspace(args: {
@@ -171,12 +185,22 @@ export interface WorkspacesWorkerGitApi {
remoteUrl: string;
branch: string;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
+ }): Promise<void>;
+
+ kieSandboxWorkspacesGit_initGitlabSnippetOnExistingWorkspace(args: {
+ workspaceId: string;
+ remoteUrl: string;
+ branch: string;
+ insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
kieSandboxWorkspacesGit_changeGitAuthSessionId(args: {
workspaceId: string;
gitAuthSessionId: string | undefined;
insecurelyDisableTlsCertificateValidation?: boolean;
+ disableEncoding?: boolean;
}): Promise<void>;
kieSandboxWorkspacesGit_initLocalOnExistingWorkspace(args: { workspaceId: string }): Promise<void>;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org