diff --git a/projects/cobbler-api/src/lib/cobbler-api.service.spec.ts b/projects/cobbler-api/src/lib/cobbler-api.service.spec.ts index 0092fbf3..f7218609 100644 --- a/projects/cobbler-api/src/lib/cobbler-api.service.spec.ts +++ b/projects/cobbler-api/src/lib/cobbler-api.service.spec.ts @@ -698,9 +698,17 @@ describe('CobblerApiService', () => { expect(service).toBeFalsy(); }); - xit('should execute the get_settings action on the Cobbler Server', () => { - service.get_settings(''); - expect(service).toBeFalsy(); + it('should execute the get_settings action on the Cobbler Server', (done) => { + // eslint-disable-next-line max-len + const methodResponse = `auto_migrate_settings0allow_duplicate_hostnames0allow_duplicate_ips0allow_duplicate_macs0allow_dynamic_settings0always_write_dhcp_entries0anamon_enabled0auth_token_expiration3600authn_pam_serviceloginautoinstall_snippets_dir/var/lib/cobbler/snippetsautoinstall_templates_dir/var/lib/cobbler/templatesbind_chroot_pathbind_zonefile_path/var/lib/namedbind_master127.0.0.1boot_loader_conf_template_dir/etc/cobbler/boot_loader_confbootloaders_dir/var/lib/cobbler/loadersbootloaders_shim_folder/usr/share/efi/*/bootloaders_shim_fileshim\\.efi$bootloaders_ipxe_folder/usr/share/ipxe/bootloaders_formatsaarch64binary_namegrubaa64.efiarmbinary_namebootarm.efiarm64-efibinary_namegrubaa64.efiextra_modulesefineti386-efibinary_namebootia32.efii386-pc-pxebinary_namegrub.0mod_diri386-pcextra_moduleschainpxebiosdiski686binary_namebootia32.efiIA64binary_namebootia64.efipowerpc-ieee1275binary_namegrub.ppc64leextra_modulesnetofnetx86_64-efibinary_namegrubx86.efiextra_moduleschainefinetbootloaders_modulesbtrfsext2xfsjfsreiserfsall_videobootcatconfigfileechofatfontgfxmenugfxtermgziohaltiso9660jpeglinuxloadenvminicmdnormalpart_applepart_gptpart_msdospassword_pbkdf2pngrebootsearchsearch_fs_filesearch_fs_uuidsearch_labelsleeptesttruevideomdraid09mdraid1xlvmserialregexptrtftphttpluksgcry_rijndaelgcry_sha1gcry_sha256grubconfig_dir/var/lib/cobbler/grub_configbuild_reporting_enabled0build_reporting_emailbuild_reporting_ignorelistbuild_reporting_senderbuild_reporting_smtp_serverlocalhostbuild_reporting_subjectbuildisodir/var/cache/cobbler/buildisocheetah_import_whitelistrerandomtimeclient_use_https0client_use_localhost0cobbler_masterconvert_server_to_ip0createrepo_flags-ccache-sshaautoinstalldefault.ksdefault_name_serversdefault_name_servers_searchdefault_ownershipadmindefault_password_crypted\\$1\\$mF86/UHC\\$WvcIcX2t6crBz2onWxyac.default_template_typecheetahdefault_virt_bridgexenbr0default_virt_disk_driverrawdefault_virt_file_size5.0default_virt_ram512default_virt_typexenpvenable_ipxe0enable_menu1grub2_mod_dir/usr/share/grub2/http_port80iso_template_dir/etc/cobbler/isojinja2_includedir/var/lib/cobbler/jinja2kernel_optionsldap_anonymous_bind1ldap_base_dnDC=devel,DC=redhat,DC=comldap_port389ldap_search_bind_dnldap_search_passwdldap_search_prefixuid=ldap_servergrimlock.devel.redhat.comldap_tls1ldap_tls_cacertdirldap_tls_cacertfileldap_tls_certfileldap_tls_keyfileldap_tls_reqcerthardldap_tls_cipher_suitebind_manage_ipmi0manage_dhcp1manage_dhcp_v60manage_dhcp_v41manage_dns0manage_forward_zonesmanage_reverse_zonesmanage_genders0manage_rsync0manage_tftpd1mgmt_classesmgmt_parametersfrom_cobbler1next_server_v4192.168.1.1next_server_v6::1nsupdate_enabled0nsupdate_log/var/log/cobbler/nsupdate.lognsupdate_tsig_algorithmhmac-sha512nsupdate_tsig_keypower_management_default_typeipmilanproxiesproxy_url_extproxy_url_intpuppet_auto_setup0puppet_parameterized_classes1puppet_serverpuppetpuppet_version2puppetca_path/usr/bin/puppetpxe_just_once1nopxe_with_triggers1redhat_management_permissive0redhat_management_serverxmlrpc.rhn.redhat.comredhat_management_keyregister_new_installs0remove_old_puppet_certs_automatically0replicate_repo_rsync_options-avzHreplicate_rsync_options-avzHreposync_flags-l-m-dreposync_rsync_flagsrestart_dhcp1restart_dns1run_install_triggers1scm_track_enabled0scm_track_modegitscm_track_authorcobbler<cobbler@localhost>scm_push_script/bin/trueserializer_pretty_json0server192.168.1.1sign_puppet_certs_automatically0signature_path/var/lib/cobbler/distro_signatures.jsonsignature_urlhttps://cobbler.github.io/signatures/3.0.x/latest.jsonsyslinux_dir/usr/share/syslinuxsyslinux_memdisk_folder/usr/share/syslinuxsyslinux_pxelinux_folder/usr/share/syslinuxtftpboot_location/srv/tftpbootvirt_auto_boot1webdir/var/www/cobblerwebdir_whitelist.link_cachemiscdistro_mirrorimageslinkslocalmirrorpubrenderedrepo_mirrorrepo_profilerepo_systemsvcwebwebuixmlrpc_port25151yum_distro_priority1yum_post_install_mirror1yumdownloader_flags--resolvewindows_enabled0windows_template_dir/etc/cobbler/windowssamba_distro_shareDISTRO` + const result = 131 + service.get_settings('').subscribe((data) => { + // Let's not compare the content as this is taken care of by the deserializer tests + expect(Object.keys(data).length).toEqual(result); + done(); + }); + const mockRequest = httpTestingController.expectOne('http://localhost/cobbler_api'); + mockRequest.flush(methodResponse); }); xit('should execute the get_signatures action on the Cobbler Server', () => { diff --git a/projects/cobbler-api/src/lib/cobbler-api.service.ts b/projects/cobbler-api/src/lib/cobbler-api.service.ts index 50549e4f..88af474f 100644 --- a/projects/cobbler-api/src/lib/cobbler-api.service.ts +++ b/projects/cobbler-api/src/lib/cobbler-api.service.ts @@ -134,7 +134,7 @@ export class CobblerApiService { background_syncsystems(options: SyncSystemsOptions, token: string): Observable { const transformedOptions: XmlRpcStruct = { members: [ - {name: 'systems', value: { data: options.systems }}, + {name: 'systems', value: {data: options.systems}}, {name: 'verbose', value: options.verbose}, ] } @@ -245,7 +245,7 @@ export class CobblerApiService { background_reposync(options: BackgroundReposyncOptions, token: string): Observable { const transformedOptions: XmlRpcStruct = { members: [ - {name: 'repos', value: { data: options.repos }}, + {name: 'repos', value: {data: options.repos}}, {name: 'only', value: options.only}, {name: 'nofail', value: options.nofail}, {name: 'tries', value: options.tries}, @@ -268,7 +268,7 @@ export class CobblerApiService { background_power_system(options: BackgroundPowerSystem, token: string): Observable { const transformedOptions: XmlRpcStruct = { members: [ - {name: 'systems', value: { data: options.systems }}, + {name: 'systems', value: {data: options.systems}}, {name: 'power', value: options.power}, ] } @@ -2050,18 +2050,44 @@ export class CobblerApiService { ); } + private convertXmlRpcStructToTypeScriptObject(inputStruct: XmlRpcStruct): object { + const result_object = {} + inputStruct.members.forEach((member) => { + let value; + if (AngularXmlrpcService.instanceOfXmlRpcArray(member.value)) { + value = this.convertXmlRpcArrayToTypeScriptArray(member.value); + } else if (AngularXmlrpcService.instanceOfXmlRpcStruct(member.value)) { + value = this.convertXmlRpcStructToTypeScriptObject(member.value); + } else { + value = member.value; + } + result_object[member.name] = value; + }) + return result_object; + } + + private convertXmlRpcArrayToTypeScriptArray(inputArray: XmlRpcArray): Array { + const resultArray = [] + inputArray.data.forEach((value) => { + if (AngularXmlrpcService.instanceOfXmlRpcArray(value)) { + resultArray.push(this.convertXmlRpcArrayToTypeScriptArray(value)); + } else if (AngularXmlrpcService.instanceOfXmlRpcStruct(value)) { + resultArray.push(this.convertXmlRpcStructToTypeScriptObject(value)); + } else { + resultArray.push(value); + } + }) + return resultArray; + } + get_settings(token: string): Observable { return this.client .methodCall('get_settings', [token]) .pipe( map((data: MethodResponse | MethodFault) => { if (AngularXmlrpcService.instanceOfMethodResponse(data)) { - const return_value = {} if (AngularXmlrpcService.instanceOfXmlRpcStruct(data.value)) { - data.value.members.forEach((value) => { - return_value[value.name] = value.value - }); - return return_value as Settings; + return this.convertXmlRpcStructToTypeScriptObject(data.value) as Settings; } throw new Error('The return value of the settings was not in the expected format of an XML-RPC Struct!') } else if (AngularXmlrpcService.instanceOfMethodFault(data)) { @@ -2211,7 +2237,7 @@ export class CobblerApiService { register_new_system(info: RegisterOptions): Observable { const transformedOptions: XmlRpcStruct = { members: [ - {name: 'name', value: info.name }, + {name: 'name', value: info.name}, {name: 'profile', value: info.profile}, {name: 'hostname', value: info.hostname}, {name: 'interfaces', value: info.interfaces as XmlRpcStruct}, diff --git a/projects/cobbler-frontend/src/app/app-routing.module.ts b/projects/cobbler-frontend/src/app/app-routing.module.ts index 82780a7a..7483b230 100644 --- a/projects/cobbler-frontend/src/app/app-routing.module.ts +++ b/projects/cobbler-frontend/src/app/app-routing.module.ts @@ -10,7 +10,7 @@ import {ImagesComponent} from './items/images/images.component'; import {TemplatesComponent} from './items/templates/templates.component'; import {SnippetsComponent} from './items/snippets/snippets.component'; import {ManagementClassesComponent} from './items/management-classes/management-classes.component'; -import {AppSettingsComponent} from './app-settings/app-settings.component'; +import {SettingsViewComponent} from './settings/view/settings-view.component'; import {PackagesComponent} from './items/packages/packages.component'; import {FilesComponent} from './items/files/files.component'; import {ImportDVDComponent} from './actions/import-dvd/import-dvd.component'; @@ -38,7 +38,7 @@ const routes: Routes = [ {path: 'templates', component: TemplatesComponent, canActivate: [AuthGuardService]}, {path: 'snippets', component: SnippetsComponent, canActivate: [AuthGuardService]}, {path: 'management-classes', component: ManagementClassesComponent, canActivate: [AuthGuardService]}, - {path: 'settings', component: AppSettingsComponent, canActivate: [AuthGuardService]}, + {path: 'settings', component: SettingsViewComponent, canActivate: [AuthGuardService]}, {path: 'packages', component: PackagesComponent, canActivate: [AuthGuardService]}, {path: 'app-files', component: FilesComponent, canActivate: [AuthGuardService]}, {path: 'import', component: ImportDVDComponent, canActivate: [AuthGuardService]}, diff --git a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.html b/projects/cobbler-frontend/src/app/app-settings/app-settings.component.html deleted file mode 100644 index 46e83171..00000000 --- a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.html +++ /dev/null @@ -1,36 +0,0 @@ -
-
- -

SETTINGS

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
No. {{element.position}} Name {{element.name}} Value {{element.value}} Type {{element.type}}
-
-
-
diff --git a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.js b/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.js deleted file mode 100644 index b60a9c84..00000000 --- a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.js +++ /dev/null @@ -1,66 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var testing_1 = require("@angular/core/testing"); -var app_settings_component_1 = require("./app-settings.component"); -describe('AppSettingsComponent', function () { - var component; - var fixture; - beforeEach(function () { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, testing_1.TestBed.configureTestingModule({ - declarations: [app_settings_component_1.AppSettingsComponent] - }) - .compileComponents()]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); }); - beforeEach(function () { - fixture = testing_1.TestBed.createComponent(app_settings_component_1.AppSettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - it('should create', function () { - expect(component).toBeTruthy(); - }); -}); -//# sourceMappingURL=app-settings.component.spec.js.map \ No newline at end of file diff --git a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.js.map b/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.js.map deleted file mode 100644 index 1b34f32d..00000000 --- a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"app-settings.component.spec.js","sourceRoot":"","sources":["app-settings.component.spec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAkE;AAElE,mEAAgE;AAEhE,QAAQ,CAAC,sBAAsB,EAAE;IAC/B,IAAI,SAA+B,CAAC;IACpC,IAAI,OAA+C,CAAC;IAEpD,UAAU,CAAC;;;wBACT,qBAAM,iBAAO,CAAC,sBAAsB,CAAC;wBACnC,YAAY,EAAE,CAAE,6CAAoB,CAAE;qBACvC,CAAC;yBACD,iBAAiB,EAAE,EAAA;;oBAHpB,SAGoB,CAAC;;;;SACtB,CAAC,CAAC;IAEH,UAAU,CAAC;QACT,OAAO,GAAG,iBAAO,CAAC,eAAe,CAAC,6CAAoB,CAAC,CAAC;QACxD,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACtC,OAAO,CAAC,aAAa,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,eAAe,EAAE;QAClB,MAAM,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.ts b/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.ts deleted file mode 100644 index 331acabb..00000000 --- a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {Component} from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import {MatTableModule} from '@angular/material/table'; - -import { AppSettingsComponent } from './app-settings.component'; - -// eslint-disable-next-line @angular-eslint/component-selector -@Component({selector: 'router-outlet', template: ''}) -class RouterOutletStubComponent { -} - -describe('AppSettingsComponent', () => { - let component: AppSettingsComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatTableModule - ], - declarations: [ - AppSettingsComponent, - RouterOutletStubComponent - ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(AppSettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.ts b/projects/cobbler-frontend/src/app/app-settings/app-settings.component.ts deleted file mode 100644 index 2310654f..00000000 --- a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {Component} from '@angular/core'; -import {ItemSettingsService} from '../services/item-settings.service'; - -@Component({ - selector: 'app-app-settings', - templateUrl: './app-settings.component.html', - styleUrls: ['./app-settings.component.css'] -}) -export class AppSettingsComponent { - data = []; - displayedColumns: string[] = ['name', 'value', 'type']; - - constructor(service: ItemSettingsService) { - this.data = service.getAll(); - } - -} diff --git a/projects/cobbler-frontend/src/app/app.component.ts b/projects/cobbler-frontend/src/app/app.component.ts index 283aa866..9c53022a 100644 --- a/projects/cobbler-frontend/src/app/app.component.ts +++ b/projects/cobbler-frontend/src/app/app.component.ts @@ -1,38 +1,13 @@ -import {Component, OnInit} from '@angular/core'; - -import {CobblerApiService} from 'cobbler-api'; -import {UserService} from './services/user.service'; +import {Component} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], - providers: [ - CobblerApiService, - {provide: 'COBBLER_URL', useFactory: () => { - const value = localStorage.getItem("COBBLER_URL") - if (value) { - return new URL(value); - } - return new URL("http://localhost/cobbler_api") - }}, - UserService - ] + providers: [] }) -export class AppComponent implements OnInit { - userStatus = { - loggedin: false, - }; - +export class AppComponent { constructor() { } - - ngOnInit(): void { - const is_user_logged_in = window.sessionStorage.getItem('loggedIn'); - if (is_user_logged_in) { - this.userStatus.loggedin = (is_user_logged_in === 'true'); - window.localStorage.userStatus = this.userStatus; - } - } } diff --git a/projects/cobbler-frontend/src/app/app.module.ts b/projects/cobbler-frontend/src/app/app.module.ts index fb2effa3..cbe73630 100644 --- a/projects/cobbler-frontend/src/app/app.module.ts +++ b/projects/cobbler-frontend/src/app/app.module.ts @@ -1,46 +1,56 @@ import {ExtendedModule, FlexModule} from '@angular/flex-layout'; import {MatButtonModule} from '@angular/material/button'; +import {MatFormFieldModule} from '@angular/material/form-field'; import {MatIconModule} from '@angular/material/icon'; import {MatInputModule} from '@angular/material/input'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatRadioModule} from '@angular/material/radio'; import {MatSidenavModule} from '@angular/material/sidenav'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; import {MatSnackBarModule} from '@angular/material/snack-bar'; import {MatTableModule} from '@angular/material/table'; import {MatTabsModule} from '@angular/material/tabs'; import {MatToolbarModule} from '@angular/material/toolbar'; -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { AppRoutingModule } from './app-routing.module'; -import { HttpClientModule } from '@angular/common/http'; -import { AppManageComponent } from './appManage'; -import { AppComponent } from './app.component'; -import { LogInFormComponent } from './login/login.component'; -import { DistrosComponent } from './items/distros/distros.component'; -import { ProfilesComponent } from './items/profiles/profiles.component'; -import { UserService } from './services/user.service'; -import { SystemsComponent } from './items/systems/systems.component'; -import { ReposComponent } from './items/repos/repos.component'; -import { ImagesComponent } from './items/images/images.component'; -import { TemplatesComponent } from './items/templates/templates.component'; -import { SnippetsComponent } from './items/snippets/snippets.component'; -import { ManagementClassesComponent } from './items/management-classes/management-classes.component'; -import { AppSettingsComponent } from './app-settings/app-settings.component'; -import { PackagesComponent } from './items/packages/packages.component'; -import { FilesComponent } from './items/files/files.component'; -import { ImportDVDComponent } from './actions/import-dvd/import-dvd.component'; -import { SyncComponent } from './actions/sync/sync.component'; -import { RepoSyncComponent } from './actions/repo-sync/repo-sync.component'; -import { BuildISOComponent } from './actions/build-iso/build-iso.component'; -import { CheckSysComponent } from './actions/check-sys/check-sys.component'; -import { AppEventsComponent } from './app-events/app-events.component'; -import { AuthGuardService } from './services/auth-guard.service'; -import { UnauthorizedComponent } from './unauthorized/unauthorized.component'; -import { NotFoundComponent } from './not-found/not-found.component'; -import { ManageMenuComponent } from './manage-menu/manage-menu.component'; -import { NavbarComponent } from './navbar/navbar.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { LayoutModule } from '@angular/cdk/layout'; -import { MatListModule } from '@angular/material/list'; +import {MatTooltipModule} from '@angular/material/tooltip'; +import {MatTreeModule} from '@angular/material/tree'; +import {BrowserModule} from '@angular/platform-browser'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {COBBLER_URL, cobblerUrlFactory} from 'cobbler-api'; +import {AppRoutingModule} from './app-routing.module'; +import {HttpClientModule} from '@angular/common/http'; +import {AppManageComponent} from './appManage'; +import {AppComponent} from './app.component'; +import {LogInFormComponent} from './login/login.component'; +import {DistrosComponent} from './items/distros/distros.component'; +import {ProfilesComponent} from './items/profiles/profiles.component'; +import {UserService} from './services/user.service'; +import {SystemsComponent} from './items/systems/systems.component'; +import {ReposComponent} from './items/repos/repos.component'; +import {ImagesComponent} from './items/images/images.component'; +import {TemplatesComponent} from './items/templates/templates.component'; +import {SnippetsComponent} from './items/snippets/snippets.component'; +import {ManagementClassesComponent} from './items/management-classes/management-classes.component'; +import {SettingsViewComponent} from './settings/view/settings-view.component'; +import {PackagesComponent} from './items/packages/packages.component'; +import {FilesComponent} from './items/files/files.component'; +import {ImportDVDComponent} from './actions/import-dvd/import-dvd.component'; +import {SyncComponent} from './actions/sync/sync.component'; +import {RepoSyncComponent} from './actions/repo-sync/repo-sync.component'; +import {BuildISOComponent} from './actions/build-iso/build-iso.component'; +import {CheckSysComponent} from './actions/check-sys/check-sys.component'; +import {AppEventsComponent} from './app-events/app-events.component'; +import {AuthGuardService} from './services/auth-guard.service'; +import {UnauthorizedComponent} from './unauthorized/unauthorized.component'; +import {NotFoundComponent} from './not-found/not-found.component'; +import {ManageMenuComponent} from './manage-menu/manage-menu.component'; +import {NavbarComponent} from './navbar/navbar.component'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {LayoutModule} from '@angular/cdk/layout'; +import {MatListModule} from '@angular/material/list'; +import { EditableTreeComponent } from './common/editable-tree/editable-tree.component'; +import { ViewableTreeComponent } from './common/viewable-tree/viewable-tree.component'; +import { SettingsEditComponent } from './settings/edit/settings-edit.component'; @NgModule({ declarations: [ @@ -55,7 +65,7 @@ import { MatListModule } from '@angular/material/list'; TemplatesComponent, SnippetsComponent, ManagementClassesComponent, - AppSettingsComponent, + SettingsViewComponent, DistrosComponent, ProfilesComponent, SystemsComponent, @@ -73,6 +83,9 @@ import { MatListModule } from '@angular/material/list'; NotFoundComponent, ManageMenuComponent, NavbarComponent, + EditableTreeComponent, + ViewableTreeComponent, + SettingsEditComponent, ], imports: [ BrowserModule, @@ -85,6 +98,7 @@ import { MatListModule } from '@angular/material/list'; MatToolbarModule, MatIconModule, MatButtonModule, + MatFormFieldModule, MatSidenavModule, FlexModule, ExtendedModule, @@ -93,8 +107,17 @@ import { MatListModule } from '@angular/material/list'; MatTableModule, MatTabsModule, MatSnackBarModule, + MatRadioModule, + MatTreeModule, + MatTooltipModule, + MatSlideToggleModule, + MatPaginatorModule, ], providers: [ + { + provide: COBBLER_URL, + useFactory: cobblerUrlFactory + }, UserService, AuthGuardService, ], diff --git a/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.html b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.html new file mode 100644 index 00000000..1c32ad31 --- /dev/null +++ b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.html @@ -0,0 +1,2 @@ + + diff --git a/projects/cobbler-frontend/src/app/app-settings/app-settings.component.css b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.scss similarity index 100% rename from projects/cobbler-frontend/src/app/app-settings/app-settings.component.css rename to projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.scss diff --git a/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.spec.ts b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.spec.ts new file mode 100644 index 00000000..2a101eab --- /dev/null +++ b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {MatTreeModule} from '@angular/material/tree'; + +import { EditableTreeComponent } from './editable-tree.component'; + +describe('EditableTreeComponent', () => { + let component: EditableTreeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatTreeModule + ], + declarations: [ EditableTreeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditableTreeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + xit('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.ts b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.ts new file mode 100644 index 00000000..a4a88af4 --- /dev/null +++ b/projects/cobbler-frontend/src/app/common/editable-tree/editable-tree.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-editable-tree', + templateUrl: './editable-tree.component.html', + styleUrls: ['./editable-tree.component.scss'] +}) +export class EditableTreeComponent { + + constructor() { } + +} diff --git a/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.html b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.html new file mode 100644 index 00000000..b0b80466 --- /dev/null +++ b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.html @@ -0,0 +1,26 @@ + + + + + + {{node.name}}: {{node.value}} + + + + + {{node.name}} + + + + { } + diff --git a/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.scss b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.spec.ts b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.spec.ts new file mode 100644 index 00000000..f212c295 --- /dev/null +++ b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {MatTreeModule} from '@angular/material/tree'; + +import { ViewableTreeComponent } from './viewable-tree.component'; + +describe('ViewableTreeComponent', () => { + let component: ViewableTreeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatTreeModule + ], + declarations: [ ViewableTreeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewableTreeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.ts b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.ts new file mode 100644 index 00000000..cd7a97cc --- /dev/null +++ b/projects/cobbler-frontend/src/app/common/viewable-tree/viewable-tree.component.ts @@ -0,0 +1,77 @@ +import {FlatTreeControl} from '@angular/cdk/tree'; +import {Component, Input, OnInit} from '@angular/core'; +import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree'; + +/** + * Food data with nested structure. + * Each node has a name and an optional list of children. + */ +interface ObjectNode { + name: string; + value: any; + children?: ObjectNode[]; +} + +/** Flat node with expandable and level information */ +interface ExampleFlatNode { + expandable: boolean; + name: string; + level: number; +} + +@Component({ + selector: 'app-viewable-tree', + templateUrl: './viewable-tree.component.html', + styleUrls: ['./viewable-tree.component.scss'] +}) +export class ViewableTreeComponent implements OnInit { + @Input() inputObject: object = {}; + viewableTreeControl = new FlatTreeControl( + node => node.level, + node => node.expandable + ); + + private _transformer = (node: ObjectNode, level: number) => { + return { + expandable: !!node.children && node.children.length > 0, + name: node.name, + value: node.value, + level: level, + }; + }; + + treeFlattener = new MatTreeFlattener( + this._transformer, + node => node.level, + node => node.expandable, + node => node.children, + ); + + dataSource = new MatTreeFlatDataSource(this.viewableTreeControl, this.treeFlattener); + + hasChild = (_: number, node: ExampleFlatNode) => node.expandable; + + constructor() { + } + + inputLength(inputObject: object): number { + return Object.keys(inputObject).length; + } + + private transformObject(inputObject: object): ObjectNode[] { + const resultStructure = []; + let children = []; + Object.keys(inputObject).forEach((key) => { + if (!Array.isArray(inputObject[key]) && typeof inputObject[key] === 'object') { + children = this.transformObject(inputObject[key]); + } + resultStructure.push({name: key, value: inputObject[key], children: children}); + }) + return resultStructure; + } + + ngOnInit(): void { + this.dataSource.data = this.transformObject(this.inputObject); + } + +} diff --git a/projects/cobbler-frontend/src/app/login/login.component.spec.ts b/projects/cobbler-frontend/src/app/login/login.component.spec.ts index 440d9315..8ad9e730 100644 --- a/projects/cobbler-frontend/src/app/login/login.component.spec.ts +++ b/projects/cobbler-frontend/src/app/login/login.component.spec.ts @@ -1,35 +1,70 @@ import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +import {Component, Injectable} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ReactiveFormsModule} from '@angular/forms'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {Router} from '@angular/router'; import {RouterTestingModule} from '@angular/router/testing'; -import {COBBLER_URL} from 'cobbler-api'; +import {COBBLER_URL, CobblerApiService} from 'cobbler-api'; +import {Observable} from 'rxjs'; import {UserService} from '../services/user.service'; import {LogInFormComponent} from './login.component'; +@Component({selector: 'app-blank', template: ''}) +class BlankStubComponent { +} + +@Injectable() +class MockCobblerApiService extends CobblerApiService { + reconfigureService(url: URL) { + console.log('reconfigure called') + } + + login(username: string, password: string) { + console.log('login called') + return new Observable((subscriber) => { + subscriber.next("token") + }); + } +} + describe('LogInFormComponent', () => { + let routerStub; let component: LogInFormComponent; let fixture: ComponentFixture; let httpTestingController: HttpTestingController; beforeEach(async () => { + routerStub = { + navigate: jasmine.createSpy('navigate'), + }; await TestBed.configureTestingModule({ declarations: [LogInFormComponent], - imports: [RouterTestingModule, HttpClientTestingModule, + imports: [ + RouterTestingModule, + HttpClientTestingModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, NoopAnimationsModule ], providers: [ - UserService, + { + provide: Router, + useValue: routerStub + }, { provide: COBBLER_URL, useValue: new URL("https://localhost/cobbler_api") - } + }, + { + provide: CobblerApiService, + useClass: MockCobblerApiService + }, + UserService ] }) .compileComponents(); @@ -45,4 +80,12 @@ describe('LogInFormComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should authorize correctly', () => { + component.login_form.controls['username'].setValue('cobbler') + component.login_form.controls['password'].setValue('cobbler') + component.Authorize() + expect(routerStub.navigate).toHaveBeenCalledWith(['/manage']); + expect(component.authO.token).toEqual('token'); + }) }); diff --git a/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.css b/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.css index e69de29b..6d1ba1be 100644 --- a/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.css +++ b/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.css @@ -0,0 +1,3 @@ +.content { + margin: 32px; +} diff --git a/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.html b/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.html index 5d585a2d..c933b7b1 100644 --- a/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.html +++ b/projects/cobbler-frontend/src/app/manage-menu/manage-menu.component.html @@ -118,7 +118,9 @@

Cobbler

(toggleSidenav)="sidenav.toggle()" > - +
+ +
diff --git a/projects/cobbler-frontend/src/app/services/item-settings.service.spec.ts b/projects/cobbler-frontend/src/app/services/item-settings.service.spec.ts index a060debe..744ea9da 100644 --- a/projects/cobbler-frontend/src/app/services/item-settings.service.spec.ts +++ b/projects/cobbler-frontend/src/app/services/item-settings.service.spec.ts @@ -1,12 +1,22 @@ -import { TestBed } from '@angular/core/testing'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {TestBed} from '@angular/core/testing'; +import {COBBLER_URL} from 'cobbler-api'; -import { ItemSettingsService } from './item-settings.service'; +import {ItemSettingsService} from './item-settings.service'; describe('ItemSettingsService', () => { let service: ItemSettingsService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + providers: [ + { + provide: COBBLER_URL, + useValue: new URL('http://localhost/cobbler_api') + } + ], + imports: [HttpClientTestingModule] + }); service = TestBed.inject(ItemSettingsService); }); diff --git a/projects/cobbler-frontend/src/app/services/item-settings.service.ts b/projects/cobbler-frontend/src/app/services/item-settings.service.ts index 30702b4f..c183a652 100644 --- a/projects/cobbler-frontend/src/app/services/item-settings.service.ts +++ b/projects/cobbler-frontend/src/app/services/item-settings.service.ts @@ -1,131 +1,35 @@ import {Injectable} from '@angular/core'; +import {CobblerApiService} from 'cobbler-api'; +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {Settings} from 'cobbler-api'; +import {UserService} from './user.service'; @Injectable({ providedIn: 'root' }) export class ItemSettingsService { - mockdata = [ - {name: "allow_duplicate_hostnames", value: 0, type: 'bool'}, - {name: 'allow_duplicate_ips', value: 0, type: 'bool'}, - {name: 'allow_duplicate_macs', value: 0, type: 'bool'}, - {name: 'allow_dynamic_settings', value: 0, type: 'bool'}, - {name: 'always_write_dhcp_entries', value: 0, type: 'bool'}, - {name: 'anamon_enabled', value: 0, type: 'bool'}, - {name: 'auth_token_expiration', value: 3600, type: 'int'}, - {name: 'authn_pam_service', value: 'login', type: 'str'}, - {name: 'autoinstall_snippets_dir', value: '/var/lib/cobbler/snippets', type: 'str'}, - {name: 'autoinstall_templates_dir', value: '/var/lib/cobbler/templates', type: 'str'}, - {name: 'bind_chroot_path', value: '', type: 'str'}, - {name: 'bind_master', value: '127.0.0.1', type: 'str'}, - {name: 'boot_loader_conf_template_dir', value: '/etc/cobbler/boot_loader_conf', type: 'str'}, - {name: 'bootloaders_dir', value: '/var/lib/cobbler/loaders', type: 'str'}, - {name: 'grubconfig_dir', value: '/var/lib/cobbler/grub_config', type: 'str'}, - {name: 'build_reporting_enabled', value: 0, type: 'bool'}, - {name: 'build_reporting_ignorelist', value: '', type: 'str'}, - {name: 'build_reporting_sender', value: '', type: 'str'}, - {name: 'build_reporting_smtp_server', value: 'localhost', type: 'str'}, - {name: 'build_reporting_subject', value: '', type: 'str'}, - {name: 'buildisodir', value: '/var/cache/cobbler/buildiso', type: 'str'}, - {name: 'cache_enabled', value: 1, type: 'bool'}, - {name: 'cheetah_import_whitelist', value: ['re', 'random', 'time'], type: 'list'}, - {name: 'client_use_https', value: 0, type: 'bool'}, - {name: 'client_use_localhost', value: 0, type: 'bool'}, - {name: 'cobbler_master', value: '', type: 'str'}, - {name: 'convert_server_to_ip', value: 0, type: 'bool'}, - {name: 'createrepo_flags', value: '-c cache -s sha', type: 'str'}, - {name: 'default_autoinstall', value: '/var/lib/cobbler/templates/default.ks', type: 'str'}, - {name: 'default_name_servers', value: [], type: 'list'}, - {name: 'default_name_servers_search', value: [], type: 'list'}, - {name: 'default_ownership', value: ['admin'], type: 'list'}, - {name: 'default_password_crypted', value: '\$1\$mF86/UHC\$WvcIcX2t6crBz2onWxyac.', type: 'str'}, - {name: 'default_template_type', value: 'cheetah', type: 'str'}, - {name: 'default_virt_bridge', value: 'xenbr0', type: 'str'}, - {name: 'default_virt_disk_driver', value: 'raw', type: 'str'}, - {name: 'default_virt_file_size', value: 5, type: 'int'}, - {name: 'default_virt_ram', value: 512, type: 'int'}, - {name: 'default_virt_type', value: 'auto', type: 'str'}, - {name: 'enable_gpxe', value: 0, type: 'bool'}, - {name: 'enable_menu', value: 1, type: 'bool'}, - {name: 'http_port', value: 80, type: 'int'}, - {name: 'include', value: ['/etc/cobbler/settings.d/*.settings'], type: 'list'}, - {name: 'iso_template_dir', value: '/etc/cobbler/iso', type: 'str'}, - {name: 'kernel_options', value: {}, type: 'dict'}, - {name: 'ldap_anonymous_bind', value: 1, type: 'bool'}, - {name: 'ldap_base_dn', value: 'DC=devel,DC=redhat,DC=com', type: 'str'}, - {name: 'ldap_port', value: 389, type: 'int'}, - {name: 'ldap_search_bind_dn', value: '', type: 'str'}, - {name: 'ldap_search_passwd', value: '', type: 'str'}, - {name: 'ldap_search_prefix', value: 'uid=', type: 'str'}, - {name: 'ldap_server', value: 'grimlock.devel.redhat.com', type: 'str'}, - {name: 'ldap_tls', value: 'on', type: 'str'}, - {name: 'ldap_tls_cacertfile', value: '', type: 'str'}, - {name: 'ldap_tls_certfile', value: '', type: 'str'}, - {name: 'ldap_tls_keyfile', value: '', type: 'str'}, - {name: 'bind_manage_ipmi', value: 0, type: 'bool'}, - {name: 'manage_dhcp', value: 0, type: 'bool'}, - {name: 'manage_dns', value: 0, type: 'bool'}, - {name: 'manage_forward_zones', value: [], type: 'list'}, - {name: 'manage_reverse_zones', value: [], type: 'list'}, - {name: 'manage_genders', value: 0, type: 'bool'}, - {name: 'manage_rsync', value: 0, type: 'bool'}, - {name: 'manage_tftp', value: 1, type: 'bool'}, - {name: 'manage_tftpd', value: 1, type: 'bool'}, - {name: 'mgmt_classes', value: [], type: 'list'}, - {name: 'mgmt_parameters', value: {}, type: 'dict'}, - {name: 'next_server', value: '127.0.0.1', type: 'str'}, - {name: 'nsupdate_enabled', value: 0, type: 'bool'}, - {name: 'power_management_default_type', value: 'ipmitool', type: 'str'}, - {name: 'proxy_url_ext', value: '', type: 'str'}, - {name: 'proxy_url_int', value: '', type: 'str'}, - {name: 'puppet_auto_setup', value: 0, type: 'bool'}, - {name: 'puppet_parameterized_classes', value: 1, type: 'bool'}, - {name: 'puppet_server', value: 'puppet', type: 'str'}, - {name: 'puppet_version', value: 2, type: 'int'}, - {name: 'puppetca_path', value: '/usr/bin/puppet', type: 'str'}, - {name: 'pxe_just_once', value: 1, type: 'bool'}, - {name: 'nopxe_with_triggers', value: 1, type: 'bool'}, - {name: 'redhat_management_permissive', value: 0, type: 'bool'}, - {name: 'redhat_management_server', value: 'xmlrpc.rhn.redhat.com', type: 'str'}, - {name: 'redhat_management_key', value: '', type: 'str'}, - {name: 'register_new_installs', value: 0, type: 'bool'}, - {name: 'remove_old_puppet_certs_automatically', value: 0, type: 'bool'}, - {name: 'replicate_repo_rsync_options', value: '-avzH', type: 'str'}, - {name: 'replicate_rsync_options', value: '-avzH', type: 'str'}, - {name: 'reposync_flags', value: '-l -m -d', type: 'str'}, - {name: 'restart_dhcp', value: 1, type: 'bool'}, - {name: 'restart_dns', value: 1, type: 'bool'}, - {name: 'run_install_triggers', value: 1, type: 'bool'}, - {name: 'scm_track_enabled', value: 0, type: 'bool'}, - {name: 'scm_track_mode', value: 'git', type: 'str'}, - {name: 'scm_track_author', value: 'cobbler ', type: 'str'}, - {name: 'scm_push_script', value: '/bin/true', type: 'str'}, - {name: 'serializer_pretty_json', value: 0, type: 'bool'}, - {name: 'server', value: '127.0.0.1', type: 'str'}, - {name: 'sign_puppet_certs_automatically', value: 0, type: 'bool'}, - {name: 'signature_path', value: '/var/lib/cobbler/distro_signatures.json', type: 'str'}, - {name: 'signature_url', value: 'https://cobbler.github.io/signatures/3.0.x/latest.json', type: 'str'}, - {name: 'tftpboot_location', value: '/var/lib/tftpboot', type: 'str'}, - {name: 'virt_auto_boot', value: 0, type: 'bool'}, - {name: 'webdir', value: '/var/www/cobbler', type: 'str'}, - {name: 'webdir_whitelist', value: ['.link_cache', 'misc', 'distro_mirror', 'images', 'links', 'localmirror', 'pub', - 'rendered', 'repo_mirror', 'repo_profile', 'repo_system', 'svc', 'web', 'webui'], type: 'list'}, - {name: 'xmlrpc_port', value: 25151, type: 'int'}, - {name: 'yum_distro_priority', value: 1, type: 'int'}, - {name: 'yum_post_install_mirror', value: 1, type: 'bool'}, - {name: 'yumdownloader_flags', value: '--resolve', type: 'str'}, - ]; - constructor() { + constructor( + public authO: UserService, + private cobblerApiService: CobblerApiService + ) { + // Nothing to see here } - getAll(): Array { - return this.mockdata; + getAll(): Observable { + return this.cobblerApiService.get_settings(this.authO.token); } - getitem(name: string): any { - const data = this.mockdata[name]; - if (data !== null) { - return data; - } + getitem(name: string): Observable { + return this.cobblerApiService.get_settings(this.authO.token) + .pipe( + map((data: Settings) => { + if (name in data) { + return data[name] + } + throw new Error('Requested name not found in the settings!') + }) + ) } } diff --git a/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.html b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.html new file mode 100644 index 00000000..6629a5f3 --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.html @@ -0,0 +1,16 @@ +
+ + + + + + + + + true + false + + + + +
diff --git a/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.scss b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.spec.ts b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.spec.ts new file mode 100644 index 00000000..ba9883ab --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.spec.ts @@ -0,0 +1,39 @@ +import {Component} from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {MatInputModule} from '@angular/material/input'; +import {MatRadioModule} from '@angular/material/radio'; + +import { SettingsEditComponent } from './settings-edit.component'; + +@Component({selector: 'app-editable-tree', template: ''}) +class EditableTreeStubComponent { +} + +describe('EditComponent', () => { + let component: SettingsEditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatRadioModule, + MatInputModule + ], + declarations: [ + SettingsEditComponent, + EditableTreeStubComponent + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.ts b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.ts new file mode 100644 index 00000000..26094a81 --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/edit/settings-edit.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-settings-edit', + templateUrl: './settings-edit.component.html', + styleUrls: ['./settings-edit.component.scss'] +}) +export class SettingsEditComponent { + + constructor() { } + +} diff --git a/projects/cobbler-frontend/src/app/settings/view/settings-view.component.css b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.css new file mode 100644 index 00000000..19aec591 --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.css @@ -0,0 +1,8 @@ +table { + width: 100%; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} diff --git a/projects/cobbler-frontend/src/app/settings/view/settings-view.component.html b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.html new file mode 100644 index 00000000..3a808a80 --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.html @@ -0,0 +1,84 @@ +
+
+ +
+

Settings

+ + + +
+
+ + Filter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name{{element.name}}Value + + + "{{element.value}}" + + + + + + + + + + {{c}} + + + + + + Actions +
+ + +
+
No data matching the filter "{{input.value}}"
+ +
+
+
diff --git a/projects/cobbler-frontend/src/app/settings/view/settings-view.component.spec.ts b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.spec.ts new file mode 100644 index 00000000..483aa191 --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.spec.ts @@ -0,0 +1,54 @@ +import {Component} from '@angular/core'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatTableModule} from '@angular/material/table'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {RouterTestingModule} from '@angular/router/testing'; +import {COBBLER_URL} from 'cobbler-api'; + +import { SettingsViewComponent } from './settings-view.component'; + +// eslint-disable-next-line @angular-eslint/component-selector +@Component({selector: 'router-outlet', template: ''}) +class RouterOutletStubComponent { +} + +describe('AppSettingsComponent', () => { + let component: SettingsViewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatTableModule, + HttpClientTestingModule, + RouterTestingModule, + MatPaginatorModule, + MatTableModule, + NoopAnimationsModule + ], + declarations: [ + SettingsViewComponent, + RouterOutletStubComponent + ], + providers: [ + { + provide: COBBLER_URL, + useValue: new URL("https://localhost/cobbler_api") + } + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/cobbler-frontend/src/app/settings/view/settings-view.component.ts b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.ts new file mode 100644 index 00000000..8ae393da --- /dev/null +++ b/projects/cobbler-frontend/src/app/settings/view/settings-view.component.ts @@ -0,0 +1,54 @@ +import {AfterViewInit, Component, ViewChild} from '@angular/core'; +import {MatPaginator} from '@angular/material/paginator'; +import {MatSort} from '@angular/material/sort'; +import {MatTableDataSource} from '@angular/material/table'; +import {Settings} from '../../../../../cobbler-api/src/lib/custom-types/settings'; +import {ItemSettingsService} from '../../services/item-settings.service'; + +interface SettingsTableRowData { + name: string + value: any + type: any +} + +@Component({ + selector: 'app-settings-view', + templateUrl: './settings-view.component.html', + styleUrls: ['./settings-view.component.css'] +}) +export class SettingsViewComponent implements AfterViewInit { + data = new MatTableDataSource([]); + displayedColumns: string[] = ['name', 'value', 'actions']; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + constructor(service: ItemSettingsService) { + service.getAll().subscribe((data: Settings) => { + const settings_data: SettingsTableRowData[] = [] + for (const key in data) { + settings_data.push({name: key, value: data[key], type: typeof data[key]}) + } + console.log(settings_data) + this.data.data = settings_data + }); + } + + ngAfterViewInit() { + this.data.paginator = this.paginator; + this.data.sort = this.sort; + } + + applyFilter(event: Event) { + const filterValue = (event.target as HTMLInputElement).value; + this.data.filter = filterValue.trim().toLowerCase(); + + if (this.data.paginator) { + this.data.paginator.firstPage(); + } + } + + isArray(input: any): boolean { + return Array.isArray(input) + } +}