Skip to content

Commit e1ab98e

Browse files
authored
UI config now primarily pulled from backend endpoint (#4273)
* UI config now primarily pulled from backend endpoint * Fix a problem in the openapi * Added skip/exclude list for property docs generator * Updated the UI readme * Simplify config merge a bit. Fix problems when server config not available. * Warning on the browser console when local config has extra properties vs. remote config
1 parent 62e2be9 commit e1ab98e

File tree

16 files changed

+536
-174
lines changed

16 files changed

+536
-174
lines changed

app/src/main/java/io/apicurio/registry/rest/v3/SystemResourceImpl.java

+54-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
package io.apicurio.registry.rest.v3;
22

3+
import java.util.HashMap;
4+
import java.util.Map;
5+
36
import io.apicurio.common.apps.core.System;
47
import io.apicurio.common.apps.logging.Logged;
8+
import io.apicurio.registry.auth.AuthConfig;
59
import io.apicurio.registry.auth.Authorized;
610
import io.apicurio.registry.auth.AuthorizedLevel;
711
import io.apicurio.registry.auth.AuthorizedStyle;
12+
import io.apicurio.registry.limits.RegistryLimitsConfiguration;
813
import io.apicurio.registry.metrics.health.liveness.ResponseErrorLivenessCheck;
914
import io.apicurio.registry.metrics.health.readiness.ResponseTimeoutReadinessCheck;
10-
import io.apicurio.registry.limits.RegistryLimitsConfiguration;
1115
import io.apicurio.registry.rest.v3.beans.Limits;
1216
import io.apicurio.registry.rest.v3.beans.SystemInfo;
13-
17+
import io.apicurio.registry.rest.v3.beans.UserInterfaceConfig;
18+
import io.apicurio.registry.rest.v3.beans.UserInterfaceConfigAuth;
19+
import io.apicurio.registry.rest.v3.beans.UserInterfaceConfigFeatures;
20+
import io.apicurio.registry.rest.v3.beans.UserInterfaceConfigUi;
21+
import io.apicurio.registry.ui.UserInterfaceConfigProperties;
1422
import jakarta.enterprise.context.ApplicationScoped;
1523
import jakarta.inject.Inject;
1624
import jakarta.interceptor.Interceptors;
@@ -22,6 +30,12 @@ public class SystemResourceImpl implements SystemResource {
2230

2331
@Inject
2432
System system;
33+
34+
@Inject
35+
AuthConfig authConfig;
36+
37+
@Inject
38+
UserInterfaceConfigProperties uiConfig;
2539

2640
@Inject
2741
RegistryLimitsConfiguration registryLimitsConfiguration;
@@ -44,6 +58,7 @@ public SystemInfo getSystemInfo() {
4458
* @see io.apicurio.registry.rest.v3.SystemResource#getResourceLimits()
4559
*/
4660
@Override
61+
@Authorized(style=AuthorizedStyle.None, level=AuthorizedLevel.None)
4762
public Limits getResourceLimits() {
4863
var limitsConfig = registryLimitsConfiguration;
4964
var limits = new Limits();
@@ -61,4 +76,41 @@ public Limits getResourceLimits() {
6176
limits.setMaxRequestsPerSecondCount(limitsConfig.getMaxRequestsPerSecondCount());
6277
return limits;
6378
}
79+
80+
/**
81+
* @see io.apicurio.registry.rest.v3.SystemResource#getUIConfig()
82+
*/
83+
@Override
84+
@Authorized(style=AuthorizedStyle.None, level=AuthorizedLevel.None)
85+
public UserInterfaceConfig getUIConfig() {
86+
return UserInterfaceConfig.builder()
87+
.ui(UserInterfaceConfigUi.builder()
88+
.contextPath(uiConfig.contextPath)
89+
.navPrefixPath(uiConfig.navPrefixPath)
90+
.oaiDocsUrl(uiConfig.docsUrl)
91+
.build())
92+
.auth(uiAuthConfig())
93+
.features(UserInterfaceConfigFeatures.builder()
94+
.readOnly("true".equals(uiConfig.featureReadOnly))
95+
.breadcrumbs("true".equals(uiConfig.featureBreadcrumbs))
96+
.roleManagement(authConfig.isRbacEnabled())
97+
.settings("true".equals(uiConfig.featureSettings))
98+
.build())
99+
.build();
100+
}
101+
102+
private UserInterfaceConfigAuth uiAuthConfig() {
103+
UserInterfaceConfigAuth rval = new UserInterfaceConfigAuth();
104+
rval.setObacEnabled(authConfig.isObacEnabled());
105+
rval.setRbacEnabled(authConfig.isRbacEnabled());
106+
rval.setType(authConfig.isAuthEnabled() ? UserInterfaceConfigAuth.Type.oidc : UserInterfaceConfigAuth.Type.none);
107+
if (authConfig.isAuthEnabled()) {
108+
Map<String, String> options = new HashMap<>();
109+
options.put("url", uiConfig.authOidcUrl);
110+
options.put("redirectUri", uiConfig.authOidcRedirectUri);
111+
options.put("clientId", uiConfig.authOidcClientId);
112+
rval.setOptions(options);
113+
}
114+
return rval;
115+
}
64116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.apicurio.registry.ui;
2+
3+
import org.eclipse.microprofile.config.inject.ConfigProperty;
4+
5+
import io.apicurio.common.apps.config.Info;
6+
import jakarta.inject.Singleton;
7+
8+
@Singleton
9+
public class UserInterfaceConfigProperties {
10+
11+
@ConfigProperty(name = "registry.ui.contextPath", defaultValue = "/")
12+
@Info(category = "ui", description = "Context path of the UI", availableSince = "3.0.0")
13+
public String contextPath;
14+
@ConfigProperty(name = "registry.ui.navPrefixPath", defaultValue = "/")
15+
@Info(category = "ui", description = "Navigation prefix for all UI paths", availableSince = "3.0.0")
16+
public String navPrefixPath;
17+
@ConfigProperty(name = "registry.ui.docsUrl", defaultValue = "/docs/")
18+
@Info(category = "ui", description = "URL of the Documentation component", availableSince = "3.0.0")
19+
public String docsUrl;
20+
21+
22+
@ConfigProperty(name = "registry.auth.url.configured")
23+
public String authOidcUrl;
24+
@ConfigProperty(name = "registry.ui.auth.oidc.redirectUri", defaultValue = "/")
25+
@Info(category = "ui", description = "The OIDC redirectUri", availableSince = "3.0.0")
26+
public String authOidcRedirectUri;
27+
@ConfigProperty(name = "registry.ui.auth.oidc.clientId", defaultValue = "apicurio-registry-ui")
28+
@Info(category = "ui", description = "The OIDC clientId", availableSince = "3.0.0")
29+
public String authOidcClientId;
30+
31+
32+
@ConfigProperty(name = "registry.ui.features.readOnly", defaultValue = "false")
33+
@Info(category = "ui", description = "Enabled to set the UI to read-only mode", availableSince = "3.0.0")
34+
public String featureReadOnly;
35+
@ConfigProperty(name = "registry.ui.features.breadcrumbs", defaultValue = "true")
36+
@Info(category = "ui", description = "Enabled to show breadcrumbs in the UI", availableSince = "3.0.0")
37+
public String featureBreadcrumbs;
38+
@ConfigProperty(name = "registry.ui.features.settings", defaultValue = "true")
39+
@Info(category = "ui", description = "Enabled to show the Settings tab in the UI", availableSince = "3.0.0")
40+
public String featureSettings;
41+
42+
}

common/src/main/resources/META-INF/openapi.json

+202
Original file line numberDiff line numberDiff line change
@@ -3380,6 +3380,33 @@
33803380
}
33813381
]
33823382
},
3383+
"/system/uiConfig": {
3384+
"summary": "Get UI configuration",
3385+
"description": "This endpoint is used by the user interface to retrieve UI specific configuration\nin a JSON payload. This allows the UI and the backend to be configured in the \nsame place (the backend process/pod). When the UI loads, it will make an API call\nto this endpoint to determine what UI features and options are configured.",
3386+
"get": {
3387+
"tags": [
3388+
"System"
3389+
],
3390+
"responses": {
3391+
"200": {
3392+
"content": {
3393+
"application/json": {
3394+
"schema": {
3395+
"$ref": "#/components/schemas/UserInterfaceConfig"
3396+
}
3397+
}
3398+
},
3399+
"description": "The UI config."
3400+
},
3401+
"500": {
3402+
"$ref": "#/components/responses/ServerError"
3403+
}
3404+
},
3405+
"operationId": "getUIConfig",
3406+
"summary": "Get UI config",
3407+
"description": "Returns the UI configuration properties for this server. The registry UI can be\nconnected to a backend using just a URL. The rest of the UI configuration can then\nbe fetched from the backend using this operation. This allows UI and backend to\nboth be configured in the same place.\n\nThis operation may fail for one of the following reasons:\n\n* A server error occurred (HTTP error `500`)\n"
3408+
}
3409+
},
33833410
"x-codegen-contextRoot": "/apis/registry/v3"
33843411
},
33853412
"components": {
@@ -4711,6 +4738,181 @@
47114738
}
47124739
]
47134740
}
4741+
},
4742+
"UserInterfaceConfig": {
4743+
"title": "Root Type for UserInterfaceConfig",
4744+
"description": "Defines the user interface configuration data type.",
4745+
"required": [
4746+
"auth"
4747+
],
4748+
"type": "object",
4749+
"properties": {
4750+
"ui": {
4751+
"$ref": "#/components/schemas/UserInterfaceConfigUi",
4752+
"properties": {
4753+
"contextPath": {
4754+
"type": "string"
4755+
},
4756+
"navPrefixPath": {
4757+
"type": "string"
4758+
},
4759+
"oaiDocsUrl": {
4760+
"type": "string"
4761+
}
4762+
}
4763+
},
4764+
"auth": {
4765+
"$ref": "#/components/schemas/UserInterfaceConfigAuth",
4766+
"properties": {
4767+
"type": {
4768+
"type": "string"
4769+
},
4770+
"rbacEnabled": {
4771+
"type": "boolean"
4772+
},
4773+
"obacEnabled": {
4774+
"type": "boolean"
4775+
},
4776+
"options": {
4777+
"type": "object",
4778+
"properties": {
4779+
"url": {
4780+
"type": "string"
4781+
},
4782+
"redirectUri": {
4783+
"type": "string"
4784+
},
4785+
"clientId": {
4786+
"type": "string"
4787+
}
4788+
}
4789+
}
4790+
}
4791+
},
4792+
"features": {
4793+
"$ref": "#/components/schemas/UserInterfaceConfigFeatures",
4794+
"properties": {
4795+
"readOnly": {
4796+
"type": "boolean"
4797+
},
4798+
"breadcrumbs": {
4799+
"type": "boolean"
4800+
},
4801+
"roleManagement": {
4802+
"type": "boolean"
4803+
},
4804+
"settings": {
4805+
"type": "boolean"
4806+
}
4807+
}
4808+
}
4809+
},
4810+
"example": {
4811+
"ui": {
4812+
"contextPath": "/",
4813+
"navPrefixPath": "/",
4814+
"oaiDocsUrl": "https://registry.apicur.io/docs"
4815+
},
4816+
"auth": {
4817+
"type": "oidc",
4818+
"rbacEnabled": true,
4819+
"obacEnabled": false,
4820+
"options": {
4821+
"url": "https://auth.apicur.io/realms/apicurio",
4822+
"redirectUri": "http://registry.apicur.io",
4823+
"clientId": "apicurio-registry-ui"
4824+
}
4825+
},
4826+
"features": {
4827+
"readOnly": false,
4828+
"breadcrumbs": true,
4829+
"roleManagement": false,
4830+
"settings": true
4831+
}
4832+
}
4833+
},
4834+
"UserInterfaceConfigAuth": {
4835+
"title": "Root Type for UserInterfaceConfigAuth",
4836+
"description": "",
4837+
"required": [
4838+
"obacEnabled",
4839+
"rbacEnabled"
4840+
],
4841+
"type": "object",
4842+
"properties": {
4843+
"type": {
4844+
"enum": [
4845+
"none",
4846+
"oidc"
4847+
],
4848+
"type": "string"
4849+
},
4850+
"rbacEnabled": {
4851+
"type": "boolean"
4852+
},
4853+
"obacEnabled": {
4854+
"type": "boolean"
4855+
},
4856+
"options": {
4857+
"$ref": "#/components/schemas/Properties"
4858+
}
4859+
},
4860+
"example": {
4861+
"type": "oidc",
4862+
"rbacEnabled": true,
4863+
"obacEnabled": false,
4864+
"options": {
4865+
"url": "https://auth.apicur.io/realms/apicurio",
4866+
"redirectUri": "https://registry.apicur.io",
4867+
"clientId": "registry-ui"
4868+
}
4869+
}
4870+
},
4871+
"UserInterfaceConfigFeatures": {
4872+
"title": "Root Type for UserInterfaceConfigFeatures",
4873+
"description": "",
4874+
"type": "object",
4875+
"properties": {
4876+
"readOnly": {
4877+
"type": "boolean"
4878+
},
4879+
"breadcrumbs": {
4880+
"type": "boolean"
4881+
},
4882+
"roleManagement": {
4883+
"type": "boolean"
4884+
},
4885+
"settings": {
4886+
"type": "boolean"
4887+
}
4888+
},
4889+
"example": {
4890+
"readOnly": false,
4891+
"breadcrumbs": true,
4892+
"roleManagement": false,
4893+
"settings": true
4894+
}
4895+
},
4896+
"UserInterfaceConfigUi": {
4897+
"title": "Root Type for UserInterfaceConfigUi",
4898+
"description": "",
4899+
"type": "object",
4900+
"properties": {
4901+
"contextPath": {
4902+
"type": "string"
4903+
},
4904+
"navPrefixPath": {
4905+
"type": "string"
4906+
},
4907+
"oaiDocsUrl": {
4908+
"type": "string"
4909+
}
4910+
},
4911+
"example": {
4912+
"contextPath": "/",
4913+
"navPrefixPath": "/",
4914+
"oaiDocsUrl": "https://registry.apicur.io/docs"
4915+
}
47144916
}
47154917
},
47164918
"responses": {

docs/generateAllConfigPartial.java

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
public class generateAllConfigPartial {
2121

2222
private static Map<String, Option> allConfiguration = new HashMap();
23+
private static Set<String> skipProperties = Set.of("registry.auth.url.configured");
2324

2425
static class Option {
2526
final String name;
@@ -152,6 +153,9 @@ public static Map<String, Option> extractConfigurations(String jarFile, Map<Stri
152153
if (allConfiguration.containsKey(configName)) {
153154
continue;
154155
}
156+
if (skipProperties.contains(configName)) {
157+
continue;
158+
}
155159
switch (annotation.target().kind()) {
156160
case FIELD:
157161
configName = configName.replace("app.authn.", "registry.auth.");

0 commit comments

Comments
 (0)