Skip to content

Commit 470432a

Browse files
authored
Merge pull request #19084 from arash77/add-secrets-to-tools
[25.1] Support credentials(secrets/variables) in tool requirements
2 parents 7976705 + 4c731d5 commit 470432a

File tree

64 files changed

+8159
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+8159
-53
lines changed

client/src/api/schema/schema.ts

Lines changed: 645 additions & 0 deletions
Large diffs are not rendered by default.

client/src/api/tools.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ export type NestedElementItem = NestedElementItems[number];
1111
export type FetchTargets = FetchDataPayload["targets"];
1212
export type AnyFetchTarget = FetchTargets[number];
1313

14+
export interface ToolIdentifier {
15+
toolId: string;
16+
toolVersion: string;
17+
}
18+
19+
export function getToolKey(toolId: string, toolVersion: string): string {
20+
return `${toolId}@${toolVersion}`;
21+
}
22+
1423
export function urlDataElement(identifier: string, uri: string): UrlDataElement {
1524
const element: UrlDataElement = {
1625
src: "url",

client/src/api/userCredentials.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* User Credentials API
3+
*
4+
* This module provides type definitions, interfaces, and utility functions
5+
* for managing user service credentials. It includes types for credential
6+
* groups, service definitions, and helper functions for credential management.
7+
*
8+
* @module userCredentials
9+
*/
10+
11+
import type { components } from "@/api";
12+
import { useToolsServiceCredentialsDefinitionsStore } from "@/stores/toolsServiceCredentialsDefinitionsStore";
13+
14+
/**
15+
* Just an alias for a string that represents a unique key for a service credentials identifier.
16+
* The key is a combination of the service name and version, formatted as "name-version".
17+
*/
18+
type ServiceCredentialsIdentifierKey = string;
19+
20+
/** Type for credential field types. */
21+
export type CredentialType = "variable" | "secret";
22+
/** Service credential group response from API. */
23+
export type ServiceCredentialGroupResponse = components["schemas"]["ServiceCredentialGroupResponse"];
24+
/** Payload for creating source credentials. */
25+
export type CreateSourceCredentialsPayload = components["schemas"]["CreateSourceCredentialsPayload"];
26+
/** User service credentials response from API. */
27+
export type UserServiceCredentialsResponse = components["schemas"]["UserServiceCredentialsResponse"];
28+
/** Service credential payload for API requests. */
29+
export type ServiceCredentialPayload = components["schemas"]["ServiceCredentialPayload"];
30+
/** Service credential group payload for API requests. */
31+
export type ServiceCredentialGroupPayload = components["schemas"]["ServiceCredentialGroupPayload"];
32+
/** User service credentials with definition response from API. */
33+
export type UserServiceCredentialsWithDefinitionResponse =
34+
components["schemas"]["UserServiceCredentialsWithDefinitionResponse"];
35+
/** Payload for selecting current credential group. */
36+
export type SelectCurrentGroupPayload = components["schemas"]["SelectCurrentGroupPayload"];
37+
/** Service parameter definition from API. */
38+
export type ServiceParameterDefinition = components["schemas"]["ServiceParameterDefinition"];
39+
/** Service credentials definition from API. */
40+
export type ServiceCredentialsDefinition = components["schemas"]["ServiceCredentialsDefinition"];
41+
42+
/**
43+
* Service credentials identifier interface.
44+
* @interface ServiceCredentialsIdentifier
45+
*/
46+
export interface ServiceCredentialsIdentifier {
47+
/** Service name. */
48+
name: string;
49+
/** Service version. */
50+
version: string;
51+
}
52+
53+
/**
54+
* Represents the definition of credentials for a particular source.
55+
* A source can be an entity using a service that uses credentials, for example, a tool.
56+
* A source may accept multiple services, each with its own credentials.
57+
*
58+
* The `services` map is indexed by the service name and version using the `getKeyFromCredentialsIdentifier` function.
59+
* @interface SourceCredentialsDefinition
60+
*/
61+
export interface SourceCredentialsDefinition {
62+
/** Type of the source (e.g., "tool"). */
63+
sourceType: string;
64+
/** Unique identifier for the source. */
65+
sourceId: string;
66+
/** Map of services indexed by service identifier key. */
67+
services: Map<ServiceCredentialsIdentifierKey, ServiceCredentialsDefinition>;
68+
}
69+
70+
/**
71+
* Service credentials context interface.
72+
* @interface ServiceCredentialsContext
73+
* @todo Replace with proper API schema model when available.
74+
*/
75+
export interface ServiceCredentialsContext {
76+
/** User credentials ID or null if not set. */
77+
user_credentials_id: string | null;
78+
/** Service name. */
79+
name: string;
80+
/** Service version. */
81+
version: string;
82+
/** Selected credential group information. */
83+
selected_group: {
84+
/** Group ID or null if not selected. */
85+
id: string | null;
86+
/** Group name. */
87+
name: string;
88+
};
89+
}
90+
91+
/**
92+
* Generates a unique key from service credentials identifier
93+
* @param {ServiceCredentialsIdentifier} credentialsIdentifier - Service credentials identifier
94+
* @returns {ServiceCredentialsIdentifierKey} Unique key in format "name-version"
95+
*/
96+
export function getKeyFromCredentialsIdentifier(
97+
credentialsIdentifier: ServiceCredentialsIdentifier,
98+
): ServiceCredentialsIdentifierKey {
99+
return `${credentialsIdentifier.name}-${credentialsIdentifier.version}`;
100+
}
101+
102+
/**
103+
* Transforms tool information into source credentials definition
104+
* @param {string} toolId - The id of the tool
105+
* @param {string} toolVersion - The version of the tool
106+
* @returns {SourceCredentialsDefinition} Source credentials definition for the tool
107+
*/
108+
export function transformToSourceCredentials(toolId: string, toolVersion: string): SourceCredentialsDefinition {
109+
const { getToolServiceCredentialsDefinitionsFor } = useToolsServiceCredentialsDefinitionsStore();
110+
111+
const toolCredentialsDefinitions = getToolServiceCredentialsDefinitionsFor(toolId, toolVersion);
112+
113+
const services = new Map(
114+
toolCredentialsDefinitions.map((service) => [getKeyFromCredentialsIdentifier(service), service]),
115+
);
116+
117+
return {
118+
sourceType: "tool",
119+
sourceId: toolId,
120+
services,
121+
};
122+
}

client/src/components/Form/FormCard.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<span class="portlet-title">
1818
<span v-if="icon" :class="['portlet-title-icon fa mr-1', icon]" />
1919
<b class="portlet-title-text" itemprop="name">{{ title }}</b>
20+
<slot name="title" />
2021
<span class="portlet-title-description" itemprop="description">{{ description }}</span>
2122
</span>
2223
</div>
@@ -25,6 +26,7 @@
2526
</div>
2627
</div>
2728
</template>
29+
2830
<script>
2931
import { library } from "@fortawesome/fontawesome-svg-core";
3032
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";

client/src/components/Tool/ToolCard.vue

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
<script setup>
2-
import { faHdd } from "@fortawesome/free-solid-svg-icons";
2+
import { faExclamationCircle, faHdd, faKey } from "@fortawesome/free-solid-svg-icons";
33
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
4-
import { BModal } from "bootstrap-vue";
4+
import { BAlert, BModal, BPopover } from "bootstrap-vue";
55
import Heading from "components/Common/Heading";
66
import FormMessage from "components/Form/FormMessage";
77
import ToolFooter from "components/Tool/ToolFooter";
88
import ToolHelp from "components/Tool/ToolHelp";
99
import { storeToRefs } from "pinia";
10-
import { computed, ref, watch } from "vue";
10+
import { computed, onBeforeMount, ref, watch } from "vue";
1111
1212
import { useStorageLocationConfiguration } from "@/composables/storageLocation";
1313
import { useConfigStore } from "@/stores/configurationStore";
14+
import { useToolsServiceCredentialsDefinitionsStore } from "@/stores/toolsServiceCredentialsDefinitionsStore";
1415
import { useUserStore } from "@/stores/userStore";
1516
1617
import ToolSelectPreferredObjectStore from "./ToolSelectPreferredObjectStore";
1718
import ToolTargetPreferredObjectStorePopover from "./ToolTargetPreferredObjectStorePopover";
1819
1920
import GButton from "../BaseComponents/GButton.vue";
2021
import GButtonGroup from "../BaseComponents/GButtonGroup.vue";
22+
import ToolCredentials from "./ToolCredentials.vue";
2123
import ToolHelpForum from "./ToolHelpForum.vue";
2224
import ToolTutorialRecommendations from "./ToolTutorialRecommendations.vue";
2325
import FormCardSticky from "@/components/Form/FormCardSticky.vue";
@@ -77,10 +79,16 @@ const props = defineProps({
7779
type: Boolean,
7880
default: false,
7981
},
82+
allowEditingCredentials: {
83+
type: Boolean,
84+
default: false,
85+
},
8086
});
8187
8288
const emit = defineEmits(["onChangeVersion", "updatePreferredObjectStoreId"]);
8389
90+
const { setToolServiceCredentialsDefinitionFor } = useToolsServiceCredentialsDefinitionsStore();
91+
8492
function onChangeVersion(v) {
8593
emit("onChangeVersion", v);
8694
}
@@ -94,6 +102,17 @@ watch(
94102
},
95103
);
96104
105+
const credentialToolTip = computed(() => {
106+
const credentialNames = props.options.credentials?.map((service) => service.name);
107+
if (!credentialNames.value) {
108+
return "";
109+
}
110+
111+
return `This tool requires the following credentials when running the workflow: ${credentialNames.value.join(
112+
", ",
113+
)}`;
114+
});
115+
97116
const { isOnlyPreference } = useStorageLocationConfiguration();
98117
const { currentUser, isAnonymous } = storeToRefs(useUserStore());
99118
const { isLoaded: isConfigLoaded, config } = storeToRefs(useConfigStore());
@@ -126,6 +145,12 @@ const showHelpForum = computed(() => isConfigLoaded.value && config.value.enable
126145
const canGenerateTours = computed(() =>
127146
Boolean(props.allowGeneratedTours && isConfigLoaded.value && config.value.enable_tool_generated_tours),
128147
);
148+
149+
onBeforeMount(() => {
150+
if (props.options.credentials) {
151+
setToolServiceCredentialsDefinitionFor(props.id, props.version, props.options.credentials);
152+
}
153+
});
129154
</script>
130155
131156
<template>
@@ -183,6 +208,38 @@ const canGenerateTours = computed(() =>
183208
</template>
184209
185210
<template v-slot>
211+
<template v-if="props.options.credentials?.length">
212+
<ToolCredentials
213+
v-if="!props.allowEditingCredentials"
214+
class="mt-2"
215+
:tool-id="props.id"
216+
:tool-version="props.version"
217+
:job-credentials-context="props.options.job_credentials_context" />
218+
<BAlert
219+
v-else-if="props.allowEditingCredentials"
220+
v-b-tooltip.hover
221+
variant="info"
222+
class="mt-2"
223+
show
224+
:title="credentialToolTip">
225+
<FontAwesomeIcon :icon="faKey" />
226+
Requires credentials to run this tool.
227+
228+
<FontAwesomeIcon id="target" :icon="faExclamationCircle" fixed-width />
229+
<BPopover target="target" triggers="hover" boundary="window">
230+
<div class="d-flex flex-column">
231+
<span
232+
v-for="(service, index) in props.options.credentials"
233+
:key="index"
234+
class="d-flex flex-column">
235+
<b> {{ service.label }}: </b>
236+
{{ service.description }}
237+
</span>
238+
</div>
239+
</BPopover>
240+
</BAlert>
241+
</template>
242+
186243
<FormMessage variant="danger" :message="errorText" :persistent="true" />
187244
<FormMessage :variant="props.messageVariant" :message="props.messageText" />
188245
<slot name="default" />

0 commit comments

Comments
 (0)