Skip to content

Commit 9360076

Browse files
authored
v3_CASL-1204_naksha-cli-auto-create-flag (#482)
* feat: add --autoCreateTarget flag Signed-off-by: Bartosz Czyż <[email protected]> * feat: add logger Signed-off-by: Bartosz Czyż <[email protected]> --------- Signed-off-by: Bartosz Czyż <[email protected]>
1 parent 46be279 commit 9360076

File tree

13 files changed

+887
-82
lines changed

13 files changed

+887
-82
lines changed

here-naksha-cli/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ description = gatherDescription()
99
val mainCliClass = "com.here.naksha.cli.Main"
1010
val fatJarBaseName = "naksha-cli"
1111

12+
configurations.all {
13+
exclude(group = "org.slf4j", module = "slf4j-simple")
14+
}
15+
1216
kotlin {
1317
jvmToolchain(23)
1418

@@ -22,6 +26,7 @@ kotlin {
2226
jvmMain {
2327
dependencies {
2428
implementation(libs.picocli)
29+
implementation(libs.bundles.logging)
2530
implementation(project(":here-naksha-lib-base"))
2631
implementation(project(":here-naksha-lib-model"))
2732
implementation(project(":here-naksha-lib-psql"))

here-naksha-cli/src/jvmMain/java/com/here/naksha/cli/Main.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.here.naksha.cli;
22

3+
import com.here.naksha.cli.loggers.LoggingMixin;
34
import picocli.CommandLine;
45

56
final class Main {
67
public static void main(String[] args) {
78
CommandLine cmd = new CommandLine(new NakshaCliCommand(), new CommandFactory())
9+
.setExecutionStrategy(LoggingMixin::executionStrategy)
810
.setParameterExceptionHandler(new ShortErrorMessageHandler())
911
.setExecutionExceptionHandler(new PrintExceptionMessageHandler());
1012
int exitCode = cmd.execute(args);

here-naksha-cli/src/jvmMain/java/com/here/naksha/cli/NakshaCliCommand.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.here.naksha.cli;
22

33
import com.here.naksha.cli.copy.CopyCommand;
4+
import com.here.naksha.cli.loggers.LoggingMixin;
45
import picocli.CommandLine;
56

67
@CommandLine.Command(
@@ -10,5 +11,7 @@
1011
CopyCommand.class
1112
}
1213
)
13-
final class NakshaCliCommand {
14+
public final class NakshaCliCommand {
15+
@CommandLine.Mixin
16+
private LoggingMixin loggingMixin;
1417
}

here-naksha-cli/src/jvmMain/java/com/here/naksha/cli/copy/CopyCommand.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.here.naksha.cli.copy;
22

33
import com.here.naksha.cli.copy.service.*;
4+
import com.here.naksha.cli.loggers.LoggingMixin;
45
import com.here.naksha.cli.parsers.JsonFileParser;
56
import com.here.naksha.cli.parsers.JsonFileParserException;
67
import com.here.naksha.cli.results.CommandFailure;
@@ -76,6 +77,15 @@ public final class CopyCommand implements Callable<Integer> {
7677
)
7778
private @Nullable String targetCollectionId;
7879

80+
@CommandLine.Option(
81+
names = {"--autoCreateTarget"},
82+
description = "Auto create target's map and collection."
83+
)
84+
private boolean autoCreateTarget = false;
85+
86+
@CommandLine.Mixin
87+
private LoggingMixin loggingMixin;
88+
7989
public CopyCommand(
8090
@NotNull CopyServiceFactory copyServiceFactory,
8191
@NotNull StorageProvider storageProvider
@@ -92,13 +102,11 @@ public Integer call() throws JsonFileParserException, CopyServiceException {
92102

93103
NakshaContext.currentContext().withAppId("nakshacli");
94104
SessionOptions sessionOptions = SessionOptions.from(NakshaContext.currentContext());
95-
96105
CommandResult<CopyServiceSuccessResultPayload, CopyServiceException> copyResult = copy(
97106
srcCopyElement,
98107
targetCopyElement,
99108
sessionOptions
100109
);
101-
102110
CopyServiceSuccessResultPayload resultPayload = requireSuccessResultAndGetPayload(copyResult);
103111

104112
PrintWriter commandLineOut = getCommandLineOut();
@@ -162,7 +170,8 @@ private CommandResult<CopyServiceSuccessResultPayload, CopyServiceException> cop
162170

163171
return copyService.copy(
164172
srcCopyElement,
165-
targetCopyElement
173+
targetCopyElement,
174+
autoCreateTarget
166175
);
167176
}
168177

here-naksha-cli/src/jvmMain/java/com/here/naksha/cli/copy/service/CopyService.java

Lines changed: 130 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@
55
import com.here.naksha.cli.results.CommandSuccess;
66
import naksha.base.StringList;
77
import naksha.model.IStorage;
8+
import naksha.model.NakshaError;
89
import naksha.model.NakshaException;
910
import naksha.model.SessionOptions;
11+
import naksha.model.objects.NakshaCollection;
1012
import naksha.model.objects.NakshaFeature;
13+
import naksha.model.objects.NakshaMap;
1114
import naksha.model.request.*;
1215
import org.jetbrains.annotations.NotNull;
16+
import org.slf4j.Logger;
17+
import org.slf4j.LoggerFactory;
1318

1419
import java.util.List;
1520

1621
import static naksha.model.util.RequestHelper.createFeaturesRequest;
1722
import static naksha.model.util.ResultHelper.extractResponseItems;
1823

1924
public final class CopyService {
25+
private static final Logger logger = LoggerFactory.getLogger(CopyService.class);
2026
private final SessionOptions sessionOptions;
2127
private final StorageProvider storageProvider;
2228

@@ -31,11 +37,16 @@ public CopyService(
3137
@NotNull
3238
public CommandResult<CopyServiceSuccessResultPayload, CopyServiceException> copy(
3339
@NotNull CopyElement src,
34-
@NotNull CopyElement target
40+
@NotNull CopyElement target,
41+
boolean autoCreateTarget
3542
) {
3643
try {
44+
IStorage targetStorage = useTargetStorage(target);
45+
if (autoCreateTarget) {
46+
createTarget(targetStorage, target);
47+
}
3748
List<NakshaFeature> features = readFeaturesFromSrc(src);
38-
SuccessResponse _ = writeFeaturesToTarget(features, target);
49+
writeFeaturesToTarget(features, target, targetStorage);
3950
return new CommandSuccess<>(buildSuccessResultPayload(features));
4051
} catch (CopyServiceException exception) {
4152
return new CommandFailure<>(exception);
@@ -48,36 +59,117 @@ private List<NakshaFeature> readFeaturesFromSrc(
4859
IStorage storage = useSrcStorage(source);
4960
ReadFeatures readFeatures = createReadFeaturesRequest(source);
5061
Response response = performReadRequest(storage, readFeatures);
51-
52-
return switch (response) {
53-
case SuccessResponse successResponse -> extractResponseItems(successResponse, NakshaFeature.class);
54-
case ErrorResponse errorResponse -> throw new CopyServiceException(
55-
"Problem with reading from source!",
56-
new NakshaException(errorResponse.getError())
57-
);
58-
default -> throw new CopyServiceException("Unexpected response from source!");
59-
};
62+
SuccessResponse successResponse = requireSourceSuccessResponse(response);
63+
return extractResponseItems(successResponse, NakshaFeature.class);
6064
}
6165

62-
private SuccessResponse writeFeaturesToTarget(
66+
private void writeFeaturesToTarget(
6367
List<NakshaFeature> features,
64-
CopyElement target
68+
CopyElement target,
69+
IStorage storage
70+
) throws CopyServiceException {
71+
Response response = performCreateFeaturesRequest(storage, target, features);
72+
requireTargetSuccessResponse(response);
73+
}
74+
75+
private Response performCreateFeaturesRequest(
76+
IStorage storage, CopyElement target, List<NakshaFeature> features
6577
) throws CopyServiceException {
66-
IStorage storage = useTargetStorage(target);
67-
WriteRequest writeRequest = createFeaturesRequest(
68-
target.getMapId(),
69-
target.getCollectionId(),
70-
features
78+
WriteRequest addFeaturesRequest = createFeaturesRequest(target.getMapId(), target.getCollectionId(), features);
79+
return performWriteRequest(storage, addFeaturesRequest);
80+
}
81+
82+
private void createTarget(IStorage storage, CopyElement target) throws CopyServiceException {
83+
if (target.getMapId() == null) {
84+
throw new CopyServiceException("Target's mapId should not be null!");
85+
}
86+
if (target.getCollectionId() == null) {
87+
throw new CopyServiceException("Target's collectionId should not be null!");
88+
}
89+
createMapIfAbsent(storage, target.getMapId());
90+
createCollectionIfAbsent(storage, target);
91+
}
92+
93+
private void createMapIfAbsent(IStorage storage, String mapId) throws CopyServiceException {
94+
Response response = performCreateMapRequest(storage, mapId);
95+
switch (response) {
96+
case SuccessResponse _ -> logger.info(
97+
"Map(id: \"{}\") was successfully created on storage(id: \"{}\")!", mapId, storage.getId()
98+
);
99+
case ErrorResponse errorResponse -> {
100+
NakshaError nakshaError = errorResponse.getError();
101+
if (!nakshaError.getCode().equals(NakshaError.MAP_EXISTS)) {
102+
throw new CopyServiceException("Problem with creating map!", new NakshaException(nakshaError));
103+
}
104+
logger.info("Map(id: \"{}\") is already present on storage(id: \"{}\")!", mapId, storage.getId());
105+
}
106+
default -> throw new CopyServiceException("Unexpected response while creating map!");
107+
}
108+
}
109+
110+
private void createCollectionIfAbsent(IStorage storage, CopyElement target) throws CopyServiceException {
111+
Response response = performCreateCollectionRequest(storage, target);
112+
switch (response) {
113+
case SuccessResponse _ -> logger.info(
114+
"Collection(id: \"{}\") was successfully created in map(id: \"{}\") on storage(id: \"{}\")!",
115+
target.getCollectionId(),
116+
target.getMapId(),
117+
storage.getId()
118+
);
119+
case ErrorResponse errorResponse -> {
120+
NakshaError nakshaError = errorResponse.getError();
121+
if (!nakshaError.getCode().equals(NakshaError.COLLECTION_EXISTS)) {
122+
throw new CopyServiceException("Problem with creating collection!", new NakshaException(nakshaError));
123+
}
124+
logger.info(
125+
"Collection(id: \"{}\") is already present in map(id: \"{}\") on storage(id: \"{}\")!",
126+
target.getCollectionId(),
127+
target.getMapId(),
128+
storage.getId()
129+
);
130+
}
131+
default -> throw new CopyServiceException("Unexpected response while creating collection!");
132+
}
133+
}
134+
135+
private Response performCreateMapRequest(IStorage storage, String mapId) throws CopyServiceException {
136+
WriteRequest createMapRequest = buildCreateMapRequest(mapId);
137+
return performWriteRequest(storage, createMapRequest);
138+
}
139+
140+
private Response performCreateCollectionRequest(IStorage storage, CopyElement target) throws CopyServiceException {
141+
WriteRequest createCollectionRequest = buildCreateCollectionRequest(target);
142+
return performWriteRequest(storage, createCollectionRequest);
143+
}
144+
145+
private SuccessResponse requireTargetSuccessResponse(Response response) throws CopyServiceException {
146+
return requireSuccessResponse(
147+
response,
148+
"Problem with writing to target!",
149+
"Unexpected response from target!"
71150
);
72-
Response response = performWriteRequest(storage, writeRequest);
151+
}
73152

153+
private SuccessResponse requireSourceSuccessResponse(Response response) throws CopyServiceException {
154+
return requireSuccessResponse(
155+
response,
156+
"Problem with reading from source!",
157+
"Unexpected response from source!"
158+
);
159+
}
160+
161+
private SuccessResponse requireSuccessResponse(
162+
Response response,
163+
String errorResponseExceptionMessage,
164+
String unexpectedResponseExceptionMessage
165+
) throws CopyServiceException {
74166
return switch (response) {
75167
case SuccessResponse successResponse -> successResponse;
76168
case ErrorResponse errorResponse -> throw new CopyServiceException(
77-
"Problem with writing to target!",
169+
errorResponseExceptionMessage,
78170
new NakshaException(errorResponse.getError())
79171
);
80-
default -> throw new CopyServiceException("Unexpected response from target!");
172+
default -> throw new CopyServiceException(unexpectedResponseExceptionMessage);
81173
};
82174
}
83175

@@ -139,4 +231,21 @@ private Response performReadRequest(IStorage storage, Request request) throws Co
139231
throw new CopyServiceException("Problem while reading features from source!", e);
140232
}
141233
}
234+
235+
private WriteRequest buildCreateMapRequest(String mapId) {
236+
WriteRequest writeRequest = new WriteRequest();
237+
NakshaMap map = new NakshaMap(mapId);
238+
Write write = new Write().createMap(map);
239+
writeRequest.add(write);
240+
return writeRequest;
241+
}
242+
243+
private WriteRequest buildCreateCollectionRequest(CopyElement target) {
244+
WriteRequest writeRequest = new WriteRequest();
245+
NakshaCollection collection = new NakshaCollection(target.getCollectionId())
246+
.withMapId(target.getMapId());
247+
Write write = new Write().createCollection(collection);
248+
writeRequest.add(write);
249+
return writeRequest;
250+
}
142251
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.here.naksha.cli.loggers;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
import org.slf4j.event.Level;
5+
6+
interface CommandLoggersConfigurator {
7+
void configureLoggers(@NotNull Level logLevel);
8+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.here.naksha.cli.loggers;
2+
3+
import org.apache.logging.log4j.core.Appender;
4+
import org.apache.logging.log4j.core.LoggerContext;
5+
import org.apache.logging.log4j.core.appender.ConsoleAppender;
6+
import org.apache.logging.log4j.core.config.LoggerConfig;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.slf4j.event.Level;
9+
10+
final class Log4jLoggersConfigurator implements CommandLoggersConfigurator {
11+
@Override
12+
public void configureLoggers(@NotNull Level logLevel) {
13+
org.apache.logging.log4j.Level mappedLevel = mapLogLevel(logLevel);
14+
LoggerContext loggerContext = LoggerContext.getContext(false);
15+
LoggerConfig config = loggerContext.getConfiguration().getRootLogger();
16+
for (Appender appender : config.getAppenders().values()) {
17+
if (appender instanceof ConsoleAppender) {
18+
config.removeAppender(appender.getName());
19+
config.addAppender(appender, mappedLevel, null);
20+
}
21+
}
22+
if (config.getLevel().isMoreSpecificThan(mappedLevel)) {
23+
config.setLevel(mappedLevel);
24+
}
25+
loggerContext.updateLoggers();
26+
}
27+
28+
29+
private org.apache.logging.log4j.Level mapLogLevel(Level level) {
30+
return switch (level) {
31+
case ERROR -> org.apache.logging.log4j.Level.ERROR;
32+
case WARN -> org.apache.logging.log4j.Level.WARN;
33+
case INFO -> org.apache.logging.log4j.Level.INFO;
34+
case DEBUG -> org.apache.logging.log4j.Level.DEBUG;
35+
case TRACE -> org.apache.logging.log4j.Level.TRACE;
36+
};
37+
}
38+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.here.naksha.cli.loggers;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
import org.slf4j.event.Level;
5+
import picocli.CommandLine;
6+
7+
public final class LoggingMixin {
8+
private static final CommandLoggersConfigurator loggersConfigurator = new Log4jLoggersConfigurator();
9+
10+
@CommandLine.Option(
11+
names = {"--logLevel"},
12+
description = {
13+
"Valid values: ${COMPLETION-CANDIDATES}"
14+
},
15+
showDefaultValue = CommandLine.Help.Visibility.ALWAYS
16+
)
17+
private static Level logLevel = Level.INFO;
18+
19+
public static int executionStrategy(@NotNull CommandLine.ParseResult parseResult) {
20+
configureLoggers();
21+
return new CommandLine.RunLast().execute(parseResult);
22+
}
23+
24+
private static void configureLoggers() {
25+
loggersConfigurator.configureLoggers(logLevel);
26+
}
27+
28+
private LoggingMixin() {
29+
}
30+
}

0 commit comments

Comments
 (0)