From 0e1a6047ca16b7961b6a4077343774ae7829c62d Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 2 May 2024 15:27:59 -0400 Subject: [PATCH] WIP: file source templates --- client/src/api/configTemplates.ts | 25 + client/src/api/fileSources.ts | 6 + client/src/api/schema/schema.ts | 337 ++++++++++++- .../ConfigTemplates/CreateInstance.test.ts | 32 ++ .../ConfigTemplates/CreateInstance.vue | 21 + .../ConfigTemplates/EditSecretsForm.test.ts | 36 ++ .../ConfigTemplates/EditSecretsForm.vue | 39 ++ .../ConfigTemplates/InstanceDropdown.test.ts | 40 ++ .../ConfigTemplates/InstanceDropdown.vue | 50 ++ .../ConfigTemplates/InstanceForm.test.ts | 42 ++ .../InstanceForm.vue | 9 +- .../ConfigTemplates/ManageIndexHeader.vue | 35 ++ .../ConfigTemplates/SelectTemplate.test.js | 33 ++ .../ConfigTemplates/SelectTemplate.vue | 44 ++ .../TemplateSummaryPopover.test.ts | 22 + .../TemplateSummaryPopover.vue | 21 + .../ConfigTemplates/VaultSecret.test.ts | 25 + .../VaultSecret.vue | 37 +- .../src/components/ConfigTemplates/fields.ts | 25 + .../ConfigTemplates/formUtil.test.ts | 100 ++++ .../components/ConfigTemplates/formUtil.ts | 236 +++++++++ .../Instances => ConfigTemplates}/icons.ts | 0 .../ConfigTemplates/test_fixtures.ts | 119 +++++ .../FileSources/FileSourceTypeSpan.vue | 23 + .../FileSources/Instances/CreateForm.vue | 53 ++ .../FileSources/Instances/CreateInstance.vue | 34 ++ .../FileSources/Instances/EditInstance.vue | 77 +++ .../FileSources/Instances/EditSecrets.vue | 28 ++ .../Instances/InstanceDropdown.vue | 30 ++ .../FileSources/Instances/ManageIndex.vue | 69 +++ .../FileSources/Instances/UpgradeForm.vue | 63 +++ .../FileSources/Instances/UpgradeInstance.vue | 27 + .../FileSources/Instances/instance.ts | 23 + .../FileSources/Instances/routing.ts | 16 + .../FileSources/Instances/services.ts | 4 + .../Templates/CreateUserFileSource.vue | 30 ++ .../FileSources/Templates/SelectTemplate.vue | 39 ++ .../FileSources/Templates/TemplateSummary.vue | 22 + .../Templates/TemplateSummaryPopover.vue | 19 + .../Templates/TemplateSummarySpan.vue | 32 ++ client/src/components/FileSources/style.css | 4 + .../ObjectStore/Instances/CreateForm.test.ts | 4 +- .../ObjectStore/Instances/CreateForm.vue | 63 +-- .../ObjectStore/Instances/EditInstance.vue | 79 ++- .../ObjectStore/Instances/EditSecrets.vue | 14 +- .../Instances/InstanceDropdown.vue | 42 +- .../ObjectStore/Instances/ManageIndex.vue | 66 +-- .../ObjectStore/Instances/UpgradeForm.test.ts | 4 +- .../ObjectStore/Instances/UpgradeForm.vue | 52 +- .../components/ObjectStore/Instances/types.ts | 5 - .../components/ObjectStore/Instances/util.ts | 41 -- .../Templates/CreateUserObjectStore.vue | 19 +- .../ObjectStore/Templates/SelectTemplate.vue | 27 +- .../ObjectStore/Templates/TemplateSummary.vue | 2 +- .../Templates/TemplateSummaryPopover.test.ts | 22 + .../Templates/TemplateSummaryPopover.vue | 12 +- .../src/components/User/UserPreferences.vue | 28 +- client/src/entry/analysis/router.js | 47 +- client/src/stores/configTemplatesUtil.ts | 56 +++ client/src/stores/fileSourceInstancesStore.ts | 51 ++ client/src/stores/fileSourceTemplatesStore.ts | 70 +++ .../stores/objectStoreInstancesStore.test.ts | 2 +- .../stores/objectStoreTemplatesStore.test.ts | 2 +- .../src/stores/objectStoreTemplatesStore.ts | 49 +- client/src/utils/navigation/navigation.yml | 23 +- client/src/utils/upload-payload.js | 1 + doc/source/admin/data.md | 469 ++++++++++++++++++ .../admin/file_source_azure_configuration.png | Bin 0 -> 21933 bytes ...le_source_azure_configuration_template.png | Bin 0 -> 20529 bytes .../admin/file_source_ftp_configuration.png | Bin 0 -> 20202 bytes ...file_source_ftp_configuration_template.png | Bin 0 -> 20281 bytes .../admin/file_source_posix_configuration.png | Bin 0 -> 13832 bytes ...le_source_posix_configuration_template.png | Bin 0 -> 16319 bytes .../admin/file_source_s3fs_configuration.png | Bin 0 -> 20111 bytes ...ile_source_s3fs_configuration_template.png | Bin 0 -> 36084 bytes doc/source/admin/file_source_templates.png | Bin 0 -> 323201 bytes doc/source/admin/gen_diagrams.py | 55 ++ doc/source/admin/index.rst | 1 + .../object_store_aws_s3_configuration.png | Bin 0 -> 39228 bytes ...ct_store_aws_s3_configuration_template.png | Bin 0 -> 50625 bytes .../object_store_azure_configuration.png | Bin 0 -> 30426 bytes ...ect_store_azure_configuration_template.png | Bin 0 -> 44521 bytes .../admin/object_store_disk_configuration.png | Bin 0 -> 17587 bytes ...ject_store_disk_configuration_template.png | Bin 0 -> 17794 bytes .../object_store_generic_s3_configuration.png | Bin 0 -> 46445 bytes ...tore_generic_s3_configuration_template.png | Bin 0 -> 61874 bytes doc/source/admin/object_store_templates.png | Bin 0 -> 383528 bytes doc/source/admin/s3_configuration.png | Bin 0 -> 38250 bytes .../admin/s3_configuration_template.png | Bin 0 -> 43645 bytes .../admin/search_for_new_screenshots.py | 30 ++ lib/galaxy/app.py | 41 +- lib/galaxy/app_unittest_utils/galaxy_mock.py | 23 + lib/galaxy/config/schemas/config_schema.yml | 15 + lib/galaxy/files/__init__.py | 146 ++++-- lib/galaxy/files/plugins.py | 5 + lib/galaxy/files/sources/__init__.py | 12 +- lib/galaxy/files/sources/azure.py | 1 + lib/galaxy/files/templates/__init__.py | 17 + .../files/templates/examples/__init__.py | 5 + .../templates/examples/admin_secrets.yml | 13 + .../examples/admin_secrets_with_defaults.yml | 15 + .../production_aws_private_bucket.yml | 30 ++ .../examples/production_aws_public_bucket.yml | 15 + .../templates/examples/production_azure.yml | 44 ++ .../templates/examples/production_ftp.yml | 39 ++ .../examples/templating_override.yml | 13 + lib/galaxy/files/templates/manager.py | 86 ++++ lib/galaxy/files/templates/models.py | 189 +++++++ lib/galaxy/files/unittest_utils/__init__.py | 7 +- lib/galaxy/managers/file_source_instances.py | 436 ++++++++++++++++ lib/galaxy/managers/object_store_instances.py | 83 ++-- lib/galaxy/model/__init__.py | 152 +++++- ...a3c93d66b_add_user_defined_file_sources.py | 57 +++ lib/galaxy/objectstore/__init__.py | 2 + lib/galaxy/objectstore/azure_blob.py | 8 +- lib/galaxy/objectstore/s3.py | 8 +- .../templates/examples/__init__.py | 5 + .../examples/azure_just_container.yml | 22 + .../templates/examples/cloudflare.yml | 48 ++ .../templates/examples/minio_example.yml | 47 ++ .../templates/examples/minio_just_buckets.yml | 22 + .../examples/production_azure_blob.yml | 34 ++ .../examples/production_generic_s3.yml | 98 ++++ .../templates/examples/simple_example.yml | 15 + lib/galaxy/objectstore/templates/manager.py | 94 +--- lib/galaxy/objectstore/templates/models.py | 95 ++-- lib/galaxy/schema/__init__.py | 1 + lib/galaxy/selenium/navigates_galaxy.py | 122 +++++ lib/galaxy/util/config_templates.py | 362 ++++++++++++++ lib/galaxy/webapps/galaxy/api/file_sources.py | 95 ++++ lib/galaxy/webapps/galaxy/buildapp.py | 5 + lib/galaxy_test/base/populators.py | 13 + lib/galaxy_test/driver/integration_util.py | 13 + packages/files/setup.cfg | 2 +- packages/objectstore/setup.cfg | 3 +- packages/test.sh | 2 +- packages/util/setup.cfg | 3 + test/integration_selenium/framework.py | 1 + .../test_objectstore_creation.py | 80 +++ .../test_user_file_source_azure.py | 64 +++ .../test_user_file_sources_ftp.py | 49 ++ .../app/managers/test_user_file_sources.py | 196 ++++++++ test/unit/files/_util.py | 7 +- test/unit/files/test_azure.py | 2 +- test/unit/files/test_posix.py | 29 +- test/unit/files/test_template_manager.py | 58 +++ test/unit/files/test_template_models.py | 268 ++++++++++ .../test_from_configuration_object.py | 1 + test/unit/objectstore/test_template_models.py | 135 ++++- .../util/test_config_template_validation.py | 138 ++++++ 150 files changed, 6207 insertions(+), 647 deletions(-) create mode 100644 client/src/api/configTemplates.ts create mode 100644 client/src/api/fileSources.ts create mode 100644 client/src/components/ConfigTemplates/CreateInstance.test.ts create mode 100644 client/src/components/ConfigTemplates/CreateInstance.vue create mode 100644 client/src/components/ConfigTemplates/EditSecretsForm.test.ts create mode 100644 client/src/components/ConfigTemplates/EditSecretsForm.vue create mode 100644 client/src/components/ConfigTemplates/InstanceDropdown.test.ts create mode 100644 client/src/components/ConfigTemplates/InstanceDropdown.vue create mode 100644 client/src/components/ConfigTemplates/InstanceForm.test.ts rename client/src/components/{ObjectStore/Instances => ConfigTemplates}/InstanceForm.vue (79%) create mode 100644 client/src/components/ConfigTemplates/ManageIndexHeader.vue create mode 100644 client/src/components/ConfigTemplates/SelectTemplate.test.js create mode 100644 client/src/components/ConfigTemplates/SelectTemplate.vue create mode 100644 client/src/components/ConfigTemplates/TemplateSummaryPopover.test.ts create mode 100644 client/src/components/ConfigTemplates/TemplateSummaryPopover.vue create mode 100644 client/src/components/ConfigTemplates/VaultSecret.test.ts rename client/src/components/{ObjectStore/Instances => ConfigTemplates}/VaultSecret.vue (50%) create mode 100644 client/src/components/ConfigTemplates/fields.ts create mode 100644 client/src/components/ConfigTemplates/formUtil.test.ts create mode 100644 client/src/components/ConfigTemplates/formUtil.ts rename client/src/components/{ObjectStore/Instances => ConfigTemplates}/icons.ts (100%) create mode 100644 client/src/components/ConfigTemplates/test_fixtures.ts create mode 100644 client/src/components/FileSources/FileSourceTypeSpan.vue create mode 100644 client/src/components/FileSources/Instances/CreateForm.vue create mode 100644 client/src/components/FileSources/Instances/CreateInstance.vue create mode 100644 client/src/components/FileSources/Instances/EditInstance.vue create mode 100644 client/src/components/FileSources/Instances/EditSecrets.vue create mode 100644 client/src/components/FileSources/Instances/InstanceDropdown.vue create mode 100644 client/src/components/FileSources/Instances/ManageIndex.vue create mode 100644 client/src/components/FileSources/Instances/UpgradeForm.vue create mode 100644 client/src/components/FileSources/Instances/UpgradeInstance.vue create mode 100644 client/src/components/FileSources/Instances/instance.ts create mode 100644 client/src/components/FileSources/Instances/routing.ts create mode 100644 client/src/components/FileSources/Instances/services.ts create mode 100644 client/src/components/FileSources/Templates/CreateUserFileSource.vue create mode 100644 client/src/components/FileSources/Templates/SelectTemplate.vue create mode 100644 client/src/components/FileSources/Templates/TemplateSummary.vue create mode 100644 client/src/components/FileSources/Templates/TemplateSummaryPopover.vue create mode 100644 client/src/components/FileSources/Templates/TemplateSummarySpan.vue create mode 100644 client/src/components/FileSources/style.css delete mode 100644 client/src/components/ObjectStore/Instances/util.ts create mode 100644 client/src/components/ObjectStore/Templates/TemplateSummaryPopover.test.ts create mode 100644 client/src/stores/configTemplatesUtil.ts create mode 100644 client/src/stores/fileSourceInstancesStore.ts create mode 100644 client/src/stores/fileSourceTemplatesStore.ts create mode 100644 doc/source/admin/data.md create mode 100644 doc/source/admin/file_source_azure_configuration.png create mode 100644 doc/source/admin/file_source_azure_configuration_template.png create mode 100644 doc/source/admin/file_source_ftp_configuration.png create mode 100644 doc/source/admin/file_source_ftp_configuration_template.png create mode 100644 doc/source/admin/file_source_posix_configuration.png create mode 100644 doc/source/admin/file_source_posix_configuration_template.png create mode 100644 doc/source/admin/file_source_s3fs_configuration.png create mode 100644 doc/source/admin/file_source_s3fs_configuration_template.png create mode 100644 doc/source/admin/file_source_templates.png create mode 100644 doc/source/admin/gen_diagrams.py create mode 100644 doc/source/admin/object_store_aws_s3_configuration.png create mode 100644 doc/source/admin/object_store_aws_s3_configuration_template.png create mode 100644 doc/source/admin/object_store_azure_configuration.png create mode 100644 doc/source/admin/object_store_azure_configuration_template.png create mode 100644 doc/source/admin/object_store_disk_configuration.png create mode 100644 doc/source/admin/object_store_disk_configuration_template.png create mode 100644 doc/source/admin/object_store_generic_s3_configuration.png create mode 100644 doc/source/admin/object_store_generic_s3_configuration_template.png create mode 100644 doc/source/admin/object_store_templates.png create mode 100644 doc/source/admin/s3_configuration.png create mode 100644 doc/source/admin/s3_configuration_template.png create mode 100644 doc/source/admin/search_for_new_screenshots.py create mode 100644 lib/galaxy/files/templates/__init__.py create mode 100644 lib/galaxy/files/templates/examples/__init__.py create mode 100644 lib/galaxy/files/templates/examples/admin_secrets.yml create mode 100644 lib/galaxy/files/templates/examples/admin_secrets_with_defaults.yml create mode 100644 lib/galaxy/files/templates/examples/production_aws_private_bucket.yml create mode 100644 lib/galaxy/files/templates/examples/production_aws_public_bucket.yml create mode 100644 lib/galaxy/files/templates/examples/production_azure.yml create mode 100644 lib/galaxy/files/templates/examples/production_ftp.yml create mode 100644 lib/galaxy/files/templates/examples/templating_override.yml create mode 100644 lib/galaxy/files/templates/manager.py create mode 100644 lib/galaxy/files/templates/models.py create mode 100644 lib/galaxy/managers/file_source_instances.py create mode 100644 lib/galaxy/model/migrations/alembic/versions_gxy/c14a3c93d66b_add_user_defined_file_sources.py create mode 100644 lib/galaxy/objectstore/templates/examples/__init__.py create mode 100644 lib/galaxy/objectstore/templates/examples/azure_just_container.yml create mode 100644 lib/galaxy/objectstore/templates/examples/cloudflare.yml create mode 100644 lib/galaxy/objectstore/templates/examples/minio_example.yml create mode 100644 lib/galaxy/objectstore/templates/examples/minio_just_buckets.yml create mode 100644 lib/galaxy/objectstore/templates/examples/production_azure_blob.yml create mode 100644 lib/galaxy/objectstore/templates/examples/production_generic_s3.yml create mode 100644 lib/galaxy/objectstore/templates/examples/simple_example.yml create mode 100644 lib/galaxy/util/config_templates.py create mode 100644 lib/galaxy/webapps/galaxy/api/file_sources.py create mode 100644 test/integration_selenium/test_objectstore_creation.py create mode 100644 test/integration_selenium/test_user_file_source_azure.py create mode 100644 test/integration_selenium/test_user_file_sources_ftp.py create mode 100644 test/unit/app/managers/test_user_file_sources.py create mode 100644 test/unit/files/test_template_manager.py create mode 100644 test/unit/files/test_template_models.py create mode 100644 test/unit/util/test_config_template_validation.py diff --git a/client/src/api/configTemplates.ts b/client/src/api/configTemplates.ts new file mode 100644 index 000000000000..5ca51cf7e2ee --- /dev/null +++ b/client/src/api/configTemplates.ts @@ -0,0 +1,25 @@ +import type { components } from "@/api/schema/schema"; + +export type Instance = + | components["schemas"]["UserFileSourceModel"] + | components["schemas"]["UserConcreteObjectStoreModel"]; + +export type TemplateVariable = + | components["schemas"]["TemplateVariableString"] + | components["schemas"]["TemplateVariableInteger"] + | components["schemas"]["TemplateVariablePathComponent"] + | components["schemas"]["TemplateVariableBoolean"]; +export type TemplateSecret = components["schemas"]["TemplateSecret"]; +export type VariableValueType = (string | boolean | number) | undefined; +export type VariableData = { [key: string]: VariableValueType }; +export type SecretData = { [key: string]: string }; + +export interface TemplateSummary { + description: string | null; + hidden?: boolean; + id: string; + name: string | null; + secrets?: TemplateSecret[] | null; + variables?: TemplateVariable[] | null; + version?: number; +} diff --git a/client/src/api/fileSources.ts b/client/src/api/fileSources.ts new file mode 100644 index 000000000000..68935db4573f --- /dev/null +++ b/client/src/api/fileSources.ts @@ -0,0 +1,6 @@ +import { type components } from "@/api/schema"; + +export type FileSourceTemplateSummary = components["schemas"]["FileSourceTemplateSummary"]; +export type FileSourceTemplateSummaries = FileSourceTemplateSummary[]; + +export type UserFileSourceModel = components["schemas"]["UserFileSourceModel"]; diff --git a/client/src/api/schema/schema.ts b/client/src/api/schema/schema.ts index 330d362ff387..8332283069d2 100644 --- a/client/src/api/schema/schema.ts +++ b/client/src/api/schema/schema.ts @@ -318,6 +318,22 @@ export interface paths { /** Download */ get: operations["download_api_drs_download__object_id__get"]; }; + "/api/file_source_instances": { + /** Get a list of persisted file source instances defined by the requesting user. */ + get: operations["file_sources__instances_index"]; + /** Create a user-bound object store. */ + post: operations["file_sources__create_instance"]; + }; + "/api/file_source_instances/{user_file_source_id}": { + /** Get a list of persisted file source instances defined by the requesting user. */ + get: operations["file_sources__instances_get"]; + /** Update or upgrade user file source instance. */ + put: operations["file_sources__instances_update"]; + }; + "/api/file_source_templates": { + /** Get a list of file source templates available to build user defined file sources from */ + get: operations["file_sources__templates_index"]; + }; "/api/folders/{folder_id}/contents": { /** * Returns a list of a folder's contents (files and sub-folders) with additional metadata about the folder. @@ -5234,6 +5250,43 @@ export interface components { */ update_time: string; }; + /** FileSourceTemplateSummaries */ + FileSourceTemplateSummaries: components["schemas"]["FileSourceTemplateSummary"][]; + /** FileSourceTemplateSummary */ + FileSourceTemplateSummary: { + /** Description */ + description: string | null; + /** + * Hidden + * @default false + */ + hidden?: boolean; + /** Id */ + id: string; + /** Name */ + name: string | null; + /** Secrets */ + secrets?: components["schemas"]["TemplateSecret"][] | null; + /** + * Type + * @enum {string} + */ + type: "ftp" | "posix" | "s3fs" | "azure"; + /** Variables */ + variables?: + | ( + | components["schemas"]["TemplateVariableString"] + | components["schemas"]["TemplateVariableInteger"] + | components["schemas"]["TemplateVariablePathComponent"] + | components["schemas"]["TemplateVariableBoolean"] + )[] + | null; + /** + * Version + * @default 0 + */ + version?: number; + }; /** FilesSourcePlugin */ FilesSourcePlugin: { /** @@ -9920,13 +9973,6 @@ export interface components { */ up_to_date: boolean; }; - /** ObjectStoreTemplateSecret */ - ObjectStoreTemplateSecret: { - /** Help */ - help: string | null; - /** Name */ - name: string; - }; /** ObjectStoreTemplateSummaries */ ObjectStoreTemplateSummaries: components["schemas"]["ObjectStoreTemplateSummary"][]; /** ObjectStoreTemplateSummary */ @@ -9945,32 +9991,27 @@ export interface components { /** Name */ name: string | null; /** Secrets */ - secrets?: components["schemas"]["ObjectStoreTemplateSecret"][] | null; + secrets?: components["schemas"]["TemplateSecret"][] | null; /** * Type * @enum {string} */ - type: "s3" | "azure_blob" | "disk" | "generic_s3"; + type: "aws_s3" | "azure_blob" | "disk" | "generic_s3"; /** Variables */ - variables?: components["schemas"]["ObjectStoreTemplateVariable"][] | null; + variables?: + | ( + | components["schemas"]["TemplateVariableString"] + | components["schemas"]["TemplateVariableInteger"] + | components["schemas"]["TemplateVariablePathComponent"] + | components["schemas"]["TemplateVariableBoolean"] + )[] + | null; /** * Version * @default 0 */ version?: number; }; - /** ObjectStoreTemplateVariable */ - ObjectStoreTemplateVariable: { - /** Help */ - help: string | null; - /** Name */ - name: string; - /** - * Type - * @enum {string} - */ - type: "string" | "boolean" | "integer"; - }; /** OutputReferenceByLabel */ OutputReferenceByLabel: { /** @@ -11847,6 +11888,92 @@ export interface components { * @enum {string} */ TaskState: "PENDING" | "STARTED" | "RETRY" | "FAILURE" | "SUCCESS"; + /** TemplateSecret */ + TemplateSecret: { + /** Help */ + help: string | null; + /** Label */ + label?: string | null; + /** Name */ + name: string; + }; + /** TemplateVariableBoolean */ + TemplateVariableBoolean: { + /** + * Default + * @default false + */ + default?: boolean; + /** Help */ + help: string | null; + /** Label */ + label?: string | null; + /** Name */ + name: string; + /** + * Type + * @constant + * @enum {string} + */ + type: "boolean"; + }; + /** TemplateVariableInteger */ + TemplateVariableInteger: { + /** + * Default + * @default 0 + */ + default?: number; + /** Help */ + help: string | null; + /** Label */ + label?: string | null; + /** Name */ + name: string; + /** + * Type + * @constant + * @enum {string} + */ + type: "integer"; + }; + /** TemplateVariablePathComponent */ + TemplateVariablePathComponent: { + /** Default */ + default?: string | null; + /** Help */ + help: string | null; + /** Label */ + label?: string | null; + /** Name */ + name: string; + /** + * Type + * @constant + * @enum {string} + */ + type: "path_component"; + }; + /** TemplateVariableString */ + TemplateVariableString: { + /** + * Default + * @default + */ + default?: string; + /** Help */ + help: string | null; + /** Label */ + label?: string | null; + /** Name */ + name: string; + /** + * Type + * @constant + * @enum {string} + */ + type: "string"; + }; /** ToolDataDetails */ ToolDataDetails: { /** @@ -12529,7 +12656,7 @@ export interface components { * Type * @enum {string} */ - type: "s3" | "azure_blob" | "disk" | "generic_s3"; + type: "aws_s3" | "azure_blob" | "disk" | "generic_s3"; /** Uuid */ uuid: string; /** Variables */ @@ -12577,6 +12704,34 @@ export interface components { */ id: string; }; + /** UserFileSourceModel */ + UserFileSourceModel: { + /** Description */ + description: string | null; + /** Id */ + id: string | number; + /** Name */ + name: string; + /** Secrets */ + secrets: string[]; + /** Template Id */ + template_id: string; + /** Template Version */ + template_version: number; + /** + * Type + * @enum {string} + */ + type: "ftp" | "posix" | "s3fs" | "azure"; + /** Uri Root */ + uri_root: string; + /** Uuid */ + uuid: string; + /** Variables */ + variables: { + [key: string]: (string | boolean | number) | undefined; + } | null; + }; /** * UserModel * @description User in a transaction context. @@ -14766,6 +14921,142 @@ export interface operations { }; }; }; + file_sources__instances_index: { + /** Get a list of persisted file source instances defined by the requesting user. */ + parameters?: { + /** @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?: { + "run-as"?: string | null; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["UserFileSourceModel"][]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + file_sources__create_instance: { + /** Create a user-bound object store. */ + parameters?: { + /** @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?: { + "run-as"?: string | null; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateInstancePayload"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["UserFileSourceModel"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + file_sources__instances_get: { + /** Get a list of persisted file source instances defined by the requesting user. */ + parameters: { + /** @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?: { + "run-as"?: string | null; + }; + /** @description The index for a persisted UserFileSourceStore object. */ + path: { + user_file_source_id: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["UserFileSourceModel"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + file_sources__instances_update: { + /** Update or upgrade user file source instance. */ + parameters: { + /** @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?: { + "run-as"?: string | null; + }; + /** @description The index for a persisted UserFileSourceStore object. */ + path: { + user_file_source_id: string; + }; + }; + requestBody: { + content: { + "application/json": + | components["schemas"]["UpdateInstanceSecretPayload"] + | components["schemas"]["UpgradeInstancePayload"] + | components["schemas"]["UpdateInstancePayload"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["UserFileSourceModel"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + file_sources__templates_index: { + /** Get a list of file source templates available to build user defined file sources from */ + parameters?: { + /** @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?: { + "run-as"?: string | null; + }; + }; + responses: { + /** @description A list of the configured file source templates. */ + 200: { + content: { + "application/json": components["schemas"]["FileSourceTemplateSummaries"]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; index_api_folders__folder_id__contents_get: { /** * Returns a list of a folder's contents (files and sub-folders) with additional metadata about the folder. diff --git a/client/src/components/ConfigTemplates/CreateInstance.test.ts b/client/src/components/ConfigTemplates/CreateInstance.test.ts new file mode 100644 index 000000000000..fe4e59ad4a59 --- /dev/null +++ b/client/src/components/ConfigTemplates/CreateInstance.test.ts @@ -0,0 +1,32 @@ +import { shallowMount } from "@vue/test-utils"; +import { getLocalVue } from "tests/jest/helpers"; + +import CreateInstance from "./CreateInstance.vue"; + +const localVue = getLocalVue(true); + +describe("CreateInstance", () => { + it("should render a loading message during loading", async () => { + const wrapper = shallowMount(CreateInstance, { + propsData: { + loading: true, + loadingMessage: "component loading...", + }, + localVue, + }); + const loadingSpan = wrapper.findComponent({ name: "LoadingSpan" }).exists(); + expect(loadingSpan).toBeTruthy(); + }); + + it("should hide a loading message after loading", async () => { + const wrapper = shallowMount(CreateInstance, { + propsData: { + loading: false, + loadingMessage: "component loading...", + }, + localVue, + }); + const loadingSpan = wrapper.findComponent({ name: "LoadingSpan" }).exists(); + expect(loadingSpan).toBeFalsy(); + }); +}); diff --git a/client/src/components/ConfigTemplates/CreateInstance.vue b/client/src/components/ConfigTemplates/CreateInstance.vue new file mode 100644 index 000000000000..334d341bc590 --- /dev/null +++ b/client/src/components/ConfigTemplates/CreateInstance.vue @@ -0,0 +1,21 @@ + + + diff --git a/client/src/components/ConfigTemplates/EditSecretsForm.test.ts b/client/src/components/ConfigTemplates/EditSecretsForm.test.ts new file mode 100644 index 000000000000..3a6252da0e29 --- /dev/null +++ b/client/src/components/ConfigTemplates/EditSecretsForm.test.ts @@ -0,0 +1,36 @@ +import { mount } from "@vue/test-utils"; +import { getLocalVue } from "tests/jest/helpers"; + +import { STANDARD_FILE_SOURCE_TEMPLATE, STANDARD_OBJECT_STORE_TEMPLATE } from "./test_fixtures"; + +import EditSecretsForm from "./EditSecretsForm.vue"; + +const localVue = getLocalVue(true); + +describe("EditSecretsForm", () => { + it("should render a secrets for for file source templates", async () => { + const wrapper = mount(EditSecretsForm, { + propsData: { + template: STANDARD_FILE_SOURCE_TEMPLATE, + title: "Secrets FORM for file source", + }, + localVue, + }); + const titleText = wrapper.find(".portlet-title-text"); + expect(titleText.exists()).toBeTruthy(); + expect(titleText.text()).toEqual("Secrets FORM for file source"); + }); + + it("should render a secrets for for object store templates", async () => { + const wrapper = mount(EditSecretsForm, { + propsData: { + template: STANDARD_OBJECT_STORE_TEMPLATE, + title: "Secrets FORM for object store", + }, + localVue, + }); + const titleText = wrapper.find(".portlet-title-text"); + expect(titleText.exists()).toBeTruthy(); + expect(titleText.text()).toEqual("Secrets FORM for object store"); + }); +}); diff --git a/client/src/components/ConfigTemplates/EditSecretsForm.vue b/client/src/components/ConfigTemplates/EditSecretsForm.vue new file mode 100644 index 000000000000..9baa7ac5f7c0 --- /dev/null +++ b/client/src/components/ConfigTemplates/EditSecretsForm.vue @@ -0,0 +1,39 @@ + + + diff --git a/client/src/components/ConfigTemplates/InstanceDropdown.test.ts b/client/src/components/ConfigTemplates/InstanceDropdown.test.ts new file mode 100644 index 000000000000..e0a54fd53489 --- /dev/null +++ b/client/src/components/ConfigTemplates/InstanceDropdown.test.ts @@ -0,0 +1,40 @@ +import { shallowMount } from "@vue/test-utils"; +import { getLocalVue } from "tests/jest/helpers"; + +import InstanceDropdown from "./InstanceDropdown.vue"; + +const localVue = getLocalVue(true); + +describe("InstanceDropdown", () => { + it("should render a drop down without upgrade if upgrade unavailable as an option", async () => { + const wrapper = shallowMount(InstanceDropdown, { + propsData: { + prefix: "file-source", + name: "my cool instance", + routeEdit: "/object_store_instance/edit", + routeUpgrade: "/object_store_instance/upgrade", + isUpgradable: false, + }, + localVue, + }); + const menu = wrapper.find(".dropdown-menu"); + const links = menu.findAll("a"); + expect(links.length).toBe(1); + }); + + it("should render a drop down with upgrade if upgrade available as an option", async () => { + const wrapper = shallowMount(InstanceDropdown, { + propsData: { + prefix: "file-source", + name: "my cool instance", + routeEdit: "/object_store_instance/edit", + routeUpgrade: "/object_store_instance/upgrade", + isUpgradable: true, + }, + localVue, + }); + const menu = wrapper.find(".dropdown-menu"); + const links = menu.findAll("a"); + expect(links.length).toBe(2); + }); +}); diff --git a/client/src/components/ConfigTemplates/InstanceDropdown.vue b/client/src/components/ConfigTemplates/InstanceDropdown.vue new file mode 100644 index 000000000000..e825479bc3e3 --- /dev/null +++ b/client/src/components/ConfigTemplates/InstanceDropdown.vue @@ -0,0 +1,50 @@ + + + diff --git a/client/src/components/ConfigTemplates/InstanceForm.test.ts b/client/src/components/ConfigTemplates/InstanceForm.test.ts new file mode 100644 index 000000000000..546a6670d68f --- /dev/null +++ b/client/src/components/ConfigTemplates/InstanceForm.test.ts @@ -0,0 +1,42 @@ +import { shallowMount } from "@vue/test-utils"; +import { getLocalVue } from "tests/jest/helpers"; + +import InstanceForm from "./InstanceForm.vue"; + +const localVue = getLocalVue(true); + +const inputs: any[] = []; +const SUBMIT_TITLE = "Submit the form!"; + +describe("InstanceForm", () => { + it("should render a loading message and not submit button if inputs is null", async () => { + const wrapper = shallowMount(InstanceForm, { + propsData: { + title: "MY FORM", + loading: true, + inputs: null, + submitTitle: SUBMIT_TITLE, + }, + localVue, + }); + const loadingSpan = wrapper.findComponent({ name: "LoadingSpan" }).exists(); + expect(loadingSpan).toBeTruthy(); + expect(wrapper.find("#submit").exists()).toBeFalsy(); + }); + + it("should hide a loading message after loading", async () => { + const wrapper = shallowMount(InstanceForm, { + propsData: { + title: "MY FORM", + loading: false, + inputs: inputs, + submitTitle: SUBMIT_TITLE, + }, + localVue, + }); + const loadingSpan = wrapper.findComponent({ name: "LoadingSpan" }).exists(); + expect(loadingSpan).toBeFalsy(); + expect(wrapper.find("#submit").exists()).toBeTruthy(); + expect(wrapper.find("#submit").text()).toEqual(SUBMIT_TITLE); + }); +}); diff --git a/client/src/components/ObjectStore/Instances/InstanceForm.vue b/client/src/components/ConfigTemplates/InstanceForm.vue similarity index 79% rename from client/src/components/ObjectStore/Instances/InstanceForm.vue rename to client/src/components/ConfigTemplates/InstanceForm.vue index 97d4525aa4cb..7e9bba13e4aa 100644 --- a/client/src/components/ObjectStore/Instances/InstanceForm.vue +++ b/client/src/components/ConfigTemplates/InstanceForm.vue @@ -1,4 +1,6 @@