Skip to content

Commit

Permalink
fixes and improvements in backend and openapi controllers and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangsa committed May 25, 2024
1 parent 45747f8 commit 1d8f838
Show file tree
Hide file tree
Showing 25 changed files with 314 additions and 211 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ public Collection<String> findAggregateInputs(Map aggregate, Options options) {
return new HashSet<>(JSONPath.get(aggregate, "$.commands[*].parameter", List.of()));
}

public Collection<Map> findAggregates(Collection<Map> entities, Options options) {
return entities.stream().filter(entity -> isAggregate((String) entity.get("name"), options)).collect(Collectors.toList());
}

public boolean isAggregate(String entityName, Options options) {
var zdl = options.get("zdl");
var isAggregateRoot = JSONPath.get(zdl, "$.entities." + entityName + "[?(@.options.aggregate == true)]", List.of());
var aggregateName = findEntityAggregate(entityName, options);
return !isAggregateRoot.isEmpty() || aggregateName != null;
}

public String findEntityAggregate(String entityName, Options options) {
var zdl = options.get("zdl");
var aggregateNames = JSONPath.get(zdl, "$.aggregates[*][?(@.aggregateRoot == '" + entityName + "')].name", List.of());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ protected ZDLProjectTemplates configureProjectTemplates() {
"{{asPackageFolder configPackage}}/TestDataLoader.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "config/DockerComposeInitializer-{{persistence}}.java", "{{mavenModulesPrefix}}-core-impl",
"{{asPackageFolder configPackage}}/DockerComposeInitializer.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "config/TestDataLoader-{{persistence}}.java", "{{mavenModulesPrefix}}-infra",
"{{asPackageFolder configPackage}}/TestDataLoader.java", JAVA, null, true);
ts.addTemplate(ts.singleTemplates, "src/test/java", "config/DockerComposeInitializer-{{persistence}}.java", "{{mavenModulesPrefix}}-infra",
"{{asPackageFolder configPackage}}/DockerComposeInitializer.java", JAVA, null, true);

ts.addTemplate(ts.singleTemplates, "src/main/java", "core/inbound/dtos/package-info.java", "{{mavenModulesPrefix}}-domain",
"{{asPackageFolder inboundDtosPackage}}/package-info.java", JAVA, null, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ public {{abstractClass entity}} class {{entity.className}} {{addExtends entity}}
{{~#if (eq relationship.type 'OneToOne')}}
public {{entity.className}} set{{capitalize relationship.fieldName}}({{{relationshipFieldType relationship}}} {{relationship.fieldName}}) {
this.{{relationship.fieldName}} = {{relationship.fieldName}};
{{relationship.fieldName}}.set{{capitalize relationship.otherEntityFieldName}}(this);
if ({{relationship.fieldName}} != null) {
{{relationship.fieldName}}.set{{capitalize relationship.otherEntityFieldName}}(this);
}
return this;
}
{{~/if}}
Expand All @@ -119,20 +121,35 @@ public {{abstractClass entity}} class {{entity.className}} {{addExtends entity}}
{{relationship.fieldName}}.set{{capitalize relationship.otherEntityFieldName}}(this);
return this;
}
public {{entity.className}} remove{{capitalize relationship.fieldName}}({{relationship.otherEntityName}} {{relationship.fieldName}}) {
this.{{relationship.fieldName}}.remove({{relationship.fieldName}});
{{relationship.fieldName}}.set{{capitalize relationship.otherEntityFieldName}}(null);
return this;
}
{{~/if}}
{{~#if (eq relationship.type 'ManyToMany')}}
public {{entity.className}} add{{capitalize relationship.fieldName}}({{relationship.otherEntityName}} {{relationship.fieldName}}) {
this.{{relationship.fieldName}}.add({{relationship.fieldName}});
{{relationship.fieldName}}.get{{capitalize relationship.otherEntityFieldName}}().add(this);
return this;
}
public {{entity.className}} remove{{capitalize relationship.fieldName}}({{relationship.otherEntityName}} {{relationship.fieldName}}) {
this.{{relationship.fieldName}}.remove({{relationship.fieldName}});
{{relationship.fieldName}}.get{{capitalize relationship.otherEntityFieldName}}().remove(this);
return this;
}
{{~/if}}
{{~#if (and (eq relationship.type 'ManyToOne') relationship.otherEntityFieldName)}}
public {{entity.className}} set{{capitalize relationship.fieldName}}({{{relationshipFieldType relationship}}} {{relationship.fieldName}}) {
this.{{relationship.fieldName}} = {{relationship.fieldName}};
{{relationship.fieldName}}.get{{capitalize relationship.otherEntityFieldName}}().add(this);
return this;
}
public {{entity.className}} remove{{capitalize relationship.fieldName}}({{{relationshipFieldType relationship}}} {{relationship.fieldName}}) {
this.{{relationship.fieldName}} = {{relationship.fieldName}};
{{relationship.fieldName}}.get{{capitalize relationship.otherEntityFieldName}}().remove(this);
return this;
}
{{~/if}}
{{/each}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@
// TODO: you may need to reload the entity here to fetch relationships 'mapped by id'
{{~> (partial '../withEvents')}}
return {{wrapWithMapper entity}};
{{~else if (isCrudMethod 'update' method=method entity=entity )}}
log.debug("[CRUD] Request to update {{entity.className}}: {}", input);
var {{entity.instanceName}} = {{entity.instanceName}}Repository.findById(id);
// saving is unnecessary: https://vladmihalcea.com/best-spring-data-jparepository/
{{entity.instanceName}} = {{entity.instanceName}}.map(existing{{entity.instanceName}} -> {{asInstanceName service.name}}Mapper.update(existing{{entity.instanceName}}, {{{mapperInputCallSignature method.parameter}}}));
{{~> (partial '../withEvents')}}
return {{wrapWithMapper entity}};
{{~else if (isCrudMethod 'list' method=method entity=entity )}}
{{~#if method.options.paginated}}
log.debug("[CRUD] Request list of {{entity.classNamePlural}}: {}", pageable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
var {{entity.instanceName}} = {{entity.instanceName}}Repository.findById(id).map(existing{{entity.className}} -> {
return {{asInstanceName service.name}}Mapper.update(existing{{entity.className}}, {{{mapperInputCallSignature method.parameter}}});
})
// saving is unnecessary https://vladmihalcea.com/best-spring-data-jparepository/
// .map({{entity.instanceName}}Repository::save)
.map({{entity.instanceName}}Repository::save)
{{~#unless (eq entity.name method.returnType)}}
.map({{asInstanceName service.name}}Mapper::as{{returnType}})
{{~/unless}}
Expand All @@ -30,8 +29,7 @@
var {{entity.instanceName}} = {{entity.instanceName}}Repository.findById(id).map(existing{{entity.className}} -> {
return {{asInstanceName service.name}}Mapper.update(existing{{entity.className}}, {{{mapperInputCallSignature method.parameter}}});
})
// saving is unnecessary https://vladmihalcea.com/best-spring-data-jparepository/
// .map({{entity.instanceName}}Repository::save)
.map({{entity.instanceName}}Repository::save)
.orElseThrow();
{{~> (partial '../withEvents')}}
return {{wrapWithMapper entity}};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package {{basePackage}}.config;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -36,64 +38,86 @@ public class DockerComposeInitializer implements ApplicationContextInitializer<C

}

// The number of services in the docker-compose.yml file
private static final int NUMBER_OF_SERVICES = 1;

static String HOST = DockerClientFactory.instance().dockerHostIpAddress();
static DockerComposeContainer container = new DockerComposeContainer(new File("src/main/docker/docker-compose.yml"))
.withEnv("HOST", HOST)
.withExposedService("mongodb", 27017, Wait.forListeningPort())
;
static boolean isContainerRunning = false;
private record Service(String name, int port, String envVar, String envValueTemplate) {}

private static final String DOCKER_COMPOSE_FILE = "./docker-compose.yml";
private static final List<Service> SERVICES = List.of(
new Service("postgresql", 5432, "DATASOURCE_URL", "jdbc:postgresql://%s:%s/DATABASENAME"),
new Service("kafka", 9092, "KAFKA_BOOTSTRAP_SERVERS", "%s:%s")
);

static String HOST = DockerClientFactory.instance().dockerHostIpAddress();
static DockerComposeContainer container = new DockerComposeContainer(new File(DOCKER_COMPOSE_FILE)).withEnv("HOST", HOST);
static {
for (Service service : SERVICES) {
if("schema-registry".equals(service.name)) {
container.withExposedService(service.name, service.port, Wait.forHttp("/subjects").forStatusCode(200));
}
else {
container.withExposedService(service.name, service.port, Wait.forListeningPort());
}
}
}
static boolean isContainerRunning = false;

@SneakyThrows
@Override
public void initialize(ConfigurableApplicationContext ctx) {
if(isDockerComposeRunningAllServices(NUMBER_OF_SERVICES)) {
if(isDockerComposeRunningAllServices(SERVICES)) {
log.info("Docker Compose Containers are running from local docker-compose. Skipping TestContainers...");
} else {
log.info("Docker Compose Containers are not running from local docker-compose. Starting from TestContainers...");
if (isContainerRunning) {
log.info("Docker Compose Containers are already running from TestContainers. Skipping...");
} else {
log.info("Starting Docker Compose Containers from TestContainers...");
container.start();
isContainerRunning = true;

// TODO: Replace with JPA...
int mongodbPort = container.getServicePort("mongodb", 27017);
log.info("Docker Compose Containers are running from TestContainers. Mongodb: {}", HOST + ":" + mongodbPort);

log.info("Container Ports Status: Mongodb: {}", isPortOpen(HOST, mongodbPort));

TestPropertyValues.of(
String.format("MONGODB_URI=mongodb://%s:%s/REVIEW?replicaSet=rs0", HOST, mongodbPort)
).applyTo(ctx.getEnvironment());
log.info("Starting Docker Compose Containers from TestContainers...");
container.start();
isContainerRunning = true;

for (Service service : SERVICES) {
int port = container.getServicePort(service.name, service.port);
log.info("DockerCompose exposed port for {}: {}", service.name, HOST + ":" + port);
log.info("DockerCompose Service {} listening: {}", service.name, isPortOpen(HOST, port));
if (service.envValueTemplate != null) {
TestPropertyValues.of(service.envVar + "=" +String.format(service.envValueTemplate, HOST, port))
.applyTo(ctx.getEnvironment());
}
}
}
}
}

private boolean isDockerComposeRunningAllServices(int numberOfServices) {
return Stream.of("docker-compose", "docker-compose.exe").anyMatch(cmd -> {
try {
return readProcessOutputStream(cmd, "-f", "src/main/docker/docker-compose.yml", "ps").size() == (numberOfServices + 1);
} catch (IOException | InterruptedException e) {
return false;
}
});
}

private List<String> readProcessOutputStream(String ...command) throws IOException, InterruptedException {
var process = new ProcessBuilder(command).start();
var reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()));
var line = "";
var output = new ArrayList<String>();
while ((line = reader.readLine()) != null) {
output.add(line);
}
process.waitFor();
return output;
}
private boolean isDockerComposeRunningAllServices(List<Service> services) {
var serviceNames = services.stream().map(Service::name).toList();
return Stream.of("docker-compose", "docker-compose.exe").anyMatch(cmd -> {
try {
return getDockerComposeRunningServices(cmd, "-f", DOCKER_COMPOSE_FILE, "ps").containsAll(serviceNames);
}
catch (IOException | InterruptedException e) {
return false;
}
});
}

private static final int SERVICE_COLUMN = 3;
public List<String> getDockerComposeRunningServices(String... command) throws IOException, InterruptedException {
List<String> services = new ArrayList<>();
var process = new ProcessBuilder(command).start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

String line;
while ((line = reader.readLine()) != null) {
if (!line.isEmpty()) {
String[] columns = line.split("\\s+");
if (columns.length > SERVICE_COLUMN) {
services.add(columns[SERVICE_COLUMN]);
}
}
}

process.waitFor();
return services.size() > 1? services.subList(1, services.size()) : services;
}

boolean isPortOpen(String host, int port) {
try (Socket socket = new Socket(host, port)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package {{basePackage}}.config;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -36,20 +38,32 @@ public class DockerComposeInitializer implements ApplicationContextInitializer<C

}

// The number of services in the docker-compose.yml file
private static final int NUMBER_OF_SERVICES = 1;

static String HOST = DockerClientFactory.instance().dockerHostIpAddress();
static DockerComposeContainer container = new DockerComposeContainer(new File("src/main/docker/docker-compose.yml"))
.withEnv("HOST", HOST)
.withExposedService("mongodb", 27017, Wait.forListeningPort())
;
static boolean isContainerRunning = false;
private record Service(String name, int port, String envVar, String envValueTemplate) {}

private static final String DOCKER_COMPOSE_FILE = "./docker-compose.yml";
private static final List<Service> SERVICES = List.of(
new Service("mongodb", 27017, "MONGODB_URI", "mongodb://%s:%s/DATABASENAME?replicaSet=rs0"),
new Service("kafka", 9092, "KAFKA_BOOTSTRAP_SERVERS", "%s:%s")
);

static String HOST = DockerClientFactory.instance().dockerHostIpAddress();
static DockerComposeContainer container = new DockerComposeContainer(new File(DOCKER_COMPOSE_FILE)).withEnv("HOST", HOST);
static {
for (Service service : SERVICES) {
if("schema-registry".equals(service.name)) {
container.withExposedService(service.name, service.port, Wait.forHttp("/subjects").forStatusCode(200));
}
else {
container.withExposedService(service.name, service.port, Wait.forListeningPort());
}
}
}
static boolean isContainerRunning = false;

@SneakyThrows
@Override
public void initialize(ConfigurableApplicationContext ctx) {
if(isDockerComposeRunningAllServices(NUMBER_OF_SERVICES)) {
if(isDockerComposeRunningAllServices(SERVICES)) {
log.info("Docker Compose Containers are running from local docker-compose. Skipping TestContainers...");
} else {
log.info("Docker Compose Containers are not running from local docker-compose. Starting from TestContainers...");
Expand All @@ -60,39 +74,50 @@ public class DockerComposeInitializer implements ApplicationContextInitializer<C
container.start();
isContainerRunning = true;

int mongodbPort = container.getServicePort("mongodb", 27017);
log.info("Docker Compose Containers are running from TestContainers. Mongodb: {}", HOST + ":" + mongodbPort);

log.info("Container Ports Status: Mongodb: {}", isPortOpen(HOST, mongodbPort));

TestPropertyValues.of(
String.format("MONGODB_URI=mongodb://%s:%s/REVIEW?replicaSet=rs0", HOST, mongodbPort)
).applyTo(ctx.getEnvironment());
for (Service service : SERVICES) {
int port = container.getServicePort(service.name, service.port);
log.info("DockerCompose exposed port for {}: {}", service.name, HOST + ":" + port);
log.info("DockerCompose Service {} listening: {}", service.name, isPortOpen(HOST, port));
if (service.envValueTemplate != null) {
TestPropertyValues.of(service.envVar + "=" +String.format(service.envValueTemplate, HOST, port))
.applyTo(ctx.getEnvironment());
}
}
}
}
}

private boolean isDockerComposeRunningAllServices(int numberOfServices) {
return Stream.of("docker-compose", "docker-compose.exe").anyMatch(cmd -> {
try {
return readProcessOutputStream(cmd, "-f", "src/main/docker/docker-compose.yml", "ps").size() == (numberOfServices + 1);
} catch (IOException | InterruptedException e) {
return false;
}
});
}

private List<String> readProcessOutputStream(String ...command) throws IOException, InterruptedException {
var process = new ProcessBuilder(command).start();
var reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()));
var line = "";
var output = new ArrayList<String>();
while ((line = reader.readLine()) != null) {
output.add(line);
}
process.waitFor();
return output;
}
private boolean isDockerComposeRunningAllServices(List<Service> services) {
var serviceNames = services.stream().map(Service::name).toList();
return Stream.of("docker-compose", "docker-compose.exe").anyMatch(cmd -> {
try {
return getDockerComposeRunningServices(cmd, "-f", DOCKER_COMPOSE_FILE, "ps").containsAll(serviceNames);
}
catch (IOException | InterruptedException e) {
return false;
}
});
}

private static final int SERVICE_COLUMN = 3;
public List<String> getDockerComposeRunningServices(String... command) throws IOException, InterruptedException {
List<String> services = new ArrayList<>();
var process = new ProcessBuilder(command).start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));

String line;
while ((line = reader.readLine()) != null) {
if (!line.isEmpty()) {
String[] columns = line.split("\\s+");
if (columns.length > SERVICE_COLUMN) {
services.add(columns[SERVICE_COLUMN]);
}
}
}

process.waitFor();
return services.size() > 1? services.subList(1, services.size()) : services;
}

boolean isPortOpen(String host, int port) {
try (Socket socket = new Socket(host, port)) {
Expand Down
Loading

0 comments on commit 1d8f838

Please sign in to comment.