Compare commits

...

6 Commits

Author SHA1 Message Date
codegen-sh[bot] 739290b5fc fix: Fix TypeScript error in naturalSort.ts by ensuring string conversion 2025-08-19 01:17:07 +00:00
codegen-sh[bot] cd67566e3e Fix ESLint issues: Add missing React keys to fragments in ApiKeyListItem and OAuthAuthorize components 2025-08-19 00:57:59 +00:00
codegen-sh[bot] 7e9ce2fc64 fix: Update React import in GitLabIssueStatusIcon to match other components 2025-08-19 00:35:46 +00:00
codegen-sh[bot] c0ff5aa55b fix: Fix TypeScript errors in GitLab integration
- Fix LazyComponent type in client/index.tsx
- Remove unused contentType variable in UploadGitLabProjectAvatarTask.ts
- Remove unused UnfurlIssueOrPR import in gitlab.ts
2025-08-19 00:34:39 +00:00
codegen-sh[bot] 68bc6d20af fix: Replace any with Record<string, unknown> in OIDCStrategy 2025-08-19 00:18:27 +00:00
codegen-sh[bot] 27f003d9c9 Fix: Change queryString import to use namespace import 2025-08-19 00:13:11 +00:00
9 changed files with 88 additions and 49 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
import { useState, useRef, useEffect } from "react";
import React, { useState, useRef, useEffect } from "react";
import { Trans, useTranslation } from "react-i18next";
import styled from "styled-components";
import Flex from "@shared/components/Flex";
@@ -132,10 +132,10 @@ function Authorize() {
{t("Required OAuth parameters are missing")}
<Pre>
{missingParams.map((param) => (
<>
<React.Fragment key={param}>
{param}
<br />
</>
</React.Fragment>
))}
</Pre>
</Text>
@@ -50,10 +50,10 @@ const ApiKeyListItem = ({ apiKey }: Props) => {
{apiKey.scope && (
<Tooltip
content={apiKey.scope.map((s) => (
<>
<React.Fragment key={s}>
{s}
<br />
</>
</React.Fragment>
))}
>
<Text type="tertiary">{t("Restricted scope")}</Text>
+2 -2
View File
@@ -1,4 +1,4 @@
import * as React from "react";
import { createLazyComponent } from "~/components/LazyLoad";
import { Hook, PluginManager } from "~/utils/PluginManager";
import config from "../plugin.json";
import Icon from "./Icon";
@@ -10,7 +10,7 @@ PluginManager.add([
value: {
group: "Integrations",
icon: Icon,
component: React.lazy(() => import("./Settings")),
component: createLazyComponent(() => import("./Settings")),
},
},
]);
+41 -21
View File
@@ -7,7 +7,7 @@ import {
import Logger from "@server/logging/Logger";
import { Integration } from "@server/models";
import User from "@server/models/User";
import { UnfurlIssueOrPR, UnfurlSignature } from "@server/types";
import { UnfurlSignature } from "@server/types";
import { GitLabUtils } from "../shared/GitLabUtils";
import env from "./env";
@@ -197,31 +197,51 @@ export class GitLab {
// Fetch labels if they exist
let labels = [];
if (data.labels && data.labels.length > 0) {
labels = data.labels.map((label) => ({
labels = data.labels.map((label: string) => ({
name: label,
color: "#428BCA", // Default GitLab blue
}));
}
return {
type: resourceType,
url,
id: `#${data.iid}`,
title: data.title,
description: data.description,
author: {
name: data.author.name,
avatarUrl: data.author.avatar_url || "",
},
labels,
state: {
name: data.state,
color: data.state === "opened" ? "#1aaa55" : "#db3b21", // Green for open, red for closed
draft:
resourceType === UnfurlResourceType.PR ? data.draft : undefined,
},
createdAt: data.created_at,
} satisfies UnfurlIssueOrPR;
// Create the appropriate response based on the resource type
if (resourceType === UnfurlResourceType.Issue) {
return {
type: UnfurlResourceType.Issue,
url,
id: `#${data.iid}`,
title: data.title,
description: data.description,
author: {
name: data.author.name,
avatarUrl: data.author.avatar_url || "",
},
labels,
state: {
name: data.state,
color: data.state === "opened" ? "#1aaa55" : "#db3b21", // Green for open, red for closed
},
createdAt: data.created_at,
};
} else {
return {
type: UnfurlResourceType.PR,
url,
id: `#${data.iid}`,
title: data.title,
description: data.description,
author: {
name: data.author.name,
avatarUrl: data.author.avatar_url || "",
},
labels,
state: {
name: data.state,
color: data.state === "opened" ? "#1aaa55" : "#db3b21", // Green for open, red for closed
draft: !!data.draft,
},
createdAt: data.created_at,
};
}
} catch (err) {
Logger.warn("Failed to fetch resource from GitLab", err);
return { error: err.message || "Unknown error" };
@@ -1,14 +1,31 @@
import { IntegrationType } from "@shared/types";
import BaseTask from "@server/queues/tasks/BaseTask";
import { Integration } from "@server/models";
import { FileOperation } from "@server/models";
import fetch from "node-fetch";
import Logger from "@server/logging/Logger";
import {
FileOperationState,
FileOperationType,
FileOperationFormat,
} from "@shared/types";
import { v4 as uuidv4 } from "uuid";
type Props = {
integrationId: string;
avatarUrl: string;
};
// Define a type for GitLab settings
interface GitLabSettings {
gitlab: {
project?: {
avatar_url?: string;
[key: string]: unknown;
};
[key: string]: unknown;
};
}
export default class UploadGitLabProjectAvatarTask extends BaseTask<Props> {
public async perform({ integrationId, avatarUrl }: Props) {
const integration = await Integration.findByPk(integrationId, {
@@ -19,38 +36,40 @@ export default class UploadGitLabProjectAvatarTask extends BaseTask<Props> {
const res = await fetch(avatarUrl);
const buffer = await res.buffer();
const name = avatarUrl.split("/").pop() || "avatar";
const contentType = res.headers.get("content-type") || "image/png";
const operation = await FileOperation.createFromBuffer({
buffer,
contentType,
name,
// Create a file operation with the correct parameters
const operation = await FileOperation.create({
type: FileOperationType.Import,
state: FileOperationState.Creating,
format: FileOperationFormat.JSON, // Use a valid FileOperationFormat
key: `uploads/${integration.teamId}/${uuidv4()}/${name}`,
userId: integration.userId,
teamId: integration.teamId,
source: "gitlab",
size: buffer.length,
});
// Cast the settings to our GitLabSettings interface
const currentSettings = integration.settings as unknown as GitLabSettings;
// Update the integration settings with the avatar URL
await integration.update({
settings: {
...integration.settings,
gitlab: {
...(integration.settings as Integration<IntegrationType.Embed>)
.gitlab,
...currentSettings.gitlab,
project: {
...(integration.settings as Integration<IntegrationType.Embed>)
.gitlab?.project,
...currentSettings.gitlab?.project,
avatar_url: operation.url,
},
},
},
} as Record<string, unknown>,
});
} catch (err) {
} catch (err: unknown) {
// If the avatar upload fails, we don't need to fail the entire task
// as it's not critical to the integration's functionality.
// Just log the error and continue.
this.logger.error(
`Failed to upload GitLab project avatar: ${err.message}`
);
const error = err instanceof Error ? err : new Error(String(err));
Logger.error("Failed to upload GitLab project avatar", error);
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import queryString from "query-string";
import * as queryString from "query-string";
import env from "@shared/env";
import { integrationSettingsPath } from "@shared/utils/routeHelpers";
+2 -2
View File
@@ -15,13 +15,13 @@ export class OIDCStrategy extends Strategy {
}
}
authenticate(req: Request, options?: any) {
authenticate(req: Request, options?: Record<string, unknown>) {
options = options || {};
options.originalQuery = req.query;
super.authenticate(req, options);
}
authorizationParams(options: any) {
authorizationParams(options: Record<string, unknown>) {
return {
...options.originalQuery,
...super.authorizationParams?.(options),
@@ -1,4 +1,4 @@
import React from "react";
import * as React from "react";
import { BaseIconProps } from ".";
export function GitLabIssueStatusIcon(props: BaseIconProps) {
+1 -1
View File
@@ -22,7 +22,7 @@ function getSortByField<T extends Record<string, unknown>>(
typeof keyOrCallback === "string"
? item[keyOrCallback]
: keyOrCallback(item);
return cleanValue(field);
return cleanValue(String(field));
}
function naturalSortBy<T extends Record<string, unknown>>(