Skip to content

Commit 1b4485a

Browse files
authored
Merge pull request #4 from interactive-instruments/dev_generate-styles
2 parents b68f130 + 72c3f42 commit 1b4485a

File tree

13 files changed

+588
-23
lines changed

13 files changed

+588
-23
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ build
88
.gradle
99
.idea
1010
.DS_Store
11+
bin

xtracfg/BUILD.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## build go library
2+
3+
docker run -it --rm -v /Users/az/development/xtraplatform-cli/xtracfg:/src -w /src --platform=linux/arm64 ghcr.io/ldproxy/golang-jdk:1.2 /bin/bash
4+
5+
CGO_CFLAGS="-I$JAVA_HOME/include -I$JAVA_HOME/include/linux" GOOS=linux GOARCH=arm64 go build -ldflags="-s -w '-extldflags=-z noexecstack'" -buildmode c-archive -o dist/libxtracfg.a
6+
7+
## build native binary
8+
9+
docker run -it --rm -v /Users/az/development/xtraplatform-cli/xtracfg:/src -v /Users/az/development/configs-ldproxy:/cfg --platform=linux/arm64 ghcr.io/ldproxy/liberica-nik:22-jdk11 /bin/bash
10+
11+
./gradlew nativeCompile --rerun-tasks
12+
13+
./build/native/nativeCompile/xtracfg info --src /cfg/demo
14+
15+

xtracfg/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ repositories {
2020
}
2121

2222
dependencies {
23-
implementation group: 'de.interactive_instruments', name: 'ldproxy-cfg', version: '4.1.0-SNAPSHOT'
23+
implementation group: 'de.interactive_instruments', name: 'ldproxy-cfg', version: '4.1.0-auto-styles-SNAPSHOT'
2424
implementation group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.35'
25+
// use this instead for debugging purposes
26+
//implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.35'
2527

2628
compileOnly group: 'org.graalvm.nativeimage', name: 'svm', version: '22.3.2'
2729
}

xtracfg/client/client.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,17 @@ func (store Store) Handle(parameters map[string]interface{}, command string, sub
9595
return nil, fmt.Errorf("Error: %s", *response.Error)
9696
}
9797

98+
9899
if response.Results == nil {
99100
return []Result{}, nil
100101
}
101102

102-
return *response.Results, nil
103+
104+
for i := range *response.Results {
105+
(*response.Results)[i].Details = response.Details
106+
}
107+
108+
return *response.Results, nil
103109
}
104110

