Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
truenas-rk3588
webui
Commits
f7039bb7
Unverified
Commit
f7039bb7
authored
2 years ago
by
Evgeny Stepanovych
Committed by
GitHub
2 years ago
Browse files
Options
Download
Email Patches
Plain Diff
NAS-116570 / 22.12 / NAS-116570: Refactoring cloudcredentials form (#6918)
parent
71521a21
Changes
159
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
.eslintrc.js
+2
-1
.eslintrc.js
src/app/enums/cloudsync-provider.enum.ts
+6
-0
src/app/enums/cloudsync-provider.enum.ts
src/app/helptext/system/cloud-credentials.ts
+0
-135
src/app/helptext/system/cloud-credentials.ts
src/app/interfaces/cloud-sync-task.interface.ts
+1
-1
src/app/interfaces/cloud-sync-task.interface.ts
src/app/interfaces/cloudsync-credential.interface.ts
+3
-3
src/app/interfaces/cloudsync-credential.interface.ts
src/app/interfaces/cloudsync-provider.interface.ts
+3
-3
src/app/interfaces/cloudsync-provider.interface.ts
src/app/modules/entity/entity-form/components/form-select/form-select.component.html
+7
-4
...ty-form/components/form-select/form-select.component.html
src/app/modules/entity/entity-form/components/form-select/form-select.component.scss
+14
-0
...ty-form/components/form-select/form-select.component.scss
src/app/modules/entity/entity-form/entity-form.component.ts
+1
-1
src/app/modules/entity/entity-form/entity-form.component.ts
src/app/modules/ix-forms/components/ix-textarea/ix-textarea.component.html
+2
-1
...x-forms/components/ix-textarea/ix-textarea.component.html
src/app/modules/ix-forms/components/ix-textarea/ix-textarea.component.scss
+8
-0
...x-forms/components/ix-textarea/ix-textarea.component.scss
src/app/modules/ix-forms/components/ix-textarea/ix-textarea.component.ts
+1
-0
.../ix-forms/components/ix-textarea/ix-textarea.component.ts
src/app/modules/ix-forms/testing/control-harnesses.helpers.ts
+2
-0
...app/modules/ix-forms/testing/control-harnesses.helpers.ts
src/app/pages/credentials/backup-credentials/backup-credentials.component.ts
+26
-18
...ntials/backup-credentials/backup-credentials.component.ts
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.html
+39
-0
...ud-credentials-form/cloud-credentials-form.component.html
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.scss
+7
-0
...ud-credentials-form/cloud-credentials-form.component.scss
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.spec.ts
+274
-0
...credentials-form/cloud-credentials-form.component.spec.ts
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.ts
+300
-0
...loud-credentials-form/cloud-credentials-form.component.ts
src/app/pages/credentials/backup-credentials/cloud-credentials-form/oauth-provider/oauth-provider.component.html
+17
-0
...entials-form/oauth-provider/oauth-provider.component.html
src/app/pages/credentials/backup-credentials/cloud-credentials-form/oauth-provider/oauth-provider.component.scss
+3
-0
...entials-form/oauth-provider/oauth-provider.component.scss
with
716 additions
and
167 deletions
+716
-167
.eslintrc.js
View file @
f7039bb7
...
...
@@ -191,7 +191,8 @@ module.exports = {
"
unused-imports/no-unused-vars
"
:
[
"
error
"
,
{
vars
:
"
local
"
,
args
:
"
after-used
"
,
argsIgnorePattern
:
"
^_$
"
argsIgnorePattern
:
"
^_$
"
,
ignoreRestSiblings
:
true
,
}],
"
@typescript-eslint/ban-types
"
:
[
"
error
"
],
"
unicorn/filename-case
"
:
[
"
error
"
,
{
case
:
"
kebabCase
"
}],
...
...
This diff is collapsed.
Click to expand it.
src/app/enums/cloudsync-provider
-name
.enum.ts
→
src/app/enums/cloudsync-provider.enum.ts
View file @
f7039bb7
...
...
@@ -18,3 +18,9 @@ export enum CloudsyncProviderName {
Webdav
=
'
WEBDAV
'
,
Yandex
=
'
YANDEX
'
,
}
export
enum
OneDriveType
{
Personal
=
'
PERSONAL
'
,
Business
=
'
BUSINESS
'
,
DocumentLibrary
=
'
DOCUMENT_LIBRARY
'
,
}
This diff is collapsed.
Click to expand it.
src/app/helptext/system/cloud-credentials.ts
View file @
f7039bb7
import
{
Validators
}
from
'
@angular/forms
'
;
import
{
marker
as
T
}
from
'
@biesbjerg/ngx-translate-extract-marker
'
;
import
{
regexValidator
}
from
'
app/modules/entity/entity-form/validators/regex-validation
'
;
export
const
helptextSystemCloudcredentials
=
{
fieldset_basic
:
T
(
'
Name and Provider
'
),
fieldset_authentication
:
T
(
'
Authentication
'
),
fieldset_advanced
:
T
(
'
Advanced Options
'
),
fieldset_oauth_authentication
:
T
(
'
OAuth Authentication
'
),
add_tooltip
:
T
(
'
Add Cloud Credential
'
),
name
:
{
placeholder
:
T
(
'
Name
'
),
tooltip
:
T
(
'
Enter a name for the new credential.
'
),
validation
:
[
Validators
.
required
],
},
provider
:
{
placeholder
:
T
(
'
Provider
'
),
tooltip
:
T
(
'
Third-party Cloud service providers. Choose a provider
\
to configure connection credentials.
'
),
validation
:
[
Validators
.
required
],
},
client_id
:
{
placeholder
:
T
(
'
OAuth Client ID
'
),
tooltip
:
T
(
''
),
},
client_secret
:
{
placeholder
:
T
(
'
OAuth Client Secret
'
),
tooltip
:
T
(
''
),
},
access_key_id_s3
:
{
placeholder
:
T
(
'
Access Key ID
'
),
tooltip
:
T
(
'
Amazon Web Services Key ID. This is found on
\
<a href="https://aws.amazon.com/" target="_blank">Amazon AWS</a> by
\
...
...
@@ -43,9 +23,7 @@ export const helptextSystemCloudcredentials = {
between 5 and 20 characters.
'
,
),
},
secret_access_key_s3
:
{
placeholder
:
T
(
'
Secret Access Key
'
),
tooltip
:
T
(
'
Amazon Web Services password. If the Secret Access Key cannot be
\
found or remembered, go to <i>My Account -> Security Credentials ->
\
...
...
@@ -53,16 +31,11 @@ export const helptextSystemCloudcredentials = {
between 8 and 40 characters.
'
,
),
},
max_upload_parts_s3
:
{
placeholder
:
T
(
'
Maximum Upload Parts
'
),
tooltip
:
T
(
'
Define the maximum number of chunks for a multipart upload. This can
\
be useful if a service does not support the 10,000 chunk AWS S3 specification.
'
),
validation
:
[
regexValidator
(
/^
\d
+$/
)],
},
endpoint_s3
:
{
placeholder
:
T
(
'
Endpoint URL
'
),
tooltip
:
T
(
'
<a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteEndpoints.html"
\
target="_blank">S3 API endpoint URL</a>. When using AWS, the endpoint
\
field can be empty to use the default endpoint for the region, and
\
...
...
@@ -71,9 +44,7 @@ export const helptextSystemCloudcredentials = {
<a href="https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_website_region_endpoints
\
target="_blank">Simple Storage Service Website Endpoints</a>.
'
),
},
region_s3
:
{
placeholder
:
T
(
'
Region
'
),
tooltip
:
T
(
'
<a href="https://docs.aws.amazon.com/general/latest/gr/rande-manage.html"
\
target="_blank">AWS resources in a geographic area</a>. Leave empty to
\
automatically detect the correct public region for the bucket. Entering
\
...
...
@@ -83,17 +54,13 @@ export const helptextSystemCloudcredentials = {
<a href="https://docs.aws.amazon.com/govcloud-us/latest/UserGuide/whatis.html" target="_blank">AWS GovCloud</a>
\
region.
'
),
},
skip_region_s3
:
{
placeholder
:
T
(
'
Disable Endpoint Region
'
),
tooltip
:
T
(
'
Skip automatic detection of the Endpoint URL region. Set this
\
when configuring a custom Endpoint URL.
'
,
),
},
signatures_v2_s3
:
{
placeholder
:
T
(
'
Use Signature Version 2
'
),
tooltip
:
T
(
'
Force using
\
<a href="https://docs.aws.amazon.com/general/latest/gr/signature-version-2.html"
\
...
...
@@ -101,9 +68,7 @@ export const helptextSystemCloudcredentials = {
when configuring a custom Endpoint URL.
'
,
),
},
account_b2
:
{
placeholder
:
T
(
'
Key ID
'
),
tooltip
:
T
(
'
Alphanumeric
\
<a href="https://www.backblaze.com/b2/cloud-storage.html"
\
target="_blank">Backblaze B2</a> Application Key ID. To
\
...
...
@@ -111,18 +76,14 @@ generate a new application key, log in to the Backblaze account, \
go to the <i>App Keys</i> page, and add a new application key.
\
Copy the application <i>keyID</i> string to this field.
'
),
},
key_b2
:
{
placeholder
:
T
(
'
Application Key
'
),
tooltip
:
T
(
'
<a href="https://www.backblaze.com/b2/cloud-storage.html"
\
target="_blank">Backblaze B2</a> Application Key. To generate
\
a new application key, log in to the Backblaze account, go to the
\
<i>App Keys</i> page, and add a new application key. Copy the
\
<i>applicationKey</i> string to this field.
'
),
},
token_box
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
A User Access Token for <a href="https://developer.box.com/"
\
target="_blank">Box</a>. An
\
...
...
@@ -132,9 +93,7 @@ a new application key, log in to the Backblaze account, go to the \
<i>T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl</i>.
'
,
),
},
token_dropbox
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
Access Token for a Dropbox account. A
\
<a href="https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account/"
\
...
...
@@ -143,39 +102,27 @@ a new application key, log in to the Backblaze account, go to the \
before adding it here.
'
,
),
},
host_ftp
:
{
placeholder
:
T
(
'
Host
'
),
tooltip
:
T
(
'
FTP Host to connect to. Example: <i>ftp.example.com</i>.
'
),
},
port_ftp
:
{
placeholder
:
T
(
'
Port
'
),
tooltip
:
T
(
'
FTP Port number. Leave blank to use the default port <i>21</i>.
'
,
),
},
user_ftp
:
{
placeholder
:
T
(
'
Username
'
),
tooltip
:
T
(
'
A username on the FTP Host system. This user must already exist
\
on the FTP Host.
'
,
),
},
pass_ftp
:
{
placeholder
:
T
(
'
Password
'
),
tooltip
:
T
(
'
Password for the user account.
'
),
},
preview_google_cloud_storage
:
{
placeholder
:
T
(
'
Preview JSON Service Account Key
'
),
tooltip
:
T
(
'
Contents of the uploaded Service Account JSON file.
'
),
},
service_account_credentials_google_cloud_storage
:
{
placeholder
:
T
(
'
Service Account
'
),
tooltip
:
T
(
'
Upload a Google
\
<a href="https://rclone.org/googlecloudstorage/#service-account-support"
\
...
...
@@ -184,11 +131,8 @@ a new application key, log in to the Backblaze account, go to the \
<a href="https://console.cloud.google.com/apis/credentials"
\
target="_blank">Google Cloud Platform Console</a>.
'
,
),
validation
:
[
Validators
.
required
],
},
token_google_drive
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
Token created with
\
<a href="https://developers.google.com/drive/api/v3/about-auth"
\
...
...
@@ -196,73 +140,53 @@ a new application key, log in to the Backblaze account, go to the \
must be refreshed.
'
,
),
},
token_google_photos
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
Token created with
\
<a href="https://developers.google.com/drive/api/v3/about-auth"
\
target="_blank">Google Drive</a>.
'
,
),
},
team_drive_google_drive
:
{
placeholder
:
T
(
'
Team Drive ID
'
),
tooltip
:
T
(
'
Only needed when connecting to a Team Drive. The ID of the top
\
level folder of the Team Drive.
'
,
),
},
url_http
:
{
placeholder
:
T
(
'
URL
'
),
tooltip
:
T
(
'
HTTP host URL.
'
),
},
token_hubic
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
Access Token <a href="https://api.hubic.com/sandbox/"
\
target="_blank">generated by a Hubic account</a>.
'
,
),
},
user_mega
:
{
placeholder
:
T
(
'
Username
'
),
tooltip
:
T
(
'
<a href="https://mega.nz/" target="_blank">MEGA</a> account
\
username.
'
,
),
},
pass_mega
:
{
placeholder
:
T
(
'
Password
'
),
tooltip
:
T
(
'
<a href="https://mega.nz/" target="_blank">MEGA</a> account
\
password.
'
,
),
},
account_azureblob
:
{
placeholder
:
T
(
'
Account Name
'
),
tooltip
:
T
(
'
<a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-create-storage-account"
\
target="_blank">Microsoft Azure</a> account name.
'
,
),
},
key_azureblob
:
{
placeholder
:
T
(
'
Account Key
'
),
tooltip
:
T
(
'
Base64 encoded key for the Azure account.
'
),
},
endpoint_azureblob
:
{
placeholder
:
T
(
'
Endpoint
'
),
tooltip
:
T
(
'
Example: blob.core.usgovcloudapi.net
'
),
},
token_onedrive
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
Microsoft Onedrive
\
<a href="https://docs.microsoft.com/en-us/onedrive/developer/rest-api/getting-started/authentication"
\
...
...
@@ -270,192 +194,133 @@ a new application key, log in to the Backblaze account, go to the \
add an access token.
'
,
),
},
drives_onedrive
:
{
placeholder
:
T
(
'
Drives List
'
),
tooltip
:
T
(
'
Drives and IDs registered to the Microsoft account.
\
Selecting a drive also fills the <i>Drive ID</i> field.
'
),
},
drive_type_onedrive
:
{
placeholder
:
T
(
'
Drive Account Type
'
),
tooltip
:
T
(
'
Type of Microsoft acount. Logging in to a Microsoft account
\
automatically chooses the correct account type.
'
,
),
},
drive_id_onedrive
:
{
placeholder
:
T
(
'
Drive ID
'
),
tooltip
:
T
(
'
Unique drive identifier. Log in to a Microsoft account and choose
\
a drive from the <i>Drives List</i> drop-down to add a valid ID.
'
,
),
},
user_openstack_swift
:
{
placeholder
:
T
(
'
User Name
'
),
tooltip
:
T
(
'
Openstack user name for login. This is the OS_USERNAME from an
\
<a href="https://rclone.org/swift/#configuration-from-an-openstack-credentials-file"
\
target="_blank">OpenStack credentials file</a>.
'
),
},
key_openstack_swift
:
{
placeholder
:
T
(
'
API Key or Password
'
),
tooltip
:
T
(
'
Openstack API key or password. This is the OS_PASSWORD from an
\
<a href="https://rclone.org/swift/#configuration-from-an-openstack-credentials-file"
\
target="_blank">OpenStack credentials file</a>.
'
),
},
auth_openstack_swift
:
{
placeholder
:
T
(
'
Authentication URL
'
),
tooltip
:
T
(
'
Authentication URL for the server. This is the OS_AUTH_URL from an
\
<a href="https://rclone.org/swift/#configuration-from-an-openstack-credentials-file"
\
target="_blank">OpenStack credentials file</a>.
'
),
},
user_id_openstack_swift
:
{
placeholder
:
T
(
'
User ID
'
),
tooltip
:
T
(
'
User ID to log in - optional - most swift systems use user and leave this blank
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
domain_openstack_swift
:
{
placeholder
:
T
(
'
User Domain
'
),
tooltip
:
T
(
'
User domain - optional
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
tenant_openstack_swift
:
{
placeholder
:
T
(
'
Tenant Name
'
),
tooltip
:
T
(
'
This is the OS_TENANT_NAME from an
\
<a href="https://rclone.org/swift/#configuration-from-an-openstack-credentials-file"
\
target="_blank">OpenStack credentials file</a>.
'
),
},
tenant_id_openstack_swift
:
{
placeholder
:
T
(
'
Tenant ID
'
),
tooltip
:
T
(
'
Tenant ID - optional for v1 auth, this or tenant required otherwise
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
tenant_domain_openstack_swift
:
{
placeholder
:
T
(
'
Tenant Domain
'
),
tooltip
:
T
(
'
Tenant domain - optional
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
region_openstack_swift
:
{
placeholder
:
T
(
'
Region Name
'
),
tooltip
:
T
(
'
Region name - optional
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
storage_url_openstack_swift
:
{
placeholder
:
T
(
'
Storage URL
'
),
tooltip
:
T
(
'
Storage URL - optional
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
auth_token_openstack_swift
:
{
placeholder
:
T
(
'
Auth Token
'
),
tooltip
:
T
(
'
Auth Token from alternate authentication - optional
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
application_credential_id_openstack_swift
:
{
placeholder
:
T
(
'
Application Credential ID
'
),
tooltip
:
T
(
'
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
application_credential_name_openstack_swift
:
{
placeholder
:
T
(
'
Application Credential Name
'
),
tooltip
:
T
(
'
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
application_credential_secret_openstack_swift
:
{
placeholder
:
T
(
'
Application Credential Secret
'
),
tooltip
:
T
(
'
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
auth_version_openstack_swift
:
{
placeholder
:
T
(
'
AuthVersion
'
),
tooltip
:
T
(
'
AuthVersion - optional - set to (1,2,3) if your auth URL has no version
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">(rclone documentation)</a>.
'
),
},
endpoint_type_openstack_swift
:
{
placeholder
:
T
(
'
Endpoint Type
'
),
tooltip
:
T
(
'
Endpoint type to choose from the service catalogue. <i>Public</i> is recommended, see the
\
<a href="https://rclone.org/swift/#standard-options" target="_blank">rclone documentation</a>.
'
),
},
token_pcloud
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
<a href="https://docs.pcloud.com/methods/intro/authentication.html"
\
target="_blank">pCloud Access Token</a>. These tokens can expire and
\
require extension.
'
,
),
},
hostname_pcloud
:
{
placeholder
:
T
(
'
Hostname
'
),
tooltip
:
T
(
'
Enter the hostname to connect to.
'
),
},
host_sftp
:
{
placeholder
:
T
(
'
Host
'
),
tooltip
:
T
(
'
SSH Host to connect to.
'
),
},
port_sftp
:
{
placeholder
:
T
(
'
Port
'
),
tooltip
:
T
(
'
SSH port number. Leave empty to use the default port
\
<i>22</i>.
'
,
),
},
user_sftp
:
{
placeholder
:
T
(
'
Username
'
),
tooltip
:
T
(
'
SSH Username.
'
),
},
pass_sftp
:
{
placeholder
:
T
(
'
Password
'
),
tooltip
:
T
(
'
Password for the SSH Username account.
'
),
},
private_key_sftp
:
{
placeholder
:
T
(
'
Private Key ID
'
),
tooltip
:
T
(
'
Import the private key from an existing SSH keypair or
\
select <i>Generate New</i> to create a new SSH key for this credential.
'
),
},
url_webdav
:
{
placeholder
:
T
(
'
URL
'
),
tooltip
:
T
(
'
URL of the HTTP host to connect to.
'
),
},
vendor_webdav
:
{
placeholder
:
T
(
'
WebDAV service
'
),
tooltip
:
T
(
'
Name of the WebDAV site, service, or software being used.
'
),
},
user_webdav
:
{
placeholder
:
T
(
'
Username
'
),
tooltip
:
T
(
'
WebDAV account username.
'
),
},
pass_webdav
:
{
placeholder
:
T
(
'
Password
'
),
tooltip
:
T
(
'
WebDAV account password.
'
),
},
token_yandex
:
{
placeholder
:
T
(
'
Access Token
'
),
tooltip
:
T
(
'
Yandex
\
<a href="https://tech.yandex.com/direct/doc/dg-v4/concepts/auth-token-docpage/"
\
target="_blank">Access Token</a>.
'
,
),
},
formTitle
:
T
(
'
Cloud Credentials
'
),
};
This diff is collapsed.
Click to expand it.
src/app/interfaces/cloud-sync-task.interface.ts
View file @
f7039bb7
...
...
@@ -8,7 +8,7 @@ export interface CloudCredential {
id
:
number
;
name
:
string
;
provider
:
string
;
attributes
:
{
[
key
:
string
]:
string
};
attributes
:
{
[
key
:
string
]:
string
|
number
|
boolean
};
}
export
interface
BwLimit
{
...
...
This diff is collapsed.
Click to expand it.
src/app/interfaces/cloudsync-credential.interface.ts
View file @
f7039bb7
import
{
CloudsyncProviderName
}
from
'
app/enums/cloudsync-provider
-name
.enum
'
;
import
{
CloudsyncProviderName
,
OneDriveType
}
from
'
app/enums/cloudsync-provider.enum
'
;
export
interface
CloudsyncCredential
{
attributes
:
{
[
attribute
:
string
]:
string
;
[
attribute
:
string
]:
string
|
number
|
boolean
;
};
id
:
number
;
name
:
string
;
...
...
@@ -25,7 +25,7 @@ export interface CloudsyncBucket {
}
export
interface
CloudsyncOneDriveDrive
{
drive_type
:
string
;
drive_type
:
OneDriveType
;
drive_id
:
string
;
}
...
...
This diff is collapsed.
Click to expand it.
src/app/interfaces/cloudsync-provider.interface.ts
View file @
f7039bb7
import
{
CloudsyncProviderName
}
from
'
app/enums/cloudsync-provider
-name
.enum
'
;
import
{
CloudsyncProviderName
}
from
'
app/enums/cloudsync-provider.enum
'
;
import
{
TransferMode
}
from
'
app/enums/transfer-mode.enum
'
;
export
interface
CloudsyncProvider
{
...
...
@@ -7,8 +7,8 @@ export interface CloudsyncProvider {
credentials_oauth
:
string
;
credentials_schema
:
any
[];
name
:
CloudsyncProviderName
;
task_schema
:
any
[];
title
:
CloudsyncProviderName
;
task_schema
:
unknown
[];
// Not really used
title
:
string
;
}
export
type
CloudsyncRestoreParams
=
[
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/entity/entity-form/components/form-select/form-select.component.html
View file @
f7039bb7
...
...
@@ -19,6 +19,13 @@
[header]=
"config.placeholder"
[message]=
"config.tooltip"
></ix-tooltip>
<a
*ngIf=
"config.linkText"
(click)=
"linkClicked()"
class=
"link"
>
{{ config.linkText | translate }}
<mat-icon
style=
"font-size: x-small;"
>
open_in_new
</mat-icon>
</a>
</div>
<ng-container
*ngIf=
"config.inlineLabel"
>
...
...
@@ -110,10 +117,6 @@
</mat-select>
</mat-form-field>
<div
*ngIf=
"config.linkText"
>
<a
(click)=
"linkClicked()"
class=
"link"
>
{{ config.linkText | translate }}
<mat-icon
style=
"font-size: x-small;"
>
open_in_new
</mat-icon></a>
</div>
<div
class=
"margin-for-error"
>
<ix-form-errors
[control]=
"group.controls[config.name]"
[config]=
"config"
></ix-form-errors>
<mat-error
*ngIf=
"config['hasErrors']"
><div
[innerHTML]=
"config['errors']"
></div></mat-error>
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/entity/entity-form/components/form-select/form-select.component.scss
View file @
f7039bb7
...
...
@@ -19,10 +19,24 @@
padding
:
8px
;
}
.label-container
{
align-items
:
flex-end
;
display
:
flex
;
}
.link
{
color
:
var
(
--
primary
);
cursor
:
pointer
;
display
:
block
;
font-size
:
x-small
;
margin-left
:
auto
;
mat-icon
{
line-height
:
40px
;
margin-left
:
3px
;
margin-right
:
1px
;
width
:
unset
;
}
}
:host-context
(
ix-system-alert
)
{
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/entity/entity-form/entity-form.component.ts
View file @
f7039bb7
...
...
@@ -80,7 +80,7 @@ export class EntityFormComponent implements OnInit, OnDestroy, OnChanges, AfterV
templateTop
:
TemplateRef
<
unknown
>
;
@
ContentChildren
(
EntityTemplateDirective
)
templates
:
QueryList
<
EntityTemplateDirective
>
;
templates
:
QueryList
<
EntityTemplateDirective
>
;
sub
:
Subscription
;
error
:
string
;
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/ix-forms/components/ix-textarea/ix-textarea.component.html
View file @
f7039bb7
...
...
@@ -8,12 +8,13 @@
<ix-tooltip
*ngIf=
"tooltip"
[header]=
"label"
class=
"tooltip"
[message]=
"tooltip"
></ix-tooltip>
</div>
<div
class=
"input-container"
[class.disabled]=
"isDisabled"
>
<div
class=
"input-container"
[class.disabled]=
"isDisabled"
[class.readonly]=
"readonly"
>
<textarea
matInput
[rows]=
"rows"
[required]=
"required"
[disabled]=
"isDisabled"
[readonly]=
"readonly"
[(ngModel)]=
"value"
(ngModelChange)=
"onChange($event)"
(blur)=
"onTouch()"
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/ix-forms/components/ix-textarea/ix-textarea.component.scss
View file @
f7039bb7
...
...
@@ -39,6 +39,14 @@
opacity
:
1
;
}
}
&
.readonly
{
background
:
var
(
--
bg2
);
input
{
cursor
:
copy
;
}
}
}
.label-container
{
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/ix-forms/components/ix-textarea/ix-textarea.component.ts
View file @
f7039bb7
...
...
@@ -18,6 +18,7 @@ export class IxTextareaComponent implements ControlValueAccessor {
@
Input
()
tooltip
:
string
;
@
Input
()
required
:
boolean
;
@
Input
()
rows
=
4
;
@
Input
()
readonly
:
boolean
;
formControl
=
new
UntypedFormControl
(
this
).
value
as
UntypedFormControl
;
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/ix-forms/testing/control-harnesses.helpers.ts
View file @
f7039bb7
...
...
@@ -3,6 +3,7 @@ import { IxCheckboxHarness } from 'app/modules/ix-forms/components/ix-checkbox/i
import
{
IxChipsHarness
}
from
'
app/modules/ix-forms/components/ix-chips/ix-chips.harness
'
;
import
{
IxComboboxHarness
}
from
'
app/modules/ix-forms/components/ix-combobox/ix-combobox.harness
'
;
import
{
IxExplorerHarness
}
from
'
app/modules/ix-forms/components/ix-explorer/ix-explorer.harness
'
;
import
{
IxFileInputHarness
}
from
'
app/modules/ix-forms/components/ix-file-input/ix-file-input.harness
'
;
import
{
IxInputHarness
}
from
'
app/modules/ix-forms/components/ix-input/ix-input.harness
'
;
import
{
IxIpInputWithNetmaskHarness
,
...
...
@@ -31,6 +32,7 @@ export const supportedFormControlSelectors = [
JiraOauthHarness
,
SchedulerHarness
,
IxIpInputWithNetmaskHarness
,
IxFileInputHarness
,
]
as
const
;
export
type
SupportedFormControlHarness
=
InstanceType
<
(
typeof
supportedFormControlSelectors
)[
number
]
>
;
...
...
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/backup-credentials.component.ts
View file @
f7039bb7
...
...
@@ -11,6 +11,9 @@ import {
KeychainSshKeyPair
,
}
from
'
app/interfaces/keychain-credential.interface
'
;
import
{
AppTableAction
,
AppTableConfig
}
from
'
app/modules/entity/table/table.component
'
;
import
{
CloudCredentialsFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component
'
;
import
{
SshConnectionFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/ssh-connection-form/ssh-connection-form.component
'
;
...
...
@@ -19,8 +22,6 @@ import {
KeychainCredentialService
,
ReplicationService
,
StorageService
,
CloudCredentialService
,
}
from
'
app/services
'
;
import
{
IxSlideInService
}
from
'
app/services/ix-slide-in.service
'
;
import
{
ModalService
}
from
'
app/services/modal.service
'
;
import
{
CloudCredentialsFormComponent
}
from
'
./forms/cloud-credentials-form.component
'
;
@
UntilDestroy
()
@
Component
({
...
...
@@ -32,12 +33,12 @@ export class BackupCredentialsComponent implements OnInit {
private
navigation
:
Navigation
;
protected
providers
:
CloudsyncProvider
[];
private
isFirstCredentialsLoad
=
true
;
constructor
(
private
router
:
Router
,
private
storage
:
StorageService
,
private
cloudCredentialsService
:
CloudCredentialService
,
private
modalService
:
ModalService
,
private
slideInService
:
IxSlideInService
,
private
translate
:
TranslateService
,
)
{
...
...
@@ -45,10 +46,6 @@ export class BackupCredentialsComponent implements OnInit {
}
ngOnInit
():
void
{
this
.
modalService
.
refreshTable$
.
pipe
(
untilDestroyed
(
this
)).
subscribe
(()
=>
{
this
.
getCards
();
});
this
.
slideInService
.
onClose$
.
pipe
(
untilDestroyed
(
this
)).
subscribe
(()
=>
{
this
.
getCards
();
});
...
...
@@ -72,24 +69,32 @@ export class BackupCredentialsComponent implements OnInit {
name
:
'
cloudCreds
'
,
columns
:
[
{
name
:
this
.
translate
.
instant
(
'
Name
'
),
prop
:
'
name
'
},
{
name
:
this
.
translate
.
instant
(
'
Provider
'
),
prop
:
'
provider
'
},
{
name
:
this
.
translate
.
instant
(
'
Provider
'
),
prop
:
'
provider
Title
'
},
],
hideHeader
:
false
,
parent
:
this
,
add
:
()
=>
{
this
.
modal
Service
.
open
InSlideIn
(
CloudCredentialsFormComponent
);
this
.
slideIn
Service
.
open
(
CloudCredentialsFormComponent
);
},
edit
:
(
row
:
CloudsyncCredential
)
=>
{
this
.
modalService
.
openInSlideIn
(
CloudCredentialsFormComponent
,
row
.
id
);
edit
:
(
credential
:
CloudsyncCredential
)
=>
{
const
form
=
this
.
slideInService
.
open
(
CloudCredentialsFormComponent
);
form
.
setCredentialsForEdit
(
credential
);
},
dataSourceHelper
:
this
.
cloudCredentialsDataSourceHelper
.
bind
(
this
),
afterGetData
:
()
=>
{
afterGetData
:
(
credentials
:
CloudsyncCredential
[]
)
=>
{
const
state
=
this
.
navigation
.
extras
.
state
as
{
editCredential
:
string
;
id
:
string
};
if
(
state
&&
state
.
editCredential
)
{
if
(
state
.
editCredential
===
'
cloudcredentials
'
)
{
this
.
modalService
.
openInSlideIn
(
CloudCredentialsFormComponent
,
state
.
id
);
}
if
(
!
state
||
state
.
editCredential
!==
'
cloudcredentials
'
||
!
this
.
isFirstCredentialsLoad
)
{
return
;
}
const
credentialToEdit
=
credentials
.
find
((
credential
)
=>
credential
.
id
===
Number
(
state
.
id
));
if
(
!
credentialToEdit
)
{
return
;
}
const
form
=
this
.
slideInService
.
open
(
CloudCredentialsFormComponent
);
form
.
setCredentialsForEdit
(
credentialToEdit
);
this
.
isFirstCredentialsLoad
=
false
;
},
},
},
{
...
...
@@ -139,12 +144,15 @@ export class BackupCredentialsComponent implements OnInit {
];
}
cloudCredentialsDataSourceHelper
(
res
:
CloudsyncCredential
[]):
CloudsyncCredential
[]
{
cloudCredentialsDataSourceHelper
(
res
:
CloudsyncCredential
[]):
(
CloudsyncCredential
&
{
providerTitle
?:
string
})
[]
{
return
res
.
map
((
item
)
=>
{
if
(
this
.
providers
)
{
const
credentialProvider
=
this
.
providers
.
find
((
provider
)
=>
provider
.
name
===
item
.
provider
);
if
(
credentialProvider
)
{
item
.
provider
=
credentialProvider
.
title
;
return
{
...
item
,
providerTitle
:
credentialProvider
.
title
,
};
}
}
return
item
;
...
...
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.html
0 → 100644
View file @
f7039bb7
<ix-modal-header
[title]=
"'Cloud Credentials' | translate"
[loading]=
"isLoading"
></ix-modal-header>
<mat-card>
<mat-card-content>
<form
class=
"ix-form-container"
(submit)=
"onSubmit()"
>
<ix-fieldset
[title]=
"'Name and Provider' | translate"
[formGroup]=
"commonForm"
>
<ix-input
formControlName=
"name"
[label]=
"'Name' | translate"
[required]=
"true"
[tooltip]=
"helptext.name.tooltip | translate"
></ix-input>
<ix-select
formControlName=
"provider"
[label]=
"'Provider' | translate"
[required]=
"true"
[options]=
"providerOptions"
[tooltip]=
"helptext.provider.tooltip | translate"
></ix-select>
</ix-fieldset>
<ng-container
#providerFormContainer
></ng-container>
<div
class=
"form-actions"
>
<button
mat-button
type=
"button"
[disabled]=
"areActionsDisabled"
(click)=
"onVerify()"
>
{{ 'Verify Credential' | translate }}
</button>
<button
mat-button
type=
"submit"
[disabled]=
"areActionsDisabled"
color=
"primary"
>
{{ 'Save' | translate }}
</button>
</div>
</form>
</mat-card-content>
</mat-card>
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.scss
0 → 100644
View file @
f7039bb7
.form-actions
{
margin
:
8px
10px
;
button
{
margin-right
:
8px
;
}
}
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.spec.ts
0 → 100644
View file @
f7039bb7
// eslint-disable-next-line max-classes-per-file
import
{
HarnessLoader
}
from
'
@angular/cdk/testing
'
;
import
{
TestbedHarnessEnvironment
}
from
'
@angular/cdk/testing/testbed
'
;
import
{
Component
}
from
'
@angular/core
'
;
import
{
FormGroup
,
ReactiveFormsModule
}
from
'
@angular/forms
'
;
import
{
MatButtonHarness
}
from
'
@angular/material/button/testing
'
;
import
{
createComponentFactory
,
mockProvider
,
Spectator
}
from
'
@ngneat/spectator/jest
'
;
import
{
of
}
from
'
rxjs
'
;
import
{
MockWebsocketService
}
from
'
app/core/testing/classes/mock-websocket.service
'
;
import
{
mockCall
,
mockWebsocket
}
from
'
app/core/testing/utils/mock-websocket.utils
'
;
import
{
CloudsyncProviderName
}
from
'
app/enums/cloudsync-provider.enum
'
;
import
{
CloudsyncCredential
}
from
'
app/interfaces/cloudsync-credential.interface
'
;
import
{
CloudsyncProvider
}
from
'
app/interfaces/cloudsync-provider.interface
'
;
import
{
IxSelectHarness
}
from
'
app/modules/ix-forms/components/ix-select/ix-select.harness
'
;
import
{
IxFormsModule
}
from
'
app/modules/ix-forms/ix-forms.module
'
;
import
{
IxFormHarness
}
from
'
app/modules/ix-forms/testing/ix-form.harness
'
;
import
{
SnackbarService
}
from
'
app/modules/snackbar/services/snackbar.service
'
;
import
{
BaseProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/base-provider-form
'
;
import
{
S3ProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/s3-provider-form/s3-provider-form.component
'
;
import
{
TokenProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/token-provider-form/token-provider-form.component
'
;
import
{
DialogService
,
WebSocketService
}
from
'
app/services
'
;
import
{
IxSlideInService
}
from
'
app/services/ix-slide-in.service
'
;
import
{
CloudCredentialsFormComponent
}
from
'
./cloud-credentials-form.component
'
;
jest
.
mock
(
'
./provider-forms/s3-provider-form/s3-provider-form.component
'
,
()
=>
{
return
{
S3ProviderFormComponent
:
Component
({
template
:
''
,
})(
class
{
provider
:
CloudsyncProvider
;
setValues
=
jest
.
fn
()
as
BaseProviderFormComponent
[
'
setValues
'
];
getSubmitAttributes
=
jest
.
fn
(()
=>
({
s3attribute
:
'
s3 value
'
,
}))
as
BaseProviderFormComponent
[
'
getSubmitAttributes
'
];
beforeSubmit
=
jest
.
fn
(()
=>
of
(
undefined
))
as
BaseProviderFormComponent
[
'
beforeSubmit
'
];
form
=
{
get
invalid
():
boolean
{
return
false
;
},
}
as
FormGroup
;
}),
};
});
jest
.
mock
(
'
./provider-forms/token-provider-form/token-provider-form.component
'
,
()
=>
{
return
{
TokenProviderFormComponent
:
Component
({
template
:
''
,
})(
class
{
provider
:
CloudsyncProvider
;
}),
};
});
describe
(
'
CloudCredentialsFormComponent
'
,
()
=>
{
let
spectator
:
Spectator
<
CloudCredentialsFormComponent
>
;
let
loader
:
HarnessLoader
;
let
form
:
IxFormHarness
;
const
s3Provider
=
{
name
:
CloudsyncProviderName
.
AmazonS3
,
title
:
'
Amazon S3
'
,
}
as
CloudsyncProvider
;
const
boxProvider
=
{
name
:
CloudsyncProviderName
.
Box
,
title
:
'
Box
'
,
}
as
CloudsyncProvider
;
const
createComponent
=
createComponentFactory
({
component
:
CloudCredentialsFormComponent
,
imports
:
[
ReactiveFormsModule
,
IxFormsModule
,
],
declarations
:
[
TokenProviderFormComponent
,
S3ProviderFormComponent
,
],
providers
:
[
mockProvider
(
IxSlideInService
),
mockProvider
(
SnackbarService
),
mockProvider
(
DialogService
),
mockWebsocket
([
mockCall
(
'
cloudsync.credentials.create
'
),
mockCall
(
'
cloudsync.credentials.update
'
),
mockCall
(
'
cloudsync.credentials.verify
'
,
{
valid
:
true
,
}),
mockCall
(
'
cloudsync.providers
'
,
[
s3Provider
,
boxProvider
]),
]),
],
});
beforeEach
(
async
()
=>
{
spectator
=
createComponent
();
loader
=
TestbedHarnessEnvironment
.
loader
(
spectator
.
fixture
);
form
=
await
loader
.
getHarness
(
IxFormHarness
);
});
describe
(
'
rendering
'
,
()
=>
{
it
(
'
loads a list of providers and shows them in Provider select
'
,
async
()
=>
{
expect
(
spectator
.
inject
(
WebSocketService
).
call
).
toHaveBeenCalledWith
(
'
cloudsync.providers
'
);
const
providersSelect
=
await
form
.
getControl
(
'
Provider
'
)
as
IxSelectHarness
;
expect
(
await
providersSelect
.
getOptionLabels
()).
toEqual
([
'
Amazon S3
'
,
'
Box
'
]);
});
it
(
'
renders dynamic provider specific form when Provider is selected
'
,
async
()
=>
{
const
providersSelect
=
await
form
.
getControl
(
'
Provider
'
)
as
IxSelectHarness
;
await
providersSelect
.
setValue
(
'
Amazon S3
'
);
const
providerForm
=
spectator
.
query
(
S3ProviderFormComponent
);
expect
(
providerForm
).
toBeTruthy
();
expect
(
providerForm
.
provider
).
toBe
(
s3Provider
);
});
it
(
'
renders a token only form for some providers
'
,
async
()
=>
{
const
providersSelect
=
await
form
.
getControl
(
'
Provider
'
)
as
IxSelectHarness
;
await
providersSelect
.
setValue
(
'
Box
'
);
const
providerForm
=
spectator
.
query
(
TokenProviderFormComponent
);
expect
(
providerForm
).
toBeTruthy
();
expect
(
providerForm
.
provider
).
toBe
(
boxProvider
);
});
});
describe
(
'
verification
'
,
()
=>
{
it
(
'
verifies entered values when user presses Verify
'
,
async
()
=>
{
await
form
.
fillForm
({
Name
:
'
New sync
'
,
Provider
:
'
Amazon S3
'
,
});
const
verifyButton
=
await
loader
.
getHarness
(
MatButtonHarness
.
with
({
text
:
'
Verify Credential
'
}));
await
verifyButton
.
click
();
expect
(
spectator
.
inject
(
WebSocketService
).
call
).
toHaveBeenCalledWith
(
'
cloudsync.credentials.verify
'
,
[{
provider
:
'
S3
'
,
attributes
:
{
s3attribute
:
'
s3 value
'
,
},
}]);
});
it
(
'
calls beforeSubmit before verifying entered values
'
,
async
()
=>
{
await
form
.
fillForm
({
Name
:
'
New sync
'
,
Provider
:
'
Amazon S3
'
,
});
const
saveButton
=
await
loader
.
getHarness
(
MatButtonHarness
.
with
({
text
:
'
Verify Credential
'
}));
await
saveButton
.
click
();
const
providerForm
=
spectator
.
query
(
S3ProviderFormComponent
);
expect
(
providerForm
.
beforeSubmit
).
toHaveBeenCalled
();
});
it
(
'
shows an error when verification fails
'
,
async
()
=>
{
const
mockWebsocket
=
spectator
.
inject
(
MockWebsocketService
);
mockWebsocket
.
mockCall
(
'
cloudsync.credentials.verify
'
,
{
valid
:
false
,
excerpt
:
'
Missing some important field
'
,
error
:
'
Some error
'
,
});
await
form
.
fillForm
({
Name
:
'
New sync
'
,
Provider
:
'
Amazon S3
'
,
});
const
verifyButton
=
await
loader
.
getHarness
(
MatButtonHarness
.
with
({
text
:
'
Verify Credential
'
}));
await
verifyButton
.
click
();
expect
(
spectator
.
inject
(
DialogService
).
errorReport
).
toHaveBeenCalledWith
(
'
Error
'
,
'
Missing some important field
'
,
expect
.
anything
(),
);
});
});
describe
(
'
saving
'
,
()
=>
{
it
(
'
shows existing values when form is opened for edit
'
,
async
()
=>
{
spectator
.
component
.
setCredentialsForEdit
({
id
:
233
,
name
:
'
My backup server
'
,
provider
:
CloudsyncProviderName
.
AmazonS3
,
attributes
:
{
hostname
:
'
backup.com
'
,
},
}
as
CloudsyncCredential
);
const
commonFormValues
=
await
form
.
getValues
();
expect
(
commonFormValues
).
toEqual
({
Name
:
'
My backup server
'
,
Provider
:
'
Amazon S3
'
,
});
const
providerForm
=
spectator
.
query
(
S3ProviderFormComponent
);
expect
(
providerForm
).
toBeTruthy
();
expect
(
providerForm
.
setValues
).
toHaveBeenCalledWith
({
hostname
:
'
backup.com
'
,
});
});
it
(
'
calls beforeSubmit before saving form
'
,
async
()
=>
{
await
form
.
fillForm
({
Name
:
'
New sync
'
,
Provider
:
'
Amazon S3
'
,
});
const
saveButton
=
await
loader
.
getHarness
(
MatButtonHarness
.
with
({
text
:
'
Save
'
}));
await
saveButton
.
click
();
const
providerForm
=
spectator
.
query
(
S3ProviderFormComponent
);
expect
(
providerForm
.
beforeSubmit
).
toHaveBeenCalled
();
});
it
(
'
saves new credentials when new form is saved
'
,
async
()
=>
{
await
form
.
fillForm
({
Name
:
'
New sync
'
,
Provider
:
'
Amazon S3
'
,
});
const
saveButton
=
await
loader
.
getHarness
(
MatButtonHarness
.
with
({
text
:
'
Save
'
}));
await
saveButton
.
click
();
expect
(
spectator
.
inject
(
WebSocketService
).
call
).
toHaveBeenCalledWith
(
'
cloudsync.credentials.create
'
,
[{
name
:
'
New sync
'
,
provider
:
CloudsyncProviderName
.
AmazonS3
,
attributes
:
{
s3attribute
:
'
s3 value
'
,
},
}]);
expect
(
spectator
.
inject
(
IxSlideInService
).
close
).
toHaveBeenCalledWith
();
expect
(
spectator
.
inject
(
SnackbarService
).
success
).
toHaveBeenCalled
();
});
it
(
'
updates existing credentials when edit form is saved
'
,
async
()
=>
{
spectator
.
component
.
setCredentialsForEdit
({
id
:
233
,
name
:
'
My backup server
'
,
provider
:
CloudsyncProviderName
.
AmazonS3
,
attributes
:
{
hostname
:
'
backup.com
'
,
},
}
as
CloudsyncCredential
);
await
form
.
fillForm
({
Name
:
'
My updated server
'
,
});
const
saveButton
=
await
loader
.
getHarness
(
MatButtonHarness
.
with
({
text
:
'
Save
'
}));
await
saveButton
.
click
();
expect
(
spectator
.
inject
(
WebSocketService
).
call
).
toHaveBeenCalledWith
(
'
cloudsync.credentials.update
'
,
[
233
,
{
name
:
'
My updated server
'
,
provider
:
CloudsyncProviderName
.
AmazonS3
,
attributes
:
{
s3attribute
:
'
s3 value
'
,
},
},
]);
expect
(
spectator
.
inject
(
IxSlideInService
).
close
).
toHaveBeenCalledWith
();
expect
(
spectator
.
inject
(
SnackbarService
).
success
).
toHaveBeenCalled
();
});
});
});
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/cloud-credentials-form/cloud-credentials-form.component.ts
0 → 100644
View file @
f7039bb7
import
{
ChangeDetectionStrategy
,
ChangeDetectorRef
,
Component
,
OnInit
,
Type
,
ViewChild
,
ViewContainerRef
,
}
from
'
@angular/core
'
;
import
{
FormBuilder
,
Validators
}
from
'
@angular/forms
'
;
import
{
UntilDestroy
,
untilDestroyed
}
from
'
@ngneat/until-destroy
'
;
import
{
TranslateService
}
from
'
@ngx-translate/core
'
;
import
{
Observable
,
of
}
from
'
rxjs
'
;
import
{
switchMap
}
from
'
rxjs/operators
'
;
import
{
CloudsyncProviderName
}
from
'
app/enums/cloudsync-provider.enum
'
;
import
{
helptextSystemCloudcredentials
as
helptext
}
from
'
app/helptext/system/cloud-credentials
'
;
import
{
CloudsyncCredential
,
CloudsyncCredentialUpdate
}
from
'
app/interfaces/cloudsync-credential.interface
'
;
import
{
CloudsyncProvider
}
from
'
app/interfaces/cloudsync-provider.interface
'
;
import
{
Option
}
from
'
app/interfaces/option.interface
'
;
import
{
EntityUtils
}
from
'
app/modules/entity/utils
'
;
import
{
FormErrorHandlerService
}
from
'
app/modules/ix-forms/services/form-error-handler.service
'
;
import
{
SnackbarService
}
from
'
app/modules/snackbar/services/snackbar.service
'
;
import
{
AzureProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/azure-provider-form/azure-provider-form.component
'
;
import
{
BackblazeB2ProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/backblaze-b2-provider-form/backblaze-b2-provider-form.component
'
;
import
{
BaseProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/base-provider-form
'
;
import
{
FtpProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/ftp-provider-form/ftp-provider-form.component
'
;
import
{
GoogleCloudProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/google-cloud-provider-form/google-cloud-provider-form.component
'
;
import
{
GoogleDriveProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/google-drive-provider-form/google-drive-provider-form.component
'
;
import
{
HttpProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/http-provider-form/http-provider-form.component
'
;
import
{
MegaProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/mega-provider-form/mega-provider-form.component
'
;
import
{
OneDriveProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/one-drive-provider-form/one-drive-provider-form.component
'
;
import
{
OpenstackSwiftProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/openstack-swift-provider-form/openstack-swift-provider-form.component
'
;
import
{
PcloudProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/pcloud-provider-form/pcloud-provider-form.component
'
;
import
{
S3ProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/s3-provider-form/s3-provider-form.component
'
;
import
{
SftpProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/sftp-provider-form/sftp-provider-form.component
'
;
import
{
TokenProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/token-provider-form/token-provider-form.component
'
;
import
{
WebdavProviderFormComponent
,
}
from
'
app/pages/credentials/backup-credentials/cloud-credentials-form/provider-forms/webdav-provider-form/webdav-provider-form.component
'
;
import
{
DialogService
,
WebSocketService
}
from
'
app/services
'
;
import
{
IxSlideInService
}
from
'
app/services/ix-slide-in.service
'
;
// TODO: Form is partially backend driven and partially hardcoded on the frontend.
@
UntilDestroy
()
@
Component
({
templateUrl
:
'
./cloud-credentials-form.component.html
'
,
styleUrls
:
[
'
./cloud-credentials-form.component.scss
'
],
changeDetection
:
ChangeDetectionStrategy
.
OnPush
,
})
export
class
CloudCredentialsFormComponent
implements
OnInit
{
commonForm
=
this
.
formBuilder
.
group
({
name
:
[
''
,
Validators
.
required
],
provider
:
[
null
as
CloudsyncProviderName
],
});
isLoading
=
false
;
existingCredential
:
CloudsyncCredential
;
providers
:
CloudsyncProvider
[]
=
[];
providerOptions
:
Observable
<
Option
[]
>
=
of
([]);
providerForm
:
BaseProviderFormComponent
;
@
ViewChild
(
'
providerFormContainer
'
,
{
static
:
true
,
read
:
ViewContainerRef
})
providerFormContainer
:
ViewContainerRef
;
readonly
helptext
=
helptext
;
constructor
(
private
ws
:
WebSocketService
,
private
formBuilder
:
FormBuilder
,
private
cdr
:
ChangeDetectorRef
,
private
slideInService
:
IxSlideInService
,
private
dialogService
:
DialogService
,
private
errorHandler
:
FormErrorHandlerService
,
private
translate
:
TranslateService
,
private
snackbarService
:
SnackbarService
,
)
{
// Has to be earlier than potential `setCredentialsForEdit` call
this
.
setFormEvents
();
}
get
isNew
():
boolean
{
return
!
this
.
existingCredential
;
}
get
selectedProvider
():
CloudsyncProvider
{
return
this
.
providers
?.
find
((
provider
)
=>
{
return
provider
.
name
===
this
.
commonForm
.
controls
.
provider
.
value
;
});
}
get
areActionsDisabled
():
boolean
{
return
this
.
isLoading
||
this
.
commonForm
.
invalid
||
this
.
providerForm
?.
form
?.
invalid
;
}
ngOnInit
():
void
{
this
.
loadProviders
();
}
setCredentialsForEdit
(
credential
:
CloudsyncCredential
):
void
{
this
.
existingCredential
=
credential
;
this
.
commonForm
.
patchValue
(
credential
);
if
(
this
.
providerForm
)
{
this
.
providerForm
.
setValues
(
this
.
existingCredential
.
attributes
);
}
}
onSubmit
():
boolean
{
this
.
isLoading
=
true
;
const
beforeSubmit$
=
this
.
providerForm
.
beforeSubmit
();
beforeSubmit$
.
pipe
(
switchMap
(()
=>
{
const
payload
=
this
.
preparePayload
();
return
this
.
isNew
?
this
.
ws
.
call
(
'
cloudsync.credentials.create
'
,
[
payload
])
:
this
.
ws
.
call
(
'
cloudsync.credentials.update
'
,
[
this
.
existingCredential
.
id
,
payload
]);
}),
untilDestroyed
(
this
),
)
.
subscribe
(
()
=>
{
this
.
isLoading
=
false
;
this
.
snackbarService
.
success
(
this
.
isNew
?
this
.
translate
.
instant
(
'
Cloud credential added.
'
)
:
this
.
translate
.
instant
(
'
Cloud credential updated.
'
),
);
this
.
slideInService
.
close
();
this
.
cdr
.
markForCheck
();
},
(
error
)
=>
{
// TODO: Errors for nested provider form will be shown in a modal. Can be improved.
this
.
isLoading
=
false
;
this
.
errorHandler
.
handleWsFormError
(
error
,
this
.
commonForm
);
this
.
cdr
.
markForCheck
();
},
);
return
false
;
}
onVerify
():
void
{
this
.
isLoading
=
true
;
const
beforeSubmit$
=
this
.
providerForm
.
beforeSubmit
();
beforeSubmit$
.
pipe
(
switchMap
(()
=>
{
const
{
name
,
...
payload
}
=
this
.
preparePayload
();
return
this
.
ws
.
call
(
'
cloudsync.credentials.verify
'
,
[
payload
]);
}),
untilDestroyed
(
this
),
)
.
subscribe
(
(
response
)
=>
{
if
(
response
.
valid
)
{
this
.
dialogService
.
info
(
this
.
translate
.
instant
(
'
Valid
'
),
this
.
translate
.
instant
(
'
The credentials are valid.
'
),
);
}
else
{
this
.
dialogService
.
errorReport
(
'
Error
'
,
response
.
excerpt
,
response
.
error
);
}
this
.
isLoading
=
false
;
this
.
cdr
.
markForCheck
();
},
(
error
)
=>
{
this
.
isLoading
=
false
;
this
.
errorHandler
.
handleWsFormError
(
error
,
this
.
commonForm
);
this
.
cdr
.
markForCheck
();
},
);
}
private
preparePayload
():
CloudsyncCredentialUpdate
{
const
commonValues
=
this
.
commonForm
.
value
;
return
{
name
:
commonValues
.
name
,
provider
:
commonValues
.
provider
,
attributes
:
this
.
providerForm
.
getSubmitAttributes
(),
};
}
private
loadProviders
():
void
{
this
.
isLoading
=
true
;
this
.
ws
.
call
(
'
cloudsync.providers
'
)
.
pipe
(
untilDestroyed
(
this
))
.
subscribe
(
(
providers
)
=>
{
this
.
providers
=
providers
;
this
.
providerOptions
=
of
(
providers
.
map
((
provider
)
=>
({
label
:
provider
.
title
,
value
:
provider
.
name
,
})),
);
this
.
renderProviderForm
();
if
(
this
.
existingCredential
)
{
this
.
providerForm
.
setValues
(
this
.
existingCredential
.
attributes
);
}
else
{
this
.
commonForm
.
patchValue
({
provider
:
providers
[
0
].
name
,
});
}
this
.
isLoading
=
false
;
this
.
cdr
.
markForCheck
();
},
(
error
)
=>
{
new
EntityUtils
().
handleWsError
(
null
,
error
,
this
.
dialogService
);
this
.
slideInService
.
close
();
},
);
}
private
setFormEvents
():
void
{
this
.
commonForm
.
controls
.
provider
.
valueChanges
.
pipe
(
untilDestroyed
(
this
))
.
subscribe
(()
=>
{
this
.
renderProviderForm
();
});
}
private
renderProviderForm
():
void
{
this
.
providerFormContainer
?.
clear
();
if
(
!
this
.
selectedProvider
)
{
return
;
}
const
formClass
=
this
.
getProviderFormClass
();
const
formRef
=
this
.
providerFormContainer
.
createComponent
(
formClass
);
formRef
.
instance
.
provider
=
this
.
selectedProvider
;
this
.
providerForm
=
formRef
.
instance
;
}
private
getProviderFormClass
():
Type
<
BaseProviderFormComponent
>
{
const
tokenOnlyProviders
=
[
CloudsyncProviderName
.
Box
,
CloudsyncProviderName
.
Dropbox
,
CloudsyncProviderName
.
GooglePhotos
,
CloudsyncProviderName
.
Hubic
,
CloudsyncProviderName
.
Yandex
,
];
if
(
tokenOnlyProviders
.
includes
(
this
.
selectedProvider
.
name
))
{
return
TokenProviderFormComponent
;
}
const
formMapping
=
new
Map
<
CloudsyncProviderName
,
Type
<
BaseProviderFormComponent
>>
([
[
CloudsyncProviderName
.
MicrosoftAzure
,
AzureProviderFormComponent
],
[
CloudsyncProviderName
.
BackblazeB2
,
BackblazeB2ProviderFormComponent
],
[
CloudsyncProviderName
.
Ftp
,
FtpProviderFormComponent
],
[
CloudsyncProviderName
.
GoogleCloudStorage
,
GoogleCloudProviderFormComponent
],
[
CloudsyncProviderName
.
GoogleDrive
,
GoogleDriveProviderFormComponent
],
[
CloudsyncProviderName
.
Http
,
HttpProviderFormComponent
],
[
CloudsyncProviderName
.
Mega
,
MegaProviderFormComponent
],
[
CloudsyncProviderName
.
MicrosoftOnedrive
,
OneDriveProviderFormComponent
],
[
CloudsyncProviderName
.
OpenstackSwift
,
OpenstackSwiftProviderFormComponent
],
[
CloudsyncProviderName
.
Pcloud
,
PcloudProviderFormComponent
],
[
CloudsyncProviderName
.
AmazonS3
,
S3ProviderFormComponent
],
[
CloudsyncProviderName
.
Sftp
,
SftpProviderFormComponent
],
[
CloudsyncProviderName
.
Webdav
,
WebdavProviderFormComponent
],
]);
return
formMapping
.
get
(
this
.
selectedProvider
.
name
);
}
}
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/cloud-credentials-form/oauth-provider/oauth-provider.component.html
0 → 100644
View file @
f7039bb7
<ix-fieldset
[title]=
"'OAuth Authentication' | translate"
[formGroup]=
"form"
>
<button
class=
"login-button"
mat-button
type=
"button"
(click)=
"onLoginPressed()"
>
{{ 'Log In To Provider' | translate }}
</button>
<ix-input
formControlName=
"client_id"
[label]=
"'OAuth Client ID' | translate"
></ix-input>
<ix-input
formControlName=
"client_secret"
[label]=
"'OAuth Client Secret' | translate"
type=
"password"
></ix-input>
</ix-fieldset>
This diff is collapsed.
Click to expand it.
src/app/pages/credentials/backup-credentials/cloud-credentials-form/oauth-provider/oauth-provider.component.scss
0 → 100644
View file @
f7039bb7
.login-button
{
width
:
100%
;
}
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
5
…
8
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment