Skip to content

Commit 49d3228

Browse files
rraystchristiangoerdespredic8
authored
Yaml bean registry (#2339)
* add first examples * add examples (-loadbalancing) * add examples (-openapi) * add examples (-orchestration) * add examples (-route-traffic) * add examples (-security/ntlm) * add examples (-security/oauth2/google) * add examples (-security) * add examples (-websockets) * add examples (done) * Rename and fix yaml example test * schema validation, moved BeanCache from core to annot * working 'annot' tests * fixed Router initialization * removed exception declaration * added child test * added attribute test * Add YamlSetterConflictTest * use multiline string * adde test * added more tests * code improvements * add documentation * fixed child element parsing where child element name is also name of setter * imoprove * fix examples * switch spec to api * fixes * spec -> api * Add Yaml examples (#2346) * add first examples * add examples (-loadbalancing) * add examples (-openapi) * add examples (-orchestration) * add examples (-route-traffic) * add examples (-security/ntlm) * add examples (-security/oauth2/google) * add examples (-security) * add examples (-websockets) * add examples (done) * Rename and fix yaml example test * fix examples * switch spec to api * fixes * spec -> api --------- Co-authored-by: Thomas Bayer <bayer@predic8.de> * close InputStream * smaller improvements * added line numbers to parsing * minor improvements * spec -> api * fix and add schema comment * fixes * fixed examples/tests * yaml parsing: rm newline from string * fix tests * fix tests * fix examples * add missing conf element * rm apis.yaml * add apis.yaml to .gitignore * enable additionalProperties when MCOtherAttributes is set * add non top-level id setter to schema (e.g. for ClaimList.Scope) * improve (see coderabbitai) * fix toplevel properties * fix test and improve error * fix JSONYAMLSchemaValidator json parsing * fix Yaml initialization order * rm getParentProxy() * add ProxyAware interface * add LiCENSE * fix setProxy for nested elements * coderabbitai changes * eliminate double unmarshalling * Make tests tolerate 404 responses due to inconsistent external service behavior * fix test * added comment * refactor: minor * use ParsingContext * extract MethodSetter * extract NodeValidationUtils * refactoring * refactoring * refactoring * add comments and rename K8sHelperGenerator.java to Grammar * rename fixes * fix wrong context for structured setter * refactor: minor * minor: refactoring * minor: refactoring * minor: refactoring * minor: refactoring * minor: refactoring * fix: yaml parsing tests * fix: yaml parsing tests * refactor: minor * refactor: minor * refactor: minor --------- Co-authored-by: Christian Gördes <christian.goerdes@outlook.de> Co-authored-by: Christian Gördes <118011644+christiangoerdes@users.noreply.github.com> Co-authored-by: Thomas Bayer <bayer@predic8.de>
1 parent c0bb65f commit 49d3228

183 files changed

Lines changed: 4726 additions & 1653 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,4 @@ maven-plugin/target/surefire/
126126
/docs/router-conf.xsd
127127
.vscode/
128128
/core/derby.log
129+
/distribution/conf/apis.yaml

.run/IDEStarter.run.xml

Lines changed: 0 additions & 17 deletions
This file was deleted.

README.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@ Solve even complex custom API requirements with simple configurations.
3535

3636
**YAML Configuration (beta):**
3737
```yaml
38-
apiVersion: membrane-api.io/v1beta2
39-
kind: api
40-
metadata:
41-
name: log
42-
spec:
38+
api:
4339
port: 2000
4440
interceptors:
4541
- log:

annot/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@
6767
<artifactId>jackson-dataformat-yaml</artifactId>
6868
<version>${jackson.version}</version>
6969
</dependency>
70+
<dependency>
71+
<groupId>com.networknt</groupId>
72+
<artifactId>json-schema-validator</artifactId>
73+
<version>2.0.0</version>
74+
</dependency>
7075

7176
<dependency>
7277
<groupId>org.hamcrest</groupId>

annot/src/main/java/com/predic8/membrane/annot/AbstractParser.java

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.HashSet;
1919
import java.util.Set;
2020

21+
import org.jetbrains.annotations.*;
22+
import org.slf4j.*;
2123
import org.springframework.beans.factory.config.BeanDefinition;
2224
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2325
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
@@ -36,10 +38,11 @@
3638

3739
public abstract class AbstractParser extends AbstractSingleBeanDefinitionParser {
3840

39-
private static final String MEMBRANE_BEANS_NAMESPACE = "http://membrane-soa.org/proxies/1/";
41+
private static final Logger log = LoggerFactory.getLogger(AbstractParser.class);
42+
4043
private static final String MEMBRANE_PROXIES_NAMESPACE = "http://membrane-soa.org/proxies/1/";
4144

42-
private boolean inlined = false;
45+
private boolean inlined = false;
4346

4447
public BeanDefinition parse(Element e) {
4548
inlined = true;
@@ -112,6 +115,7 @@ protected void setProperties(String prop, Element e, BeanDefinitionBuilder build
112115
builder.addPropertyValue(prop, attrs);
113116
}
114117

118+
// TODO @Tobias can that be deleted?
115119
protected void parseElementToProperty(Element ele, ParserContext parserContext, BeanDefinitionBuilder builder, String property) {
116120
BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
117121

@@ -143,32 +147,35 @@ protected void handleChildElement(Element ele, ParserContext parserContext, Bean
143147

144148
try {
145149
Object o = delegate.parsePropertySubElement(ele, builder.getBeanDefinition());
146-
147-
String clazz = null;
148-
if (o instanceof BeanDefinitionHolder) {
149-
clazz = ((BeanDefinitionHolder) o).getBeanDefinition().getBeanClassName();
150-
} else if (o instanceof RuntimeBeanReference) {
151-
clazz = parserContext.getRegistry().getBeanDefinition(((RuntimeBeanReference) o).getBeanName()).getBeanClassName();
152-
} else if (o instanceof RuntimeBeanNameReference) {
153-
clazz = parserContext.getRegistry().getBeanDefinition(((RuntimeBeanNameReference) o).getBeanName()).getBeanClassName();
154-
} else {
155-
parserContext.getReaderContext().error("Don't know how to get bean class from " + o.getClass(), ele);
156-
}
157-
158-
handleChildObject(ele, parserContext, builder, Class.forName(clazz), o);
150+
handleChildObject(ele, parserContext, builder, Thread.currentThread().getContextClassLoader().loadClass(getBeanClassNameFromObject(ele, parserContext, o)), o);
159151
} catch (ClassNotFoundException e) {
160152
throw new RuntimeException(e);
161153
}
162154
}
163155

164-
protected int incrementCounter(BeanDefinitionBuilder builder, String counter) {
156+
private static @Nullable String getBeanClassNameFromObject(Element ele, ParserContext parserContext, Object o) {
157+
return switch (o) {
158+
case BeanDefinitionHolder beanDefinitionHolder -> beanDefinitionHolder.getBeanDefinition().getBeanClassName();
159+
case RuntimeBeanReference runtimeBeanReference -> parserContext.getRegistry().getBeanDefinition(runtimeBeanReference.getBeanName()).getBeanClassName();
160+
case RuntimeBeanNameReference runtimeBeanNameReference -> parserContext.getRegistry().getBeanDefinition(runtimeBeanNameReference.getBeanName()).getBeanClassName();
161+
default -> {
162+
var msg = "Don't know how to get bean class from " + o.getClass();
163+
log.warn(msg);
164+
parserContext.getReaderContext().error(msg, ele);
165+
throw new RuntimeException(msg);
166+
}
167+
};
168+
}
169+
170+
protected int incrementCounter(BeanDefinitionBuilder builder, String counter) {
165171
Integer i = (Integer) builder.getRawBeanDefinition().getAttribute(counter);
166172
if (i == null)
167173
i = 0;
168174
builder.getRawBeanDefinition().setAttribute(counter, i+1);
169175
return i;
170176
}
171177

178+
// TODO needed?
172179
protected boolean isMembraneNamespace(String namespace) {
173180
return MEMBRANE_PROXIES_NAMESPACE.equals(namespace);
174181
}

annot/src/main/java/com/predic8/membrane/annot/K8sHelperGenerator.java renamed to annot/src/main/java/com/predic8/membrane/annot/Grammar.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616

1717
import java.util.List;
1818

19-
public interface K8sHelperGenerator {
19+
public interface Grammar {
2020

2121
Class<?> getElement(String key);
2222

2323
Class<?> getLocal(String context, String key);
2424

2525
List<String> getCrdSingularNames();
2626

27+
String getSchemaLocation();
2728
}

annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java

Lines changed: 33 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* - Choose/cases/case has one nesting to much
3838
* - apiKey/extractors/expressionExtractor/expression => too much?
3939
*/
40-
public class JsonSchemaGenerator extends AbstractK8sGenerator {
40+
public class JsonSchemaGenerator extends AbstractGrammar {
4141

4242
private final Map<String, Boolean> topLevelAdded = new HashMap<>();
4343

@@ -78,71 +78,33 @@ private void assemble(Model m, MainInfo main) throws IOException {
7878
topLevelAdded.clear();
7979

8080
addParserDefinitions(m, main);
81-
addTopLevelProperties();
81+
addTopLevelProperties(m, main);
8282

8383
writeSchema(main, schema);
8484
}
8585

86-
private void addTopLevelProperties() {
87-
schema.additionalProperties(false)
88-
.property(ref("soapProxy")
89-
.ref("#/$defs/com_predic8_membrane_core_config_spring_SoapProxyParser"))
90-
.property(ref("internal")
91-
.ref("#/$defs/com_predic8_membrane_core_config_spring_InternalParser"))
92-
.property(ref("proxy")
93-
.ref("#/$defs/com_predic8_membrane_core_config_spring_ProxyParser"))
94-
.property(ref("api")
95-
.ref("#/$defs/com_predic8_membrane_core_config_spring_ApiParser"))
96-
.property(ref("stompProxy")
97-
.ref("#/$defs/com_predic8_membrane_core_config_spring_StompProxyParser"))
98-
.property(ref("sslProxy")
99-
.ref("#/$defs/com_predic8_membrane_core_config_spring_SslProxyParser"));
100-
101-
List<SchemaObject> kinds = new ArrayList<>();
102-
103-
kinds.add(object()
104-
.additionalProperties(false)
105-
.property(ref("api")
106-
.ref("#/$defs/com_predic8_membrane_core_config_spring_ApiParser")
107-
.required(true)));
108-
109-
kinds.add(object()
110-
.additionalProperties(false)
111-
.property(ref("soapProxy")
112-
.ref("#/$defs/com_predic8_membrane_core_config_spring_SoapProxyParser")
113-
.required(true)));
114-
115-
kinds.add(object()
116-
.additionalProperties(false)
117-
.property(ref("internal")
118-
.ref("#/$defs/com_predic8_membrane_core_config_spring_InternalParser")
119-
.required(true)));
120-
121-
kinds.add(object()
122-
.additionalProperties(false)
123-
.property(ref("proxy")
124-
.ref("#/$defs/com_predic8_membrane_core_config_spring_ProxyParser")
125-
.required(true)));
126-
127-
kinds.add(object()
128-
.additionalProperties(false)
129-
.property(ref("stompProxy")
130-
.ref("#/$defs/com_predic8_membrane_core_config_spring_StompProxyParser")
131-
.required(true)));
132-
133-
kinds.add(object()
134-
.additionalProperties(false)
135-
.property(ref("sslProxy")
136-
.ref("#/$defs/com_predic8_membrane_core_config_spring_SslProxyParser")
137-
.required(true)));
138-
139-
kinds.add(object()
140-
.additionalProperties(false)
141-
.property(ref("bean")
142-
.ref("#/$defs/com_predic8_membrane_core_config_spring_BeanParser")
143-
.required(true)));
144-
145-
schema.oneOf(new ArrayList<>(kinds));
86+
private void addTopLevelProperties(Model m, MainInfo main) {
87+
schema.additionalProperties(false);
88+
List<AbstractSchema<?>> kinds = new ArrayList<>();
89+
90+
main.getElements().values().forEach(e -> {
91+
if (!e.getAnnotation().topLevel())
92+
return;
93+
94+
String name = e.getAnnotation().name();
95+
String refName = "#/$defs/" + e.getXSDTypeName(m);
96+
97+
schema.property(ref(name).ref(refName));
98+
99+
kinds.add(object()
100+
.additionalProperties(false)
101+
.property(ref(name)
102+
.ref(refName)
103+
.required(true)));
104+
});
105+
106+
if (!kinds.isEmpty())
107+
schema.oneOf(kinds);
146108
}
147109

148110
private void addParserDefinitions(Model m, MainInfo main) {
@@ -180,7 +142,7 @@ private SchemaObject createParser(Model m, MainInfo main, ElementInfo elementInf
180142
}
181143

182144
SchemaObject parser = object(parserName)
183-
.additionalProperties(false)
145+
.additionalProperties(elementInfo.getOai() != null)
184146
.description(getDescriptionContent(elementInfo));
185147
collectProperties(m, main, elementInfo, parser);
186148
return parser;
@@ -203,16 +165,20 @@ private FileObject createFile(MainInfo main) throws IOException {
203165
return processingEnv.getFiler()
204166
.createResource(
205167
CLASS_OUTPUT,
206-
"com.predic8.membrane.core.config.json",
168+
main.getAnnotation().outputPackage().replaceAll("\\.spring$", ".json"),
207169
"membrane.schema.json",
208170
sources.toArray(new Element[0])
209171
);
210172
}
211173

212174
private void processMCAttributes(ElementInfo i, SchemaObject so) {
213-
i.getAis().stream()
214-
.filter(ai -> !ai.getXMLName().equals("id"))
215-
.forEach(ai -> so.property(createProperty(ai)));
175+
i.getAis().forEach(ai -> {
176+
// hide id only on top-level elements
177+
if ("id".equals(ai.getXMLName()) && i.getAnnotation().topLevel()) {
178+
return;
179+
}
180+
so.property(createProperty(ai));
181+
});
216182
}
217183

218184
private AbstractSchema<?> createProperty(AttributeInfo ai) {

annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/AbstractK8sGenerator.java renamed to annot/src/main/java/com/predic8/membrane/annot/generator/kubernetes/AbstractGrammar.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
package com.predic8.membrane.annot.generator.kubernetes;
1515

1616
import com.fasterxml.jackson.databind.*;
17-
import com.fasterxml.jackson.databind.node.*;
18-
import com.predic8.membrane.annot.generator.kubernetes.model.*;
1917
import com.predic8.membrane.annot.model.ElementInfo;
2018
import com.predic8.membrane.annot.model.MainInfo;
2119
import com.predic8.membrane.annot.model.Model;
@@ -34,7 +32,7 @@
3432
/**
3533
* Bundles functionality for kubernetes file generation
3634
*/
37-
public abstract class AbstractK8sGenerator {
35+
public abstract class AbstractGrammar {
3836

3937
protected final ObjectMapper om = new ObjectMapper();
4038
protected final ObjectWriter writer = om.writerWithDefaultPrettyPrinter();
@@ -53,7 +51,7 @@ public WritableNames(ElementInfo ei) {
5351
}
5452
}
5553

56-
public AbstractK8sGenerator(final ProcessingEnvironment processingEnv) {
54+
public AbstractGrammar(final ProcessingEnvironment processingEnv) {
5755
this.processingEnv = processingEnv;
5856
}
5957

0 commit comments

Comments
 (0)