105111
func (store Store) request(parameters map[string]interface{}, command string, subcommands ...string) (response *Response, err error) {
@@ -109,8 +115,10 @@ func (store Store) request(parameters map[string]interface{}, command string, su
109115
parameters["subcommand"] = subcommands[0]
110116
}
111117

118+
112119
request, err := json.Marshal(parameters)
113120

121+
114122
if err != nil {
115123
return nil, fmt.Errorf("Error: Failed to marshal the request body. %s", err)
116124
}

xtracfg/client/response.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ const (
2020
type Result struct {
2121
Status Status
2222
Message *string
23+
Details map[string]interface{}
2324
}
2425

2526
type Response struct {
2627
Results *[]Result
2728
Error *string
29+
Details map[string]interface{}
2830
}
2931

3032
var (

xtracfg/cmd/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@ func init() {
6767

6868
checkCmd := store.CheckCmd(storeSrc, name, verbose, debug)
6969

70+
createCmd := store.CreateCmd(storeSrc, name, verbose, debug)
71+
7072
upgradeCmd := store.UpgradeCmd(storeSrc, name, verbose, debug)
7173

7274
listenCmd := store.ListenCmd(storeSrc, name, version(), verbose, debug)
7375

7476
RootCmd.AddCommand(infoCmd)
7577
RootCmd.AddCommand(checkCmd)
78+
RootCmd.AddCommand(createCmd)
7679
RootCmd.AddCommand(upgradeCmd)
7780
RootCmd.AddCommand(listenCmd)
7881

xtracfg/cmd/store/create.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package store
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"os"
8+
9+
"github.com/interactive-instruments/xtraplatform-cli/xtracfg/client"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
var valueType *string
14+
var api *string
15+
var fileName *string
16+
var collectionColors *string
17+
18+
var validValueTypes = []string{"maplibre-styles"}
19+
20+
func isValidValueType(value string) bool {
21+
for _, v := range validValueTypes {
22+
if v == value {
23+
return true
24+
}
25+
}
26+
return false
27+
}
28+
29+
func CreateCmd(store client.Store, name string, verbose *bool, debug *bool) *cobra.Command {
30+
create := &cobra.Command{
31+
Use: "create",
32+
Short: "Create values for an API",
33+
Args: cobra.NoArgs,
34+
Run: func(cmd *cobra.Command, args []string) {
35+
results, err := store.Handle(map[string]interface{}{}, "create")
36+
client.PrintResults(results, err)
37+
},
38+
}
39+
40+
create.PersistentFlags().SortFlags = false
41+
create.Flags().SortFlags = false
42+
43+
createValue := &cobra.Command{
44+
Use: "value [path]",
45+
Short: "Create value for an API",
46+
Example: name + " create value -v -r \n" + name + " create value -v -r --valueType maplibre-styles --api strassen --name strassenStyles --src /cfg default",
47+
Args: func(cmd *cobra.Command, args []string) error {
48+
if len(args) > 1 {
49+
return errors.New("only one argument expected")
50+
}
51+
return nil
52+
},
53+
Run: func(cmd *cobra.Command, args []string) {
54+
if *debug {
55+
fmt.Fprint(os.Stdout, "Creating value for the api: ", store.Label(), "\n")
56+
}
57+
58+
if !isValidValueType(*valueType) {
59+
fmt.Fprintln(os.Stderr, "Invalid valueType:", *valueType)
60+
os.Exit(1)
61+
}
62+
63+
// Call analyze
64+
resultAnalyze, err := store.Handle(map[string]interface{}{"type": *valueType, "apiId": *api, "name": *fileName}, "autoValue", "analyze")
65+
if err != nil {
66+
client.PrintResults(resultAnalyze, err)
67+
os.Exit(1)
68+
} else {
69+
70+
if len(resultAnalyze) > 0 {
71+
firstResult := resultAnalyze[0]
72+
detailsMap := firstResult.Details
73+
74+
if len(detailsMap) == 0 {
75+
fmt.Fprintln(os.Stderr, "Details map is empty")
76+
os.Exit(1)
77+
}
78+
79+
collectionColorsInterface, ok := detailsMap["Collection Colors"]
80+
if !ok {
81+
fmt.Fprintln(os.Stderr, "Collection Colors key not found in Details map")
82+
os.Exit(1)
83+
}
84+
collectionColorsMap, ok := collectionColorsInterface.(map[string]interface{})
85+
if !ok {
86+
fmt.Fprintln(os.Stderr, "Collection Colors is not of type map[string]interface{}")
87+
os.Exit(1)
88+
}
89+
90+
collectionColorsJSON, _ := json.Marshal(collectionColorsMap)
91+
collectionColorsStr := string(collectionColorsJSON)
92+
93+
collectionColors = &collectionColorsStr
94+
} else {
95+
fmt.Fprintln(os.Stderr, "resultAnalyze is empty")
96+
os.Exit(1)
97+
}
98+
99+
results, err := store.Handle(map[string]interface{}{
100+
"type": *valueType,
101+
"apiId": *api,
102+
"name": *fileName,
103+
"collectionColors": *collectionColors,
104+
}, "autoValue", "generate")
105+
106+
if err != nil {
107+
fmt.Fprintln(os.Stderr, "Generate failed:", err)
108+
os.Exit(1)
109+
}
110+
111+
client.PrintResults(results, err)
112+
}
113+
},
114+
}
115+
116+
valueType = createValue.PersistentFlags().StringP("valueType", "t", "maplibre-styles", "type of the value to create")
117+
api = createValue.PersistentFlags().StringP("api", "a", "", "id of the api to create the value for")
118+
fileName = createValue.PersistentFlags().StringP("name", "n", "", "name of the value file to create")
119+
120+
createValue.PersistentFlags().SortFlags = false
121+
createValue.Flags().SortFlags = false
122+
123+
create.AddCommand(createValue)
124+
125+
return create
126+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package de.ii.xtraplatform.cli;
2+
3+
import de.ii.ldproxy.cfg.LdproxyCfg;
4+
import de.ii.ogcapi.foundation.domain.OgcApiDataV2;
5+
import de.ii.ogcapi.styles.domain.MbStyleStylesheet;
6+
import de.ii.xtraplatform.values.domain.*;
7+
import java.nio.file.Path;
8+
import java.util.*;
9+
import java.util.function.Consumer;
10+
import shadow.com.fasterxml.jackson.core.type.TypeReference;
11+
import shadow.com.fasterxml.jackson.databind.ObjectMapper;
12+
import shadow.com.google.common.base.Strings;
13+
14+
public class AutoValueHandler {
15+
16+
private static final Set<String> ALLOWED_TYPES = Set.of("maplibre-styles");
17+
18+
public static Result preCheck(Map<String, String> parameters, LdproxyCfg ldproxyCfg) {
19+
if (Objects.isNull(ldproxyCfg)) {
20+
return Result.failure("Not connected to store");
21+
}
22+
23+
ldproxyCfg.initStore();
24+
25+
String type = parameters.get("type");
26+
String name = parameters.get("name");
27+
String apiId = parameters.get("apiId");
28+
29+
if (Strings.isNullOrEmpty(type)) {
30+
return Result.failure("No value type given");
31+
}
32+
33+
if (!ALLOWED_TYPES.contains(type)) {
34+
return Result.failure(String.format("Value type '%s' is not supported", type));
35+
}
36+
37+
if (Strings.isNullOrEmpty(name)) {
38+
return Result.failure("No name given");
39+
}
40+
41+
if (Strings.isNullOrEmpty(apiId)) {
42+
return Result.failure("No API id given");
43+
}
44+
45+
if (!ldproxyCfg.getEntityDataStore().forType(OgcApiDataV2.class).hasAny(apiId)) {
46+
return Result.failure(String.format("API with id '%s' not found", apiId));
47+
}
48+
49+
if (ldproxyCfg.hasValue(type, name, apiId)) {
50+
return Result.failure(
51+
String.format(
52+
"Value of type '%s' with name '%s' already exists for API '%s'", type, name, apiId));
53+
}
54+
55+
return Result.empty();
56+
}
57+
58+
public static Result analyze(Map<String, String> parameters, LdproxyCfg ldproxyCfg) {
59+
Result result = new Result();
60+
61+
try {
62+
String apiId = parameters.get("apiId");
63+
64+
AutoValueFactory<MbStyleStylesheet, String, Map<String, String>> valueFactory =
65+
(AutoValueFactory<MbStyleStylesheet, String, Map<String, String>>)
66+
getValueFactory(ldproxyCfg, "maplibre-styles");
67+
68+
Map<String, String> collectionColors = valueFactory.analyze(apiId);
69+
70+
if (!collectionColors.isEmpty()) {
71+
result.success("All good");
72+
result.details("Collection Colors", collectionColors);
73+
} else {
74+
result.success("No stylesheet information found");
75+
}
76+
} catch (Throwable e) {
77+
e.printStackTrace();
78+
if (Objects.nonNull(e.getMessage())) {
79+
result.error(e.getMessage());
80+
}
81+
}
82+
83+
return result;
84+
}
85+
86+
public static Result generate(
87+
Map<String, String> parameters,
88+
LdproxyCfg ldproxyCfg,
89+
Consumer<Result> tracker,
90+
ObjectMapper jsonMapper) {
91+
AutoValueFactory<MbStyleStylesheet, String, Map<String, String>> valueFactory =
92+
(AutoValueFactory<MbStyleStylesheet, String, Map<String, String>>)
93+
getValueFactory(ldproxyCfg, "maplibre-styles");
94+
Result result = new Result();
95+
96+
try {
97+
String type = parameters.get("type");
98+
String apiId = parameters.get("apiId");
99+
String name = parameters.get("name");
100+
String collectionColorsString = parameters.get("collectionColors");
101+
boolean isTest = Objects.equals(collectionColorsString, "TEST");
102+
103+
if (Strings.isNullOrEmpty(collectionColorsString) && !isTest) {
104+
return Result.failure("No collections given");
105+
}
106+
107+
Map<String, String> collectionColors =
108+
isTest
109+
? valueFactory.analyze(apiId)
110+
: jsonMapper.readValue(
111+
collectionColorsString, new TypeReference<Map<String, String>>() {});
112+
113+
MbStyleStylesheet stylesheet = valueFactory.generate(apiId, collectionColors);
114+
115+
ldproxyCfg.writeValue(stylesheet, name, apiId);
116+
117+
Path newPath = ldproxyCfg.getValuesPath().resolve(Path.of(type, apiId, name + ".json"));
118+
119+
result.success(
120+
String.format("Value of type '%s' was created successfully: %s", type, newPath));
121+
} catch (Throwable e) {
122+
e.printStackTrace();
123+
if (Objects.nonNull(e.getMessage())) {
124+
result.error(e.getMessage());
125+
}
126+
}
127+
128+
return result;
129+
}
130+
131+
private static AutoValueFactory<?, ?, ?> getValueFactory(
132+
LdproxyCfg ldproxyCfg, String valueType) {
133+
ValueFactory factory = ldproxyCfg.getValueFactories().get(valueType);
134+
135+
AutoValueFactory<?, ?, ?> valueFactory =
136+
factory
137+
.auto()
138+
.orElseThrow(
139+
() ->
140+
new IllegalArgumentException(
141+
"No value factory found for value type " + valueType));
142+
return valueFactory;
143+
}
144+
}

xtracfg/src/main/java/de/ii/xtraplatform/cli/Call.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public enum Command {
1717
auto,
1818
schemas,
1919
file_type,
20-
unknown
20+
unknown,
21+
autoValue
2122
}
2223

2324
final Command command;

xtracfg/src/main/java/de/ii/xtraplatform/cli/CommandHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ private Result handle(Call call, boolean autoConnect, Consumer<Result> tracker)
112112
return new Upgrade(call.subcommand, call.parameters, false).run(context);
113113
case auto:
114114
return new Auto(call.subcommand, call.parameters, tracker).run(context.ldproxyCfg);
115+
case autoValue:
116+
return new AutoValue(call.subcommand, call.parameters, tracker, jsonMapper).run(context.ldproxyCfg);
115117
case schemas:
116118
return new Schemas(call.parameters).run(context.ldproxyCfg);
117119
case file_type:

0 commit comments

Comments
 (0)