mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-27 17:02:03 +00:00
set custom logo without rebuilding (#4674)
This commit is contained in:
parent
8e2bd00059
commit
0c61dee07b
@ -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<>(
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -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`.
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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<{
|
||||||
|
|||||||
@ -31,6 +31,9 @@ query appConfig {
|
|||||||
managedIngestionConfig {
|
managedIngestionConfig {
|
||||||
enabled
|
enabled
|
||||||
}
|
}
|
||||||
|
visualConfig {
|
||||||
|
logoUrl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user