mirror of https://github.com/apache/nifi.git
NIFI-13373: Adding support for banner text (#8947)
* NIFI-13373: - Adding support for banner text. * NIFI-13373: - Prettier. * NIFI-13373: - Removing unused property. * NIFI-13373: - Defining reponse payload when loading banner text. - Removing banner text from login, logout, and error pages. * NIFI-13373: - Only loading the banner text when necessary. This closes #8947
This commit is contained in:
parent
2ef31f2372
commit
b3c7952ef3
|
@ -92,6 +92,7 @@ public class MiNiFiPropertiesGenerator {
|
|||
Triple.of(NiFiProperties.BORED_YIELD_DURATION, "10 millis",
|
||||
"# If a component has no work to do (is \"bored\"), how long should we wait before checking again for work"),
|
||||
Triple.of(NiFiProperties.LOGIN_IDENTITY_PROVIDER_CONFIGURATION_FILE, "./conf/login-identity-providers.xml", EMPTY),
|
||||
Triple.of(NiFiProperties.UI_BANNER_TEXT, EMPTY, EMPTY),
|
||||
Triple.of(NiFiProperties.NAR_LIBRARY_DIRECTORY, "./lib", EMPTY),
|
||||
Triple.of(NiFiProperties.NAR_WORKING_DIRECTORY, "./work/nar/", EMPTY),
|
||||
Triple.of(NiFiProperties.NAR_LIBRARY_AUTOLOAD_DIRECTORY, "./extensions", EMPTY),
|
||||
|
|
|
@ -223,6 +223,9 @@ public class NiFiProperties extends ApplicationProperties {
|
|||
public static final String WEB_REQUEST_LOG_FORMAT = "nifi.web.request.log.format";
|
||||
public static final String WEB_JMX_METRICS_ALLOWED_FILTER_PATTERN = "nifi.web.jmx.metrics.allowed.filter.pattern";
|
||||
|
||||
// ui properties
|
||||
public static final String UI_BANNER_TEXT = "nifi.ui.banner.text";
|
||||
|
||||
// cluster common properties
|
||||
public static final String CLUSTER_PROTOCOL_HEARTBEAT_INTERVAL = "nifi.cluster.protocol.heartbeat.interval";
|
||||
public static final String CLUSTER_PROTOCOL_HEARTBEAT_MISSABLE_MAX = "nifi.cluster.protocol.heartbeat.missable.max";
|
||||
|
@ -803,6 +806,17 @@ public class NiFiProperties extends ApplicationProperties {
|
|||
return new File(getProperty(NAR_LIBRARY_AUTOLOAD_DIRECTORY, DEFAULT_NAR_LIBRARY_AUTOLOAD_DIR));
|
||||
}
|
||||
|
||||
// getters for ui properties //
|
||||
|
||||
/**
|
||||
* Get the banner text.
|
||||
*
|
||||
* @return The banner text
|
||||
*/
|
||||
public String getBannerText() {
|
||||
return this.getProperty(UI_BANNER_TEXT, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if auto reload of the keystore and truststore is enabled.
|
||||
*
|
||||
|
|
|
@ -44,6 +44,8 @@ public class NiFiPropertiesTest {
|
|||
|
||||
NiFiProperties properties = loadNiFiProperties("/NiFiProperties/conf/nifi.properties", null);
|
||||
|
||||
assertEquals("UI Banner Text", properties.getBannerText());
|
||||
|
||||
Set<File> expectedDirectories = new HashSet<>();
|
||||
expectedDirectories.add(new File("./target/resources/NiFiProperties/lib/"));
|
||||
expectedDirectories.add(new File("./target/resources/NiFiProperties/lib2/"));
|
||||
|
|
|
@ -23,6 +23,7 @@ nifi.administrative.yield.duration=30 sec
|
|||
|
||||
nifi.reporting.task.configuration.file=./target/reporting-tasks.xml
|
||||
nifi.controller.service.configuration.file=./target/controller-services.xml
|
||||
nifi.ui.banner.text=UI Banner Text
|
||||
nifi.nar.library.directory=./target/resources/NiFiProperties/lib/
|
||||
nifi.nar.library.directory.alt=./target/resources/NiFiProperties/lib2/
|
||||
nifi.nar.working.directory=./target/work/nar/
|
||||
|
|
|
@ -3212,6 +3212,7 @@ This cleanup mechanism takes into account only automatically created archived _f
|
|||
|`nifi.authorizer.configuration.file`*|This is the location of the file that specifies how authorizers are defined. The default value is `./conf/authorizers.xml`.
|
||||
|`nifi.login.identity.provider.configuration.file`*|This is the location of the file that specifies how username/password authentication is performed. This file is
|
||||
only considered if `nifi.security.user.login.identity.provider` is configured with a provider identifier. The default value is `./conf/login-identity-providers.xml`.
|
||||
|`nifi.ui.banner.text`|This is banner text that may be configured to display at the top of the User Interface. It is blank by default.
|
||||
|`nifi.nar.library.directory`|The location of the nar library. The default value is `./lib` and probably should be left as is.
|
||||
|`nifi.restore.directory`|The location that certain providers (e.g. UserGroupProviders) will look for previous configurations to restore from. There is no default value.
|
||||
+
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.web.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import jakarta.xml.bind.annotation.XmlType;
|
||||
|
||||
/**
|
||||
* Banners that should appear on the top and bottom of this NiFi.
|
||||
*/
|
||||
@XmlType(name = "banners")
|
||||
public class BannerDTO {
|
||||
|
||||
private String headerText;
|
||||
private String footerText;
|
||||
|
||||
/* getters / setters */
|
||||
/**
|
||||
* The banner footer text.
|
||||
*
|
||||
* @return The footer text
|
||||
*/
|
||||
@Schema(description = "The footer text."
|
||||
)
|
||||
public String getFooterText() {
|
||||
return footerText;
|
||||
}
|
||||
|
||||
public void setFooterText(String footerText) {
|
||||
this.footerText = footerText;
|
||||
}
|
||||
|
||||
/**
|
||||
* The banner header text.
|
||||
*
|
||||
* @return The header text
|
||||
*/
|
||||
@Schema(description = "The header text."
|
||||
)
|
||||
public String getHeaderText() {
|
||||
return headerText;
|
||||
}
|
||||
|
||||
public void setHeaderText(String headerText) {
|
||||
this.headerText = headerText;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.web.api.entity;
|
||||
|
||||
import jakarta.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.apache.nifi.web.api.dto.BannerDTO;
|
||||
|
||||
/**
|
||||
* A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a BannerDTO.
|
||||
*/
|
||||
@XmlRootElement(name = "bannersEntity")
|
||||
public class BannerEntity extends Entity {
|
||||
|
||||
private BannerDTO banners;
|
||||
|
||||
/**
|
||||
* The BannerDTO that is being serialized.
|
||||
*
|
||||
* @return The BannerDTO object
|
||||
*/
|
||||
public BannerDTO getBanners() {
|
||||
return banners;
|
||||
}
|
||||
|
||||
public void setBanners(BannerDTO about) {
|
||||
this.banners = about;
|
||||
}
|
||||
|
||||
}
|
|
@ -72,6 +72,7 @@
|
|||
<nifi.content.viewer.url>../nifi-content-viewer/</nifi.content.viewer.url>
|
||||
|
||||
<nifi.restore.directory />
|
||||
<nifi.ui.banner.text />
|
||||
<nifi.nar.library.directory>./lib</nifi.nar.library.directory>
|
||||
<nifi.nar.library.autoload.directory>./extensions</nifi.nar.library.autoload.directory>
|
||||
<nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory>
|
||||
|
|
|
@ -31,6 +31,7 @@ nifi.queue.backpressure.size=${nifi.queue.backpressure.size}
|
|||
|
||||
nifi.authorizer.configuration.file=${nifi.authorizer.configuration.file}
|
||||
nifi.login.identity.provider.configuration.file=${nifi.login.identity.provider.configuration.file}
|
||||
nifi.ui.banner.text=${nifi.ui.banner.text}
|
||||
nifi.nar.library.directory=${nifi.nar.library.directory}
|
||||
nifi.nar.library.autoload.directory=${nifi.nar.library.autoload.directory}
|
||||
nifi.nar.working.directory=${nifi.nar.working.directory}
|
||||
|
|
|
@ -28,6 +28,8 @@ nifi.bored.yield.duration=10 millis
|
|||
|
||||
nifi.authorizer.configuration.file=./target/conf/authorizers.xml
|
||||
nifi.login.identity.provider.configuration.file=./target/conf/login-identity-providers.xml
|
||||
nifi.ui.banner.text=dXwnu9mLyPETJrq1||n9e5dk5+HSTBCGOA/Sy6VYzwPw3baeRNvglalA1Pr1PcToyc4/qT6md24YOP4xVz14jd
|
||||
nifi.ui.banner.text.protected=aes/gcm/256
|
||||
nifi.nar.library.directory=./target/lib
|
||||
nifi.nar.working.directory=./target/work/nar/
|
||||
nifi.documentation.working.directory=./target/work/docs/components
|
||||
|
@ -128,7 +130,7 @@ nifi.web.jetty.threads=200
|
|||
nifi.sensitive.props.key=dQU402Mz4J+t+e18||6+ictR0Nssq3/rR/d8fq5CFAKmpakr9jCyPIJYxG7n6D86gxsu2TRp4M48ugUw==
|
||||
nifi.sensitive.props.key.protected=aes/gcm/256
|
||||
nifi.sensitive.props.algorithm=NIFI_PBKDF2_AES_GCM_256
|
||||
nifi.sensitive.props.additional.keys=
|
||||
nifi.sensitive.props.additional.keys=nifi.ui.banner.text
|
||||
|
||||
nifi.security.keystore=/path/to/keystore.jks
|
||||
nifi.security.keystoreType=JKS
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.apache.nifi.web.NiFiServiceFacade;
|
|||
import org.apache.nifi.web.ResourceNotFoundException;
|
||||
import org.apache.nifi.web.Revision;
|
||||
import org.apache.nifi.web.api.dto.AboutDTO;
|
||||
import org.apache.nifi.web.api.dto.BannerDTO;
|
||||
import org.apache.nifi.web.api.dto.BulletinBoardDTO;
|
||||
import org.apache.nifi.web.api.dto.BulletinQueryDTO;
|
||||
import org.apache.nifi.web.api.dto.ClusterDTO;
|
||||
|
@ -75,6 +76,7 @@ import org.apache.nifi.web.api.dto.status.ControllerStatusDTO;
|
|||
import org.apache.nifi.web.api.entity.AboutEntity;
|
||||
import org.apache.nifi.web.api.entity.ActionEntity;
|
||||
import org.apache.nifi.web.api.entity.ActivateControllerServicesEntity;
|
||||
import org.apache.nifi.web.api.entity.BannerEntity;
|
||||
import org.apache.nifi.web.api.entity.BulletinBoardEntity;
|
||||
import org.apache.nifi.web.api.entity.ClusterSearchResultsEntity;
|
||||
import org.apache.nifi.web.api.entity.ClusterSummaryEntity;
|
||||
|
@ -1340,6 +1342,50 @@ public class FlowResource extends ApplicationResource {
|
|||
return generateOkResponse(entity).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the banners for this NiFi.
|
||||
*
|
||||
* @return A bannerEntity.
|
||||
*/
|
||||
@GET
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("banners")
|
||||
@Operation(
|
||||
summary = "Retrieves the banners for this NiFi",
|
||||
responses = @ApiResponse(content = @Content(schema = @Schema(implementation = BannerEntity.class))),
|
||||
security = {
|
||||
@SecurityRequirement(name = "Read - /flow")
|
||||
}
|
||||
)
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "400", description = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."),
|
||||
@ApiResponse(responseCode = "401", description = "Client could not be authenticated."),
|
||||
@ApiResponse(responseCode = "403", description = "Client is not authorized to make this request."),
|
||||
@ApiResponse(responseCode = "409", description = "The request was valid but NiFi was not in the appropriate state to process it.")
|
||||
}
|
||||
)
|
||||
public Response getBanners() {
|
||||
|
||||
authorizeFlow();
|
||||
|
||||
// get the banner from the properties - will come from the NCM when clustered
|
||||
final String bannerText = getProperties().getBannerText();
|
||||
|
||||
// create the DTO
|
||||
final BannerDTO bannerDTO = new BannerDTO();
|
||||
bannerDTO.setHeaderText(bannerText);
|
||||
bannerDTO.setFooterText(bannerText);
|
||||
|
||||
// create the response entity
|
||||
final BannerEntity entity = new BannerEntity();
|
||||
entity.setBanners(bannerDTO);
|
||||
|
||||
// generate the response
|
||||
return generateOkResponse(entity).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the types of processors that this NiFi supports.
|
||||
*
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
<mat-spinner color="warn"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<router-outlet></router-outlet>
|
||||
}
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
|
|
@ -49,6 +49,7 @@ import { ClusterSummaryEffects } from './state/cluster-summary/cluster-summary.e
|
|||
import { PropertyVerificationEffects } from './state/property-verification/property-verification.effects';
|
||||
import { loadingInterceptor } from './service/interceptors/loading.interceptor';
|
||||
import { LoginConfigurationEffects } from './state/login-configuration/login-configuration.effects';
|
||||
import { BannerTextEffects } from './state/banner-text/banner-text.effects';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
|
@ -71,6 +72,7 @@ import { LoginConfigurationEffects } from './state/login-configuration/login-con
|
|||
CurrentUserEffects,
|
||||
ExtensionTypesEffects,
|
||||
AboutEffects,
|
||||
BannerTextEffects,
|
||||
FlowConfigurationEffects,
|
||||
LoginConfigurationEffects,
|
||||
StatusHistoryEffects,
|
||||
|
|
|
@ -15,14 +15,16 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5">
|
||||
<h3 class="primary-color">Access Policies</h3>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5">
|
||||
<h3 class="primary-color">Access Policies</h3>
|
||||
</div>
|
||||
<div class="pb-5 px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pb-5 px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -21,6 +21,7 @@ import { RouterModule } from '@angular/router';
|
|||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('AccessPolicies', () => {
|
||||
let component: AccessPolicies;
|
||||
|
@ -29,7 +30,7 @@ describe('AccessPolicies', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AccessPolicies],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(Navigation)]
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(BannerText), MockComponent(Navigation)]
|
||||
});
|
||||
fixture = TestBed.createComponent(AccessPolicies);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -27,6 +27,7 @@ import { AccessPolicyEffects } from '../state/access-policy/access-policy.effect
|
|||
import { TenantsEffects } from '../state/tenants/tenants.effects';
|
||||
import { PolicyComponentEffects } from '../state/policy-component/policy-component.effects';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AccessPolicies],
|
||||
|
@ -37,7 +38,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
|||
StoreModule.forFeature(accessPoliciesFeatureKey, reducers),
|
||||
EffectsModule.forFeature(AccessPolicyEffects, TenantsEffects, PolicyComponentEffects),
|
||||
MatDialogModule,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class AccessPoliciesModule {}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Bulletin Board</h3>
|
||||
<bulletin-board class="flex-1"></bulletin-board>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Bulletin Board</h3>
|
||||
<bulletin-board class="flex-1"></bulletin-board>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -22,6 +22,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||
import { BulletinBoard } from '../ui/bulletin-board/bulletin-board.component';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Bulletins', () => {
|
||||
let component: Bulletins;
|
||||
|
@ -30,7 +31,13 @@ describe('Bulletins', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Bulletins],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(Navigation), MockComponent(BulletinBoard)]
|
||||
imports: [
|
||||
RouterModule,
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation),
|
||||
MockComponent(BulletinBoard)
|
||||
]
|
||||
});
|
||||
fixture = TestBed.createComponent(Bulletins);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -26,6 +26,7 @@ import { BulletinsRoutingModule } from './bulletins-routing.module';
|
|||
import { CounterListingModule } from '../../counters/ui/counter-listing/counter-listing.module';
|
||||
import { BulletinBoard } from '../ui/bulletin-board/bulletin-board.component';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Bulletins],
|
||||
|
@ -37,7 +38,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
|||
EffectsModule.forFeature(BulletinBoardEffects),
|
||||
CounterListingModule,
|
||||
BulletinBoard,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class BulletinsModule {}
|
||||
|
|
|
@ -15,46 +15,48 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Cluster</h3>
|
||||
<error-banner></error-banner>
|
||||
<div class="flex flex-col h-full gap-y-2">
|
||||
@if (getTabLinks(); as tabs) {
|
||||
<!-- Don't show the tab bar if there is only 1 tab to show -->
|
||||
<div class="cluster-tabs" [class.hidden]="tabs.length === 1">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
@for (tab of tabs; track tab) {
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="[tab.link]"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive">
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Cluster</h3>
|
||||
<error-banner></error-banner>
|
||||
<div class="flex flex-col h-full gap-y-2">
|
||||
@if (getTabLinks(); as tabs) {
|
||||
<!-- Don't show the tab bar if there is only 1 tab to show -->
|
||||
<div class="cluster-tabs" [class.hidden]="tabs.length === 1">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
@for (tab of tabs; track tab) {
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="[tab.link]"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive">
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
<div class="mt-4 flex-1">
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
</div>
|
||||
}
|
||||
<div class="pt-4 flex-1">
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between align-middle">
|
||||
<div class="text-sm flex items-center gap-x-2">
|
||||
<button mat-icon-button color="primary" (click)="refresh()">
|
||||
<i class="fa fa-refresh" [class.fa-spin]="listingStatus() === 'loading'"></i>
|
||||
</button>
|
||||
<div>Last updated:</div>
|
||||
<div class="accent-color font-medium">{{ loadedTimestamp() }}</div>
|
||||
<div class="flex justify-between align-middle">
|
||||
<div class="text-sm flex items-center gap-x-2">
|
||||
<button mat-icon-button color="primary" (click)="refresh()">
|
||||
<i class="fa fa-refresh" [class.fa-spin]="listingStatus() === 'loading'"></i>
|
||||
</button>
|
||||
<div>Last updated:</div>
|
||||
<div class="accent-color font-medium">{{ loadedTimestamp() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -28,6 +28,7 @@ import { ClusterState } from '../state';
|
|||
import { ErrorBanner } from '../../../ui/common/error-banner/error-banner.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Cluster', () => {
|
||||
let component: Cluster;
|
||||
|
@ -43,6 +44,7 @@ describe('Cluster', () => {
|
|||
ClusterNodeListing,
|
||||
MatTabsModule,
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation),
|
||||
MockComponent(ErrorBanner)
|
||||
],
|
||||
|
|
|
@ -27,6 +27,7 @@ import { ClusterRoutingModule } from './cluster-routing.module';
|
|||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { ErrorBanner } from '../../../ui/common/error-banner/error-banner.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Cluster],
|
||||
|
@ -39,7 +40,8 @@ import { ErrorBanner } from '../../../ui/common/error-banner/error-banner.compon
|
|||
EffectsModule.forFeature(ClusterListingEffects),
|
||||
MatTabsModule,
|
||||
MatIconButton,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class ClusterModule {}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Counters</h3>
|
||||
<counter-listing class="flex-1"></counter-listing>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Counters</h3>
|
||||
<counter-listing class="flex-1"></counter-listing>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -25,6 +25,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||
import { countersFeatureKey } from '../state';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Counters', () => {
|
||||
let component: Counters;
|
||||
|
@ -33,7 +34,7 @@ describe('Counters', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Counters, CounterListing],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(Navigation)],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(BannerText), MockComponent(Navigation)],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
|
|
|
@ -26,6 +26,7 @@ import { CounterListingEffects } from '../state/counter-listing/counter-listing.
|
|||
import { CounterListingModule } from '../ui/counter-listing/counter-listing.module';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Counters],
|
||||
|
@ -37,7 +38,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
|||
EffectsModule.forFeature(CounterListingEffects),
|
||||
CounterListingModule,
|
||||
MatDialogModule,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class CountersModule {}
|
||||
|
|
|
@ -15,15 +15,17 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="p-2 flex flex-1 bg-white">
|
||||
@if (frameSource) {
|
||||
<iframe class="flex-1" [src]="frameSource"></iframe>
|
||||
} @else {
|
||||
<iframe class="flex-1" src="../nifi-docs/documentation"></iframe>
|
||||
}
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="p-2 flex flex-1 bg-white">
|
||||
@if (frameSource) {
|
||||
<iframe class="flex-1" [src]="frameSource"></iframe>
|
||||
} @else {
|
||||
<iframe class="flex-1" src="../nifi-docs/documentation"></iframe>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -21,10 +21,11 @@ import { Documentation } from './documentation.component';
|
|||
import { DocumentationRoutingModule } from './documentation-routing.module';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Documentation],
|
||||
exports: [Documentation],
|
||||
imports: [CommonModule, MatDialogModule, DocumentationRoutingModule, Navigation]
|
||||
imports: [CommonModule, MatDialogModule, DocumentationRoutingModule, Navigation, BannerText]
|
||||
})
|
||||
export class DocumentationModule {}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="error-background pt-24 pl-24 h-screen">
|
||||
<div class="error-background pt-24 pl-24 h-full">
|
||||
@if (errorDetail$ | async; as errorDetail) {
|
||||
<page-content [title]="errorDetail.title">
|
||||
<div class="text-base">{{ errorDetail.message }}</div>
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">Flow Configuration History</h3>
|
||||
<flow-configuration-history-listing class="flex-1"></flow-configuration-history-listing>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">Flow Configuration History</h3>
|
||||
<flow-configuration-history-listing class="flex-1"></flow-configuration-history-listing>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -23,6 +23,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||
import { FlowConfigurationHistoryListing } from '../ui/flow-configuration-history-listing/flow-configuration-history-listing.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('FlowConfigurationHistory', () => {
|
||||
let component: FlowConfigurationHistory;
|
||||
|
@ -34,6 +35,7 @@ describe('FlowConfigurationHistory', () => {
|
|||
imports: [
|
||||
RouterModule,
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation),
|
||||
MockComponent(FlowConfigurationHistoryListing)
|
||||
]
|
||||
|
|
|
@ -25,6 +25,7 @@ import { StoreModule } from '@ngrx/store';
|
|||
import { flowConfigurationHistoryFeatureKey, reducers } from '../state';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { FlowConfigurationHistoryListingEffects } from '../state/flow-configuration-history-listing/flow-configuration-history-listing.effects';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -33,7 +34,8 @@ import { FlowConfigurationHistoryListingEffects } from '../state/flow-configurat
|
|||
FlowConfigurationHistoryRoutingModule,
|
||||
FlowConfigurationHistoryListing,
|
||||
StoreModule.forFeature(flowConfigurationHistoryFeatureKey, reducers),
|
||||
EffectsModule.forFeature(FlowConfigurationHistoryListingEffects)
|
||||
EffectsModule.forFeature(FlowConfigurationHistoryListingEffects),
|
||||
BannerText
|
||||
],
|
||||
declarations: [FlowConfigurationHistory],
|
||||
exports: [FlowConfigurationHistory]
|
||||
|
|
|
@ -15,4 +15,6 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
<banner-text>
|
||||
<router-outlet></router-outlet>
|
||||
</banner-text>
|
||||
|
|
|
@ -21,6 +21,8 @@ import { FlowDesigner } from './flow-designer.component';
|
|||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState } from '../state/flow/flow.reducer';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('FlowDesigner', () => {
|
||||
let component: FlowDesigner;
|
||||
|
@ -29,7 +31,7 @@ describe('FlowDesigner', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FlowDesigner],
|
||||
imports: [RouterTestingModule],
|
||||
imports: [RouterTestingModule, MockComponent(BannerText)],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState
|
||||
|
|
|
@ -29,6 +29,7 @@ import { MatDialogModule } from '@angular/material/dialog';
|
|||
import { ControllerServicesEffects } from '../state/controller-services/controller-services.effects';
|
||||
import { ParameterEffects } from '../state/parameter/parameter.effects';
|
||||
import { QueueEffects } from '../state/queue/queue.effects';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [FlowDesigner, VersionControlTip],
|
||||
|
@ -45,7 +46,8 @@ import { QueueEffects } from '../state/queue/queue.effects';
|
|||
QueueEffects
|
||||
),
|
||||
NgOptimizedImage,
|
||||
MatDialogModule
|
||||
MatDialogModule,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class FlowDesignerModule {}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col justify-between">
|
||||
<div class="flex flex-col h-full">
|
||||
<fd-header></fd-header>
|
||||
<div class="flex-1">
|
||||
<div id="canvas-container" class="canvas-background select-none" [cdkContextMenuTriggerFor]="contextMenu.menu">
|
||||
<graph-controls></graph-controls>
|
||||
</div>
|
||||
<graph-controls></graph-controls>
|
||||
<div
|
||||
id="canvas-container"
|
||||
class="canvas-background select-none h-full"
|
||||
[cdkContextMenuTriggerFor]="contextMenu.menu"></div>
|
||||
<fd-context-menu #contextMenu [menuProvider]="canvasContextMenu" menuId="root"></fd-context-menu>
|
||||
</div>
|
||||
<fd-footer></fd-footer>
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
*/
|
||||
|
||||
.canvas-background {
|
||||
position: absolute;
|
||||
top: 97px;
|
||||
left: 0;
|
||||
bottom: 33px;
|
||||
right: 0;
|
||||
background-size: 14px 14px;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
// Get hues from palette
|
||||
|
||||
.breadcrumb-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.25);
|
||||
height: 33px;
|
||||
width: 100%;
|
||||
background-color: var(--mat-app-background-color);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="graph-controls flex flex-col gap-y-0.5">
|
||||
<navigation-control [shouldDockWhenCollapsed]="(operationCollapsed$ | async)!"></navigation-control>
|
||||
<operation-control
|
||||
[shouldDockWhenCollapsed]="(navigationCollapsed$ | async)!"
|
||||
[breadcrumbEntity]="(breadcrumbEntity$ | async)!"></operation-control>
|
||||
<div class="relative">
|
||||
<div class="graph-controls flex flex-col gap-y-0.5">
|
||||
<navigation-control [shouldDockWhenCollapsed]="(operationCollapsed$ | async)!"></navigation-control>
|
||||
<operation-control
|
||||
[shouldDockWhenCollapsed]="(navigationCollapsed$ | async)!"
|
||||
[breadcrumbEntity]="(breadcrumbEntity$ | async)!"></operation-control>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
-->
|
||||
|
||||
@if (loading) {
|
||||
<div class="splash h-screen p-20">
|
||||
<div class="splash h-full p-20">
|
||||
<div class="splash-img h-full flex items-center justify-center">
|
||||
<mat-spinner color="warn"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="login-background pt-24 pl-24 h-screen">
|
||||
<div class="login-background pt-24 pl-24 h-full">
|
||||
@if (currentUserState$ | async; as userState) {
|
||||
@if (userState.status === 'success') {
|
||||
<page-content [title]="'Success'">
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="logout-background pt-24 pl-24 h-screen">
|
||||
<div class="logout-background pt-24 pl-24 h-full">
|
||||
<page-content [title]="'Logout successful'">
|
||||
<div class="text-sm">You have have successfully logged out. You may now close the window.</div>
|
||||
</page-content>
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Logout } from './logout.component';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState } from '../../../state/current-user/current-user.reducer';
|
||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { PageContent } from '../../../ui/common/page-content/page-content.component';
|
||||
|
||||
describe('Login', () => {
|
||||
let component: Logout;
|
||||
|
@ -29,8 +29,7 @@ describe('Login', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Logout],
|
||||
imports: [MatProgressSpinner],
|
||||
providers: [provideMockStore({ initialState })]
|
||||
imports: [MatProgressSpinner, MockComponent(PageContent)]
|
||||
});
|
||||
fixture = TestBed.createComponent(Logout);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">Parameter Contexts</h3>
|
||||
<parameter-context-listing class="flex-1"></parameter-context-listing>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">Parameter Contexts</h3>
|
||||
<parameter-context-listing class="flex-1"></parameter-context-listing>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -23,6 +23,7 @@ import { RouterModule } from '@angular/router';
|
|||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('ParameterContexts', () => {
|
||||
let component: ParameterContexts;
|
||||
|
@ -34,6 +35,7 @@ describe('ParameterContexts', () => {
|
|||
imports: [
|
||||
RouterModule,
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation),
|
||||
MockComponent(ParameterContextListing)
|
||||
]
|
||||
|
|
|
@ -25,6 +25,7 @@ import { parameterContextsFeatureKey, reducers } from '../state';
|
|||
import { ParameterContextListingEffects } from '../state/parameter-context-listing/parameter-context-listing.effects';
|
||||
import { ParameterContextListingModule } from '../ui/parameter-context-listing/parameter-context-listing.module';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParameterContexts],
|
||||
|
@ -35,7 +36,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
|||
StoreModule.forFeature(parameterContextsFeatureKey, reducers),
|
||||
EffectsModule.forFeature(ParameterContextListingEffects),
|
||||
ParameterContextListingModule,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class ParameterContextsModule {}
|
||||
|
|
|
@ -15,14 +15,16 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5">
|
||||
<h3 class="primary-color">Provenance</h3>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="px-5">
|
||||
<h3 class="primary-color">Provenance</h3>
|
||||
</div>
|
||||
<div class="pb-5 px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pb-5 px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -26,6 +26,7 @@ import { MockComponent } from 'ng-mocks';
|
|||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { provenanceFeatureKey } from '../state';
|
||||
import { provenanceEventListingFeatureKey } from '../state/provenance-event-listing';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Provenance', () => {
|
||||
let component: Provenance;
|
||||
|
@ -34,7 +35,7 @@ describe('Provenance', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Provenance],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(Navigation)],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(BannerText), MockComponent(Navigation)],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
|
|
|
@ -26,6 +26,7 @@ import { ProvenanceEventListingEffects } from '../state/provenance-event-listing
|
|||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { LineageEffects } from '../state/lineage/lineage.effects';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Provenance],
|
||||
|
@ -36,7 +37,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
|||
ProvenanceRoutingModule,
|
||||
StoreModule.forFeature(provenanceFeatureKey, reducers),
|
||||
EffectsModule.forFeature(ProvenanceEventListingEffects, LineageEffects),
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class ProvenanceModule {}
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -22,6 +22,7 @@ import { RouterModule } from '@angular/router';
|
|||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Queue', () => {
|
||||
let component: Queue;
|
||||
|
@ -30,7 +31,7 @@ describe('Queue', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Queue],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(Navigation)]
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(BannerText), MockComponent(Navigation)]
|
||||
});
|
||||
fixture = TestBed.createComponent(Queue);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -21,10 +21,11 @@ import { Queue } from './queue.component';
|
|||
import { QueueRoutingModule } from './queue-routing.module';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Queue],
|
||||
exports: [Queue],
|
||||
imports: [CommonModule, MatDialogModule, QueueRoutingModule, Navigation]
|
||||
imports: [CommonModule, MatDialogModule, QueueRoutingModule, Navigation, BannerText]
|
||||
})
|
||||
export class QueueModule {}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="error-background pt-24 pl-24 h-screen">
|
||||
<div class="error-background pt-24 pl-24 h-full">
|
||||
<page-content title="Route Not Found">
|
||||
<div class="text-base">The URL entered is not recognized as a supported route.</div>
|
||||
</page-content>
|
||||
|
|
|
@ -15,30 +15,32 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Settings</h3>
|
||||
<div class="settings-tabs">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
@for (tab of tabLinks; track tab) {
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="[tab.link]"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive">
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="pt-5 flex-1">
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Settings</h3>
|
||||
<div class="settings-tabs">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
@for (tab of tabLinks; track tab) {
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="[tab.link]"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive">
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="mt-5 flex-1">
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -27,6 +27,7 @@ import { MockComponent } from 'ng-mocks';
|
|||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { settingsFeatureKey } from '../state';
|
||||
import { generalFeatureKey } from '../state/general';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Settings', () => {
|
||||
let component: Settings;
|
||||
|
@ -35,7 +36,13 @@ describe('Settings', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Settings],
|
||||
imports: [MatTabsModule, RouterModule, RouterTestingModule, MockComponent(Navigation)],
|
||||
imports: [
|
||||
MatTabsModule,
|
||||
RouterModule,
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation)
|
||||
],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
|
|
|
@ -36,6 +36,7 @@ import { RegistryClientsEffects } from '../state/registry-clients/registry-clien
|
|||
import { FlowAnalysisRulesEffects } from '../state/flow-analysis-rules/flow-analysis-rules.effects';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { ParameterProvidersEffects } from '../state/parameter-providers/parameter-providers.effects';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Settings],
|
||||
|
@ -59,7 +60,8 @@ import { ParameterProvidersEffects } from '../state/parameter-providers/paramete
|
|||
ParameterProvidersEffects
|
||||
),
|
||||
MatTabsModule,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class SettingsModule {}
|
||||
|
|
|
@ -15,40 +15,42 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">
|
||||
@if (selectedClusterNode$ | async; as selectedNode) {
|
||||
@if (selectedNode.id !== 'All') {
|
||||
{{ selectedNode.address }} Summary
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">
|
||||
@if (selectedClusterNode$ | async; as selectedNode) {
|
||||
@if (selectedNode.id !== 'All') {
|
||||
{{ selectedNode.address }} Summary
|
||||
} @else {
|
||||
NiFi Summary
|
||||
}
|
||||
} @else {
|
||||
NiFi Summary
|
||||
}
|
||||
} @else {
|
||||
NiFi Summary
|
||||
}
|
||||
</h3>
|
||||
<div class="summary-tabs">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
@for (tab of tabLinks; track tab) {
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="[tab.link]"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive">
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="mt-5 flex-1">
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
</h3>
|
||||
<div class="summary-tabs">
|
||||
<nav mat-tab-nav-bar color="primary" [tabPanel]="tabPanel">
|
||||
@for (tab of tabLinks; track tab) {
|
||||
<a
|
||||
mat-tab-link
|
||||
[routerLink]="[tab.link]"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
[active]="rla.isActive">
|
||||
{{ tab.label }}
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="mt-5 flex-1">
|
||||
<mat-tab-nav-panel #tabPanel>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-tab-nav-panel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@use '@angular/material' as mat;
|
||||
:host {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.summary-tabs {
|
||||
border-bottom-width: 1px;
|
||||
|
|
|
@ -26,6 +26,7 @@ import { MockComponent } from 'ng-mocks';
|
|||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { summaryFeatureKey } from '../state';
|
||||
import { summaryListingFeatureKey } from '../state/summary-listing';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Summary', () => {
|
||||
let component: Summary;
|
||||
|
@ -34,7 +35,13 @@ describe('Summary', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Summary],
|
||||
imports: [MatTabsModule, RouterModule, RouterTestingModule, MockComponent(Navigation)],
|
||||
imports: [
|
||||
MatTabsModule,
|
||||
RouterModule,
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation)
|
||||
],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
|
|
|
@ -33,6 +33,7 @@ import { SummaryListingEffects } from '../state/summary-listing/summary-listing.
|
|||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { ComponentClusterStatusEffects } from '../state/component-cluster-status/component-cluster-status.effects';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Summary],
|
||||
|
@ -51,7 +52,8 @@ import { ComponentClusterStatusEffects } from '../state/component-cluster-status
|
|||
StoreModule.forFeature(summaryFeatureKey, reducers),
|
||||
EffectsModule.forFeature(SummaryListingEffects, ComponentClusterStatusEffects),
|
||||
NgxSkeletonLoaderModule,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class SummaryModule {}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<div class="summary-table-filter-container">
|
||||
<div>
|
||||
<form [formGroup]="filterForm" class="my-2">
|
||||
<div class="flex mt-2 gap-1 items-baseline">
|
||||
<div class="flex mt-2 gap-1 items-center">
|
||||
<div>
|
||||
<mat-form-field subscriptSizing="dynamic">
|
||||
<mat-label>Filter</mat-label>
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="user-header primary-color">NiFi Users</h3>
|
||||
<user-listing class="flex-1"></user-listing>
|
||||
<banner-text>
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="mb-5 nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="user-header primary-color">NiFi Users</h3>
|
||||
<user-listing class="flex-1"></user-listing>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</banner-text>
|
||||
|
|
|
@ -25,6 +25,7 @@ import { initialState } from '../state/user-listing/user-listing.reducer';
|
|||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { usersFeatureKey } from '../state';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
describe('Users', () => {
|
||||
let component: Users;
|
||||
|
@ -33,7 +34,7 @@ describe('Users', () => {
|
|||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Users, UserListing],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(Navigation)],
|
||||
imports: [RouterModule, RouterTestingModule, MockComponent(BannerText), MockComponent(Navigation)],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
|
|
|
@ -26,6 +26,7 @@ import { MatDialogModule } from '@angular/material/dialog';
|
|||
import { UserListingEffects } from '../state/user-listing/user-listing.effects';
|
||||
import { UserListingModule } from '../ui/user-listing/user-listing.module';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Users],
|
||||
|
@ -37,7 +38,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
|||
EffectsModule.forFeature(UserListingEffects),
|
||||
MatDialogModule,
|
||||
UserListingModule,
|
||||
Navigation
|
||||
Navigation,
|
||||
BannerText
|
||||
]
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BannerTextEntity } from '../state/banner-text';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class BannerTextService {
|
||||
private static readonly API: string = '../nifi-api';
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
getBannerText(): Observable<BannerTextEntity> {
|
||||
return this.httpClient.get<BannerTextEntity>(`${BannerTextService.API}/flow/banners`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createAction, props } from '@ngrx/store';
|
||||
import { LoadBannerTextResponse } from './index';
|
||||
|
||||
export const loadBannerText = createAction('[Banner Text] Load Banner Text');
|
||||
|
||||
export const loadBannerTextSuccess = createAction(
|
||||
'[Banner Text] Load Banner Text Success',
|
||||
props<{ response: LoadBannerTextResponse }>()
|
||||
);
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import * as BannerTextActions from './banner-text.actions';
|
||||
import { filter, from, map, switchMap } from 'rxjs';
|
||||
import { BannerTextService } from '../../service/banner-text.service';
|
||||
import { NiFiState } from '../index';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { concatLatestFrom } from '@ngrx/operators';
|
||||
import { selectBannerText } from './banner-text.selectors';
|
||||
|
||||
@Injectable()
|
||||
export class BannerTextEffects {
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private bannerTextService: BannerTextService,
|
||||
private store: Store<NiFiState>
|
||||
) {}
|
||||
|
||||
loadBannerText$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(BannerTextActions.loadBannerText),
|
||||
concatLatestFrom(() => this.store.select(selectBannerText)),
|
||||
filter(([, bannerText]) => bannerText === null),
|
||||
switchMap(() =>
|
||||
from(
|
||||
this.bannerTextService.getBannerText().pipe(
|
||||
map((response) =>
|
||||
BannerTextActions.loadBannerTextSuccess({
|
||||
response: {
|
||||
bannerText: response.banners
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createReducer, on } from '@ngrx/store';
|
||||
import { BannerTextState } from './index';
|
||||
import { loadBannerText, loadBannerTextSuccess } from './banner-text.actions';
|
||||
|
||||
export const initialState: BannerTextState = {
|
||||
bannerText: null,
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
export const bannerTextReducer = createReducer(
|
||||
initialState,
|
||||
on(loadBannerText, (state) => ({
|
||||
...state,
|
||||
status: 'loading' as const
|
||||
})),
|
||||
on(loadBannerTextSuccess, (state, { response }) => ({
|
||||
...state,
|
||||
bannerText: response.bannerText,
|
||||
status: 'success' as const
|
||||
}))
|
||||
);
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createFeatureSelector, createSelector } from '@ngrx/store';
|
||||
import { bannerTextFeatureKey, BannerTextState } from './index';
|
||||
|
||||
export const selectBannerTextState = createFeatureSelector<BannerTextState>(bannerTextFeatureKey);
|
||||
|
||||
export const selectBannerText = createSelector(selectBannerTextState, (state: BannerTextState) => state.bannerText);
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const bannerTextFeatureKey = 'bannerText';
|
||||
|
||||
export interface LoadBannerTextResponse {
|
||||
bannerText: BannerText;
|
||||
}
|
||||
|
||||
export interface BannerTextEntity {
|
||||
banners: BannerText;
|
||||
}
|
||||
|
||||
export interface BannerText {
|
||||
headerText: string;
|
||||
footerText: string;
|
||||
}
|
||||
|
||||
export interface BannerTextState {
|
||||
bannerText: BannerText | null;
|
||||
status: 'pending' | 'loading' | 'success';
|
||||
}
|
|
@ -45,6 +45,8 @@ import { propertyVerificationFeatureKey, PropertyVerificationState } from './pro
|
|||
import { propertyVerificationReducer } from './property-verification/property-verification.reducer';
|
||||
import { navigationFeatureKey, NavigationState } from './navigation';
|
||||
import { navigationReducer } from './navigation/navigation.reducer';
|
||||
import { bannerTextFeatureKey, BannerTextState } from './banner-text';
|
||||
import { bannerTextReducer } from './banner-text/banner-text.reducer';
|
||||
|
||||
export interface NiFiState {
|
||||
[DEFAULT_ROUTER_FEATURENAME]: RouterReducerState;
|
||||
|
@ -52,6 +54,7 @@ export interface NiFiState {
|
|||
[currentUserFeatureKey]: CurrentUserState;
|
||||
[extensionTypesFeatureKey]: ExtensionTypesState;
|
||||
[aboutFeatureKey]: AboutState;
|
||||
[bannerTextFeatureKey]: BannerTextState;
|
||||
[navigationFeatureKey]: NavigationState;
|
||||
[flowConfigurationFeatureKey]: FlowConfigurationState;
|
||||
[loginConfigurationFeatureKey]: LoginConfigurationState;
|
||||
|
@ -70,6 +73,7 @@ export const rootReducers: ActionReducerMap<NiFiState> = {
|
|||
[currentUserFeatureKey]: currentUserReducer,
|
||||
[extensionTypesFeatureKey]: extensionTypesReducer,
|
||||
[aboutFeatureKey]: aboutReducer,
|
||||
[bannerTextFeatureKey]: bannerTextReducer,
|
||||
[navigationFeatureKey]: navigationReducer,
|
||||
[flowConfigurationFeatureKey]: flowConfigurationReducer,
|
||||
[loginConfigurationFeatureKey]: loginConfigurationReducer,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen">
|
||||
<div class="flex flex-col h-full">
|
||||
<header class="nifi-header">
|
||||
<navigation></navigation>
|
||||
</header>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="flex flex-col h-screen">
|
||||
@if (bannerText(); as bannerText) {
|
||||
@if (bannerText.headerText) {
|
||||
<div class="flex justify-center">
|
||||
{{ bannerText.headerText }}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="flex-1">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
@if (bannerText(); as bannerText) {
|
||||
@if (bannerText.footerText) {
|
||||
<div class="flex justify-center">
|
||||
{{ bannerText.footerText }}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { BannerText } from './banner-text.component';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import * as fromBannerText from '../../../state/banner-text/banner-text.reducer';
|
||||
import { bannerTextFeatureKey } from '../../../state/banner-text';
|
||||
|
||||
describe('BannerText', () => {
|
||||
let component: BannerText;
|
||||
let fixture: ComponentFixture<BannerText>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [BannerText],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
[bannerTextFeatureKey]: fromBannerText.initialState
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
fixture = TestBed.createComponent(BannerText);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { loadBannerText } from '../../../state/banner-text/banner-text.actions';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { NiFiState } from '../../../state';
|
||||
import { selectBannerText } from '../../../state/banner-text/banner-text.selectors';
|
||||
|
||||
@Component({
|
||||
selector: 'banner-text',
|
||||
standalone: true,
|
||||
templateUrl: './banner-text.component.html',
|
||||
styleUrls: ['./banner-text.component.scss']
|
||||
})
|
||||
export class BannerText implements OnInit {
|
||||
bannerText = this.store.selectSignal(selectBannerText);
|
||||
|
||||
constructor(private store: Store<NiFiState>) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.store.dispatch(loadBannerText());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue