Compare commits
19 Commits
6a91eab764
...
edp-punchi
| Author | SHA1 | Date | |
|---|---|---|---|
| bc00515c1b | |||
| 60f9aa05a0 | |||
| 4705649ae8 | |||
| 5acaffc224 | |||
| 0a1b901b12 | |||
| 440c13cc49 | |||
| 9b9557105c | |||
| af360d7793 | |||
| 452e6189e4 | |||
| 7e4fb66fc6 | |||
| 213f560c4a | |||
| 8449578424 | |||
| 1adcc04efc | |||
| 0e5de99f55 | |||
| 0dae53017d | |||
| 934937feb0 | |||
| dcb6850306 | |||
| f264f90f3b | |||
| cd11b389e5 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -30,6 +30,8 @@
|
|||||||
/.nb-gradle/
|
/.nb-gradle/
|
||||||
/cygnus-onprem-app/target
|
/cygnus-onprem-app/target
|
||||||
/cygnus-cloud-client/target
|
/cygnus-cloud-client/target
|
||||||
/cygnus-installer/src/test
|
|
||||||
/cygnus-cloud-client/target
|
|
||||||
/cygnus-cloud-service/target
|
/cygnus-cloud-service/target
|
||||||
|
/cygnus-installer/src/target
|
||||||
|
/cygnus-installer/target
|
||||||
|
/cygnus-onprem-db/target
|
||||||
|
/cygnus-lib/target
|
||||||
|
|||||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -59,14 +59,18 @@
|
|||||||
"REDIS_HOST": "103.125.129.116",
|
"REDIS_HOST": "103.125.129.116",
|
||||||
"REDIS_PORT": "7901",
|
"REDIS_PORT": "7901",
|
||||||
"REDIS_PASSWORD": "M@triXR3d1s@6202",
|
"REDIS_PASSWORD": "M@triXR3d1s@6202",
|
||||||
|
"REDIS_DATABASE": "1",
|
||||||
"REDIS_SSL": "false",
|
"REDIS_SSL": "false",
|
||||||
|
"CYGNUS_QUERY_CACHE_ENABLED": "false",
|
||||||
"CYGNUS_CLOUD_BASE_URL": "http://localhost:8090",
|
"CYGNUS_CLOUD_BASE_URL": "http://localhost:8090",
|
||||||
"CYGNUS_TOKEN_URL": "http://localhost:8090/oauth2/token",
|
"CYGNUS_TOKEN_URL": "http://localhost:8090/oauth2/token",
|
||||||
"CYGNUS_CLIENT_ID": "matrix",
|
"CYGNUS_CLIENT_ID": "matrix",
|
||||||
"CYGNUS_INSTALLATION_ID": "matrix-delhi-cygnus-01",
|
"CYGNUS_INSTALLATION_ID": "matrix-delhi-cygnus-01",
|
||||||
"CYGNUS_CLIENT_ASSERTION": "file:${workspaceFolder}/matrix-installation/config/machine-assertion.jwt",
|
"CYGNUS_CLIENT_ASSERTION": "file:${workspaceFolder}/config/clients/matrix/matrix-matrix-delhi-cygnus-01-assertion.jwt",
|
||||||
"CYGNUS_LOGIN_KEY_ID": "cygnus-login-2026-01",
|
"CYGNUS_LOGIN_KEY_ID": "cygnus-login-2026-01",
|
||||||
"CYGNUS_LOGIN_PUBLIC_KEY": "file:${workspaceFolder}/matrix-installation/config/keys/login-public.pem",
|
"CYGNUS_LOGIN_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/login-public.pem",
|
||||||
|
"CYGNUS_PAYLOAD_PRIVATE_KEY": "file:${workspaceFolder}/config/keys/case-save-private.pem",
|
||||||
|
"CYGNUS_PAYLOAD_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/case-save-public.pem",
|
||||||
"CYGNUS_CLOUD_REQUEST_TIMEOUT": "PT10S",
|
"CYGNUS_CLOUD_REQUEST_TIMEOUT": "PT10S",
|
||||||
"CYGNUS_TOKEN_REFRESH_SKEW": "PT30S"
|
"CYGNUS_TOKEN_REFRESH_SKEW": "PT30S"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.cygnus.client;
|
package com.cygnus.client;
|
||||||
|
|
||||||
import com.cygnus.client.model.CloudIdentitySession;
|
import com.cygnus.client.model.CloudIdentitySession;
|
||||||
|
import com.cygnus.client.model.CloudQueryResponse;
|
||||||
import com.cygnus.client.model.LoginPayload;
|
import com.cygnus.client.model.LoginPayload;
|
||||||
import com.cygnus.client.security.LoginEnvelopeEncryptor;
|
import com.cygnus.client.security.LoginEnvelopeEncryptor;
|
||||||
import com.cygnus.client.security.MachineTokenProvider;
|
import com.cygnus.client.security.MachineTokenProvider;
|
||||||
@@ -50,4 +51,26 @@ public class CloudIdentityClient {
|
|||||||
.bodyToMono(CloudIdentitySession.class))
|
.bodyToMono(CloudIdentitySession.class))
|
||||||
.timeout(properties.requestTimeout());
|
.timeout(properties.requestTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Mono<CloudQueryResponse> fetchQuery(int queryId) {
|
||||||
|
return tokenProvider.accessToken()
|
||||||
|
.flatMap(token -> webClient.get()
|
||||||
|
.uri(properties.baseUri().resolve("/api/v1/queries/" + queryId))
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(CloudQueryResponse.class))
|
||||||
|
.timeout(properties.requestTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<CloudQueryResponse> fetchQuery(String queryKey) {
|
||||||
|
return tokenProvider.accessToken()
|
||||||
|
.flatMap(token -> webClient.get()
|
||||||
|
.uri(properties.baseUri().resolve("/api/v1/queries/key/" + queryKey))
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(CloudQueryResponse.class))
|
||||||
|
.timeout(properties.requestTimeout());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.cygnus.client.model;
|
||||||
|
|
||||||
|
public record CloudQueryResponse(int queryId, String query) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
|
public record CloudQuery(int queryId, String query) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/queries")
|
||||||
|
public class CloudQueryController {
|
||||||
|
|
||||||
|
private final QueryCatalogRepository repository;
|
||||||
|
|
||||||
|
public CloudQueryController(QueryCatalogRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{queryId}")
|
||||||
|
public Mono<CloudQuery> query(
|
||||||
|
@PathVariable @Min(1) int queryId) {
|
||||||
|
return repository.findEnabled(queryId)
|
||||||
|
.switchIfEmpty(Mono.error(new QueryNotFoundException(queryId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/key/{queryKey}")
|
||||||
|
public Mono<CloudQuery> queryByKey(@PathVariable String queryKey) {
|
||||||
|
return repository.findEnabled(queryKey)
|
||||||
|
.switchIfEmpty(Mono.error(new QueryNotFoundException(queryKey)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
|
import com.cygnus.cloud.database.ReactiveDatabaseClient;
|
||||||
|
import io.vertx.sqlclient.Tuple;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class QueryCatalogRepository {
|
||||||
|
|
||||||
|
private static final String INITIALIZE = """
|
||||||
|
CREATE SCHEMA IF NOT EXISTS platform;
|
||||||
|
CREATE SEQUENCE IF NOT EXISTS platform.application_query_id_seq;
|
||||||
|
CREATE TABLE IF NOT EXISTS platform.application_query (
|
||||||
|
query_id integer PRIMARY KEY DEFAULT nextval('platform.application_query_id_seq'),
|
||||||
|
query_key varchar(100),
|
||||||
|
query_text text NOT NULL,
|
||||||
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
|
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
|
CONSTRAINT ck_platform_application_query_id
|
||||||
|
CHECK (query_id > 0),
|
||||||
|
CONSTRAINT ck_platform_application_query_text
|
||||||
|
CHECK (length(btrim(query_text)) > 0)
|
||||||
|
);
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
ADD COLUMN IF NOT EXISTS query_key varchar(100);
|
||||||
|
ALTER TABLE platform.application_query ALTER COLUMN query_id
|
||||||
|
SET DEFAULT nextval('platform.application_query_id_seq');
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS ux_platform_application_query_key
|
||||||
|
ON platform.application_query (query_key) WHERE query_key IS NOT NULL;
|
||||||
|
SELECT setval('platform.application_query_id_seq',
|
||||||
|
greatest(coalesce((SELECT max(query_id) FROM platform.application_query), 0) + 1, 1), false)
|
||||||
|
""";
|
||||||
|
private static final String FIND = """
|
||||||
|
SELECT query_id, query_text
|
||||||
|
FROM platform.application_query
|
||||||
|
WHERE query_id = $1
|
||||||
|
AND enabled = true
|
||||||
|
""";
|
||||||
|
private static final String FIND_BY_KEY = """
|
||||||
|
SELECT query_id, query_text
|
||||||
|
FROM platform.application_query
|
||||||
|
WHERE query_key = $1
|
||||||
|
AND enabled = true
|
||||||
|
""";
|
||||||
|
private final ReactiveDatabaseClient database;
|
||||||
|
|
||||||
|
public QueryCatalogRepository(ReactiveDatabaseClient database) {
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Void> initialize() {
|
||||||
|
return database.query(INITIALIZE).then();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<CloudQuery> findEnabled(int queryId) {
|
||||||
|
return database.preparedQuery(FIND, Tuple.of(queryId))
|
||||||
|
.flatMapMany(rows -> reactor.core.publisher.Flux.fromIterable(rows))
|
||||||
|
.next()
|
||||||
|
.map(row -> new CloudQuery(
|
||||||
|
row.getInteger("query_id"), row.getString("query_text")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<CloudQuery> findEnabled(String queryKey) {
|
||||||
|
return database.preparedQuery(FIND_BY_KEY, Tuple.of(queryKey))
|
||||||
|
.flatMapMany(rows -> reactor.core.publisher.Flux.fromIterable(rows))
|
||||||
|
.next()
|
||||||
|
.map(row -> new CloudQuery(row.getInteger("query_id"), row.getString("query_text")));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class QueryCatalogSchemaInitializer implements ApplicationRunner {
|
||||||
|
|
||||||
|
private final QueryCatalogRepository repository;
|
||||||
|
|
||||||
|
public QueryCatalogSchemaInitializer(QueryCatalogRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments arguments) {
|
||||||
|
repository.initialize()
|
||||||
|
.block(Duration.ofMinutes(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.cygnus.cloud.query;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
|
public class QueryNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
public QueryNotFoundException(int queryId) {
|
||||||
|
super("Query was not found: " + queryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryNotFoundException(String queryKey) {
|
||||||
|
super("Query was not found: " + queryKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,8 @@ public class CloudSecurityConfiguration {
|
|||||||
.permitAll()
|
.permitAll()
|
||||||
.pathMatchers("/api/v1/identity/login")
|
.pathMatchers("/api/v1/identity/login")
|
||||||
.hasAuthority("SCOPE_identity.login")
|
.hasAuthority("SCOPE_identity.login")
|
||||||
|
.pathMatchers("/api/v1/queries/**")
|
||||||
|
.hasAuthority("SCOPE_identity.login")
|
||||||
.pathMatchers("/api/v1/admin/**")
|
.pathMatchers("/api/v1/admin/**")
|
||||||
.hasAuthority("SCOPE_cygnus.admin")
|
.hasAuthority("SCOPE_cygnus.admin")
|
||||||
.anyExchange().authenticated())
|
.anyExchange().authenticated())
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
CREATE SCHEMA IF NOT EXISTS platform;
|
||||||
|
CREATE SEQUENCE IF NOT EXISTS platform.application_query_id_seq;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS platform.application_query (
|
||||||
|
query_id integer PRIMARY KEY DEFAULT nextval('platform.application_query_id_seq'),
|
||||||
|
query_key varchar(100),
|
||||||
|
query_text text NOT NULL,
|
||||||
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
|
updated_at timestamptz NOT NULL DEFAULT current_timestamp,
|
||||||
|
CONSTRAINT ck_platform_application_query_id
|
||||||
|
CHECK (query_id > 0),
|
||||||
|
CONSTRAINT ck_platform_application_query_text
|
||||||
|
CHECK (length(btrim(query_text)) > 0)
|
||||||
|
);
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
ADD COLUMN IF NOT EXISTS query_key varchar(100);
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS ux_platform_application_query_key
|
||||||
|
ON platform.application_query (query_key) WHERE query_key IS NOT NULL;
|
||||||
|
ALTER TABLE platform.application_query ALTER COLUMN query_id
|
||||||
|
SET DEFAULT nextval('platform.application_query_id_seq');
|
||||||
|
SELECT setval('platform.application_query_id_seq',
|
||||||
|
greatest(coalesce((SELECT max(query_id) FROM platform.application_query), 0) + 1, 1), false);
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
current_type text;
|
||||||
|
BEGIN
|
||||||
|
SELECT data_type
|
||||||
|
INTO current_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'platform'
|
||||||
|
AND table_name = 'application_query'
|
||||||
|
AND column_name = 'query_id';
|
||||||
|
|
||||||
|
IF current_type IN ('character varying', 'text') THEN
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
DROP CONSTRAINT IF EXISTS ck_platform_application_query_id;
|
||||||
|
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
ALTER COLUMN query_id TYPE integer
|
||||||
|
USING regexp_replace(query_id, '^Query', '')::integer;
|
||||||
|
|
||||||
|
ALTER TABLE platform.application_query
|
||||||
|
ADD CONSTRAINT ck_platform_application_query_id
|
||||||
|
CHECK (query_id > 0);
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- Stable query key; query_id is generated by PostgreSQL.
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
INSERT INTO platform.application_query(query_key, query_text, enabled, created_at, updated_at, ismigrated)
|
||||||
|
VALUES ('APPLICATION_SAVE',
|
||||||
|
'procedure!C0L!select * from public.save_application_details(?::jsonb,?::smallint,?::smallint,?::smallint)',
|
||||||
|
true, clock_timestamp(), clock_timestamp(), true)
|
||||||
|
ON CONFLICT (query_key) WHERE query_key IS NOT NULL DO UPDATE SET
|
||||||
|
query_text=excluded.query_text, enabled=true, updated_at=clock_timestamp(), ismigrated=true;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -30,6 +30,7 @@ public class DeploymentWriter {
|
|||||||
try {
|
try {
|
||||||
Path normalizedOutput = output.toAbsolutePath().normalize();
|
Path normalizedOutput = output.toAbsolutePath().normalize();
|
||||||
Path config = normalizedOutput.resolve("config");
|
Path config = normalizedOutput.resolve("config");
|
||||||
|
protectCloudConfiguration(config, profile);
|
||||||
Path keyDirectory = config.resolve("keys");
|
Path keyDirectory = config.resolve("keys");
|
||||||
Files.createDirectories(keyDirectory);
|
Files.createDirectories(keyDirectory);
|
||||||
|
|
||||||
@@ -82,6 +83,23 @@ public class DeploymentWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void protectCloudConfiguration(Path deploymentConfig, ProductProfile profile) {
|
||||||
|
if (isInside(deploymentConfig, profile.assertionEncryptionPublicKey())
|
||||||
|
|| isInside(deploymentConfig, profile.loginEncryptionPublicKey())) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Refusing to write deployment files over the cloud-service "
|
||||||
|
+ "configuration directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInside(Path directory, Path file) {
|
||||||
|
if (file == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file.toAbsolutePath().normalize().startsWith(
|
||||||
|
directory.toAbsolutePath().normalize());
|
||||||
|
}
|
||||||
|
|
||||||
private String installationConfiguration(
|
private String installationConfiguration(
|
||||||
UUID installationUuid,
|
UUID installationUuid,
|
||||||
String installationCode,
|
String installationCode,
|
||||||
@@ -174,6 +192,7 @@ public class DeploymentWriter {
|
|||||||
appendEnvironment(env, "REDIS_HOST", runtime.redisHost());
|
appendEnvironment(env, "REDIS_HOST", runtime.redisHost());
|
||||||
appendEnvironment(env, "REDIS_PORT", runtime.redisPort());
|
appendEnvironment(env, "REDIS_PORT", runtime.redisPort());
|
||||||
appendEnvironment(env, "REDIS_PASSWORD", runtime.redisPassword());
|
appendEnvironment(env, "REDIS_PASSWORD", runtime.redisPassword());
|
||||||
|
appendEnvironment(env, "REDIS_DATABASE", "1");
|
||||||
appendEnvironment(env, "REDIS_SSL", Boolean.toString(runtime.redisSsl()));
|
appendEnvironment(env, "REDIS_SSL", Boolean.toString(runtime.redisSsl()));
|
||||||
appendEnvironment(
|
appendEnvironment(
|
||||||
env, "CYGNUS_CLOUD_BASE_URL", normalizedCloudServiceUrl(runtime));
|
env, "CYGNUS_CLOUD_BASE_URL", normalizedCloudServiceUrl(runtime));
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ public class InstallationService {
|
|||||||
if (request.outputDirectory() == null) {
|
if (request.outputDirectory() == null) {
|
||||||
throw new IllegalArgumentException("Output directory is required");
|
throw new IllegalArgumentException("Output directory is required");
|
||||||
}
|
}
|
||||||
|
validateOutputIsolation(request.outputDirectory());
|
||||||
RuntimeConfiguration runtime = request.runtimeConfiguration();
|
RuntimeConfiguration runtime = request.runtimeConfiguration();
|
||||||
if (runtime == null) {
|
if (runtime == null) {
|
||||||
throw new IllegalArgumentException("Runtime configuration is required");
|
throw new IllegalArgumentException("Runtime configuration is required");
|
||||||
@@ -204,6 +205,27 @@ public class InstallationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateOutputIsolation(Path outputDirectory) {
|
||||||
|
Path deploymentConfig = outputDirectory
|
||||||
|
.toAbsolutePath()
|
||||||
|
.normalize()
|
||||||
|
.resolve("config");
|
||||||
|
if (isInside(deploymentConfig, profile.assertionEncryptionPublicKey())
|
||||||
|
|| isInside(deploymentConfig, profile.loginEncryptionPublicKey())) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Output directory would overwrite the cloud-service config folder. "
|
||||||
|
+ "Select a dedicated deployment directory, such as "
|
||||||
|
+ "'matrix-installation'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInside(Path directory, Path file) {
|
||||||
|
if (file == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file.toAbsolutePath().normalize().startsWith(directory);
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizedCloudServiceUrl(String value) {
|
private String normalizedCloudServiceUrl(String value) {
|
||||||
String normalized = value.trim();
|
String normalized = value.trim();
|
||||||
return normalized.endsWith("/")
|
return normalized.endsWith("/")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.cygnus.installer;
|
package com.cygnus.installer;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -108,6 +109,7 @@ class DeploymentWriterTest {
|
|||||||
.contains("MATRIX_IMAGE=\"registry.example.com/matrix:1.0.0\"")
|
.contains("MATRIX_IMAGE=\"registry.example.com/matrix:1.0.0\"")
|
||||||
.contains("MATRIX_DB_URL=\"jdbc:postgresql://db:5432/matrix\"")
|
.contains("MATRIX_DB_URL=\"jdbc:postgresql://db:5432/matrix\"")
|
||||||
.contains("REDIS_HOST=\"redis\"")
|
.contains("REDIS_HOST=\"redis\"")
|
||||||
|
.contains("REDIS_DATABASE=\"1\"")
|
||||||
.contains("CYGNUS_CLOUD_BASE_URL=\"http://host.docker.internal:8090\"")
|
.contains("CYGNUS_CLOUD_BASE_URL=\"http://host.docker.internal:8090\"")
|
||||||
.contains("CYGNUS_TOKEN_URL=\"http://host.docker.internal:8090/oauth2/token\"")
|
.contains("CYGNUS_TOKEN_URL=\"http://host.docker.internal:8090/oauth2/token\"")
|
||||||
.contains("CYGNUS_CLIENT_ID=\"matrix-client\"")
|
.contains("CYGNUS_CLIENT_ID=\"matrix-client\"")
|
||||||
@@ -121,4 +123,84 @@ class DeploymentWriterTest {
|
|||||||
assertThat(output.resolve("config/keys/client-signing-private.pem"))
|
assertThat(output.resolve("config/keys/client-signing-private.pem"))
|
||||||
.isRegularFile();
|
.isRegularFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void refusesToOverwriteCloudServiceConfiguration() throws Exception {
|
||||||
|
Path cloudConfig = temporaryDirectory.resolve("config");
|
||||||
|
Path cloudKeys = cloudConfig.resolve("keys");
|
||||||
|
Files.createDirectories(cloudKeys);
|
||||||
|
Path assertionPublic = cloudKeys.resolve("assertion-decryption-public.pem");
|
||||||
|
Path loginPublic = cloudKeys.resolve("login-public.pem");
|
||||||
|
Files.writeString(assertionPublic, "assertion-public-key");
|
||||||
|
Files.writeString(loginPublic, "login-public-key");
|
||||||
|
|
||||||
|
var profile = new ProductProfile(
|
||||||
|
"matrix",
|
||||||
|
"Matrix",
|
||||||
|
"matrix-onprem",
|
||||||
|
"MATRIX_IMAGE",
|
||||||
|
"matrix",
|
||||||
|
"installation.yml",
|
||||||
|
"MATRIX_INSTALLATION_CONFIG",
|
||||||
|
"/srv/matrix/config/installation.yml",
|
||||||
|
"8080:8080",
|
||||||
|
"/matrix/",
|
||||||
|
"/oauth2/token",
|
||||||
|
assertionPublic,
|
||||||
|
loginPublic,
|
||||||
|
"cygnus-login-2026-01",
|
||||||
|
List.of());
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> new DeploymentWriter().write(
|
||||||
|
temporaryDirectory,
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"primary",
|
||||||
|
"matrix-client",
|
||||||
|
new ActivationDtos.ValidationResponse(
|
||||||
|
"activation-token",
|
||||||
|
OffsetDateTime.now().plusMinutes(5),
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"matrix-client",
|
||||||
|
"FULL",
|
||||||
|
2),
|
||||||
|
new ActivationDtos.RegistrationResponse(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
UUID.randomUUID(),
|
||||||
|
UUID.randomUUID(),
|
||||||
|
null,
|
||||||
|
"primary",
|
||||||
|
1,
|
||||||
|
"ACTIVE"),
|
||||||
|
new InstallationKeyService().generate(),
|
||||||
|
"assertion",
|
||||||
|
new InstallerSettings(
|
||||||
|
"matrix",
|
||||||
|
URI.create("https://cloud.example.com"),
|
||||||
|
URI.create("https://cloud.example.com"),
|
||||||
|
"/api/v1/installations",
|
||||||
|
"production",
|
||||||
|
temporaryDirectory,
|
||||||
|
temporaryDirectory,
|
||||||
|
"1",
|
||||||
|
new IniDocument(Map.of())),
|
||||||
|
profile,
|
||||||
|
"registry.example.com/matrix:1.0.0",
|
||||||
|
Map.of(),
|
||||||
|
new RuntimeConfiguration(
|
||||||
|
"https://cloud.example.com",
|
||||||
|
"jdbc:postgresql://db/matrix",
|
||||||
|
"postgres",
|
||||||
|
"secret",
|
||||||
|
"redis",
|
||||||
|
"7901",
|
||||||
|
"secret",
|
||||||
|
false,
|
||||||
|
"PT10S",
|
||||||
|
"PT30S")))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessageContaining("cloud-service configuration");
|
||||||
|
|
||||||
|
assertThat(assertionPublic).hasContent("assertion-public-key");
|
||||||
|
assertThat(loginPublic).hasContent("login-public-key");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
spring:
|
|
||||||
main:
|
|
||||||
banner-mode: "off"
|
|
||||||
application:
|
|
||||||
name: technobee-service-installer
|
|
||||||
technobee:
|
|
||||||
installer:
|
|
||||||
config: ${TECHNOBEE_INSTALLER_CONFIG:./installer.ini}
|
|
||||||
version: ${TECHNOBEE_INSTALLER_VERSION:1.0.0}
|
|
||||||
logging:
|
|
||||||
level:
|
|
||||||
root: WARN
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
|||||||
artifactId=technobee-service-installer
|
|
||||||
groupId=com.cygnus
|
|
||||||
version=1.0.0-SNAPSHOT
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
com/cygnus/installer/MachineAssertionService.class
|
|
||||||
com/cygnus/installer/ActivationDtos$ValidationResponse.class
|
|
||||||
com/cygnus/installer/SwingInstallationWizard$2.class
|
|
||||||
com/cygnus/installer/CommandResult.class
|
|
||||||
com/cygnus/installer/InstallationProgressListener.class
|
|
||||||
com/cygnus/installer/ProductProfileRegistry.class
|
|
||||||
com/cygnus/installer/InstallationRequest.class
|
|
||||||
com/cygnus/installer/InstallerConfiguration.class
|
|
||||||
com/cygnus/installer/ProductProfile.class
|
|
||||||
com/cygnus/installer/InstallationWizard.class
|
|
||||||
com/cygnus/installer/TechnobeeServiceInstallerApplication.class
|
|
||||||
com/cygnus/installer/DeploymentWriter.class
|
|
||||||
com/cygnus/installer/InstallationKeyService.class
|
|
||||||
com/cygnus/installer/DockerComposeDeploymentService.class
|
|
||||||
com/cygnus/installer/ActivationClient.class
|
|
||||||
com/cygnus/installer/ActivationDtos$RegistrationResponse.class
|
|
||||||
com/cygnus/installer/IniDocument.class
|
|
||||||
com/cygnus/installer/SwingInstallationWizard$3.class
|
|
||||||
com/cygnus/installer/InstallerConfigurationException.class
|
|
||||||
com/cygnus/installer/DockerRegistryLoginService.class
|
|
||||||
com/cygnus/installer/SwingInstallationWizard.class
|
|
||||||
com/cygnus/installer/ActivationDtos.class
|
|
||||||
com/cygnus/installer/DockerPrerequisiteChecker.class
|
|
||||||
com/cygnus/installer/InstallerBootstrapProperties.class
|
|
||||||
com/cygnus/installer/SwingInstallationWizard$1.class
|
|
||||||
com/cygnus/installer/CommandExecutor.class
|
|
||||||
com/cygnus/installer/IniConfigurationLoader.class
|
|
||||||
com/cygnus/installer/InstallationService.class
|
|
||||||
com/cygnus/installer/InstallationResult.class
|
|
||||||
com/cygnus/installer/RuntimeConfiguration.class
|
|
||||||
com/cygnus/installer/ActivationDtos$RegistrationRequest.class
|
|
||||||
com/cygnus/installer/ActivationDtos$ValidationRequest.class
|
|
||||||
com/cygnus/installer/PrerequisiteException.class
|
|
||||||
com/cygnus/installer/InstallerSettings.class
|
|
||||||
com/cygnus/installer/InstallationKeyPair.class
|
|
||||||
com/cygnus/installer/SwingInstallationWizard$ProgressUpdate.class
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/ActivationClient.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/ActivationDtos.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/CommandExecutor.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/CommandResult.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/DeploymentWriter.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/DockerComposeDeploymentService.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/DockerPrerequisiteChecker.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/DockerRegistryLoginService.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/IniConfigurationLoader.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/IniDocument.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationKeyPair.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationKeyService.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationProgressListener.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationRequest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationResult.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationService.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallationWizard.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallerBootstrapProperties.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallerConfiguration.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallerConfigurationException.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/InstallerSettings.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/MachineAssertionService.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/PrerequisiteException.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/ProductProfile.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/ProductProfileRegistry.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/RuntimeConfiguration.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/SwingInstallationWizard.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/main/java/com/cygnus/installer/TechnobeeServiceInstallerApplication.java
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
com/cygnus/installer/MachineAssertionServiceTest.class
|
|
||||||
com/cygnus/installer/DockerRegistryLoginServiceTest$CapturingExecutor.class
|
|
||||||
com/cygnus/installer/DockerRegistryLoginServiceTest.class
|
|
||||||
com/cygnus/installer/DockerComposeDeploymentServiceTest$CapturingExecutor.class
|
|
||||||
com/cygnus/installer/InstallationServiceTest.class
|
|
||||||
com/cygnus/installer/DockerPrerequisiteCheckerTest$1.class
|
|
||||||
com/cygnus/installer/ProductProfileRegistryTest.class
|
|
||||||
com/cygnus/installer/IniConfigurationLoaderTest.class
|
|
||||||
com/cygnus/installer/DockerComposeDeploymentServiceTest.class
|
|
||||||
com/cygnus/installer/DeploymentWriterTest.class
|
|
||||||
com/cygnus/installer/DockerPrerequisiteCheckerTest.class
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/DeploymentWriterTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/DockerComposeDeploymentServiceTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/DockerPrerequisiteCheckerTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/DockerRegistryLoginServiceTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/IniConfigurationLoaderTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/InstallationServiceTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/MachineAssertionServiceTest.java
|
|
||||||
/Users/maddy/Projects/cygnus/matrix/cygnus-installer/src/test/java/com/cygnus/installer/ProductProfileRegistryTest.java
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.DeploymentWriterTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.174 s -- in com.cygnus.installer.DeploymentWriterTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.DockerComposeDeploymentServiceTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in com.cygnus.installer.DockerComposeDeploymentServiceTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.DockerPrerequisiteCheckerTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 s -- in com.cygnus.installer.DockerPrerequisiteCheckerTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.DockerRegistryLoginServiceTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.010 s -- in com.cygnus.installer.DockerRegistryLoginServiceTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.IniConfigurationLoaderTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 s -- in com.cygnus.installer.IniConfigurationLoaderTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.InstallationServiceTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in com.cygnus.installer.InstallationServiceTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.MachineAssertionServiceTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.570 s -- in com.cygnus.installer.MachineAssertionServiceTest
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-------------------------------------------------------------------------------
|
|
||||||
Test set: com.cygnus.installer.ProductProfileRegistryTest
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in com.cygnus.installer.ProductProfileRegistryTest
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
47
cygnus-lib/pom.xml
Normal file
47
cygnus-lib/pom.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.cygnus</groupId>
|
||||||
|
<artifactId>cygnus-parent</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>cygnus-lib</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>Cygnus Shared Library</name>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
101
cygnus-lib/src/main/java/lib/constants/ApplicationMessage.java
Normal file
101
cygnus-lib/src/main/java/lib/constants/ApplicationMessage.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package lib.constants;
|
||||||
|
|
||||||
|
/** Canonical user-facing messages shared by Cygnus applications. */
|
||||||
|
public enum ApplicationMessage {
|
||||||
|
BAD_REQUEST(400, "HTTP-400", "Invalid request",
|
||||||
|
"Cygnus could not process the submitted request.",
|
||||||
|
"Review the supplied information and try again."),
|
||||||
|
SESSION_REQUIRED(401, "AUTH-401", "Sign in required",
|
||||||
|
"Your session is missing or has expired.",
|
||||||
|
"Sign in again to continue securely."),
|
||||||
|
ACCESS_DENIED(403, "AUTH-403", "Access denied",
|
||||||
|
"You do not have permission to open this page.",
|
||||||
|
"Contact your administrator if this feature should be available to you."),
|
||||||
|
PAGE_NOT_FOUND(404, "HTTP-404", "Page not found",
|
||||||
|
"The requested page could not be found.",
|
||||||
|
"Check the address or return to the application dashboard."),
|
||||||
|
METHOD_NOT_ALLOWED(405, "HTTP-405", "Action not allowed",
|
||||||
|
"This page does not support the requested action.",
|
||||||
|
"Return to the previous page and try an available action."),
|
||||||
|
CONFLICT(409, "HTTP-409", "Request conflict",
|
||||||
|
"The request conflicts with the current state of the data.",
|
||||||
|
"Refresh the page and try again."),
|
||||||
|
PAYLOAD_TOO_LARGE(413, "HTTP-413", "File is too large",
|
||||||
|
"The submitted content exceeds the permitted size.",
|
||||||
|
"Reduce the file size and try again."),
|
||||||
|
TOO_MANY_REQUESTS(429, "HTTP-429", "Too many requests",
|
||||||
|
"Cygnus has received too many requests in a short period.",
|
||||||
|
"Wait briefly before trying again."),
|
||||||
|
PAYLOAD_INVALID(400, "REQ-4001", "Invalid request details",
|
||||||
|
"The encrypted request could not be verified.",
|
||||||
|
"Refresh the page and submit the form again."),
|
||||||
|
APPLICATION_VALIDATION_FAILED(422, "APP-4221", "Application validation failed",
|
||||||
|
"One or more application fields contain invalid information.",
|
||||||
|
"Correct the highlighted fields and submit the application again."),
|
||||||
|
APPLICATION_ACCESS_DENIED(403, "APP-4031", "Application access denied",
|
||||||
|
"The selected portfolio or application is not available to your account.",
|
||||||
|
"Select an authorized portfolio or contact your administrator."),
|
||||||
|
APPLICATION_SAVE_CONFLICT(409, "APP-4091", "Application save conflict",
|
||||||
|
"This request was already processed or the application changed.",
|
||||||
|
"Refresh the application before submitting it again."),
|
||||||
|
APPLICATION_SAVED(200, "APP-2001", "Application saved",
|
||||||
|
"The application details were saved successfully.", ""),
|
||||||
|
APPLICATIONS_LOADED(200, "APP-2002", "Applications loaded",
|
||||||
|
"The application records were loaded successfully.", ""),
|
||||||
|
APPLICATION_LOAD_FAILED(500, "APP-5002", "Applications could not be loaded",
|
||||||
|
"Cygnus could not load the application details.",
|
||||||
|
"Try again. If the issue continues, share the reference ID with support."),
|
||||||
|
APPLICATION_SAVE_FAILED(500, "APP-5001", "Application could not be saved",
|
||||||
|
"Cygnus could not save the application details.",
|
||||||
|
"Try again. If the issue continues, share the reference ID with support."),
|
||||||
|
INTERNAL_SERVER_ERROR(500, "HTTP-500", "Something went wrong",
|
||||||
|
"Cygnus could not complete your request.",
|
||||||
|
"Try again. If the issue continues, share the reference ID with support."),
|
||||||
|
BAD_GATEWAY(502, "HTTP-502", "Service response unavailable",
|
||||||
|
"A required service returned an invalid response.",
|
||||||
|
"Try again shortly. If the issue continues, contact support."),
|
||||||
|
SERVICE_UNAVAILABLE(503, "HTTP-503", "Service temporarily unavailable",
|
||||||
|
"A required Cygnus service is currently unavailable.",
|
||||||
|
"Wait briefly and try again."),
|
||||||
|
GATEWAY_TIMEOUT(504, "HTTP-504", "Service response timed out",
|
||||||
|
"A required service took too long to respond.",
|
||||||
|
"Try the request again shortly.");
|
||||||
|
|
||||||
|
private final int httpStatus;
|
||||||
|
private final String code;
|
||||||
|
private final String title;
|
||||||
|
private final String message;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
ApplicationMessage(
|
||||||
|
int httpStatus, String code, String title, String message, String description) {
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.code = code;
|
||||||
|
this.title = title;
|
||||||
|
this.message = message;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationMessage fromHttpStatus(int status) {
|
||||||
|
return switch (status) {
|
||||||
|
case 400 -> BAD_REQUEST;
|
||||||
|
case 401 -> SESSION_REQUIRED;
|
||||||
|
case 403 -> ACCESS_DENIED;
|
||||||
|
case 404 -> PAGE_NOT_FOUND;
|
||||||
|
case 405 -> METHOD_NOT_ALLOWED;
|
||||||
|
case 409 -> CONFLICT;
|
||||||
|
case 413 -> PAYLOAD_TOO_LARGE;
|
||||||
|
case 429 -> TOO_MANY_REQUESTS;
|
||||||
|
case 502 -> BAD_GATEWAY;
|
||||||
|
case 503 -> SERVICE_UNAVAILABLE;
|
||||||
|
case 504 -> GATEWAY_TIMEOUT;
|
||||||
|
default -> INTERNAL_SERVER_ERROR;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHttpStatus() { return httpStatus; }
|
||||||
|
public String getCode() { return code; }
|
||||||
|
public String getTitle() { return title; }
|
||||||
|
public String getMessage() { return message; }
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package lib.exceptions;
|
||||||
|
|
||||||
|
import lib.constants.ApplicationMessage;
|
||||||
|
|
||||||
|
/** Single typed application exception carrying a safe user-facing message. */
|
||||||
|
public class ApplicationException extends RuntimeException {
|
||||||
|
private final ApplicationMessage applicationMessage;
|
||||||
|
|
||||||
|
public ApplicationException(ApplicationMessage message, String technicalMessage) {
|
||||||
|
super(technicalMessage);
|
||||||
|
this.applicationMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationException(ApplicationMessage message, String technicalMessage, Throwable cause) {
|
||||||
|
super(technicalMessage, cause);
|
||||||
|
this.applicationMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationMessage getApplicationMessage() { return applicationMessage; }
|
||||||
|
}
|
||||||
12
cygnus-lib/src/main/java/lib/models/ApiResponse.java
Normal file
12
cygnus-lib/src/main/java/lib/models/ApiResponse.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
/** Consistent API response contract for every migrated workflow. */
|
||||||
|
public record ApiResponse<T>(boolean success, T data, MessageDetails message) {
|
||||||
|
public static <T> ApiResponse<T> success(T data, MessageDetails message) {
|
||||||
|
return new ApiResponse<>(true, data, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ApiResponse<T> failure(MessageDetails message) {
|
||||||
|
return new ApiResponse<>(false, null, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
87
cygnus-lib/src/main/java/lib/models/ApplicationDetails.java
Normal file
87
cygnus-lib/src/main/java/lib/models/ApplicationDetails.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/** User-editable case initiation data. Identity and audit fields come from UserSession. */
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ApplicationDetails implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Integer applicationId;
|
||||||
|
private Short portfolioId;
|
||||||
|
private Short bankBranchId;
|
||||||
|
private String applicationNumber;
|
||||||
|
private String bankCode;
|
||||||
|
private String product;
|
||||||
|
private String loanAmount;
|
||||||
|
private String customerName;
|
||||||
|
private String fatherName;
|
||||||
|
private String applicationType;
|
||||||
|
private String category;
|
||||||
|
private String dateOfBirth;
|
||||||
|
private String contactPerson;
|
||||||
|
private String mobileNumber;
|
||||||
|
private String specialInstruction;
|
||||||
|
|
||||||
|
private Boolean residenceVerification;
|
||||||
|
private Boolean residenceTelephoneVerification;
|
||||||
|
private Boolean officeVerification;
|
||||||
|
private Boolean officeTelephoneVerification;
|
||||||
|
private Boolean propertyVerification;
|
||||||
|
private Boolean referenceVerification;
|
||||||
|
private Boolean documentVerification;
|
||||||
|
private Boolean residenceCoApplicant;
|
||||||
|
private Boolean residenceCoProprietor;
|
||||||
|
private Boolean officeCoProprietor;
|
||||||
|
private Boolean sameResidenceAddress;
|
||||||
|
private Boolean sameOfficeAddress;
|
||||||
|
private Boolean samePropertyAddress;
|
||||||
|
|
||||||
|
private String residenceAddress1;
|
||||||
|
private String residenceAddress2;
|
||||||
|
private String residenceAddress3;
|
||||||
|
private String residenceLandmark;
|
||||||
|
private Integer residenceColonyId;
|
||||||
|
private String residenceCity;
|
||||||
|
private String residencePincode;
|
||||||
|
private String residencePhone;
|
||||||
|
|
||||||
|
private String companyName;
|
||||||
|
private String officeAddress1;
|
||||||
|
private String officeAddress2;
|
||||||
|
private String officeAddress3;
|
||||||
|
private String officeLandmark;
|
||||||
|
private Integer officeColonyId;
|
||||||
|
private String officeCity;
|
||||||
|
private String officePincode;
|
||||||
|
private String department;
|
||||||
|
private String designation;
|
||||||
|
private String officePhone;
|
||||||
|
private String extension;
|
||||||
|
|
||||||
|
private String propertyAddress1;
|
||||||
|
private String propertyAddress2;
|
||||||
|
private String propertyAddress3;
|
||||||
|
private String propertyLandmark;
|
||||||
|
private Integer propertyColonyId;
|
||||||
|
private String propertyCity;
|
||||||
|
private String propertyPincode;
|
||||||
|
|
||||||
|
private String referenceName1;
|
||||||
|
private String referenceAddress1;
|
||||||
|
private String referenceContactNumber1;
|
||||||
|
private String referenceName2;
|
||||||
|
private String referenceAddress2;
|
||||||
|
private String referenceContactNumber2;
|
||||||
|
|
||||||
|
private Map<String, String> dynamicFields = new LinkedHashMap<>();
|
||||||
|
private Boolean autoCutOff;
|
||||||
|
private Integer formMode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
/** Data returned after saving a loan-verification application. */
|
||||||
|
public record ApplicationSaveData(String operation, Integer applicationId, String mvCode) {}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/** Result row returned by the application-save database operation. */
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ApplicationSaveResult {
|
||||||
|
private String operation;
|
||||||
|
private Integer applicationId;
|
||||||
|
private String mvCode;
|
||||||
|
private String internalUuid;
|
||||||
|
}
|
||||||
23
cygnus-lib/src/main/java/lib/models/CasePunching.java
Normal file
23
cygnus-lib/src/main/java/lib/models/CasePunching.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CasePunching {
|
||||||
|
private MessageDetails messageDetails;
|
||||||
|
private Map<String, String> optionsl;
|
||||||
|
private Map<String, List<Option>> options;
|
||||||
|
private List<String> visibleSections;
|
||||||
|
private Short portfolioId;
|
||||||
|
private Integer formMode;
|
||||||
|
private String userId;
|
||||||
|
private String dynamicFields;
|
||||||
|
private String dynamicHtml;
|
||||||
|
private String verificationCaseId;
|
||||||
|
private String documentCaseId;
|
||||||
|
}
|
||||||
11
cygnus-lib/src/main/java/lib/models/CaseSaveRequest.java
Normal file
11
cygnus-lib/src/main/java/lib/models/CaseSaveRequest.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
/** Hybrid RSA-OAEP/AES-GCM envelope submitted by the initiation page. */
|
||||||
|
public record CaseSaveRequest(
|
||||||
|
String keyId,
|
||||||
|
String encryptedKey,
|
||||||
|
String initializationVector,
|
||||||
|
String encryptedPayload,
|
||||||
|
String requestId,
|
||||||
|
String timestamp) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Existing application details used by the case-initiation duplicate checks. */
|
||||||
|
public record DuplicateDetailsData(List<Map<String, Object>> records) {
|
||||||
|
public DuplicateDetailsData {
|
||||||
|
records = records == null ? List.of() : records.stream()
|
||||||
|
.map(record -> Collections.unmodifiableMap(new LinkedHashMap<>(record)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
cygnus-lib/src/main/java/lib/models/EditCaseSummary.java
Normal file
19
cygnus-lib/src/main/java/lib/models/EditCaseSummary.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class EditCaseSummary {
|
||||||
|
private String documentCaseId;
|
||||||
|
private String mvCode;
|
||||||
|
private String fileNumber;
|
||||||
|
private String customerName;
|
||||||
|
private String applicationType;
|
||||||
|
private String product;
|
||||||
|
private String receivedOn;
|
||||||
|
private String punchedBy;
|
||||||
|
private String branchCode;
|
||||||
|
private Short portfolioId;
|
||||||
|
}
|
||||||
10
cygnus-lib/src/main/java/lib/models/LocalityData.java
Normal file
10
cygnus-lib/src/main/java/lib/models/LocalityData.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Localities returned to the case-initiation autocomplete. */
|
||||||
|
public record LocalityData(List<LocalityOption> localities) {
|
||||||
|
public LocalityData {
|
||||||
|
localities = localities == null ? List.of() : List.copyOf(localities);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
cygnus-lib/src/main/java/lib/models/LocalityOption.java
Normal file
4
cygnus-lib/src/main/java/lib/models/LocalityOption.java
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
/** One company-authorized locality autocomplete result. */
|
||||||
|
public record LocalityOption(Integer id, String location, String pincode, String city) {}
|
||||||
58
cygnus-lib/src/main/java/lib/models/MessageDetails.java
Normal file
58
cygnus-lib/src/main/java/lib/models/MessageDetails.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import lib.constants.ApplicationMessage;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/** Dependency-free error information shared across Cygnus modules. */
|
||||||
|
@Getter
|
||||||
|
public final class MessageDetails implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final int httpStatus;
|
||||||
|
private final String code;
|
||||||
|
private final String title;
|
||||||
|
private final String message;
|
||||||
|
private final String description;
|
||||||
|
private final String referenceId;
|
||||||
|
private final Instant timestamp;
|
||||||
|
|
||||||
|
public MessageDetails(ApplicationMessage message) {
|
||||||
|
this(message.getHttpStatus(), message.getCode(), message.getTitle(),
|
||||||
|
message.getMessage(), message.getDescription(),
|
||||||
|
UUID.randomUUID().toString(), Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageDetails(
|
||||||
|
int httpStatus,
|
||||||
|
String code,
|
||||||
|
String title,
|
||||||
|
String message,
|
||||||
|
String description,
|
||||||
|
String referenceId,
|
||||||
|
Instant timestamp) {
|
||||||
|
if (httpStatus < 100 || httpStatus > 599) {
|
||||||
|
throw new IllegalArgumentException("HTTP status must be between 100 and 599");
|
||||||
|
}
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.code = required(code, "code");
|
||||||
|
this.title = required(title, "title");
|
||||||
|
this.message = required(message, "message");
|
||||||
|
this.description = description == null ? "" : description.trim();
|
||||||
|
this.referenceId = required(referenceId, "referenceId");
|
||||||
|
this.timestamp = Objects.requireNonNull(timestamp, "timestamp");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String required(String value, String name) {
|
||||||
|
if (value == null || value.isBlank()) {
|
||||||
|
throw new IllegalArgumentException(name + " is required");
|
||||||
|
}
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
cygnus-lib/src/main/java/lib/models/Option.java
Normal file
13
cygnus-lib/src/main/java/lib/models/Option.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Option {
|
||||||
|
private Object value;
|
||||||
|
private String label;
|
||||||
|
private String description;
|
||||||
|
private String group;
|
||||||
|
}
|
||||||
15
cygnus-lib/src/main/java/lib/models/PunchedRecordsData.java
Normal file
15
cygnus-lib/src/main/java/lib/models/PunchedRecordsData.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package lib.models;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/** Form-shaped punched applications returned to the case-initiation workspace. */
|
||||||
|
public record PunchedRecordsData(List<Map<String, Object>> records) {
|
||||||
|
public PunchedRecordsData {
|
||||||
|
records = records == null ? List.of() : records.stream()
|
||||||
|
.map(record -> Collections.unmodifiableMap(new LinkedHashMap<>(record)))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user