diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 000000000000..3cf65b36a7c5 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,2 @@ +paths-ignore: + - 'lib/galaxy/datatypes/test/*.bcsl.ts' diff --git a/.github/labeler.yml b/.github/labeler.yml index fc5c7eb38d11..898b95abbdf9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,73 +1,129 @@ --- area/admin: - - client/src/components/admin/**/* - - doc/source/admin/**/* + - changed-files: + - any-glob-to-any-file: + - client/src/components/admin/**/* + - doc/source/admin/**/* area/API: - - lib/galaxy/webapps/galaxy/api/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/webapps/galaxy/api/**/* area/auth: - - lib/galaxy/auth/**/* - - lib/galaxy/authnz/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/auth/**/* + - lib/galaxy/authnz/**/* area/client: - - client/* + - changed-files: + - any-glob-to-any-file: + - client/* area/database: - - lib/galaxy/model/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/model/**/* area/datatypes: - - lib/galaxy/datatypes/**/* - - lib/galaxy/config/sample/datatypes_conf.xml.sample + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/datatypes/**/* + - lib/galaxy/config/sample/datatypes_conf.xml.sample area/dependencies: - - lib/galaxy/dependencies/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/dependencies/**/* area/documentation: - - doc/**/* + - changed-files: + - any-glob-to-any-file: + - doc/**/* area/jobs: - - lib/galaxy/jobs/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/jobs/**/* area/libraries: - - client/src/components/LibraryFolder/**/* - - lib/galaxy/webapps/galaxy/api/libraries.py + - changed-files: + - any-glob-to-any-file: + - client/src/components/LibraryFolder/**/* + - lib/galaxy/webapps/galaxy/api/libraries.py area/objectstore: - - lib/galaxy/objectstore/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/objectstore/**/* area/packaging: - - packages/**/* + - changed-files: + - any-glob-to-any-file: + - packages/**/* area/reports: - - client/src/reports/**/* - - lib/galaxy/webapps/reports/**/* - - templates/webapps/reports/**/* + - changed-files: + - any-glob-to-any-file: + - client/src/reports/**/* + - lib/galaxy/webapps/reports/**/* + - templates/webapps/reports/**/* area/scripts: - - scripts/**/* + - changed-files: + - any-glob-to-any-file: + - scripts/**/* area/security: - - lib/galaxy/security/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/security/**/* area/testing: - - lib/galaxy_test/**/* - - run_tests.sh - - test/**/* - - test-data/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy_test/**/* + - run_tests.sh + - test/**/* + - test-data/**/* area/testing/api: - - lib/galaxy_test/api/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy_test/api/**/* area/testing/integration: - - test/integration/**/* - - test/integration_selenium/**/* + - changed-files: + - any-glob-to-any-file: + - test/integration/**/* + - test/integration_selenium/**/* area/testing/selenium: - - lib/galaxy/selenium/**/* - - lib/galaxy_test/selenium/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/selenium/**/* + - lib/galaxy_test/selenium/**/* area/tool-dependencies: - - lib/galaxy/tool_util/deps/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/tool_util/deps/**/* area/tool-framework: - - lib/galaxy/tools/**/* - - lib/galaxy/tool_util/**/* - - lib/galaxy/webapps/galaxy/api/tools.py + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/tools/**/* + - lib/galaxy/tool_util/**/* + - lib/galaxy/webapps/galaxy/api/tools.py area/tools: - - tools/**/* - - tool-data/**/* + - changed-files: + - any-glob-to-any-file: + - tools/**/* + - tool-data/**/* area/toolshed: - - client/src/toolshed/**/* - - lib/galaxy/webapps/galaxy/api/toolshed.py - - lib/toolshed/**/* - - templates/webapps/tool_shed/**/* + - changed-files: + - any-glob-to-any-file: + - client/src/toolshed/**/* + - lib/galaxy/webapps/galaxy/api/toolshed.py + - lib/toolshed/**/* + - templates/webapps/tool_shed/**/* area/UI-UX: - - all: ["client/src/**/*", "!client/src/api/schema/schema.ts"] - any: ["templates/**/*"] + - changed-files: + - any-glob-to-any-file: + - client/src/**/* + - templates/**/* + - all-globs-to-all-files: + - '!client/src/api/schema/schema.ts' area/util: - - lib/galaxy/util/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/util/**/* area/visualizations: - - config/plugins/visualizations/**/* + - changed-files: + - any-glob-to-any-file: + - config/plugins/visualizations/**/* area/workflows: - - lib/galaxy/workflow/**/* + - changed-files: + - any-glob-to-any-file: + - lib/galaxy/workflow/**/* diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cad852d6813c..bbacec3a450b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -54,6 +54,9 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality + config-file: ./.github/codeql/codeql-config.yml + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild diff --git a/client/openapi_to_schema.mjs b/client/openapi_to_schema.mjs index eb6e22a290cc..d85b349f096d 100644 --- a/client/openapi_to_schema.mjs +++ b/client/openapi_to_schema.mjs @@ -7,11 +7,15 @@ const inputFilePath = process.argv[2]; const localPath = new URL(inputFilePath, import.meta.url); openapiTS(localPath, { transform(schemaObject, metadata) { - if ( - "const" in schemaObject && - (typeof schemaObject.const === "string" || schemaObject.const instanceof String) - ) { - return `"${schemaObject.const}"`; + if ("const" in schemaObject) { + const constType = typeof schemaObject.const; + switch (constType) { + case "number": + case "boolean": + return `${schemaObject.const}`; + default: + return `"${schemaObject.const}"`; + } } }, }).then((output) => console.log(output)); diff --git a/client/src/api/index.ts b/client/src/api/index.ts index 1b1aa6f66c93..4b1f086d46f0 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -94,6 +94,11 @@ export type HDADetailed = components["schemas"]["HDADetailed"]; */ export type HistoryItemSummary = HDASummary | HDCASummary; +/** + * Represents a HistoryDatasetAssociation that is inaccessible to the user. + */ +export type HDAInaccessible = components["schemas"]["HDAInaccessible"]; + /** * Contains storage (object store, quota, etc..) details for a dataset. */ @@ -102,7 +107,7 @@ export type DatasetStorageDetails = components["schemas"]["DatasetStorageDetails /** * Represents a HistoryDatasetAssociation with either summary or detailed information. */ -export type DatasetEntry = HDASummary | HDADetailed; +export type DatasetEntry = HDASummary | HDADetailed | HDAInaccessible; /** * Contains summary information about a DCE (DatasetCollectionElement). @@ -188,6 +193,10 @@ export function hasDetails(entry: DatasetEntry): entry is HDADetailed { return "peek" in entry; } +export function isInaccessible(entry: DatasetEntry): entry is HDAInaccessible { + return "accessible" in entry && !entry.accessible; +} + /** * Contains dataset metadata information. */ diff --git a/client/src/api/remoteFiles.ts b/client/src/api/remoteFiles.ts index 942e295c5dc6..ff149655c037 100644 --- a/client/src/api/remoteFiles.ts +++ b/client/src/api/remoteFiles.ts @@ -46,16 +46,42 @@ export async function fetchFileSources(options: FilterFileSourcesOptions = {}): export const remoteFilesFetcher = fetcher.path("/api/remote_files").method("get").create(); +export interface BrowseRemoteFilesResult { + entries: RemoteEntry[]; + totalMatches: number; +} + /** * Get the list of files and directories from the server for the given file source URI. * @param uri The file source URI to browse. * @param isRecursive Whether to recursively retrieve all files inside subdirectories. * @param writeable Whether to return only entries that can be written to. + * @param limit The maximum number of entries to return. + * @param offset The number of entries to skip before returning the rest. + * @param query The query string to filter the entries. + * @param sortBy The field to sort the entries by. * @returns The list of files and directories from the server for the given URI. */ -export async function browseRemoteFiles(uri: string, isRecursive = false, writeable = false): Promise { - const { data } = await remoteFilesFetcher({ target: uri, recursive: isRecursive, writeable }); - return data as RemoteEntry[]; +export async function browseRemoteFiles( + uri: string, + isRecursive = false, + writeable = false, + limit?: number, + offset?: number, + query?: string, + sortBy?: string +): Promise { + const { data, headers } = await remoteFilesFetcher({ + target: uri, + recursive: isRecursive, + writeable, + limit, + offset, + query, + sort_by: sortBy, + }); + const totalMatches = parseInt(headers.get("total_matches") ?? "0"); + return { entries: data as RemoteEntry[], totalMatches }; } const createEntry = fetcher.path("/api/remote_files").method("post").create(); diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index 2584707d0d76..5ba0e62f3d3a 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -398,6 +398,8 @@ export interface paths { * Displays remote files available to the user. Please use /api/remote_files instead. * @deprecated * @description Lists all remote files available to the user from different sources. + * + * The total count of files and directories is returned in the 'total_matches' header. */ get: operations["index_api_ftp_files_get"]; }; @@ -1439,6 +1441,8 @@ export interface paths { /** * Displays remote files available to the user. * @description Lists all remote files available to the user from different sources. + * + * The total count of files and directories is returned in the 'total_matches' header. */ get: operations["index_api_remote_files_get"]; /** @@ -2687,6 +2691,15 @@ export interface components { * @description Only users with the roles specified here can access this files source. */ requires_roles?: string | null; + /** + * @description Features supported by this file source. + * @default { + * "pagination": false, + * "search": false, + * "sorting": false + * } + */ + supports?: components["schemas"]["FilesSourceSupports"]; /** * Type * @description The type of the plugin. @@ -5201,6 +5214,15 @@ export interface components { * @description Only users with the roles specified here can access this files source. */ requires_roles?: string | null; + /** + * @description Features supported by this file source. + * @default { + * "pagination": false, + * "search": false, + * "sorting": false + * } + */ + supports?: components["schemas"]["FilesSourceSupports"]; /** * Type * @description The type of the plugin. @@ -5225,6 +5247,27 @@ export interface components { | components["schemas"]["BrowsableFilesSourcePlugin"] | components["schemas"]["FilesSourcePlugin"] )[]; + /** FilesSourceSupports */ + FilesSourceSupports: { + /** + * Pagination + * @description Whether this file source supports server-side pagination. + * @default false + */ + pagination?: boolean; + /** + * Search + * @description Whether this file source supports server-side search. + * @default false + */ + search?: boolean; + /** + * Sorting + * @description Whether this file source supports server-side sorting. + * @default false + */ + sorting?: boolean; + }; /** FillStepDefaultsAction */ FillStepDefaultsAction: { /** @@ -5988,11 +6031,96 @@ export interface components { */ visible: boolean; }; + /** + * HDAInaccessible + * @description History Dataset Association information when the user can not access it. + */ + HDAInaccessible: { + /** + * Accessible + * @constant + * @enum {boolean} + */ + accessible: false; + /** Copied From Ldda Id */ + copied_from_ldda_id?: string | null; + /** + * Create Time + * @description The time and date this item was created. + */ + create_time: string | null; + /** + * Deleted + * @description Whether this item is marked as deleted. + */ + deleted: boolean; + /** + * HID + * @description The index position of this item in the History. + */ + hid: number; + /** + * History Content Type + * @description This is always `dataset` for datasets. + * @constant + * @enum {string} + */ + history_content_type: "dataset"; + /** + * History ID + * @example 0123456789ABCDEF + */ + history_id: string; + /** + * Id + * @example 0123456789ABCDEF + */ + id: string; + /** + * Name + * @description The name of the item. + */ + name: string | null; + /** + * State + * @description The current state of this dataset. + */ + state: components["schemas"]["DatasetState"]; + tags: components["schemas"]["TagCollection"]; + /** + * Type + * @description The type of this item. + */ + type: string; + /** + * Type - ID + * @description The type and the encoded ID of this item. Used for caching. + */ + type_id?: string | null; + /** + * Update Time + * @description The last time and date this item was updated. + */ + update_time: string | null; + /** + * URL + * @deprecated + * @description The relative URL to access this item. + */ + url: string; + /** + * Visible + * @description Whether this item is visible or hidden to the user by default. + */ + visible: boolean; + }; /** * HDAObject * @description History Dataset Association Object */ HDAObject: { + /** Accessible */ + accessible?: boolean | null; /** Copied From Ldda Id */ copied_from_ldda_id?: string | null; /** @@ -6925,6 +7053,7 @@ export interface components { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] )[]; @@ -6941,6 +7070,7 @@ export interface components { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] )[]; @@ -13788,6 +13918,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] )[]; @@ -13910,6 +14041,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"]; }; @@ -14078,7 +14210,8 @@ export interface operations { "application/json": | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] - | components["schemas"]["HDASummary"]; + | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"]; }; }; /** @description Validation Error */ @@ -15334,6 +15467,8 @@ export interface operations { * Displays remote files available to the user. Please use /api/remote_files instead. * @deprecated * @description Lists all remote files available to the user from different sources. + * + * The total count of files and directories is returned in the 'total_matches' header. */ parameters?: { /** @description The source to load datasets from. Possible values: ftpdir, userdir, importdir */ @@ -15341,12 +15476,20 @@ export interface operations { /** @description Whether to recursively lists all sub-directories. This will be `True` by default depending on the `target`. */ /** @description (This only applies when `format` is `jstree`) The value can be either `folders` or `files` and it will disable the corresponding nodes of the tree. */ /** @description Whether the query is made with the intention of writing to the source. If set to True, only entries that can be written to will be returned. */ + /** @description Maximum number of entries to return. */ + /** @description Number of entries to skip. */ + /** @description Search query to filter entries by. The syntax could be different depending on the target source. */ + /** @description Sort the entries by the specified field. */ query?: { target?: string; format?: components["schemas"]["RemoteFilesFormat"] | null; recursive?: boolean | null; disable?: components["schemas"]["RemoteFilesDisableMode"] | null; writeable?: boolean | null; + limit?: number | null; + offset?: number | null; + query?: string | null; + sort_by?: string | null; }; /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { @@ -17017,12 +17160,14 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] | ( | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] )[]; @@ -17606,6 +17751,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"]; }; @@ -17657,6 +17803,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"]; }; @@ -17889,12 +18036,14 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] | ( | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] )[]; @@ -17945,6 +18094,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"]; }; @@ -17995,6 +18145,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"]; }; @@ -18226,6 +18377,7 @@ export interface operations { | components["schemas"]["HDACustom"] | components["schemas"]["HDADetailed"] | components["schemas"]["HDASummary"] + | components["schemas"]["HDAInaccessible"] | components["schemas"]["HDCADetailed"] | components["schemas"]["HDCASummary"] )[]; @@ -21650,6 +21802,8 @@ export interface operations { /** * Displays remote files available to the user. * @description Lists all remote files available to the user from different sources. + * + * The total count of files and directories is returned in the 'total_matches' header. */ parameters?: { /** @description The source to load datasets from. Possible values: ftpdir, userdir, importdir */ @@ -21657,12 +21811,20 @@ export interface operations { /** @description Whether to recursively lists all sub-directories. This will be `True` by default depending on the `target`. */ /** @description (This only applies when `format` is `jstree`) The value can be either `folders` or `files` and it will disable the corresponding nodes of the tree. */ /** @description Whether the query is made with the intention of writing to the source. If set to True, only entries that can be written to will be returned. */ + /** @description Maximum number of entries to return. */ + /** @description Number of entries to skip. */ + /** @description Search query to filter entries by. The syntax could be different depending on the target source. */ + /** @description Sort the entries by the specified field. */ query?: { target?: string; format?: components["schemas"]["RemoteFilesFormat"] | null; recursive?: boolean | null; disable?: components["schemas"]["RemoteFilesDisableMode"] | null; writeable?: boolean | null; + limit?: number | null; + offset?: number | null; + query?: string | null; + sort_by?: string | null; }; /** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */ header?: { diff --git a/client/src/components/ActivityBar/ActivitySettings.test.js b/client/src/components/ActivityBar/ActivitySettings.test.js index fc4bfe543383..e5555451ec85 100644 --- a/client/src/components/ActivityBar/ActivitySettings.test.js +++ b/client/src/components/ActivityBar/ActivitySettings.test.js @@ -29,10 +29,7 @@ function testActivity(id, newOptions = {}) { } async function testSearch(wrapper, query, result) { - const searchField = wrapper.find("input"); - searchField.element.value = query; - searchField.trigger("change"); - await wrapper.vm.$nextTick(); + await wrapper.setProps({ query }); const filtered = wrapper.findAll(activityItemSelector); expect(filtered.length).toBe(result); } @@ -48,6 +45,9 @@ describe("ActivitySettings", () => { wrapper = mount(mountTarget, { localVue, pinia, + props: { + query: "", + }, stubs: { icon: { template: "
" }, }, diff --git a/client/src/components/ActivityBar/ActivitySettings.vue b/client/src/components/ActivityBar/ActivitySettings.vue index 16c596e531f4..835691d3c0b3 100644 --- a/client/src/components/ActivityBar/ActivitySettings.vue +++ b/client/src/components/ActivityBar/ActivitySettings.vue @@ -4,12 +4,10 @@ import { faSquare } from "@fortawesome/free-regular-svg-icons"; import { faCheckSquare, faThumbtack, faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { storeToRefs } from "pinia"; -import { computed, type ComputedRef, type Ref, ref } from "vue"; +import { computed, type ComputedRef } from "vue"; import { type Activity, useActivityStore } from "@/stores/activityStore"; -import DelayedInput from "@/components/Common/DelayedInput.vue"; - library.add({ faCheckSquare, faSquare, @@ -17,13 +15,16 @@ library.add({ faThumbtack, }); +const props = defineProps<{ + query: string; +}>(); + const activityStore = useActivityStore(); const { activities } = storeToRefs(activityStore); -const query: Ref = ref(""); const filteredActivities = computed(() => { - if (query.value.length > 0) { - const queryLower = query.value.toLowerCase(); + if (props.query?.length > 0) { + const queryLower = props.query.toLowerCase(); const results = activities.value.filter((a: Activity) => { const attributeValues = [a.title, a.description]; for (const value of attributeValues) { @@ -52,16 +53,11 @@ function onClick(activity: Activity) { function onRemove(activity: Activity) { activityStore.remove(activity.id); } - -function onQuery(newQuery: string) { - query.value = newQuery; -}