from typing import Optional
from ..controllers.rgw import RgwRESTController
+from ..exceptions import DashboardException
+from ..rest_client import RequestException
from ..security import Scope
from ..services.rgw_iam import RgwAccounts
from ..tools import str_to_bool
"""
return self.get_account(account_id, daemon_name)
+ @EndpointDoc("Check if account name exists",
+ parameters={'account_name': (str, 'Account name'),
+ 'daemon_name': (str, 'Name of the daemon')})
+ @RESTController.Collection(method='GET', path='/exists')
+ def exists(self, account_name: str, daemon_name=None):
+ """
+ Check if an account with the given name exists
+ Returns True if account exists, False otherwise
+ """
+ try:
+ self.proxy(daemon_name, 'GET', 'account', {'name': account_name})
+ # If we get a result without error, the account exists
+ return True
+ except (DashboardException, RequestException):
+ # If we get an error (e.g., account not found), it doesn't exist
+ return False
+
def get_account(self, account_id, daemon_name=None) -> dict:
return self.proxy(daemon_name, 'GET', 'account', {'id': account_id})
placeholder="Enter account name"
id="acc_name"
name="acc_name"
+ cdValidate
+ #accName="cdValidate"
+ [invalid]="accName.isInvalid"
formControlName="name"/>
</cds-text-label>
<ng-template #accountIdError>
class="invalid-feedback">
<ng-container i18n>This field is required.</ng-container>
</span>
+ @if(accountForm.showError('name', formDir, 'notUnique')) {
+ <div class="invalid-feedback">
+ <ng-container i18n>An account with this name already exists.</ng-container>
+ </div>
+ }
</ng-template>
</div>
<!-- Tenant -->
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed, fakeAsync, tick, flush } from '@angular/core/testing';
import { RgwUserAccountsFormComponent } from './rgw-user-accounts-form.component';
import { ComponentsModule } from '~/app/shared/components/components.module';
import { ModalModule } from 'carbon-components-angular';
import { ReactiveFormsModule } from '@angular/forms';
import { RgwUserAccountsComponent } from '../rgw-user-accounts/rgw-user-accounts.component';
+import { DUE_TIMER } from '~/app/shared/forms/cd-validators';
class MockRgwUserAccountsService {
create = jest.fn().mockReturnValue(of(null));
modify = jest.fn().mockReturnValue(of(null));
setQuota = jest.fn().mockReturnValue(of(null));
+ exists = jest.fn().mockReturnValue(of(false));
}
describe('RgwUserAccountsFormComponent', () => {
expect(component).toBeTruthy();
});
- it('should call create method of MockRgwUserAccountsService and show success notification', () => {
+ it('should call create method of MockRgwUserAccountsService and show success notification', fakeAsync(() => {
component.editing = false;
- component.accountForm.get('name').setValue('test');
+ component.accountForm.get('name').setValue('test', { emitEvent: true });
+ tick(DUE_TIMER);
const payload = {
account_name: 'test',
email: '',
max_group: 1000,
max_access_keys: 4
};
+ const mockAccount = { id: 'RGW12312312312312312', name: 'test' };
const spy = jest.spyOn(component, 'submit');
- const createDataSpy = jest.spyOn(rgwUserAccountsService, 'create').mockReturnValue(of(null));
+ const createDataSpy = jest
+ .spyOn(rgwUserAccountsService, 'create')
+ .mockReturnValue(of(mockAccount));
component.submit();
expect(component.accountForm.valid).toBe(true);
expect(spy).toHaveBeenCalled();
expect(createDataSpy).toHaveBeenCalled();
expect(createDataSpy).toHaveBeenCalledWith(payload);
- });
+ flush();
+ }));
- it('should call modify method of MockRgwUserAccountsService and show success notification', () => {
+ it('should call modify method of MockRgwUserAccountsService and show success notification', fakeAsync(() => {
component.editing = true;
- component.accountForm.get('name').setValue('test');
+ component.originalAccountName = 'test';
+ component.accountForm.get('name').setValue('test', { emitEvent: true });
+ tick(DUE_TIMER);
component.accountForm.get('id').setValue('RGW12312312312312312');
component.accountForm.get('email').setValue('test@test.com');
const payload = {
max_group: 1000,
max_access_keys: 4
};
+ const mockAccount = { id: 'RGW12312312312312312', name: 'test' };
const spy = jest.spyOn(component, 'submit');
- const modifyDataSpy = jest.spyOn(rgwUserAccountsService, 'modify').mockReturnValue(of(null));
+ const modifyDataSpy = jest
+ .spyOn(rgwUserAccountsService, 'modify')
+ .mockReturnValue(of(mockAccount));
component.submit();
expect(component.accountForm.valid).toBe(true);
expect(spy).toHaveBeenCalled();
expect(modifyDataSpy).toHaveBeenCalled();
expect(modifyDataSpy).toHaveBeenCalledWith(payload);
- });
+ flush();
+ }));
it('should call setQuota for "account" if account quota is dirty', () => {
component.accountForm.get('id').setValue('123');
resource: string;
editing: boolean = false;
submitObservables: Observable<Object>[] = [];
+ originalAccountName: string = '';
constructor(
private router: Router,
this.route.paramMap.subscribe((params: any) => {
const account_id = params.get('id');
this.rgwUserAccountsService.get(account_id).subscribe((accountData: Account) => {
+ this.originalAccountName = accountData.name;
// Get the default values.
const defaults = _.clone(this.accountForm.value);
// Extract the values displayed in the form.
this.accountForm = this.formBuilder.group({
id: [''],
tenant: [''],
- name: ['', Validators.required],
+ name: [
+ '',
+ [Validators.required],
+ [CdValidators.unique(this.rgwUserAccountsService.exists, this.rgwUserAccountsService)]
+ ],
email: ['', CdValidators.email],
max_users_mode: ['1'],
max_users: [
) {
return this.http.put(`${this.url}/${account_id}/quota`, payload);
}
+
+ exists(account_name: string): Observable<boolean> {
+ return this.rgwDaemonService.request((params: HttpParams) => {
+ params = params.append('account_name', account_name);
+ return this.http.get<boolean>(`${this.url}/exists`, { params });
+ });
+ }
}
summary: Update RGW account info
tags:
- RgwUserAccounts
+ /api/rgw/accounts/exists:
+ get:
+ description: "\n Check if an account with the given name exists\n \
+ \ Returns True if account exists, False otherwise\n "
+ parameters:
+ - description: Account name
+ in: query
+ name: account_name
+ required: true
+ schema:
+ type: string
+ - allowEmptyValue: true
+ description: Name of the daemon
+ in: query
+ name: daemon_name
+ schema:
+ type: string
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ type: object
+ application/vnd.ceph.api.v1.0+json:
+ schema:
+ type: object
+ description: OK
+ '400':
+ description: Operation exception. Please check the response body for details.
+ '401':
+ description: Unauthenticated access. Please login first.
+ '403':
+ description: Unauthorized access. Please check your permissions.
+ '500':
+ description: Unexpected error. Please check the response body for the stack
+ trace.
+ security:
+ - jwt: []
+ summary: Check if account name exists
+ tags:
+ - RgwUserAccounts
/api/rgw/accounts/{account_id}:
delete:
description: "\n Removes an account\n\n :param account_id: account\
get_accounts_cmd = ['account', 'list']
return cls.send_rgw_cmd(get_accounts_cmd)
+ @classmethod
+ def get_account_by_name(cls, account_name: str):
+ """
+ Get account info by account name
+ Returns account info if found, raises exception otherwise
+ """
+ get_account_cmd = ['account', 'info', '--account-name', account_name]
+ return cls.send_rgw_cmd(get_account_cmd)
+
@classmethod
def set_quota(cls, quota_type: str, account_id: str, max_size: str, max_objects: str,
enabled: bool):