set custom logo without rebuilding (#4674)

This commit is contained in:
Gabe Lyons 2022-04-15 09:37:08 -07:00 committed by GitHub
parent 8e2bd00059
commit 0c61dee07b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 91 additions and 8 deletions

View File

@ -52,6 +52,7 @@ import com.linkedin.datahub.graphql.generated.SearchAcrossLineageResult;
import com.linkedin.datahub.graphql.generated.SearchResult; import com.linkedin.datahub.graphql.generated.SearchResult;
import com.linkedin.datahub.graphql.generated.UsageQueryResult; import com.linkedin.datahub.graphql.generated.UsageQueryResult;
import com.linkedin.datahub.graphql.generated.UserUsageCounts; import com.linkedin.datahub.graphql.generated.UserUsageCounts;
import com.linkedin.datahub.graphql.generated.VisualConfiguration;
import com.linkedin.datahub.graphql.resolvers.AuthenticatedResolver; import com.linkedin.datahub.graphql.resolvers.AuthenticatedResolver;
import com.linkedin.datahub.graphql.resolvers.MeResolver; import com.linkedin.datahub.graphql.resolvers.MeResolver;
import com.linkedin.datahub.graphql.resolvers.assertion.AssertionRunEventResolver; import com.linkedin.datahub.graphql.resolvers.assertion.AssertionRunEventResolver;
@ -221,6 +222,7 @@ public class GmsGraphQLEngine {
private final IngestionConfiguration ingestionConfiguration; private final IngestionConfiguration ingestionConfiguration;
private final AuthorizationConfiguration authorizationConfiguration; private final AuthorizationConfiguration authorizationConfiguration;
private final VisualConfiguration visualConfiguration;
private final DatasetType datasetType; private final DatasetType datasetType;
private final CorpUserType corpUserType; private final CorpUserType corpUserType;
@ -286,7 +288,8 @@ public class GmsGraphQLEngine {
null, null,
null, null,
null, null,
false); false,
null);
} }
public GmsGraphQLEngine( public GmsGraphQLEngine(
@ -303,7 +306,8 @@ public class GmsGraphQLEngine {
final IngestionConfiguration ingestionConfiguration, final IngestionConfiguration ingestionConfiguration,
final AuthorizationConfiguration authorizationConfiguration, final AuthorizationConfiguration authorizationConfiguration,
final GitVersion gitVersion, final GitVersion gitVersion,
final boolean supportsImpactAnalysis final boolean supportsImpactAnalysis,
final VisualConfiguration visualConfiguration
) { ) {
this.entityClient = entityClient; this.entityClient = entityClient;
@ -322,6 +326,7 @@ public class GmsGraphQLEngine {
this.ingestionConfiguration = Objects.requireNonNull(ingestionConfiguration); this.ingestionConfiguration = Objects.requireNonNull(ingestionConfiguration);
this.authorizationConfiguration = Objects.requireNonNull(authorizationConfiguration); this.authorizationConfiguration = Objects.requireNonNull(authorizationConfiguration);
this.visualConfiguration = visualConfiguration;
this.datasetType = new DatasetType(entityClient); this.datasetType = new DatasetType(entityClient);
this.corpUserType = new CorpUserType(entityClient); this.corpUserType = new CorpUserType(entityClient);
@ -558,7 +563,7 @@ public class GmsGraphQLEngine {
new AppConfigResolver(gitVersion, analyticsService != null, new AppConfigResolver(gitVersion, analyticsService != null,
this.ingestionConfiguration, this.ingestionConfiguration,
this.authorizationConfiguration, this.authorizationConfiguration,
supportsImpactAnalysis)) supportsImpactAnalysis, this.visualConfiguration))
.dataFetcher("me", new AuthenticatedResolver<>( .dataFetcher("me", new AuthenticatedResolver<>(
new MeResolver(this.entityClient))) new MeResolver(this.entityClient)))
.dataFetcher("search", new AuthenticatedResolver<>( .dataFetcher("search", new AuthenticatedResolver<>(

View File

@ -11,6 +11,7 @@ import com.linkedin.datahub.graphql.generated.ManagedIngestionConfig;
import com.linkedin.datahub.graphql.generated.PoliciesConfig; import com.linkedin.datahub.graphql.generated.PoliciesConfig;
import com.linkedin.datahub.graphql.generated.Privilege; import com.linkedin.datahub.graphql.generated.Privilege;
import com.linkedin.datahub.graphql.generated.ResourcePrivileges; import com.linkedin.datahub.graphql.generated.ResourcePrivileges;
import com.linkedin.datahub.graphql.generated.VisualConfiguration;
import com.linkedin.metadata.config.IngestionConfiguration; import com.linkedin.metadata.config.IngestionConfiguration;
import com.linkedin.metadata.version.GitVersion; import com.linkedin.metadata.version.GitVersion;
import graphql.schema.DataFetcher; import graphql.schema.DataFetcher;
@ -29,18 +30,21 @@ public class AppConfigResolver implements DataFetcher<CompletableFuture<AppConfi
private final IngestionConfiguration _ingestionConfiguration; private final IngestionConfiguration _ingestionConfiguration;
private final AuthorizationConfiguration _authorizationConfiguration; private final AuthorizationConfiguration _authorizationConfiguration;
private final boolean _supportsImpactAnalysis; private final boolean _supportsImpactAnalysis;
private final VisualConfiguration _visualConfiguration;
public AppConfigResolver( public AppConfigResolver(
final GitVersion gitVersion, final GitVersion gitVersion,
final boolean isAnalyticsEnabled, final boolean isAnalyticsEnabled,
final IngestionConfiguration ingestionConfiguration, final IngestionConfiguration ingestionConfiguration,
final AuthorizationConfiguration authorizationConfiguration, final AuthorizationConfiguration authorizationConfiguration,
final boolean supportsImpactAnalysis) { final boolean supportsImpactAnalysis,
final VisualConfiguration visualConfiguration) {
_gitVersion = gitVersion; _gitVersion = gitVersion;
_isAnalyticsEnabled = isAnalyticsEnabled; _isAnalyticsEnabled = isAnalyticsEnabled;
_ingestionConfiguration = ingestionConfiguration; _ingestionConfiguration = ingestionConfiguration;
_authorizationConfiguration = authorizationConfiguration; _authorizationConfiguration = authorizationConfiguration;
_supportsImpactAnalysis = supportsImpactAnalysis; _supportsImpactAnalysis = supportsImpactAnalysis;
_visualConfiguration = visualConfiguration;
} }
@Override @Override
@ -83,6 +87,8 @@ public class AppConfigResolver implements DataFetcher<CompletableFuture<AppConfi
appConfig.setIdentityManagementConfig(identityManagementConfig); appConfig.setIdentityManagementConfig(identityManagementConfig);
appConfig.setManagedIngestionConfig(ingestionConfig); appConfig.setManagedIngestionConfig(ingestionConfig);
appConfig.setVisualConfig(_visualConfiguration);
return CompletableFuture.completedFuture(appConfig); return CompletableFuture.completedFuture(appConfig);
} }

View File

@ -102,6 +102,21 @@ type AppConfig {
Configurations related to Lineage Configurations related to Lineage
""" """
lineageConfig: LineageConfig! lineageConfig: LineageConfig!
"""
Configurations related to visual appearance, allows styling the UI without rebuilding the bundle
"""
visualConfig: VisualConfiguration!
}
"""
Configurations related to visual appearance of the app
"""
type VisualConfiguration {
"""
Custom logo url for the homepage & top banner
"""
logoUrl: String
} }
""" """

View File

@ -72,6 +72,11 @@ The best workaround is to revert to the Active LTS version of Node, 16.13.0 with
### Theming ### Theming
#### Customizing your App without rebuilding assets
To see the results of any change to a theme, you will need to rebuild your datahub-frontend-react container. While this may work for some users, if you don't want to rebuild your container
you can still customize the homepage's logo without rebuilding. You can do this by setting the REACT_APP_LOGO_URL env variable when deploying GMS.
#### Selecting a theme #### Selecting a theme
Theme configurations are stored in `./src/conf/theme`. To select a theme, choose one and update the `REACT_APP_THEME_CONFIG` env variable stored in `.env`. Theme configurations are stored in `./src/conf/theme`. To select a theme, choose one and update the `REACT_APP_THEME_CONFIG` env variable stored in `.env`.

View File

@ -16,6 +16,7 @@ import { EntityType } from '../../types.generated';
import analytics, { EventType } from '../analytics'; import analytics, { EventType } from '../analytics';
import { AdminHeaderLinks } from '../shared/admin/AdminHeaderLinks'; import { AdminHeaderLinks } from '../shared/admin/AdminHeaderLinks';
import { ANTD_GRAY } from '../entity/shared/constants'; import { ANTD_GRAY } from '../entity/shared/constants';
import { useAppConfig } from '../useAppConfig';
const Background = styled.div` const Background = styled.div`
width: 100%; width: 100%;
@ -115,6 +116,7 @@ export const HomePageHeader = () => {
const [getAutoCompleteResultsForMultiple, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); const [getAutoCompleteResultsForMultiple, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery();
const user = useGetAuthenticatedUser()?.corpUser; const user = useGetAuthenticatedUser()?.corpUser;
const themeConfig = useTheme(); const themeConfig = useTheme();
const appConfig = useAppConfig();
const onSearch = (query: string, type?: EntityType) => { const onSearch = (query: string, type?: EntityType) => {
if (!query || query.trim().length === 0) { if (!query || query.trim().length === 0) {
@ -193,7 +195,11 @@ export const HomePageHeader = () => {
</NavGroup> </NavGroup>
</Row> </Row>
<HeaderContainer> <HeaderContainer>
<Image src={themeConfig.assets.logoUrl} preview={false} style={styles.logoImage} /> <Image
src={appConfig.config.visualConfig.logoUrl || themeConfig.assets.logoUrl}
preview={false}
style={styles.logoImage}
/>
{!!themeConfig.content.subtitle && ( {!!themeConfig.content.subtitle && (
<Typography.Text style={styles.subtitle}>{themeConfig.content.subtitle}</Typography.Text> <Typography.Text style={styles.subtitle}>{themeConfig.content.subtitle}</Typography.Text>
)} )}

View File

@ -9,6 +9,7 @@ import { AutoCompleteResultForEntity, EntityType } from '../../types.generated';
import EntityRegistry from '../entity/EntityRegistry'; import EntityRegistry from '../entity/EntityRegistry';
import { ANTD_GRAY } from '../entity/shared/constants'; import { ANTD_GRAY } from '../entity/shared/constants';
import { AdminHeaderLinks } from '../shared/admin/AdminHeaderLinks'; import { AdminHeaderLinks } from '../shared/admin/AdminHeaderLinks';
import { useAppConfig } from '../useAppConfig';
const { Header } = Layout; const { Header } = Layout;
@ -74,12 +75,16 @@ export const SearchHeader = ({
entityRegistry, entityRegistry,
}: Props) => { }: Props) => {
const themeConfig = useTheme(); const themeConfig = useTheme();
const appConfig = useAppConfig();
return ( return (
<Header style={styles.header as any}> <Header style={styles.header as any}>
<LogoSearchContainer> <LogoSearchContainer>
<Link to="/"> <Link to="/">
<LogoImage src={themeConfig.assets.logoUrl} preview={false} /> <LogoImage
src={appConfig.config.visualConfig.logoUrl || themeConfig.assets.logoUrl}
preview={false}
/>
</Link> </Link>
<SearchBar <SearchBar
initialQuery={initialQuery} initialQuery={initialQuery}

View File

@ -19,6 +19,9 @@ export const DEFAULT_APP_CONFIG = {
lineageConfig: { lineageConfig: {
supportsImpactAnalysis: false, supportsImpactAnalysis: false,
}, },
visualConfig: {
logoUrl: undefined,
},
}; };
export const AppConfigContext = React.createContext<{ export const AppConfigContext = React.createContext<{

View File

@ -31,6 +31,9 @@ query appConfig {
managedIngestionConfig { managedIngestionConfig {
enabled enabled
} }
visualConfig {
logoUrl
}
} }
} }

View File

@ -0,0 +1,24 @@
package com.linkedin.gms.factory.common;
import com.linkedin.datahub.graphql.generated.VisualConfiguration;
import javax.annotation.Nonnull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VisualConfigFactory {
@Value("${visualConfig.assets.logoUrl}")
private String logoUrl;
@Nonnull
@Bean(name = "visualConfig")
protected VisualConfiguration getInstance() {
VisualConfiguration config = new VisualConfiguration();
config.setLogoUrl(logoUrl);
return config;
}
}

View File

@ -4,6 +4,7 @@ import com.datahub.authentication.token.TokenService;
import com.linkedin.datahub.graphql.GmsGraphQLEngine; import com.linkedin.datahub.graphql.GmsGraphQLEngine;
import com.linkedin.datahub.graphql.GraphQLEngine; import com.linkedin.datahub.graphql.GraphQLEngine;
import com.linkedin.datahub.graphql.analytics.service.AnalyticsService; import com.linkedin.datahub.graphql.analytics.service.AnalyticsService;
import com.linkedin.datahub.graphql.generated.VisualConfiguration;
import com.linkedin.entity.client.JavaEntityClient; import com.linkedin.entity.client.JavaEntityClient;
import com.linkedin.gms.factory.auth.DataHubTokenServiceFactory; import com.linkedin.gms.factory.auth.DataHubTokenServiceFactory;
import com.linkedin.gms.factory.common.GitVersionFactory; import com.linkedin.gms.factory.common.GitVersionFactory;
@ -92,6 +93,10 @@ public class GraphQLEngineFactory {
@Qualifier("gitVersion") @Qualifier("gitVersion")
private GitVersion _gitVersion; private GitVersion _gitVersion;
@Autowired
@Qualifier("visualConfig")
private VisualConfiguration _visualConfiguration;
@Value("${platformAnalytics.enabled}") // TODO: Migrate to DATAHUB_ANALYTICS_ENABLED @Value("${platformAnalytics.enabled}") // TODO: Migrate to DATAHUB_ANALYTICS_ENABLED
private Boolean isAnalyticsEnabled; private Boolean isAnalyticsEnabled;
@ -113,7 +118,8 @@ public class GraphQLEngineFactory {
_configProvider.getIngestion(), _configProvider.getIngestion(),
_configProvider.getAuthorization(), _configProvider.getAuthorization(),
_gitVersion, _gitVersion,
_graphService.supportsMultiHop() _graphService.supportsMultiHop(),
_visualConfiguration
).builder().build(); ).builder().build();
} }
return new GmsGraphQLEngine( return new GmsGraphQLEngine(
@ -130,7 +136,8 @@ public class GraphQLEngineFactory {
_configProvider.getIngestion(), _configProvider.getIngestion(),
_configProvider.getAuthorization(), _configProvider.getAuthorization(),
_gitVersion, _gitVersion,
_graphService.supportsMultiHop() _graphService.supportsMultiHop(),
_visualConfiguration
).builder().build(); ).builder().build();
} }
} }

View File

@ -81,6 +81,10 @@ configEntityRegistry:
platformAnalytics: platformAnalytics:
enabled: ${ANALYTICS_ENABLED:true} enabled: ${ANALYTICS_ENABLED:true}
visualConfig:
assets:
logoUrl: ${REACT_APP_LOGO_URL:#{null}}
# Storage Layer # Storage Layer
ebean: ebean:
username: ${EBEAN_DATASOURCE_USERNAME:datahub} username: ${EBEAN_DATASOURCE_USERNAME:datahub}