diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d16065e..198afeb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,7 +4,7 @@ on: [pull_request, workflow_dispatch] env: GO_VERSION_BUILD: "1.25" - JAVA_VERSION: "11" + JAVA_VERSION: "25" jobs: main-go: diff --git a/CustomRegionGenerator.java b/CustomRegionGenerator.java index 160fda5..932de41 100644 --- a/CustomRegionGenerator.java +++ b/CustomRegionGenerator.java @@ -5,11 +5,28 @@ import org.openapitools.codegen.CodegenParameter; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.HexFormat; +import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; public class CustomRegionGenerator extends GoClientCodegen { + private final static String NAMESPACE = "cloud/stackit/codegen/"; + private final Map overrides; @Override public String getName() { @@ -20,6 +37,14 @@ public String getName() { public CustomRegionGenerator() { super(); System.out.println("=== CUSTOM GO CLIENT GENERATOR INITIALIZED ==="); + try { + overrides = OverrideConfig.load().stream().collect(Collectors.toMap( + o -> o.path, + Function.identity() + )); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -68,4 +93,64 @@ private boolean isRegionField(String name) { } return name.equalsIgnoreCase("region") || name.equalsIgnoreCase("regionId"); } + + @Override + public void postProcessFile(File file, String fileType) { + var path = file.getAbsolutePath(); + var servicesIdx = path.indexOf("/services/"); + var subPath = path.substring(servicesIdx + "/services/".length()); + if (overrides.containsKey(subPath)) { + var override = overrides.get(subPath); + try { + var content = Files.readAllBytes(file.toPath()); + var digest = MessageDigest.getInstance("MD5"); + var hex = HexFormat.of(); + var hash = hex.formatHex(digest.digest(content)); + if (hash.equals(override.hash)) { + var replacementPath = Path.of(Thread.currentThread().getContextClassLoader().getResource(NAMESPACE + override.replacementPath).getPath()); + var replacementContent = Files.readAllBytes(replacementPath); + Files.write(file.toPath(), replacementContent, StandardOpenOption.TRUNCATE_EXISTING); + } else { + throw new IllegalStateException( + "expected iaas/v2api/model_area_id.go to hash to " + + override.hash + " but got " + hash + + "\nedit CustomRegionGenerator.java in the sdk-generator and update IAAS_AREA_ID_HASH" + + "to accept this change" + ); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + super.postProcessFile(file, fileType); + } + + private static class OverrideConfig { + public String name; + public String path; + public String replacementPath; + public String hash; + + public static List load() throws IOException { + var path = Thread.currentThread().getContextClassLoader().getResource(NAMESPACE + "overrides.properties").getPath(); + var overrideProps = new Properties(); + overrideProps.load(new FileInputStream(path)); + var byName = new HashMap(); + overrideProps.stringPropertyNames().forEach(key -> { + var prefixLen = key.indexOf('.'); + var prefix = key.substring(0, prefixLen); + var config = byName.computeIfAbsent(prefix, (_) -> new OverrideConfig()); + config.name = prefix; + var suffix = key.substring(prefixLen + 1); + switch (suffix) { + case "path" -> config.path = overrideProps.getProperty(key); + case "replacementPath" -> config.replacementPath = overrideProps.getProperty(key); + case "hash" -> config.hash = overrideProps.getProperty(key); + default -> throw new IllegalStateException("unexpected suffix: " + suffix); + } + }); + return byName.values().stream().toList(); + } + } + } diff --git a/languages/golang/overrides/overrides.properties b/languages/golang/overrides/overrides.properties new file mode 100644 index 0000000..58a7a15 --- /dev/null +++ b/languages/golang/overrides/overrides.properties @@ -0,0 +1,11 @@ +iaasV2ApiAreaId.path=iaas/v2api/model_area_id.go +iaasV2ApiAreaId.replacementPath=v2api_model_area_id.go +iaasV2ApiAreaId.hash=b7425456c4633b2e8c0a61e9ab3b579e + +iaasV1ApiAreaId.path=iaas/v1api/model_area_id.go +iaasV1ApiAreaId.replacementPath=v1api_model_area_id.go +iaasV1ApiAreaId.hash=a9d72364fec0794cab3b51f3680de584 + +iaasV2BetaApiAreaId.path="iaas/v2beta1api/model_area_id.go +iaasV2BetaApiAreaId.replacementPath=v2beta_model_area_id.go +iaasV2BetaApiAreaId.hash=bb5583419348ee95ebbde0b4133a36f0 diff --git a/languages/golang/overrides/v1api_model_area_id.go b/languages/golang/overrides/v1api_model_area_id.go new file mode 100644 index 0000000..7dab3f9 --- /dev/null +++ b/languages/golang/overrides/v1api_model_area_id.go @@ -0,0 +1,164 @@ +/* +STACKIT IaaS API + +This API is deprecated. It will be retired on 01.03.2027. Please use the STACKIT IaaS API V2 instead. + +API version: 1 +Contact: stackit-iaas@mail.schwarz +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package v1api + +import ( + "encoding/json" + "fmt" + "regexp" +) + +// AreaId - The identifier (ID) of an area. +type AreaId struct { + StaticAreaID *StaticAreaID + String *string +} + +// StaticAreaIDAsAreaId is a convenience function that returns StaticAreaID wrapped in AreaId +func StaticAreaIDAsAreaId(v *StaticAreaID) AreaId { + return AreaId{ + StaticAreaID: v, + } +} + +// stringAsAreaId is a convenience function that returns string wrapped in AreaId +func StringAsAreaId(v *string) AreaId { + return AreaId{ + String: v, + } +} + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *AreaId) UnmarshalJSON(data []byte) error { + var err error + match := 0 + // try to unmarshal data into StaticAreaID + err = json.Unmarshal(data, &dst.StaticAreaID) + if err == nil { + jsonStaticAreaID, _ := json.Marshal(dst.StaticAreaID) + if string(jsonStaticAreaID) == "{}" { // empty struct + dst.StaticAreaID = nil + } else { + match++ + } + } else { + dst.StaticAreaID = nil + } + + // try to unmarshal data into String + err = json.Unmarshal(data, &dst.String) + if err == nil { + jsonString, _ := json.Marshal(dst.String) + regex := `^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$` + isMatched, _ := regexp.MatchString(regex, *dst.String) + if string(jsonString) != "{}" && isMatched { // empty struct + match++ + } else { + dst.String = nil + } + } else { + dst.String = nil + } + + if match > 1 { // more than 1 match + // reset to nil + dst.StaticAreaID = nil + dst.String = nil + + return fmt.Errorf("data matches more than one schema in oneOf(AreaId)") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("data failed to match schemas in oneOf(AreaId)") + } +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src AreaId) MarshalJSON() ([]byte, error) { + if src.StaticAreaID != nil { + return json.Marshal(&src.StaticAreaID) + } + + if src.String != nil { + return json.Marshal(&src.String) + } + + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *AreaId) GetActualInstance() interface{} { + if obj == nil { + return nil + } + if obj.StaticAreaID != nil { + return obj.StaticAreaID + } + + if obj.String != nil { + return obj.String + } + + // all schemas are nil + return nil +} + +// Get the actual instance value +func (obj AreaId) GetActualInstanceValue() interface{} { + if obj.StaticAreaID != nil { + return *obj.StaticAreaID + } + + if obj.String != nil { + return *obj.String + } + + // all schemas are nil + return nil +} + +type NullableAreaId struct { + value *AreaId + isSet bool +} + +func (v NullableAreaId) Get() *AreaId { + return v.value +} + +func (v *NullableAreaId) Set(val *AreaId) { + v.value = val + v.isSet = true +} + +func (v NullableAreaId) IsSet() bool { + return v.isSet +} + +func (v *NullableAreaId) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAreaId(val *AreaId) *NullableAreaId { + return &NullableAreaId{value: val, isSet: true} +} + +func (v NullableAreaId) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAreaId) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + diff --git a/languages/golang/overrides/v2api_model_area_id.go b/languages/golang/overrides/v2api_model_area_id.go new file mode 100644 index 0000000..5159f8b --- /dev/null +++ b/languages/golang/overrides/v2api_model_area_id.go @@ -0,0 +1,163 @@ +/* +STACKIT IaaS API + +This API allows you to create and modify IaaS resources. + +API version: 2 +Contact: stackit-iaas@mail.schwarz +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package v2api + +import ( + "encoding/json" + "fmt" + "regexp" +) + +// AreaId - The identifier (ID) of an area. +type AreaId struct { + StaticAreaID *StaticAreaID + String *string +} + +// StaticAreaIDAsAreaId is a convenience function that returns StaticAreaID wrapped in AreaId +func StaticAreaIDAsAreaId(v *StaticAreaID) AreaId { + return AreaId{ + StaticAreaID: v, + } +} + +// stringAsAreaId is a convenience function that returns string wrapped in AreaId +func StringAsAreaId(v *string) AreaId { + return AreaId{ + String: v, + } +} + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *AreaId) UnmarshalJSON(data []byte) error { + var err error + match := 0 + // try to unmarshal data into StaticAreaID + err = json.Unmarshal(data, &dst.StaticAreaID) + if err == nil { + jsonStaticAreaID, _ := json.Marshal(dst.StaticAreaID) + if string(jsonStaticAreaID) == "{}" { // empty struct + dst.StaticAreaID = nil + } else { + match++ + } + } else { + dst.StaticAreaID = nil + } + + // try to unmarshal data into String + err = json.Unmarshal(data, &dst.String) + if err == nil { + jsonString, _ := json.Marshal(dst.String) + regex := `^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$` + isMatched, _ := regexp.MatchString(regex, *dst.String) + if string(jsonString) != "{}" && isMatched { // empty struct + match++ + } else { + dst.String = nil + } + } else { + dst.String = nil + } + + if match > 1 { // more than 1 match + // reset to nil + dst.StaticAreaID = nil + dst.String = nil + + return fmt.Errorf("data matches more than one schema in oneOf(AreaId)") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("data failed to match schemas in oneOf(AreaId)") + } +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src AreaId) MarshalJSON() ([]byte, error) { + if src.StaticAreaID != nil { + return json.Marshal(&src.StaticAreaID) + } + + if src.String != nil { + return json.Marshal(&src.String) + } + + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *AreaId) GetActualInstance() interface{} { + if obj == nil { + return nil + } + if obj.StaticAreaID != nil { + return obj.StaticAreaID + } + + if obj.String != nil { + return obj.String + } + + // all schemas are nil + return nil +} + +// Get the actual instance value +func (obj AreaId) GetActualInstanceValue() interface{} { + if obj.StaticAreaID != nil { + return *obj.StaticAreaID + } + + if obj.String != nil { + return *obj.String + } + + // all schemas are nil + return nil +} + +type NullableAreaId struct { + value *AreaId + isSet bool +} + +func (v NullableAreaId) Get() *AreaId { + return v.value +} + +func (v *NullableAreaId) Set(val *AreaId) { + v.value = val + v.isSet = true +} + +func (v NullableAreaId) IsSet() bool { + return v.isSet +} + +func (v *NullableAreaId) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAreaId(val *AreaId) *NullableAreaId { + return &NullableAreaId{value: val, isSet: true} +} + +func (v NullableAreaId) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAreaId) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/languages/golang/overrides/v2beta_model_area_id.go b/languages/golang/overrides/v2beta_model_area_id.go new file mode 100644 index 0000000..f7bcd61 --- /dev/null +++ b/languages/golang/overrides/v2beta_model_area_id.go @@ -0,0 +1,163 @@ +/* +STACKIT IaaS API + +This API allows you to create and modify IaaS resources. + +API version: 2beta1 +Contact: stackit-iaas@mail.schwarz +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package v2beta1api + +import ( + "encoding/json" + "fmt" + "regexp" +) + +// AreaId - The identifier (ID) of an area. +type AreaId struct { + StaticAreaID *StaticAreaID + String *string +} + +// StaticAreaIDAsAreaId is a convenience function that returns StaticAreaID wrapped in AreaId +func StaticAreaIDAsAreaId(v *StaticAreaID) AreaId { + return AreaId{ + StaticAreaID: v, + } +} + +// stringAsAreaId is a convenience function that returns string wrapped in AreaId +func StringAsAreaId(v *string) AreaId { + return AreaId{ + String: v, + } +} + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *AreaId) UnmarshalJSON(data []byte) error { + var err error + match := 0 + // try to unmarshal data into StaticAreaID + err = json.Unmarshal(data, &dst.StaticAreaID) + if err == nil { + jsonStaticAreaID, _ := json.Marshal(dst.StaticAreaID) + regex := `^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$` + isMatched, _ := regexp.MatchString(regex, string(*dst.StaticAreaID)) + if string(jsonStaticAreaID) != "{}" && isMatched { // empty struct + match++ + } else { + dst.StaticAreaID = nil + } + } else { + dst.StaticAreaID = nil + } + + // try to unmarshal data into String + err = json.Unmarshal(data, &dst.String) + if err == nil { + jsonString, _ := json.Marshal(dst.String) + if string(jsonString) == "{}" { // empty struct + dst.String = nil + } else { + match++ + } + } else { + dst.String = nil + } + + if match > 1 { // more than 1 match + // reset to nil + dst.StaticAreaID = nil + dst.String = nil + + return fmt.Errorf("data matches more than one schema in oneOf(AreaId)") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("data failed to match schemas in oneOf(AreaId)") + } +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src AreaId) MarshalJSON() ([]byte, error) { + if src.StaticAreaID != nil { + return json.Marshal(&src.StaticAreaID) + } + + if src.String != nil { + return json.Marshal(&src.String) + } + + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *AreaId) GetActualInstance() interface{} { + if obj == nil { + return nil + } + if obj.StaticAreaID != nil { + return obj.StaticAreaID + } + + if obj.String != nil { + return obj.String + } + + // all schemas are nil + return nil +} + +// Get the actual instance value +func (obj AreaId) GetActualInstanceValue() interface{} { + if obj.StaticAreaID != nil { + return *obj.StaticAreaID + } + + if obj.String != nil { + return *obj.String + } + + // all schemas are nil + return nil +} + +type NullableAreaId struct { + value *AreaId + isSet bool +} + +func (v NullableAreaId) Get() *AreaId { + return v.value +} + +func (v *NullableAreaId) Set(val *AreaId) { + v.value = val + v.isSet = true +} + +func (v NullableAreaId) IsSet() bool { + return v.isSet +} + +func (v *NullableAreaId) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAreaId(val *AreaId) *NullableAreaId { + return &NullableAreaId{value: val, isSet: true} +} + +func (v NullableAreaId) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAreaId) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/scripts/generate-sdk/languages/go.sh b/scripts/generate-sdk/languages/go.sh index 87036fd..638c42b 100644 --- a/scripts/generate-sdk/languages/go.sh +++ b/scripts/generate-sdk/languages/go.sh @@ -111,7 +111,8 @@ generate_go_sdk() { cd ${ROOT_DIR} mkdir -p custom/cloud/stackit/codegen javac -cp "${GENERATOR_JAR_PATH}" CustomRegionGenerator.java - mv CustomRegionGenerator.class custom/cloud/stackit/codegen/CustomRegionGenerator.class + mv ./*.class custom/cloud/stackit/codegen + cp languages/golang/overrides/* custom/cloud/stackit/codegen warning=""