From c0687c7e22001881832220532e3c3e003e0d2968 Mon Sep 17 00:00:00 2001 From: Angel Marin Date: Mon, 12 Jan 2026 18:12:54 +0100 Subject: [PATCH 1/3] HYPERFLEET-501 - feat: manage oapi-codegen with bingo --- .bingo/Variables.mk | 6 ++ .bingo/oapi-codegen.mod | 5 ++ .bingo/oapi-codegen.sum | 147 ++++++++++++++++++++++++++++++++++++++++ .bingo/variables.env | 4 +- 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 .bingo/oapi-codegen.mod create mode 100644 .bingo/oapi-codegen.sum diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index ebd7bbd..270dc54 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -41,3 +41,9 @@ $(MOCKGEN): $(BINGO_DIR)/mockgen.mod @echo "(re)installing $(GOBIN)/mockgen-v0.6.0" @cd $(BINGO_DIR) && GOWORK=off GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) GOARM=$(GOHOSTARM) $(GO) build -mod=mod -modfile=mockgen.mod -o=$(GOBIN)/mockgen-v0.6.0 "go.uber.org/mock/mockgen" +OAPI_CODEGEN := $(GOBIN)/oapi-codegen-v2.5.1 +$(OAPI_CODEGEN): $(BINGO_DIR)/oapi-codegen.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/oapi-codegen-v2.5.1" + @cd $(BINGO_DIR) && GOWORK=off GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) GOARM=$(GOHOSTARM) $(GO) build -mod=mod -modfile=oapi-codegen.mod -o=$(GOBIN)/oapi-codegen-v2.5.1 "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" + diff --git a/.bingo/oapi-codegen.mod b/.bingo/oapi-codegen.mod new file mode 100644 index 0000000..88203e3 --- /dev/null +++ b/.bingo/oapi-codegen.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.25.0 + +require github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 // cmd/oapi-codegen diff --git a/.bingo/oapi-codegen.sum b/.bingo/oapi-codegen.sum new file mode 100644 index 0000000..e4d15a8 --- /dev/null +++ b/.bingo/oapi-codegen.sum @@ -0,0 +1,147 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= +github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= +github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= +github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 h1:5vHNY1uuPBRBWqB2Dp0G7YB03phxLQZupZTIZaeorjc= +github.com/oapi-codegen/oapi-codegen/v2 v2.5.1/go.mod h1:ro0npU1BWkcGpCgGD9QwPp44l5OIZ94tB3eabnT7DjQ= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/speakeasy-api/jsonpath v0.6.0 h1:IhtFOV9EbXplhyRqsVhHoBmmYjblIRh5D1/g8DHMXJ8= +github.com/speakeasy-api/jsonpath v0.6.0/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= +github.com/speakeasy-api/openapi-overlay v0.10.2 h1:VOdQ03eGKeiHnpb1boZCGm7x8Haj6gST0P3SGTX95GU= +github.com/speakeasy-api/openapi-overlay v0.10.2/go.mod h1:n0iOU7AqKpNFfEt6tq7qYITC4f0yzVVdFw0S7hukemg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= +github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= +github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= +github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.25.1 h1:YeIyhd0M7gStYR9jb2IFXVVT+QJhgXu1ZECOuRwofh4= +golang.org/x/tools v0.25.1/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/.bingo/variables.env b/.bingo/variables.env index e1ce49f..ce59e8f 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -8,9 +8,11 @@ if [ -z "$GOBIN" ]; then fi -GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.64.8" +GOLANGCI_LINT="${GOBIN}/golangci-lint-v2.7.0" GOTESTSUM="${GOBIN}/gotestsum-v1.13.0" MOCKGEN="${GOBIN}/mockgen-v0.6.0" +OAPI_CODEGEN="${GOBIN}/oapi-codegen-v2.5.1" + From 960cdae24684bbf77c88be269f13fa9d164f5093 Mon Sep 17 00:00:00 2001 From: Angel Marin Date: Mon, 12 Jan 2026 18:13:24 +0100 Subject: [PATCH 2/3] HYPERFLEET-501 - feat: change to oapi-codegen --- Dockerfile.openapi | 31 --- Dockerfile.openapi.dockerignore | 5 - Makefile | 31 ++- go.mod | 5 +- go.sum | 20 +- openapi/oapi-codegen.yaml | 17 ++ openapi/openapi.yaml | 406 +++++++++++++++++--------------- openapitools.json | 7 - 8 files changed, 270 insertions(+), 252 deletions(-) delete mode 100755 Dockerfile.openapi delete mode 100644 Dockerfile.openapi.dockerignore create mode 100644 openapi/oapi-codegen.yaml delete mode 100755 openapitools.json diff --git a/Dockerfile.openapi b/Dockerfile.openapi deleted file mode 100755 index a71b541..0000000 --- a/Dockerfile.openapi +++ /dev/null @@ -1,31 +0,0 @@ -FROM openapitools/openapi-generator-cli:v7.16.0 - -# -o APT::Sandbox::User=root is a workaround for rootless podman setgroups error in Prow env -RUN apt-get -o APT::Sandbox::User=root update -RUN apt-get -o APT::Sandbox::User=root install -y make sudo git wget - -# Install Go 1.24 -RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz && \ - tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz && \ - rm go1.24.0.linux-amd64.tar.gz - -RUN mkdir -p /local - - -# Copy the rest of the project -COPY openapi/openapi.yaml /local/openapi/openapi.yaml - -ENV PATH="/uhc/bin:/usr/local/go/bin:${PATH}" -ENV GOPATH="/uhc" -ENV GOBIN /usr/local/go/bin/ -ENV CGO_ENABLED=0 - -# these git and go flags to avoid self signed certificate errors - -WORKDIR /local - -# Install go-bindata -RUN go install -a github.com/go-bindata/go-bindata/...@v3.1.2 -RUN bash /usr/local/bin/docker-entrypoint.sh generate -i /local/openapi/openapi.yaml -g go -o /local/pkg/api/openapi -RUN rm /local/pkg/api/openapi/go.mod /local/pkg/api/openapi/go.sum -RUN rm -r /local/pkg/api/openapi/test diff --git a/Dockerfile.openapi.dockerignore b/Dockerfile.openapi.dockerignore deleted file mode 100644 index d034905..0000000 --- a/Dockerfile.openapi.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -# ignore all files -* - -# but the openapi contract, if it changes, we need to regenerate go types -!openapi/openapi.yaml diff --git a/Makefile b/Makefile index 02a1d16..3bf96b9 100755 --- a/Makefile +++ b/Makefile @@ -223,12 +223,23 @@ test-integration: install secrets $(GOTESTSUM) ./test/integration .PHONY: test-integration -# Regenerate openapi client and models -generate: +# Regenerate openapi types using oapi-codegen +generate: $(OAPI_CODEGEN) rm -rf pkg/api/openapi - $(container_tool) build -t hyperfleet-openapi -f Dockerfile.openapi . - $(eval OPENAPI_IMAGE_ID=`$(container_tool) create -t hyperfleet-openapi -f Dockerfile.openapi .`) - $(container_tool) cp $(OPENAPI_IMAGE_ID):/local/pkg/api/openapi ./pkg/api/openapi + mkdir -p pkg/api/openapi + $(OAPI_CODEGEN) --config openapi/oapi-codegen.yaml openapi/openapi.yaml + @printf '%s\n' \ + 'package openapi' \ + '' \ + '// Ptr returns a pointer to the given value.' \ + 'func Ptr[T any](v T) *T { return &v }' \ + '' \ + '// PtrString returns a pointer to the given string.' \ + 'func PtrString(v string) *string { return &v }' \ + '' \ + '// PtrInt32 returns a pointer to the given int32.' \ + 'func PtrInt32(v int32) *int32 { return &v }' \ + > pkg/api/openapi/helpers.go .PHONY: generate # Generate mock implementations for service interfaces @@ -240,14 +251,8 @@ generate-mocks: $(MOCKGEN) generate-all: generate generate-mocks .PHONY: generate-all -# Regenerate openapi client and models using vendor (avoids downloading dependencies) -generate-vendor: - rm -rf pkg/api/openapi - mkdir -p data/generated/openapi - $(container_tool) build -t hyperfleet-openapi-vendor -f Dockerfile.openapi.vendor . - $(eval OPENAPI_IMAGE_ID=`$(container_tool) create -t hyperfleet-openapi-vendor -f Dockerfile.openapi.vendor .`) - $(container_tool) cp $(OPENAPI_IMAGE_ID):/local/pkg/api/openapi ./pkg/api/openapi - $(container_tool) cp $(OPENAPI_IMAGE_ID):/local/data/generated/openapi/openapi.go ./data/generated/openapi/openapi.go +# generate-vendor is now equivalent to generate (oapi-codegen handles dependencies) +generate-vendor: generate .PHONY: generate-vendor run: build diff --git a/go.mod b/go.mod index 88bdc01..0a1474b 100755 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/jinzhu/inflection v1.0.0 github.com/lib/pq v1.10.9 github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 + github.com/oapi-codegen/runtime v1.1.2 github.com/onsi/gomega v1.27.1 github.com/openshift-online/ocm-sdk-go v0.1.334 github.com/prometheus/client_golang v1.16.0 @@ -43,6 +44,7 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect @@ -84,7 +86,7 @@ require ( github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/microcosm-cc/bluemonday v1.0.23 // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.1.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect @@ -128,7 +130,6 @@ require ( golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect - golang.org/x/time v0.3.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect google.golang.org/grpc v1.75.1 // indirect diff --git a/go.sum b/go.sum index f143ba0..03ee599 100755 --- a/go.sum +++ b/go.sum @@ -51,6 +51,7 @@ github.com/Masterminds/squirrel v1.1.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZl github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -62,6 +63,8 @@ github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9Pq github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b h1:IyTcB1l64U991qSZ0ufqiJv9GVEOUBiSPwsObDm7+cc= github.com/antlr/antlr4 v0.0.0-20190518164840-edae2a1c9b4b/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b h1:CvoEHGmxWl5kONC5icxwqV899dkf4VjOScbxLpllEnw= github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM= @@ -71,6 +74,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bxcodec/faker/v3 v3.2.0 h1:L3cTa9Tptyk0jsF/R6RooDZwxwA8dDi6IWdkIu8jwKo= github.com/bxcodec/faker/v3 v3.2.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -376,6 +380,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -435,8 +440,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 h1:Z/i1e+gTZrmcGeZyWckaLfucYG6KYOXLWo4co8pZYNY= github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103/go.mod h1:o9YPB5aGP8ob35Vy6+vyq3P3bWe7NQWzf+JLiXCiMaE= github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= -github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY= -github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -472,6 +477,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= @@ -582,6 +589,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -607,8 +615,8 @@ github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0h github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= @@ -868,8 +876,8 @@ golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/openapi/oapi-codegen.yaml b/openapi/oapi-codegen.yaml new file mode 100644 index 0000000..3a71e77 --- /dev/null +++ b/openapi/oapi-codegen.yaml @@ -0,0 +1,17 @@ +# oapi-codegen configuration +# See: https://github.com/oapi-codegen/oapi-codegen + +package: openapi +output: pkg/api/openapi/openapi.gen.go +generate: + models: true + chi-server: false + client: true + embedded-spec: true +output-options: + skip-prune: false +compatibility: + # Use old allOf merge behavior where schemas are inlined + old-merge-schemas: true + # Use old behavior generating type definitions instead of aliases + old-aliasing: true diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 7b20a9f..d19b926 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: HyperFleet API - version: 1.0.1 + version: 1.0.2 contact: name: HyperFleet Team license: @@ -20,11 +20,11 @@ paths: operationId: getClusters summary: List clusters parameters: + - $ref: '#/components/parameters/SearchParams' - $ref: '#/components/parameters/QueryParams.page' - $ref: '#/components/parameters/QueryParams.pageSize' - $ref: '#/components/parameters/QueryParams.orderBy' - $ref: '#/components/parameters/QueryParams.order' - - $ref: '#/components/parameters/SearchParams' responses: '200': description: The request has succeeded. @@ -115,11 +115,11 @@ paths: description: Cluster ID schema: type: string + - $ref: '#/components/parameters/SearchParams' - $ref: '#/components/parameters/QueryParams.page' - $ref: '#/components/parameters/QueryParams.pageSize' - $ref: '#/components/parameters/QueryParams.orderBy' - $ref: '#/components/parameters/QueryParams.order' - - $ref: '#/components/parameters/SearchParams' responses: '200': description: The request has succeeded. @@ -263,11 +263,11 @@ paths: required: true schema: type: string + - $ref: '#/components/parameters/SearchParams' - $ref: '#/components/parameters/QueryParams.page' - $ref: '#/components/parameters/QueryParams.pageSize' - $ref: '#/components/parameters/QueryParams.orderBy' - $ref: '#/components/parameters/QueryParams.order' - - $ref: '#/components/parameters/SearchParams' responses: '200': description: The request has succeeded. @@ -332,11 +332,11 @@ paths: description: Cluster ID schema: type: string + - $ref: '#/components/parameters/SearchParams' - $ref: '#/components/parameters/QueryParams.page' - $ref: '#/components/parameters/QueryParams.pageSize' - $ref: '#/components/parameters/QueryParams.orderBy' - $ref: '#/components/parameters/QueryParams.order' - - $ref: '#/components/parameters/SearchParams' responses: '200': description: The request has succeeded. @@ -356,11 +356,11 @@ paths: summary: List all nodepools for cluster description: Returns the list of all nodepools parameters: + - $ref: '#/components/parameters/SearchParams' - $ref: '#/components/parameters/QueryParams.page' - $ref: '#/components/parameters/QueryParams.pageSize' - $ref: '#/components/parameters/QueryParams.orderBy' - $ref: '#/components/parameters/QueryParams.order' - - $ref: '#/components/parameters/SearchParams' responses: '200': description: The request has succeeded. @@ -424,20 +424,30 @@ components: type: string explode: false schemas: - APIResource: - type: object - properties: - labels: - type: object - additionalProperties: - type: string - description: labels for the API resource as pairs of name:value strings - allOf: - - $ref: '#/components/schemas/ObjectReference' AdapterCondition: type: object - allOf: - - $ref: '#/components/schemas/ConditionBase' + required: + - type + - status + - last_transition_time + properties: + type: + type: string + description: Condition type + status: + $ref: '#/components/schemas/ConditionStatus' + reason: + type: string + description: Machine-readable reason code + message: + type: string + description: Human-readable message + last_transition_time: + type: string + format: date-time + description: |- + When this condition last transitioned status (API-managed) + Only updated when status changes (True/False/Unknown), not when reason/message changes description: |- Condition in AdapterStatus Used for standard Kubernetes condition types: "Available", "Applied", "Health" @@ -446,10 +456,42 @@ components: AdapterStatus: type: object required: + - adapter + - observed_generation - conditions - created_time - last_report_time properties: + adapter: + type: string + description: Adapter name (e.g., "validator", "dns", "provisioner") + observed_generation: + type: integer + format: int32 + description: Which generation of the resource this status reflects + metadata: + type: object + properties: + job_name: + type: string + job_namespace: + type: string + attempt: + type: integer + format: int32 + started_time: + type: string + format: date-time + completed_time: + type: string + format: date-time + duration: + type: string + description: Job execution metadata + data: + type: object + additionalProperties: {} + description: Adapter-specific data (structure varies by adapter type) conditions: type: array items: @@ -468,8 +510,6 @@ components: When this adapter last reported its status (API-managed) Updated every time the adapter POSTs, even if conditions haven't changed Used by Sentinel to detect adapter liveness - allOf: - - $ref: '#/components/schemas/AdapterStatusBase' description: |- AdapterStatus represents the complete status report from an adapter Contains multiple conditions, job metadata, and adapter-specific data @@ -506,11 +546,13 @@ components: failed: 0 created_time: '2021-01-01T10:00:00Z' last_report_time: '2021-01-01T10:02:00Z' - AdapterStatusBase: + AdapterStatusCreateRequest: type: object required: - adapter - observed_generation + - observed_time + - conditions properties: adapter: type: string @@ -542,13 +584,6 @@ components: type: object additionalProperties: {} description: Adapter-specific data (structure varies by adapter type) - description: Base fields shared by AdapterStatus and AdapterStatusCreateRequest - AdapterStatusCreateRequest: - type: object - required: - - observed_time - - conditions - properties: observed_time: type: string format: date-time @@ -559,8 +594,6 @@ components: type: array items: $ref: '#/components/schemas/ConditionRequest' - allOf: - - $ref: '#/components/schemas/AdapterStatusBase' description: Request payload for creating/updating adapter status example: adapter: validator @@ -594,14 +627,27 @@ components: AdapterStatusList: type: object required: + - kind + - page + - size + - total - items properties: + kind: + type: string + page: + type: integer + format: int32 + size: + type: integer + format: int32 + total: + type: integer + format: int32 items: type: array items: $ref: '#/components/schemas/AdapterStatus' - allOf: - - $ref: '#/components/schemas/List' description: List of adapter statuses with pagination metadata example: kind: AdapterStatusList @@ -649,6 +695,8 @@ components: Cluster: type: object required: + - name + - spec - created_time - updated_time - created_by @@ -656,6 +704,28 @@ components: - generation - status properties: + id: + type: string + description: Resource identifier + kind: + type: string + description: Resource kind + href: + type: string + description: Resource URI + labels: + type: object + additionalProperties: + type: string + description: labels for the API resource as pairs of name:value strings + name: + type: string + minLength: 3 + maxLength: 63 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + description: Cluster name (unique) + spec: + $ref: '#/components/schemas/ClusterSpec' created_time: type: string format: date-time @@ -675,8 +745,6 @@ components: description: Generation field is updated on customer updates, reflecting the version of the "intent" of the customer status: $ref: '#/components/schemas/ClusterStatus' - allOf: - - $ref: '#/components/schemas/ClusterBase' example: kind: Cluster id: cluster-123 @@ -713,16 +781,26 @@ components: last_transition_time: '2021-01-01T10:01:00Z' created_by: user-123@example.com updated_by: user-123@example.com - ClusterBase: + ClusterCreateRequest: type: object required: - - kind - name - spec properties: + id: + type: string + description: Resource identifier kind: type: string - default: Cluster + description: Resource kind + href: + type: string + description: Resource URI + labels: + type: object + additionalProperties: + type: string + description: labels for the API resource as pairs of name:value strings name: type: string minLength: 3 @@ -730,18 +808,7 @@ components: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ description: Cluster name (unique) spec: - allOf: - - $ref: '#/components/schemas/ClusterSpec' - description: |- - Cluster specification - CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job - But CLM will validate the schema before accepting the request - allOf: - - $ref: '#/components/schemas/APIResource' - ClusterCreateRequest: - type: object - allOf: - - $ref: '#/components/schemas/ClusterBase' + $ref: '#/components/schemas/ClusterSpec' example: kind: Cluster name: cluster-123 @@ -752,14 +819,27 @@ components: ClusterList: type: object required: + - kind + - page + - size + - total - items properties: + kind: + type: string + page: + type: integer + format: int32 + size: + type: integer + format: int32 + total: + type: integer + format: int32 items: type: array items: $ref: '#/components/schemas/Cluster' - allOf: - - $ref: '#/components/schemas/List' ClusterSpec: type: object description: |- @@ -776,12 +856,7 @@ components: - conditions properties: phase: - allOf: - - $ref: '#/components/schemas/ResourcePhase' - description: |- - Current cluster phase (native database column). - Updated when conditions are reported. - Note: status.phase provides aggregated view from all conditions. + $ref: '#/components/schemas/ResourcePhase' last_transition_time: type: string format: date-time @@ -815,33 +890,6 @@ components: It is aggregated from condition updates posted to `/clusters/{id}/statuses`. Provides quick overview of all reported conditions and aggregated phase. - ConditionBase: - type: object - required: - - type - - status - - last_transition_time - properties: - type: - type: string - description: Condition type - status: - allOf: - - $ref: '#/components/schemas/ConditionStatus' - description: Condition status - reason: - type: string - description: Machine-readable reason code - message: - type: string - description: Human-readable message - last_transition_time: - type: string - format: date-time - description: |- - When this condition last transitioned status (API-managed) - Only updated when status changes (True/False/Unknown), not when reason/message changes - description: Base condition fields shared by all condition types ConditionRequest: type: object required: @@ -895,32 +943,11 @@ components: type: string description: Validation error message for this field description: Field-level validation errors (optional) - List: - type: object - required: - - kind - - page - - size - - total - - items - properties: - kind: - type: string - page: - type: integer - format: int32 - size: - type: integer - format: int32 - total: - type: integer - format: int32 - items: - type: array - items: {} NodePool: type: object required: + - name + - spec - created_time - updated_time - created_by @@ -929,6 +956,28 @@ components: - owner_references - status properties: + id: + type: string + description: Resource identifier + kind: + type: string + description: Resource kind + href: + type: string + description: Resource URI + labels: + type: object + additionalProperties: + type: string + description: labels for the API resource as pairs of name:value strings + name: + type: string + minLength: 3 + maxLength: 63 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + description: NodePool name (unique in a cluster) + spec: + $ref: '#/components/schemas/NodePoolSpec' created_time: type: string format: date-time @@ -950,8 +999,6 @@ components: $ref: '#/components/schemas/ObjectReference' status: $ref: '#/components/schemas/NodePoolStatus' - allOf: - - $ref: '#/components/schemas/NodePoolBase' example: kind: NodePool id: nodepool-123 @@ -992,17 +1039,12 @@ components: created_time: '2021-01-01T10:01:00Z' last_updated_time: '2021-01-01T10:01:00Z' last_transition_time: '2021-01-01T10:01:00Z' - NodePoolBase: + NodePoolCreateRequest: type: object required: - name - spec properties: - labels: - type: object - additionalProperties: - type: string - description: labels for the API resource as pairs of name:value strings id: type: string description: Resource identifier @@ -1012,39 +1054,11 @@ components: href: type: string description: Resource URI - name: - type: string - minLength: 3 - maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - description: NodePool name (unique in a cluster) - spec: - allOf: - - $ref: '#/components/schemas/NodePoolSpec' - description: |- - NodePool specification - CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job - But CLM will validate the schema before accepting the request - NodePoolCreateRequest: - type: object - required: - - name - - spec - properties: labels: type: object additionalProperties: type: string description: labels for the API resource as pairs of name:value strings - id: - type: string - description: Resource identifier - kind: - type: string - description: Resource kind - href: - type: string - description: Resource URI name: type: string minLength: 3 @@ -1052,12 +1066,7 @@ components: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ description: NodePool name (unique in a cluster) spec: - allOf: - - $ref: '#/components/schemas/NodePoolSpec' - description: |- - NodePool specification - CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job - But CLM will validate the schema before accepting the request + $ref: '#/components/schemas/NodePoolSpec' example: name: worker-pool-1 labels: @@ -1067,6 +1076,8 @@ components: NodePoolCreateResponse: type: object required: + - name + - spec - created_time - updated_time - created_by @@ -1074,9 +1085,29 @@ components: - generation - owner_references - status - - name - - spec properties: + id: + type: string + description: Resource identifier + kind: + type: string + description: Resource kind + href: + type: string + description: Resource URI + labels: + type: object + additionalProperties: + type: string + description: labels for the API resource as pairs of name:value strings + name: + type: string + minLength: 3 + maxLength: 63 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + description: NodePool name (unique in a cluster) + spec: + $ref: '#/components/schemas/NodePoolSpec' created_time: type: string format: date-time @@ -1098,44 +1129,30 @@ components: $ref: '#/components/schemas/ObjectReference' status: $ref: '#/components/schemas/NodePoolStatus' - labels: - type: object - additionalProperties: - type: string - description: labels for the API resource as pairs of name:value strings - id: - type: string - description: Resource identifier - kind: - type: string - description: Resource kind - href: - type: string - description: Resource URI - name: - type: string - minLength: 3 - maxLength: 63 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - description: NodePool name (unique in a cluster) - spec: - allOf: - - $ref: '#/components/schemas/NodePoolSpec' - description: |- - NodePool specification - CLM doesn't know how to unmarshall the spec, it only stores and forwards to adapters to do their job - But CLM will validate the schema before accepting the request NodePoolList: type: object required: + - kind + - page + - size + - total - items properties: + kind: + type: string + page: + type: integer + format: int32 + size: + type: integer + format: int32 + total: + type: integer + format: int32 items: type: array items: $ref: '#/components/schemas/NodePool' - allOf: - - $ref: '#/components/schemas/List' NodePoolSpec: type: object description: |- @@ -1152,12 +1169,7 @@ components: - conditions properties: phase: - allOf: - - $ref: '#/components/schemas/ResourcePhase' - description: |- - Current NodePool phase (native database column). - Updated when conditions are reported. - Note: status.phase provides aggregated view from all conditions. + $ref: '#/components/schemas/ResourcePhase' observed_generation: type: integer format: int32 @@ -1207,10 +1219,30 @@ components: ResourceCondition: type: object required: + - type + - status + - last_transition_time - observed_generation - created_time - last_updated_time properties: + type: + type: string + description: Condition type + status: + $ref: '#/components/schemas/ConditionStatus' + reason: + type: string + description: Machine-readable reason code + message: + type: string + description: Human-readable message + last_transition_time: + type: string + format: date-time + description: |- + When this condition last transitioned status (API-managed) + Only updated when status changes (True/False/Unknown), not when reason/message changes observed_generation: type: integer format: int32 @@ -1226,8 +1258,6 @@ components: When the corresponding adapter last reported (API-managed) Updated every time the adapter POSTs, even if condition status hasn't changed Copied from AdapterStatus.last_report_time - allOf: - - $ref: '#/components/schemas/ConditionBase' description: |- Condition in Cluster/NodePool status Used for semantic condition types: "ValidationSuccessful", "DNSSuccessful", "NodePoolSuccessful", etc. diff --git a/openapitools.json b/openapitools.json deleted file mode 100755 index 3d812d3..0000000 --- a/openapitools.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "spaces": 2, - "generator-cli": { - "version": "7.16.0" - } -} From 221ad8a62bdc1873231cec1ad10681997847f10b Mon Sep 17 00:00:00 2001 From: Angel Marin Date: Mon, 12 Jan 2026 15:11:21 +0100 Subject: [PATCH 3/3] HYPERFLEET-501 - feat: change code to adapt to new generated types --- pkg/api/openapi_embed.go | 14 - pkg/api/presenters/adapter_status.go | 27 +- pkg/api/presenters/adapter_status_test.go | 65 ++-- pkg/api/presenters/cluster.go | 63 ++-- pkg/api/presenters/cluster_test.go | 50 +-- pkg/api/presenters/node_pool.go | 61 ++-- pkg/api/presenters/node_pool_test.go | 28 +- pkg/errors/errors.go | 12 +- pkg/handlers/cluster.go | 2 +- pkg/handlers/cluster_nodepools.go | 2 +- pkg/handlers/cluster_nodepools_test.go | 11 +- pkg/handlers/node_pool.go | 2 +- pkg/handlers/openapi.go | 17 +- pkg/handlers/validation_test.go | 8 +- test/factories/clusters.go | 2 + test/factories/node_pools.go | 10 +- test/helper.go | 48 ++- test/integration/adapter_status_test.go | 285 ++++++++++-------- test/integration/api_contract_test.go | 24 +- test/integration/clusters_test.go | 227 ++++++++------ test/integration/node_pools_test.go | 81 +++-- test/integration/search_field_mapping_test.go | 90 ++++-- test/registration.go | 2 +- 23 files changed, 681 insertions(+), 450 deletions(-) delete mode 100755 pkg/api/openapi_embed.go diff --git a/pkg/api/openapi_embed.go b/pkg/api/openapi_embed.go deleted file mode 100755 index 289168d..0000000 --- a/pkg/api/openapi_embed.go +++ /dev/null @@ -1,14 +0,0 @@ -package api - -import ( - "embed" - "io/fs" -) - -//go:embed openapi/api/openapi.yaml -var openapiFS embed.FS - -// GetOpenAPISpec returns the embedded OpenAPI YAML file contents -func GetOpenAPISpec() ([]byte, error) { - return fs.ReadFile(openapiFS, "openapi/api/openapi.yaml") -} diff --git a/pkg/api/presenters/adapter_status.go b/pkg/api/presenters/adapter_status.go index 6019836..6e3496b 100644 --- a/pkg/api/presenters/adapter_status.go +++ b/pkg/api/presenters/adapter_status.go @@ -40,10 +40,10 @@ func ConvertAdapterStatus( return nil, fmt.Errorf("failed to marshal adapter conditions: %w", err) } - // Marshal Data - data := make(map[string]map[string]interface{}) + // Marshal Data - req.Data is *map[string]interface{} + data := make(map[string]interface{}) if req.Data != nil { - data = req.Data + data = *req.Data } dataJSON, err := json.Marshal(data) if err != nil { @@ -87,7 +87,7 @@ func PresentAdapterStatus(adapterStatus *api.AdapterStatus) (openapi.AdapterStat for i, cond := range conditions { openapiConditions[i] = openapi.AdapterCondition{ Type: cond.Type, - Status: openapi.ConditionStatus(string(cond.Status)), + Status: openapi.ConditionStatus(cond.Status), Reason: cond.Reason, Message: cond.Message, LastTransitionTime: cond.LastTransitionTime, @@ -95,15 +95,22 @@ func PresentAdapterStatus(adapterStatus *api.AdapterStatus) (openapi.AdapterStat } // Unmarshal Data - var data map[string]map[string]interface{} + var data map[string]interface{} if len(adapterStatus.Data) > 0 { if err := json.Unmarshal(adapterStatus.Data, &data); err != nil { return openapi.AdapterStatus{}, fmt.Errorf("failed to unmarshal adapter status data: %w", err) } } - // Unmarshal Metadata - var metadata *openapi.AdapterStatusBaseMetadata + // Unmarshal Metadata - inline struct type + var metadata *struct { + Attempt *int32 `json:"attempt,omitempty"` + CompletedTime *time.Time `json:"completed_time,omitempty"` + Duration *string `json:"duration,omitempty"` + JobName *string `json:"job_name,omitempty"` + JobNamespace *string `json:"job_namespace,omitempty"` + StartedTime *time.Time `json:"started_time,omitempty"` + } if len(adapterStatus.Metadata) > 0 { if err := json.Unmarshal(adapterStatus.Metadata, &metadata); err != nil { return openapi.AdapterStatus{}, fmt.Errorf("failed to unmarshal adapter status metadata: %w", err) @@ -123,11 +130,11 @@ func PresentAdapterStatus(adapterStatus *api.AdapterStatus) (openapi.AdapterStat return openapi.AdapterStatus{ Adapter: adapterStatus.Adapter, - ObservedGeneration: adapterStatus.ObservedGeneration, Conditions: openapiConditions, - Data: data, - Metadata: metadata, CreatedTime: createdTime, + Data: &data, LastReportTime: lastReportTime, + Metadata: metadata, + ObservedGeneration: adapterStatus.ObservedGeneration, }, nil } diff --git a/pkg/api/presenters/adapter_status_test.go b/pkg/api/presenters/adapter_status_test.go index 06204e8..adfeb32 100644 --- a/pkg/api/presenters/adapter_status_test.go +++ b/pkg/api/presenters/adapter_status_test.go @@ -19,20 +19,27 @@ func createTestAdapterStatusRequest() *openapi.AdapterStatusCreateRequest { return &openapi.AdapterStatusCreateRequest{ Adapter: "test-adapter", ObservedGeneration: 5, + Data: &map[string]interface{}{ + "section1": map[string]interface{}{"key": "value"}, + }, + Metadata: &struct { + Attempt *int32 `json:"attempt,omitempty"` + CompletedTime *time.Time `json:"completed_time,omitempty"` + Duration *string `json:"duration,omitempty"` + JobName *string `json:"job_name,omitempty"` + JobNamespace *string `json:"job_namespace,omitempty"` + StartedTime *time.Time `json:"started_time,omitempty"` + }{ + JobName: strPtr("test-job"), + }, Conditions: []openapi.ConditionRequest{ { Type: "Ready", - Status: openapi.TRUE, + Status: openapi.True, Reason: &reason, Message: &message, }, }, - Data: map[string]map[string]interface{}{ - "section1": {"key": "value"}, - }, - Metadata: &openapi.AdapterStatusBaseMetadata{ - JobName: strPtr("test-job"), - }, ObservedTime: observedTime, } } @@ -76,7 +83,9 @@ func TestConvertAdapterStatus_Complete(t *testing.T) { // Verify Metadata marshaled correctly Expect(result.Metadata).ToNot(BeNil()) - var metadata openapi.AdapterStatusBaseMetadata + var metadata struct { + JobName *string `json:"job_name,omitempty"` + } err = json.Unmarshal(result.Metadata, &metadata) Expect(err).To(BeNil()) Expect(*metadata.JobName).To(Equal("test-job")) @@ -158,8 +167,8 @@ func TestConvertAdapterStatus_NilData(t *testing.T) { req := &openapi.AdapterStatusCreateRequest{ Adapter: "test-adapter", ObservedGeneration: 1, - Conditions: []openapi.ConditionRequest{}, Data: nil, // Nil data + Conditions: []openapi.ConditionRequest{}, } result, err := ConvertAdapterStatus("Cluster", "cluster-000", req) @@ -178,8 +187,8 @@ func TestConvertAdapterStatus_NilMetadata(t *testing.T) { req := &openapi.AdapterStatusCreateRequest{ Adapter: "test-adapter", ObservedGeneration: 1, - Conditions: []openapi.ConditionRequest{}, Metadata: nil, // Nil metadata + Conditions: []openapi.ConditionRequest{}, } result, err := ConvertAdapterStatus("Cluster", "cluster-111", req) @@ -197,9 +206,9 @@ func TestConvertAdapterStatus_ConditionStatusConversion(t *testing.T) { openapiStatus openapi.ConditionStatus expectedDomain api.ConditionStatus }{ - {openapi.TRUE, api.ConditionTrue}, - {openapi.FALSE, api.ConditionFalse}, - {openapi.UNKNOWN, api.ConditionUnknown}, + {openapi.True, api.ConditionTrue}, + {openapi.False, api.ConditionFalse}, + {openapi.Unknown, api.ConditionUnknown}, } for _, tc := range testCases { @@ -249,7 +258,14 @@ func TestPresentAdapterStatus_Complete(t *testing.T) { } dataJSON, _ := json.Marshal(data) - metadata := &openapi.AdapterStatusBaseMetadata{ + metadata := &struct { + Attempt *int32 `json:"attempt,omitempty"` + CompletedTime *time.Time `json:"completed_time,omitempty"` + Duration *string `json:"duration,omitempty"` + JobName *string `json:"job_name,omitempty"` + JobNamespace *string `json:"job_namespace,omitempty"` + StartedTime *time.Time `json:"started_time,omitempty"` + }{ JobName: strPtr("adapter-job"), } metadataJSON, _ := json.Marshal(metadata) @@ -276,11 +292,14 @@ func TestPresentAdapterStatus_Complete(t *testing.T) { // Verify conditions converted correctly Expect(len(result.Conditions)).To(Equal(1)) Expect(result.Conditions[0].Type).To(Equal("Ready")) - Expect(result.Conditions[0].Status).To(Equal(openapi.TRUE)) + Expect(result.Conditions[0].Status).To(Equal(openapi.True)) Expect(*result.Conditions[0].Reason).To(Equal("Success")) // Verify data unmarshaled correctly - Expect(result.Data["metrics"]["cpu"]).To(Equal("50%")) + Expect(result.Data).ToNot(BeNil()) + Expect((*result.Data)["metrics"]).To(BeAssignableToTypeOf(map[string]interface{}{})) + metrics := (*result.Data)["metrics"].(map[string]interface{}) + Expect(metrics["cpu"]).To(Equal("50%")) // Verify metadata unmarshaled correctly Expect(result.Metadata).ToNot(BeNil()) @@ -357,7 +376,7 @@ func TestPresentAdapterStatus_EmptyData(t *testing.T) { Expect(err).To(BeNil()) Expect(result.Data).ToNot(BeNil()) - Expect(len(result.Data)).To(Equal(0)) + Expect(len(*result.Data)).To(Equal(0)) } // TestPresentAdapterStatus_ConditionStatusConversion tests status conversion from domain to openapi @@ -368,9 +387,9 @@ func TestPresentAdapterStatus_ConditionStatusConversion(t *testing.T) { domainStatus api.ConditionStatus expectedOpenAPI openapi.ConditionStatus }{ - {api.ConditionTrue, openapi.TRUE}, - {api.ConditionFalse, openapi.FALSE}, - {api.ConditionUnknown, openapi.UNKNOWN}, + {api.ConditionTrue, openapi.True}, + {api.ConditionFalse, openapi.False}, + {api.ConditionUnknown, openapi.Unknown}, } now := time.Now() @@ -429,7 +448,11 @@ func TestConvertAndPresent_RoundTrip(t *testing.T) { Expect(*result.Conditions[0].Message).To(Equal(*originalReq.Conditions[0].Message)) // Verify data preserved - Expect(result.Data["section1"]["key"]).To(Equal(originalReq.Data["section1"]["key"])) + Expect(result.Data).ToNot(BeNil()) + Expect(originalReq.Data).ToNot(BeNil()) + resultSection := (*result.Data)["section1"].(map[string]interface{}) + originalSection := (*originalReq.Data)["section1"].(map[string]interface{}) + Expect(resultSection["key"]).To(Equal(originalSection["key"])) // Verify metadata preserved Expect(result.Metadata).ToNot(BeNil()) diff --git a/pkg/api/presenters/cluster.go b/pkg/api/presenters/cluster.go index fe5d2df..e34d657 100644 --- a/pkg/api/presenters/cluster.go +++ b/pkg/api/presenters/cluster.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + openapi_types "github.com/oapi-codegen/runtime/types" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi" ) @@ -33,8 +34,14 @@ func ConvertCluster(req *openapi.ClusterCreateRequest, createdBy string) (*api.C return nil, fmt.Errorf("failed to marshal cluster status conditions: %w", err) } + // Get Kind value, use default if not provided + kind := "Cluster" + if req.Kind != nil { + kind = *req.Kind + } + return &api.Cluster{ - Kind: req.Kind, + Kind: kind, Name: req.Name, Spec: specJSON, Labels: labelsJSON, @@ -47,6 +54,11 @@ func ConvertCluster(req *openapi.ClusterCreateRequest, createdBy string) (*api.C }, nil } +// Helper to convert string to openapi_types.Email +func toEmail(s string) openapi_types.Email { + return openapi_types.Email(s) +} + // PresentCluster converts api.Cluster (GORM model) to openapi.Cluster func PresentCluster(cluster *api.Cluster) (openapi.Cluster, error) { // Unmarshal Spec @@ -79,20 +91,6 @@ func PresentCluster(cluster *api.Cluster) (openapi.Cluster, error) { href = "/api/hyperfleet/v1/clusters/" + cluster.ID } - result := openapi.Cluster{ - Id: &cluster.ID, - Kind: cluster.Kind, - Href: &href, - Name: cluster.Name, - Spec: spec, - Labels: &labels, - Generation: cluster.Generation, - CreatedTime: cluster.CreatedTime, - UpdatedTime: cluster.UpdatedTime, - CreatedBy: cluster.CreatedBy, - UpdatedBy: cluster.UpdatedBy, - } - // Build ClusterStatus - set required fields with defaults if nil lastTransitionTime := time.Time{} if cluster.StatusLastTransitionTime != nil { @@ -114,23 +112,36 @@ func PresentCluster(cluster *api.Cluster) (openapi.Cluster, error) { openapiConditions := make([]openapi.ResourceCondition, len(statusConditions)) for i, cond := range statusConditions { openapiConditions[i] = openapi.ResourceCondition{ - ObservedGeneration: cond.ObservedGeneration, CreatedTime: cond.CreatedTime, + LastTransitionTime: cond.LastTransitionTime, LastUpdatedTime: cond.LastUpdatedTime, - Type: cond.Type, - Status: openapi.ConditionStatus(string(cond.Status)), - Reason: cond.Reason, Message: cond.Message, - LastTransitionTime: cond.LastTransitionTime, + ObservedGeneration: cond.ObservedGeneration, + Reason: cond.Reason, + Status: openapi.ConditionStatus(cond.Status), + Type: cond.Type, } } - result.Status = openapi.ClusterStatus{ - Phase: openapi.ResourcePhase(string(phase)), - ObservedGeneration: cluster.StatusObservedGeneration, - Conditions: openapiConditions, - LastTransitionTime: lastTransitionTime, - LastUpdatedTime: lastUpdatedTime, + result := openapi.Cluster{ + CreatedBy: toEmail(cluster.CreatedBy), + CreatedTime: cluster.CreatedTime, + Generation: cluster.Generation, + Href: &href, + Id: &cluster.ID, + Kind: openapi.PtrString(cluster.Kind), + Labels: &labels, + Name: cluster.Name, + Spec: spec, + Status: openapi.ClusterStatus{ + Conditions: openapiConditions, + LastTransitionTime: lastTransitionTime, + LastUpdatedTime: lastUpdatedTime, + ObservedGeneration: cluster.StatusObservedGeneration, + Phase: openapi.ResourcePhase(phase), + }, + UpdatedBy: toEmail(cluster.UpdatedBy), + UpdatedTime: cluster.UpdatedTime, } return result, nil diff --git a/pkg/api/presenters/cluster_test.go b/pkg/api/presenters/cluster_test.go index c2100fa..db90f9f 100644 --- a/pkg/api/presenters/cluster_test.go +++ b/pkg/api/presenters/cluster_test.go @@ -6,6 +6,7 @@ import ( "time" . "github.com/onsi/gomega" + openapi_types "github.com/oapi-codegen/runtime/types" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi" ) @@ -15,13 +16,13 @@ func createTestClusterRequest() *openapi.ClusterCreateRequest { labels := map[string]string{"env": "test"} return &openapi.ClusterCreateRequest{ - Kind: "Cluster", - Name: "test-cluster", + Labels: &labels, + Kind: openapi.PtrString("Cluster"), + Name: "test-cluster", Spec: map[string]interface{}{ "region": "us-central1", "provider": "gcp", }, - Labels: &labels, } } @@ -38,8 +39,8 @@ func TestConvertCluster_Complete(t *testing.T) { // Verify basic fields Expect(result.Kind).To(Equal("Cluster")) Expect(result.Name).To(Equal("test-cluster")) - Expect(result.CreatedBy).To(Equal("user123")) - Expect(result.UpdatedBy).To(Equal("user123")) + Expect(result.CreatedBy).To(Equal(createdBy)) + Expect(result.UpdatedBy).To(Equal(createdBy)) // Verify defaults Expect(result.Generation).To(Equal(int32(1))) @@ -76,10 +77,10 @@ func TestConvertCluster_WithLabels(t *testing.T) { } req := &openapi.ClusterCreateRequest{ - Kind: "Cluster", + Labels: &labels, + Kind: openapi.PtrString("Cluster"), Name: "labeled-cluster", Spec: map[string]interface{}{"test": "spec"}, - Labels: &labels, } result, err := ConvertCluster(req, "user456") @@ -97,10 +98,10 @@ func TestConvertCluster_WithoutLabels(t *testing.T) { RegisterTestingT(t) req := &openapi.ClusterCreateRequest{ - Kind: "Cluster", + Labels: nil, // Nil labels + Kind: openapi.PtrString("Cluster"), Name: "unlabeled-cluster", Spec: map[string]interface{}{"test": "spec"}, - Labels: nil, // Nil labels } result, err := ConvertCluster(req, "user789") @@ -129,7 +130,7 @@ func TestConvertCluster_SpecMarshaling(t *testing.T) { } req := &openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "complex-cluster", Spec: complexSpec, } @@ -196,8 +197,8 @@ func TestPresentCluster_Complete(t *testing.T) { StatusConditions: conditionsJSON, StatusLastTransitionTime: &now, StatusLastUpdatedTime: &now, - CreatedBy: "user123", - UpdatedBy: "user456", + CreatedBy: "user123@example.com", + UpdatedBy: "user456@example.com", } cluster.ID = "cluster-abc123" cluster.CreatedTime = now @@ -208,12 +209,12 @@ func TestPresentCluster_Complete(t *testing.T) { // Verify basic fields Expect(*result.Id).To(Equal("cluster-abc123")) - Expect(result.Kind).To(Equal("Cluster")) + Expect(*result.Kind).To(Equal("Cluster")) Expect(*result.Href).To(Equal("/api/hyperfleet/v1/clusters/cluster-abc123")) Expect(result.Name).To(Equal("presented-cluster")) Expect(result.Generation).To(Equal(int32(10))) - Expect(result.CreatedBy).To(Equal("user123")) - Expect(result.UpdatedBy).To(Equal("user456")) + Expect(result.CreatedBy).To(Equal(openapi_types.Email("user123@example.com"))) + Expect(result.UpdatedBy).To(Equal(openapi_types.Email("user456@example.com"))) // Verify Spec unmarshaled correctly Expect(result.Spec["region"]).To(Equal("us-west1")) @@ -222,11 +223,11 @@ func TestPresentCluster_Complete(t *testing.T) { Expect((*result.Labels)["env"]).To(Equal("staging")) // Verify Status - Expect(result.Status.Phase).To(Equal(openapi.READY)) + Expect(result.Status.Phase).To(Equal(openapi.Ready)) Expect(result.Status.ObservedGeneration).To(Equal(int32(5))) Expect(len(result.Status.Conditions)).To(Equal(1)) Expect(result.Status.Conditions[0].Type).To(Equal("Available")) - Expect(result.Status.Conditions[0].Status).To(Equal(openapi.TRUE)) + Expect(result.Status.Conditions[0].Status).To(Equal(openapi.True)) Expect(*result.Status.Conditions[0].Reason).To(Equal("Ready")) // Verify timestamps @@ -274,7 +275,7 @@ func TestPresentCluster_EmptyStatusPhase(t *testing.T) { Expect(err).To(BeNil()) // Should use NOT_READY as default - Expect(result.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(result.Status.Phase).To(Equal(openapi.NotReady)) } // TestPresentCluster_NilStatusTimestamps tests handling of nil timestamps @@ -357,13 +358,13 @@ func TestPresentCluster_StatusConditionsConversion(t *testing.T) { // First condition Expect(result.Status.Conditions[0].Type).To(Equal("Available")) - Expect(result.Status.Conditions[0].Status).To(Equal(openapi.TRUE)) + Expect(result.Status.Conditions[0].Status).To(Equal(openapi.True)) Expect(*result.Status.Conditions[0].Reason).To(Equal("Ready")) Expect(*result.Status.Conditions[0].Message).To(Equal("All systems operational")) // Second condition Expect(result.Status.Conditions[1].Type).To(Equal("Progressing")) - Expect(result.Status.Conditions[1].Status).To(Equal(openapi.FALSE)) + Expect(result.Status.Conditions[1].Status).To(Equal(openapi.False)) Expect(*result.Status.Conditions[1].Reason).To(Equal("Degraded")) Expect(*result.Status.Conditions[1].Message).To(Equal("Some components unavailable")) } @@ -373,9 +374,10 @@ func TestConvertAndPresentCluster_RoundTrip(t *testing.T) { RegisterTestingT(t) originalReq := createTestClusterRequest() + createdBy := "user999@example.com" // Convert from OpenAPI request to domain - cluster, err := ConvertCluster(originalReq, "user999") + cluster, err := ConvertCluster(originalReq, createdBy) Expect(err).To(BeNil()) // Simulate database fields (ID, timestamps) @@ -392,8 +394,8 @@ func TestConvertAndPresentCluster_RoundTrip(t *testing.T) { Expect(*result.Id).To(Equal("cluster-roundtrip-123")) Expect(result.Kind).To(Equal(originalReq.Kind)) Expect(result.Name).To(Equal(originalReq.Name)) - Expect(result.CreatedBy).To(Equal("user999")) - Expect(result.UpdatedBy).To(Equal("user999")) + Expect(result.CreatedBy).To(Equal(openapi_types.Email(createdBy))) + Expect(result.UpdatedBy).To(Equal(openapi_types.Email(createdBy))) // Verify Spec preserved Expect(result.Spec["region"]).To(Equal(originalReq.Spec["region"])) @@ -403,7 +405,7 @@ func TestConvertAndPresentCluster_RoundTrip(t *testing.T) { Expect((*result.Labels)["env"]).To(Equal((*originalReq.Labels)["env"])) // Verify Status defaults - Expect(result.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(result.Status.Phase).To(Equal(openapi.NotReady)) Expect(result.Status.ObservedGeneration).To(Equal(int32(0))) Expect(len(result.Status.Conditions)).To(Equal(0)) } diff --git a/pkg/api/presenters/node_pool.go b/pkg/api/presenters/node_pool.go index b4849a5..bebf9ce 100644 --- a/pkg/api/presenters/node_pool.go +++ b/pkg/api/presenters/node_pool.go @@ -91,26 +91,6 @@ func PresentNodePool(nodePool *api.NodePool) (openapi.NodePool, error) { ownerHref = "/api/hyperfleet/v1/clusters/" + nodePool.OwnerID } - kind := nodePool.Kind - result := openapi.NodePool{ - Id: &nodePool.ID, - Kind: &kind, - Href: &href, - Name: nodePool.Name, - Spec: spec, - Labels: &labels, - Generation: nodePool.Generation, - OwnerReferences: openapi.ObjectReference{ - Id: &nodePool.OwnerID, - Kind: &nodePool.OwnerKind, - Href: &ownerHref, - }, - CreatedTime: nodePool.CreatedTime, - UpdatedTime: nodePool.UpdatedTime, - CreatedBy: nodePool.CreatedBy, - UpdatedBy: nodePool.UpdatedBy, - } - // Build NodePoolStatus - set required fields with defaults if nil lastTransitionTime := time.Time{} if nodePool.StatusLastTransitionTime != nil { @@ -132,23 +112,42 @@ func PresentNodePool(nodePool *api.NodePool) (openapi.NodePool, error) { openapiConditions := make([]openapi.ResourceCondition, len(statusConditions)) for i, cond := range statusConditions { openapiConditions[i] = openapi.ResourceCondition{ - ObservedGeneration: cond.ObservedGeneration, CreatedTime: cond.CreatedTime, + LastTransitionTime: cond.LastTransitionTime, LastUpdatedTime: cond.LastUpdatedTime, - Type: cond.Type, - Status: openapi.ConditionStatus(string(cond.Status)), - Reason: cond.Reason, Message: cond.Message, - LastTransitionTime: cond.LastTransitionTime, + ObservedGeneration: cond.ObservedGeneration, + Reason: cond.Reason, + Status: openapi.ConditionStatus(cond.Status), + Type: cond.Type, } } - result.Status = openapi.NodePoolStatus{ - Phase: openapi.ResourcePhase(string(phase)), - ObservedGeneration: nodePool.StatusObservedGeneration, - Conditions: openapiConditions, - LastTransitionTime: lastTransitionTime, - LastUpdatedTime: lastUpdatedTime, + kind := nodePool.Kind + result := openapi.NodePool{ + CreatedBy: toEmail(nodePool.CreatedBy), + CreatedTime: nodePool.CreatedTime, + Generation: nodePool.Generation, + Href: &href, + Id: &nodePool.ID, + Kind: &kind, + Labels: &labels, + Name: nodePool.Name, + OwnerReferences: openapi.ObjectReference{ + Id: &nodePool.OwnerID, + Kind: &nodePool.OwnerKind, + Href: &ownerHref, + }, + Spec: spec, + Status: openapi.NodePoolStatus{ + Conditions: openapiConditions, + LastTransitionTime: lastTransitionTime, + LastUpdatedTime: lastUpdatedTime, + ObservedGeneration: nodePool.StatusObservedGeneration, + Phase: openapi.ResourcePhase(phase), + }, + UpdatedBy: toEmail(nodePool.UpdatedBy), + UpdatedTime: nodePool.UpdatedTime, } return result, nil diff --git a/pkg/api/presenters/node_pool_test.go b/pkg/api/presenters/node_pool_test.go index f70a87b..f4b4eea 100644 --- a/pkg/api/presenters/node_pool_test.go +++ b/pkg/api/presenters/node_pool_test.go @@ -6,6 +6,7 @@ import ( "time" . "github.com/onsi/gomega" + openapi_types "github.com/oapi-codegen/runtime/types" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi" ) @@ -193,8 +194,8 @@ func TestPresentNodePool_Complete(t *testing.T) { StatusConditions: conditionsJSON, StatusLastTransitionTime: &now, StatusLastUpdatedTime: &now, - CreatedBy: "user123", - UpdatedBy: "user456", + CreatedBy: "user123@example.com", + UpdatedBy: "user456@example.com", } nodePool.ID = "nodepool-xyz" nodePool.CreatedTime = now @@ -208,8 +209,8 @@ func TestPresentNodePool_Complete(t *testing.T) { Expect(*result.Kind).To(Equal("NodePool")) Expect(*result.Href).To(Equal("/api/hyperfleet/v1/clusters/cluster-abc/nodepools/nodepool-xyz")) Expect(result.Name).To(Equal("presented-nodepool")) - Expect(result.CreatedBy).To(Equal("user123")) - Expect(result.UpdatedBy).To(Equal("user456")) + Expect(result.CreatedBy).To(Equal(openapi_types.Email("user123@example.com"))) + Expect(result.UpdatedBy).To(Equal(openapi_types.Email("user456@example.com"))) // Verify Spec unmarshaled correctly Expect(result.Spec["replicas"]).To(BeNumerically("==", 5)) @@ -223,11 +224,11 @@ func TestPresentNodePool_Complete(t *testing.T) { Expect(*result.OwnerReferences.Href).To(Equal("/api/hyperfleet/v1/clusters/cluster-abc")) // Verify Status - Expect(result.Status.Phase).To(Equal(openapi.READY)) + Expect(result.Status.Phase).To(Equal(openapi.Ready)) Expect(result.Status.ObservedGeneration).To(Equal(int32(5))) Expect(len(result.Status.Conditions)).To(Equal(1)) Expect(result.Status.Conditions[0].Type).To(Equal("Available")) - Expect(result.Status.Conditions[0].Status).To(Equal(openapi.TRUE)) + Expect(result.Status.Conditions[0].Status).To(Equal(openapi.True)) // Verify timestamps Expect(result.CreatedTime.Unix()).To(Equal(now.Unix())) @@ -322,7 +323,7 @@ func TestPresentNodePool_EmptyStatusPhase(t *testing.T) { Expect(err).To(BeNil()) // Should use NOT_READY as default - Expect(result.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(result.Status.Phase).To(Equal(openapi.NotReady)) } // TestPresentNodePool_StatusConditionsConversion tests condition conversion @@ -380,13 +381,13 @@ func TestPresentNodePool_StatusConditionsConversion(t *testing.T) { // First condition Expect(result.Status.Conditions[0].Type).To(Equal("Progressing")) - Expect(result.Status.Conditions[0].Status).To(Equal(openapi.TRUE)) + Expect(result.Status.Conditions[0].Status).To(Equal(openapi.True)) Expect(*result.Status.Conditions[0].Reason).To(Equal("Scaling")) Expect(*result.Status.Conditions[0].Message).To(Equal("Scaling in progress")) // Second condition Expect(result.Status.Conditions[1].Type).To(Equal("Healthy")) - Expect(result.Status.Conditions[1].Status).To(Equal(openapi.TRUE)) + Expect(result.Status.Conditions[1].Status).To(Equal(openapi.True)) Expect(*result.Status.Conditions[1].Reason).To(Equal("Healthy")) Expect(*result.Status.Conditions[1].Message).To(Equal("All nodes healthy")) } @@ -397,9 +398,10 @@ func TestConvertAndPresentNodePool_RoundTrip(t *testing.T) { originalReq := createTestNodePoolRequest() ownerID := "cluster-roundtrip-789" + createdBy := "user-roundtrip@example.com" // Convert from OpenAPI request to domain - nodePool, err := ConvertNodePool(originalReq, ownerID, "user-roundtrip") + nodePool, err := ConvertNodePool(originalReq, ownerID, createdBy) Expect(err).To(BeNil()) // Simulate database fields (ID, timestamps) @@ -416,8 +418,8 @@ func TestConvertAndPresentNodePool_RoundTrip(t *testing.T) { Expect(*result.Id).To(Equal("nodepool-roundtrip-123")) Expect(*result.Kind).To(Equal(*originalReq.Kind)) Expect(result.Name).To(Equal(originalReq.Name)) - Expect(result.CreatedBy).To(Equal("user-roundtrip")) - Expect(result.UpdatedBy).To(Equal("user-roundtrip")) + Expect(result.CreatedBy).To(Equal(openapi_types.Email(createdBy))) + Expect(result.UpdatedBy).To(Equal(openapi_types.Email(createdBy))) // Verify Spec preserved Expect(result.Spec["replicas"]).To(BeNumerically("==", originalReq.Spec["replicas"])) @@ -431,7 +433,7 @@ func TestConvertAndPresentNodePool_RoundTrip(t *testing.T) { Expect(*result.OwnerReferences.Kind).To(Equal("Cluster")) // Verify Status defaults - Expect(result.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(result.Status.Phase).To(Equal(openapi.NotReady)) Expect(result.Status.ObservedGeneration).To(Equal(int32(0))) Expect(len(result.Status.Conditions)).To(Equal(0)) } diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index d29fa7a..17b3576 100755 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -162,14 +162,20 @@ func (e *ServiceError) AsOpenapiError(operationID string) openapi.Error { // Add validation details if present if len(e.Details) > 0 { - details := make([]openapi.ErrorDetailsInner, len(e.Details)) + details := make([]struct { + Error *string `json:"error,omitempty"` + Field *string `json:"field,omitempty"` + }, len(e.Details)) for i, detail := range e.Details { - details[i] = openapi.ErrorDetailsInner{ + details[i] = struct { + Error *string `json:"error,omitempty"` + Field *string `json:"field,omitempty"` + }{ Field: openapi.PtrString(detail.Field), Error: openapi.PtrString(detail.Error), } } - openapiErr.Details = details + openapiErr.Details = &details } return openapiErr diff --git a/pkg/handlers/cluster.go b/pkg/handlers/cluster.go index 3285c9e..319524c 100644 --- a/pkg/handlers/cluster.go +++ b/pkg/handlers/cluster.go @@ -39,7 +39,7 @@ func (h clusterHandler) Create(w http.ResponseWriter, r *http.Request) { func() (interface{}, *errors.ServiceError) { ctx := r.Context() // Use the presenters.ConvertCluster helper to convert the request - clusterModel, err := presenters.ConvertCluster(&req, "system") + clusterModel, err := presenters.ConvertCluster(&req, "system@hyperfleet.local") if err != nil { return nil, errors.GeneralError("Failed to convert cluster: %v", err) } diff --git a/pkg/handlers/cluster_nodepools.go b/pkg/handlers/cluster_nodepools.go index a474e0e..5d79fa2 100644 --- a/pkg/handlers/cluster_nodepools.go +++ b/pkg/handlers/cluster_nodepools.go @@ -149,7 +149,7 @@ func (h clusterNodePoolsHandler) Create(w http.ResponseWriter, r *http.Request) } // Use the presenters.ConvertNodePool helper to convert the request - nodePoolModel, convErr := presenters.ConvertNodePool(&req, cluster.ID, "system") + nodePoolModel, convErr := presenters.ConvertNodePool(&req, cluster.ID, "system@hyperfleet.local") if convErr != nil { return nil, errors.GeneralError("Failed to convert nodepool: %v", convErr) } diff --git a/pkg/handlers/cluster_nodepools_test.go b/pkg/handlers/cluster_nodepools_test.go index 170fa36..952ea89 100644 --- a/pkg/handlers/cluster_nodepools_test.go +++ b/pkg/handlers/cluster_nodepools_test.go @@ -56,9 +56,14 @@ func TestClusterNodePoolsHandler_Get(t *testing.T) { CreatedTime: now, UpdatedTime: now, }, - Kind: "NodePool", - Name: "test-nodepool", - OwnerID: clusterID, + Kind: "NodePool", + Name: "test-nodepool", + OwnerID: clusterID, + Spec: []byte("{}"), + Labels: []byte("{}"), + StatusConditions: []byte("[]"), + CreatedBy: "user@example.com", + UpdatedBy: "user@example.com", }, nil) return mockClusterSvc, mockNodePoolSvc, mockGenericSvc diff --git a/pkg/handlers/node_pool.go b/pkg/handlers/node_pool.go index 508c7a2..efdf4f5 100644 --- a/pkg/handlers/node_pool.go +++ b/pkg/handlers/node_pool.go @@ -38,7 +38,7 @@ func (h nodePoolHandler) Create(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // For standalone nodepools, owner_id would need to come from somewhere // This is likely not a supported use case, but using empty string for now - nodePoolModel, convErr := presenters.ConvertNodePool(&req, "", "system") + nodePoolModel, convErr := presenters.ConvertNodePool(&req, "", "system@hyperfleet.local") if convErr != nil { return nil, errors.GeneralError("Failed to convert nodepool: %v", convErr) } diff --git a/pkg/handlers/openapi.go b/pkg/handlers/openapi.go index 5fee317..6478320 100755 --- a/pkg/handlers/openapi.go +++ b/pkg/handlers/openapi.go @@ -5,9 +5,8 @@ import ( "io/fs" "net/http" - "github.com/ghodss/yaml" "github.com/golang/glog" - "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api" + "github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger" ) @@ -21,24 +20,24 @@ type openAPIHandler struct { } func NewOpenAPIHandler() (*openAPIHandler, error) { - // Load the fully resolved OpenAPI spec from embedded filesystem - resolvedData, err := api.GetOpenAPISpec() + // Load the OpenAPI spec from the generated code's embedded swagger + swagger, err := openapi.GetSwagger() if err != nil { return nil, errors.GeneralError( - "can't load OpenAPI specification from embedded file: %v", + "can't load OpenAPI specification from generated code: %v", err, ) } - // Convert YAML to JSON - data, err := yaml.YAMLToJSON(resolvedData) + // Marshal the swagger spec to JSON + data, err := swagger.MarshalJSON() if err != nil { return nil, errors.GeneralError( - "can't convert OpenAPI specification from YAML to JSON: %v", + "can't marshal OpenAPI specification to JSON: %v", err, ) } - glog.Info("Loaded fully resolved OpenAPI specification from embedded pkg/api/openapi/api/openapi.yaml") + glog.Info("Loaded OpenAPI specification from generated oapi-codegen swagger") // Load the OpenAPI UI HTML content uiContent, err := fs.ReadFile(openapiui, "openapi-ui.html") diff --git a/pkg/handlers/validation_test.go b/pkg/handlers/validation_test.go index 57b3c9a..52d87b5 100644 --- a/pkg/handlers/validation_test.go +++ b/pkg/handlers/validation_test.go @@ -98,7 +98,7 @@ func TestValidateKind_Valid(t *testing.T) { RegisterTestingT(t) req := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), } validator := validateKind(&req, "Kind", "kind", "Cluster") err := validator() @@ -117,7 +117,7 @@ func TestValidateKind_Invalid(t *testing.T) { for _, kind := range invalidKinds { req := openapi.ClusterCreateRequest{ - Kind: kind, + Kind: &kind, } validator := validateKind(&req, "Kind", "kind", "Cluster") err := validator() @@ -129,7 +129,7 @@ func TestValidateKind_Empty(t *testing.T) { RegisterTestingT(t) req := openapi.ClusterCreateRequest{ - Kind: "", + Kind: openapi.PtrString(""), } validator := validateKind(&req, "Kind", "kind", "Cluster") err := validator() @@ -141,7 +141,7 @@ func TestValidateKind_WrongKind(t *testing.T) { RegisterTestingT(t) req := openapi.ClusterCreateRequest{ - Kind: "WrongKind", + Kind: openapi.PtrString("WrongKind"), } validator := validateKind(&req, "Kind", "kind", "Cluster") err := validator() diff --git a/test/factories/clusters.go b/test/factories/clusters.go index c727e93..deeb107 100644 --- a/test/factories/clusters.go +++ b/test/factories/clusters.go @@ -21,6 +21,8 @@ func (f *Factories) NewCluster(id string) (*api.Cluster, error) { Name: "test-cluster-" + id, // Use unique name based on ID Spec: []byte(`{"test": "spec"}`), Generation: 42, + CreatedBy: "test@example.com", + UpdatedBy: "test@example.com", } sub, err := clusterService.Create(context.Background(), cluster) diff --git a/test/factories/node_pools.go b/test/factories/node_pools.go index bf1b8b9..3b4f7dd 100644 --- a/test/factories/node_pools.go +++ b/test/factories/node_pools.go @@ -32,10 +32,12 @@ func (f *Factories) NewNodePool(id string) (*api.NodePool, error) { } nodePool := &api.NodePool{ - Meta: api.Meta{ID: id}, - Name: "test-nodepool-" + id, // Use unique name based on ID - Spec: []byte(`{"test": "spec"}`), - OwnerID: cluster.ID, // Use real cluster ID + Meta: api.Meta{ID: id}, + Name: "test-nodepool-" + id, // Use unique name based on ID + Spec: []byte(`{"test": "spec"}`), + OwnerID: cluster.ID, // Use real cluster ID + CreatedBy: "test@example.com", + UpdatedBy: "test@example.com", } sub, serviceErr := nodePoolService.Create(context.Background(), nodePool) diff --git a/test/helper.go b/test/helper.go index f79b9fa..4c894cb 100755 --- a/test/helper.go +++ b/test/helper.go @@ -6,6 +6,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net/http" "os" "strings" "sync" @@ -241,16 +242,17 @@ func (helper *Helper) HealthCheckURL(path string) string { return fmt.Sprintf("http://%s%s", helper.AppConfig.HealthCheck.BindAddress, path) } -func (helper *Helper) NewApiClient() *openapi.APIClient { - config := openapi.NewConfiguration() - // Override the server URL to use the local test server +func (helper *Helper) NewApiClient() *openapi.ClientWithResponses { + // Build the server URL protocol := "http" if helper.AppConfig.Server.EnableHTTPS { protocol = "https" } - config.Host = helper.AppConfig.Server.BindAddress - config.Scheme = protocol - client := openapi.NewAPIClient(config) + serverURL := fmt.Sprintf("%s://%s", protocol, helper.AppConfig.Server.BindAddress) + client, err := openapi.NewClientWithResponses(serverURL) + if err != nil { + helper.T.Fatalf("Failed to create API client: %v", err) + } return client } @@ -283,9 +285,34 @@ func (helper *Helper) NewAccount(username, name, email string) *amv1.Account { return acct } +// contextKeyAccessToken is a context key for storing the access token +type contextKeyAccessToken struct{} + +// ContextAccessToken is the context key for access tokens (used by tests) +var ContextAccessToken = contextKeyAccessToken{} + func (helper *Helper) NewAuthenticatedContext(account *amv1.Account) context.Context { tokenString := helper.CreateJWTString(account) - return context.WithValue(context.Background(), openapi.ContextAccessToken, tokenString) + return context.WithValue(context.Background(), ContextAccessToken, tokenString) +} + +// GetAccessTokenFromContext extracts the access token from the context +func GetAccessTokenFromContext(ctx context.Context) string { + if token, ok := ctx.Value(ContextAccessToken).(string); ok { + return token + } + return "" +} + +// WithAuthToken returns a RequestEditorFn that adds the Authorization header from context +func WithAuthToken(ctx context.Context) openapi.RequestEditorFn { + return func(_ context.Context, req *http.Request) error { + token := GetAccessTokenFromContext(ctx) + if token != "" { + req.Header.Set("Authorization", "Bearer "+token) + } + return nil + } } func (helper *Helper) StartJWKCertServerMock() (teardown func() error) { @@ -418,11 +445,10 @@ func (helper *Helper) CreateJWTToken(account *amv1.Account) *jwt.Token { return token } -// OpenapiError Convert an error response from the openapi client to an openapi error struct -func (helper *Helper) OpenapiError(err error) openapi.Error { - generic := err.(openapi.GenericOpenAPIError) +// OpenapiError Convert an error response body to an openapi error struct +func (helper *Helper) OpenapiError(body []byte) openapi.Error { var exErr openapi.Error - jsonErr := json.Unmarshal(generic.Body(), &exErr) + jsonErr := json.Unmarshal(body, &exErr) if jsonErr != nil { helper.T.Errorf("Unable to convert error response to openapi error: %s", jsonErr) } diff --git a/test/integration/adapter_status_test.go b/test/integration/adapter_status_test.go index af6936a..d1d6f6a 100644 --- a/test/integration/adapter_status_test.go +++ b/test/integration/adapter_status_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" . "github.com/onsi/gomega" @@ -11,6 +12,17 @@ import ( "github.com/openshift-hyperfleet/hyperfleet-api/test" ) +// Helper to create AdapterStatusCreateRequest +func newAdapterStatusRequest(adapter string, observedGen int32, conditions []openapi.ConditionRequest, data *map[string]interface{}) openapi.AdapterStatusCreateRequest { + return openapi.AdapterStatusCreateRequest{ + Adapter: adapter, + ObservedGeneration: observedGen, + Data: data, + Conditions: conditions, + ObservedTime: time.Now(), + } +} + // TestClusterStatusPost tests creating adapter status for a cluster func TestClusterStatusPost(t *testing.T) { h, client := test.RegisterIntegration(t) @@ -23,27 +35,29 @@ func TestClusterStatusPost(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Create an adapter status for the cluster - statusInput := openapi.AdapterStatusCreateRequest{ - Adapter: "test-adapter", - ObservedGeneration: cluster.Generation, - Conditions: []openapi.ConditionRequest{ + data := map[string]interface{}{ + "test_key": map[string]interface{}{"value": "test_value"}, + } + statusInput := newAdapterStatusRequest( + "test-adapter", + cluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, Reason: openapi.PtrString("AdapterReady"), }, }, - Data: map[string]map[string]interface{}{ - "test_key": {"value": "test_value"}, - }, - } + &data, + ) - statusOutput, resp, err := client.DefaultAPI.PostClusterStatuses(ctx, cluster.ID).AdapterStatusCreateRequest(statusInput).Execute() + resp, err := client.PostClusterStatusesWithResponse(ctx, cluster.ID, openapi.PostClusterStatusesJSONRequestBody(statusInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error posting cluster status: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(statusOutput.Adapter).To(Equal("test-adapter")) - Expect(statusOutput.ObservedGeneration).To(Equal(cluster.Generation)) - Expect(len(statusOutput.Conditions)).To(BeNumerically(">", 0)) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + Expect(resp.JSON201).NotTo(BeNil()) + Expect(resp.JSON201.Adapter).To(Equal("test-adapter")) + Expect(resp.JSON201.ObservedGeneration).To(Equal(cluster.Generation)) + Expect(len(resp.JSON201.Conditions)).To(BeNumerically(">", 0)) } // TestClusterStatusGet tests retrieving adapter statuses for a cluster @@ -59,26 +73,27 @@ func TestClusterStatusGet(t *testing.T) { // Create a few adapter statuses for i := 0; i < 3; i++ { - statusInput := openapi.AdapterStatusCreateRequest{ - Adapter: fmt.Sprintf("adapter-%d", i), - ObservedGeneration: cluster.Generation, - Conditions: []openapi.ConditionRequest{ + statusInput := newAdapterStatusRequest( + fmt.Sprintf("adapter-%d", i), + cluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, }, }, - } - _, _, err := client.DefaultAPI.PostClusterStatuses(ctx, cluster.ID).AdapterStatusCreateRequest(statusInput).Execute() + nil, + ) + _, err := client.PostClusterStatusesWithResponse(ctx, cluster.ID, openapi.PostClusterStatusesJSONRequestBody(statusInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) } // Get all statuses for the cluster - list, resp, err := client.DefaultAPI.GetClusterStatuses(ctx, cluster.ID).Execute() + resp, err := client.GetClusterStatusesWithResponse(ctx, cluster.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting cluster statuses: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(list).NotTo(BeNil()) - Expect(len(list.Items)).To(BeNumerically(">=", 3)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + Expect(resp.JSON200).NotTo(BeNil()) + Expect(len(resp.JSON200.Items)).To(BeNumerically(">=", 3)) } // TestNodePoolStatusPost tests creating adapter status for a nodepool @@ -96,27 +111,29 @@ func TestNodePoolStatusPost(t *testing.T) { Expect(nodePool.ID).NotTo(BeEmpty(), "nodePool.ID should not be empty") // Create an adapter status for the nodepool - statusInput := openapi.AdapterStatusCreateRequest{ - Adapter: "test-nodepool-adapter", - ObservedGeneration: 1, - Conditions: []openapi.ConditionRequest{ + data := map[string]interface{}{ + "nodepool_data": map[string]interface{}{"value": "test_value"}, + } + statusInput := newAdapterStatusRequest( + "test-nodepool-adapter", + 1, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "False", + Status: openapi.False, Reason: openapi.PtrString("Initializing"), }, }, - Data: map[string]map[string]interface{}{ - "nodepool_data": {"value": "test_value"}, - }, - } + &data, + ) // Use nodePool.OwnerID as the cluster_id parameter - statusOutput, resp, err := client.DefaultAPI.PostNodePoolStatuses(ctx, nodePool.OwnerID, nodePool.ID).AdapterStatusCreateRequest(statusInput).Execute() + resp, err := client.PostNodePoolStatusesWithResponse(ctx, nodePool.OwnerID, nodePool.ID, openapi.PostNodePoolStatusesJSONRequestBody(statusInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error posting nodepool status: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(statusOutput.Adapter).To(Equal("test-nodepool-adapter")) - Expect(len(statusOutput.Conditions)).To(BeNumerically(">", 0)) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + Expect(resp.JSON201).NotTo(BeNil()) + Expect(resp.JSON201.Adapter).To(Equal("test-nodepool-adapter")) + Expect(len(resp.JSON201.Conditions)).To(BeNumerically(">", 0)) } // TestNodePoolStatusGet tests retrieving adapter statuses for a nodepool @@ -132,27 +149,28 @@ func TestNodePoolStatusGet(t *testing.T) { // Create a few adapter statuses for i := 0; i < 2; i++ { - statusInput := openapi.AdapterStatusCreateRequest{ - Adapter: fmt.Sprintf("nodepool-adapter-%d", i), - ObservedGeneration: 1, - Conditions: []openapi.ConditionRequest{ + statusInput := newAdapterStatusRequest( + fmt.Sprintf("nodepool-adapter-%d", i), + 1, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, }, }, - } + nil, + ) // Use nodePool.OwnerID as the cluster_id parameter - _, _, err := client.DefaultAPI.PostNodePoolStatuses(ctx, nodePool.OwnerID, nodePool.ID).AdapterStatusCreateRequest(statusInput).Execute() + _, err := client.PostNodePoolStatusesWithResponse(ctx, nodePool.OwnerID, nodePool.ID, openapi.PostNodePoolStatusesJSONRequestBody(statusInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) } // Get all statuses for the nodepool - list, resp, err := client.DefaultAPI.GetNodePoolsStatuses(ctx, nodePool.OwnerID, nodePool.ID).Execute() + resp, err := client.GetNodePoolsStatusesWithResponse(ctx, nodePool.OwnerID, nodePool.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting nodepool statuses: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(list).NotTo(BeNil()) - Expect(len(list.Items)).To(BeNumerically(">=", 2)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + Expect(resp.JSON200).NotTo(BeNil()) + Expect(len(resp.JSON200.Items)).To(BeNumerically(">=", 2)) } // TestAdapterStatusPaging tests paging for adapter statuses @@ -168,25 +186,33 @@ func TestAdapterStatusPaging(t *testing.T) { // Create multiple statuses for i := 0; i < 10; i++ { - statusInput := openapi.AdapterStatusCreateRequest{ - Adapter: fmt.Sprintf("adapter-%d", i), - ObservedGeneration: cluster.Generation, - Conditions: []openapi.ConditionRequest{ + statusInput := newAdapterStatusRequest( + fmt.Sprintf("adapter-%d", i), + cluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, }, }, - } - _, _, err := client.DefaultAPI.PostClusterStatuses(ctx, cluster.ID).AdapterStatusCreateRequest(statusInput).Execute() + nil, + ) + _, err := client.PostClusterStatusesWithResponse(ctx, cluster.ID, openapi.PostClusterStatusesJSONRequestBody(statusInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) } // Test paging - list, _, err := client.DefaultAPI.GetClusterStatuses(ctx, cluster.ID).Page(1).PageSize(5).Execute() + page := openapi.QueryParamsPage(1) + pageSize := openapi.QueryParamsPageSize(5) + params := &openapi.GetClusterStatusesParams{ + Page: &page, + PageSize: &pageSize, + } + resp, err := client.GetClusterStatusesWithResponse(ctx, cluster.ID, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(len(list.Items)).To(BeNumerically("<=", 5)) - Expect(list.Page).To(Equal(int32(1))) + Expect(resp.JSON200).NotTo(BeNil()) + Expect(len(resp.JSON200.Items)).To(BeNumerically("<=", 5)) + Expect(resp.JSON200.Page).To(Equal(int32(1))) } // TestAdapterStatusIdempotency tests that posting the same adapter twice updates instead of creating duplicate @@ -201,57 +227,62 @@ func TestAdapterStatusIdempotency(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // First POST: Create adapter status - statusInput1 := openapi.AdapterStatusCreateRequest{ - Adapter: "idempotency-test-adapter", - ObservedGeneration: cluster.Generation, - Conditions: []openapi.ConditionRequest{ + data1 := map[string]interface{}{ + "version": map[string]interface{}{"value": "1.0"}, + } + statusInput1 := newAdapterStatusRequest( + "idempotency-test-adapter", + cluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "False", + Status: openapi.False, Reason: openapi.PtrString("Initializing"), }, }, - Data: map[string]map[string]interface{}{ - "version": {"value": "1.0"}, - }, - } + &data1, + ) - status1, resp, err := client.DefaultAPI.PostClusterStatuses(ctx, cluster.ID).AdapterStatusCreateRequest(statusInput1).Execute() + resp1, err := client.PostClusterStatusesWithResponse(ctx, cluster.ID, openapi.PostClusterStatusesJSONRequestBody(statusInput1), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(status1.Adapter).To(Equal("idempotency-test-adapter")) - Expect(string(status1.Conditions[0].Status)).To(Equal(string(openapi.FALSE))) + Expect(resp1.StatusCode()).To(Equal(http.StatusCreated)) + Expect(resp1.JSON201).NotTo(BeNil()) + Expect(resp1.JSON201.Adapter).To(Equal("idempotency-test-adapter")) + Expect(resp1.JSON201.Conditions[0].Status).To(Equal(openapi.False)) // Second POST: Update the same adapter with different conditions - statusInput2 := openapi.AdapterStatusCreateRequest{ - Adapter: "idempotency-test-adapter", - ObservedGeneration: cluster.Generation, - Conditions: []openapi.ConditionRequest{ + data2 := map[string]interface{}{ + "version": map[string]interface{}{"value": "2.0"}, + } + statusInput2 := newAdapterStatusRequest( + "idempotency-test-adapter", + cluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, Reason: openapi.PtrString("AdapterReady"), }, }, - Data: map[string]map[string]interface{}{ - "version": {"value": "2.0"}, - }, - } + &data2, + ) - status2, resp, err := client.DefaultAPI.PostClusterStatuses(ctx, cluster.ID).AdapterStatusCreateRequest(statusInput2).Execute() + resp2, err := client.PostClusterStatusesWithResponse(ctx, cluster.ID, openapi.PostClusterStatusesJSONRequestBody(statusInput2), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(status2.Adapter).To(Equal("idempotency-test-adapter")) - Expect(status2.Conditions[0].Status).To(Equal(openapi.TRUE)) + Expect(resp2.StatusCode()).To(Equal(http.StatusCreated)) + Expect(resp2.JSON201).NotTo(BeNil()) + Expect(resp2.JSON201.Adapter).To(Equal("idempotency-test-adapter")) + Expect(resp2.JSON201.Conditions[0].Status).To(Equal(openapi.True)) // GET all statuses - should have only ONE status for "idempotency-test-adapter" - list, _, err := client.DefaultAPI.GetClusterStatuses(ctx, cluster.ID).Execute() + listResp, err := client.GetClusterStatusesWithResponse(ctx, cluster.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) + Expect(listResp.JSON200).NotTo(BeNil()) // Count how many times this adapter appears adapterCount := 0 var finalStatus openapi.AdapterStatus - for _, s := range list.Items { + for _, s := range listResp.JSON200.Items { if s.Adapter == "idempotency-test-adapter" { adapterCount++ finalStatus = s @@ -260,7 +291,7 @@ func TestAdapterStatusIdempotency(t *testing.T) { // Verify: should have exactly ONE entry for this adapter (updated, not duplicated) Expect(adapterCount).To(Equal(1), "Adapter should be updated, not duplicated") - Expect(finalStatus.Conditions[0].Status).To(Equal(openapi.TRUE), "Conditions should be updated to latest") + Expect(finalStatus.Conditions[0].Status).To(Equal(openapi.True), "Conditions should be updated to latest") } // TestAdapterStatusPagingEdgeCases tests edge cases in pagination @@ -276,17 +307,18 @@ func TestAdapterStatusPagingEdgeCases(t *testing.T) { // Create exactly 10 statuses for i := 0; i < 10; i++ { - statusInput := openapi.AdapterStatusCreateRequest{ - Adapter: fmt.Sprintf("edge-adapter-%d", i), - ObservedGeneration: cluster.Generation, - Conditions: []openapi.ConditionRequest{ + statusInput := newAdapterStatusRequest( + fmt.Sprintf("edge-adapter-%d", i), + cluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, }, }, - } - _, _, err := client.DefaultAPI.PostClusterStatuses(ctx, cluster.ID).AdapterStatusCreateRequest(statusInput).Execute() + nil, + ) + _, err := client.PostClusterStatusesWithResponse(ctx, cluster.ID, openapi.PostClusterStatusesJSONRequestBody(statusInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) } @@ -294,61 +326,76 @@ func TestAdapterStatusPagingEdgeCases(t *testing.T) { emptyCluster, err := h.Factories.NewClusters(h.NewID()) Expect(err).NotTo(HaveOccurred()) - emptyList, _, err := client.DefaultAPI.GetClusterStatuses(ctx, emptyCluster.ID).Execute() + emptyResp, err := client.GetClusterStatusesWithResponse(ctx, emptyCluster.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(emptyList.Total).To(Equal(int32(0))) - Expect(len(emptyList.Items)).To(Equal(0)) + Expect(emptyResp.JSON200).NotTo(BeNil()) + Expect(emptyResp.JSON200.Total).To(Equal(int32(0))) + Expect(len(emptyResp.JSON200.Items)).To(Equal(0)) // Test 2: Page beyond total pages - beyondList, _, err := client.DefaultAPI.GetClusterStatuses(ctx, cluster.ID).Page(100).PageSize(5).Execute() + page100 := openapi.QueryParamsPage(100) + pageSize5 := openapi.QueryParamsPageSize(5) + beyondParams := &openapi.GetClusterStatusesParams{ + Page: &page100, + PageSize: &pageSize5, + } + beyondResp, err := client.GetClusterStatusesWithResponse(ctx, cluster.ID, beyondParams, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(len(beyondList.Items)).To(Equal(0), "Should return empty when page exceeds total pages") - Expect(beyondList.Total).To(Equal(int32(10)), "Total should still reflect actual count") + Expect(beyondResp.JSON200).NotTo(BeNil()) + Expect(len(beyondResp.JSON200.Items)).To(Equal(0), "Should return empty when page exceeds total pages") + Expect(beyondResp.JSON200.Total).To(Equal(int32(10)), "Total should still reflect actual count") // Test 3: Single item dataset singleCluster, err := h.Factories.NewClusters(h.NewID()) Expect(err).NotTo(HaveOccurred()) - singleStatus := openapi.AdapterStatusCreateRequest{ - Adapter: "single-adapter", - ObservedGeneration: singleCluster.Generation, - Conditions: []openapi.ConditionRequest{ + singleStatus := newAdapterStatusRequest( + "single-adapter", + singleCluster.Generation, + []openapi.ConditionRequest{ { Type: "Ready", - Status: "True", + Status: openapi.True, }, }, - } - _, _, err = client.DefaultAPI.PostClusterStatuses(ctx, singleCluster.ID).AdapterStatusCreateRequest(singleStatus).Execute() + nil, + ) + _, err = client.PostClusterStatusesWithResponse(ctx, singleCluster.ID, openapi.PostClusterStatusesJSONRequestBody(singleStatus), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - singleList, _, err := client.DefaultAPI.GetClusterStatuses(ctx, singleCluster.ID).Execute() + singleResp, err := client.GetClusterStatusesWithResponse(ctx, singleCluster.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(singleList.Total).To(Equal(int32(1))) - Expect(len(singleList.Items)).To(Equal(1)) - Expect(singleList.Page).To(Equal(int32(1))) + Expect(singleResp.JSON200).NotTo(BeNil()) + Expect(singleResp.JSON200.Total).To(Equal(int32(1))) + Expect(len(singleResp.JSON200.Items)).To(Equal(1)) + Expect(singleResp.JSON200.Page).To(Equal(int32(1))) // Test 4: Pagination consistency - verify no duplicates and no missing items allItems := make(map[string]bool) - page := 1 - pageSize := 3 + pageNum := openapi.QueryParamsPage(1) + pageSz := openapi.QueryParamsPageSize(3) for { - list, _, err := client.DefaultAPI.GetClusterStatuses(ctx, cluster.ID).Page(int32(page)).PageSize(int32(pageSize)).Execute() + params := &openapi.GetClusterStatusesParams{ + Page: &pageNum, + PageSize: &pageSz, + } + listResp, err := client.GetClusterStatusesWithResponse(ctx, cluster.ID, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) + Expect(listResp.JSON200).NotTo(BeNil()) - if len(list.Items) == 0 { + if len(listResp.JSON200.Items) == 0 { break } - for _, item := range list.Items { + for _, item := range listResp.JSON200.Items { adapter := item.Adapter Expect(allItems[adapter]).To(BeFalse(), "Duplicate adapter found in pagination: %s", adapter) allItems[adapter] = true } - page++ - if page > 10 { + pageNum++ + if pageNum > 10 { break // Safety limit } } diff --git a/test/integration/api_contract_test.go b/test/integration/api_contract_test.go index 8592e7e..8bf4f48 100644 --- a/test/integration/api_contract_test.go +++ b/test/integration/api_contract_test.go @@ -16,12 +16,12 @@ func TestAPIContract_ResourcePhaseConstants(t *testing.T) { // Verify domain constants match OpenAPI generated constants // If OpenAPI spec changes, this test will fail and alert us to update domain constants - Expect(string(api.PhaseNotReady)).To(Equal(string(openapi.NOT_READY)), - "api.PhaseNotReady must match openapi.NOT_READY") - Expect(string(api.PhaseReady)).To(Equal(string(openapi.READY)), - "api.PhaseReady must match openapi.READY") - Expect(string(api.PhaseFailed)).To(Equal(string(openapi.FAILED)), - "api.PhaseFailed must match openapi.FAILED") + Expect(string(api.PhaseNotReady)).To(Equal(string(openapi.NotReady)), + "api.PhaseNotReady must match openapi.NotReady") + Expect(string(api.PhaseReady)).To(Equal(string(openapi.Ready)), + "api.PhaseReady must match openapi.Ready") + Expect(string(api.PhaseFailed)).To(Equal(string(openapi.Failed)), + "api.PhaseFailed must match openapi.Failed") } // TestAPIContract_ConditionStatusConstants verifies that domain and OpenAPI constants are in sync @@ -31,10 +31,10 @@ func TestAPIContract_ConditionStatusConstants(t *testing.T) { // Verify domain constants match OpenAPI generated constants // If OpenAPI spec changes, this test will fail and alert us to update domain constants - Expect(string(api.ConditionTrue)).To(Equal(string(openapi.TRUE)), - "api.ConditionTrue must match openapi.TRUE") - Expect(string(api.ConditionFalse)).To(Equal(string(openapi.FALSE)), - "api.ConditionFalse must match openapi.FALSE") - Expect(string(api.ConditionUnknown)).To(Equal(string(openapi.UNKNOWN)), - "api.ConditionUnknown must match openapi.UNKNOWN") + Expect(string(api.ConditionTrue)).To(Equal(string(openapi.True)), + "api.ConditionTrue must match openapi.True") + Expect(string(api.ConditionFalse)).To(Equal(string(openapi.False)), + "api.ConditionFalse must match openapi.False") + Expect(string(api.ConditionUnknown)).To(Equal(string(openapi.Unknown)), + "api.ConditionUnknown must match openapi.Unknown") } diff --git a/test/integration/clusters_test.go b/test/integration/clusters_test.go index 0a0a656..0dd23d0 100644 --- a/test/integration/clusters_test.go +++ b/test/integration/clusters_test.go @@ -25,23 +25,26 @@ func TestClusterGet(t *testing.T) { ctx := h.NewAuthenticatedContext(account) // 401 using no JWT token - _, _, err := client.DefaultAPI.GetClusterById(context.Background(), "foo").Execute() - Expect(err).To(HaveOccurred(), "Expected 401 but got nil error") + resp, err := client.GetClusterByIdWithResponse(context.Background(), "foo", nil) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusUnauthorized), "Expected 401 but got %d", resp.StatusCode()) // GET responses per openapi spec: 200 and 404, - _, resp, err := client.DefaultAPI.GetClusterById(ctx, "foo").Execute() - Expect(err).To(HaveOccurred(), "Expected 404") - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + resp, err = client.GetClusterByIdWithResponse(ctx, "foo", nil, test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusNotFound), "Expected 404") clusterModel, err := h.Factories.NewClusters(h.NewID()) Expect(err).NotTo(HaveOccurred()) - clusterOutput, resp, err := client.DefaultAPI.GetClusterById(ctx, clusterModel.ID).Execute() + resp, err = client.GetClusterByIdWithResponse(ctx, clusterModel.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + clusterOutput := resp.JSON200 + Expect(clusterOutput).NotTo(BeNil()) Expect(*clusterOutput.Id).To(Equal(clusterModel.ID), "found object does not match test object") - Expect(clusterOutput.Kind).To(Equal("Cluster")) + Expect(*clusterOutput.Kind).To(Equal("Cluster")) Expect(*clusterOutput.Href).To(Equal(fmt.Sprintf("/api/hyperfleet/v1/clusters/%s", clusterModel.ID))) Expect(clusterOutput.CreatedTime).To(BeTemporally("~", clusterModel.CreatedTime)) Expect(clusterOutput.UpdatedTime).To(BeTemporally("~", clusterModel.UpdatedTime)) @@ -55,21 +58,24 @@ func TestClusterPost(t *testing.T) { // POST responses per openapi spec: 201, 409, 500 clusterInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "test-name", Spec: map[string]interface{}{"test": "spec"}, } // 201 Created - clusterOutput, resp, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(clusterInput).Execute() + resp, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(clusterInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + + clusterOutput := resp.JSON201 + Expect(clusterOutput).NotTo(BeNil()) Expect(*clusterOutput.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") - Expect(clusterOutput.Kind).To(Equal("Cluster")) + Expect(*clusterOutput.Kind).To(Equal("Cluster")) Expect(*clusterOutput.Href).To(Equal(fmt.Sprintf("/api/hyperfleet/v1/clusters/%s", *clusterOutput.Id))) // 400 bad request. posting junk json is one way to trigger 400. - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) restyResp, err := resty.R(). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", fmt.Sprintf("Bearer %s", jwtToken)). @@ -94,15 +100,25 @@ func TestClusterPaging(t *testing.T) { _, err := h.Factories.NewClustersList("Bronto", 20) Expect(err).NotTo(HaveOccurred()) - list, _, err := client.DefaultAPI.GetClusters(ctx).Execute() + resp, err := client.GetClustersWithResponse(ctx, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting cluster list: %v", err) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(Equal(20)) Expect(list.Size).To(Equal(int32(20))) Expect(list.Total).To(Equal(int32(20))) Expect(list.Page).To(Equal(int32(1))) - list, _, err = client.DefaultAPI.GetClusters(ctx).Page(2).PageSize(5).Execute() + page := openapi.QueryParamsPage(2) + pageSize := openapi.QueryParamsPageSize(5) + params := &openapi.GetClustersParams{ + Page: &page, + PageSize: &pageSize, + } + resp, err = client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting cluster list: %v", err) + list = resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(Equal(5)) Expect(list.Size).To(Equal(int32(5))) Expect(list.Total).To(Equal(int32(20))) @@ -118,9 +134,15 @@ func TestClusterListSearch(t *testing.T) { clusters, err := h.Factories.NewClustersList("bronto", 20) Expect(err).NotTo(HaveOccurred(), "Error creating test clusters: %v", err) - search := fmt.Sprintf("id in ('%s')", clusters[0].ID) - list, _, err := client.DefaultAPI.GetClusters(ctx).Search(search).Execute() + searchStr := fmt.Sprintf("id in ('%s')", clusters[0].ID) + search := openapi.SearchParams(searchStr) + params := &openapi.GetClustersParams{ + Search: &search, + } + resp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting cluster list: %v", err) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(Equal(1)) Expect(list.Total).To(Equal(int32(1))) Expect(*list.Items[0].Id).To(Equal(clusters[0].ID)) @@ -138,8 +160,12 @@ func TestClusterSearchSQLInjection(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Test 1: SQL injection attempt with OR - maliciousSearch := "id='anything' OR '1'='1'" - _, _, err = client.DefaultAPI.GetClusters(ctx).Search(maliciousSearch).Execute() + maliciousSearchStr := "id='anything' OR '1'='1'" + maliciousSearch := openapi.SearchParams(maliciousSearchStr) + params := &openapi.GetClustersParams{ + Search: &maliciousSearch, + } + _, err = client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) // Should either return 400 error or return empty/controlled results // Not crash or return all data if err == nil { @@ -148,23 +174,33 @@ func TestClusterSearchSQLInjection(t *testing.T) { } // Test 2: SQL injection attempt with DROP - dropSearch := "id='; DROP TABLE clusters; --" - _, _, err = client.DefaultAPI.GetClusters(ctx).Search(dropSearch).Execute() + dropSearchStr := "id='; DROP TABLE clusters; --" + dropSearch := openapi.SearchParams(dropSearchStr) + params = &openapi.GetClustersParams{ + Search: &dropSearch, + } + _, err = client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) // Should not crash if err == nil { t.Logf("Search with DROP statement did not error - implementation may handle it gracefully") } // Test 3: Verify clusters still exist after injection attempts - list, _, err := client.DefaultAPI.GetClusters(ctx).Execute() + resp, err := client.GetClustersWithResponse(ctx, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(list.Total).To(BeNumerically(">=", 5), "Clusters should still exist after injection attempts") // Test 4: Valid search still works - validSearch := fmt.Sprintf("id='%s'", clusters[0].ID) - validList, _, err := client.DefaultAPI.GetClusters(ctx).Search(validSearch).Execute() + validSearchStr := fmt.Sprintf("id='%s'", clusters[0].ID) + validSearch := openapi.SearchParams(validSearchStr) + params = &openapi.GetClustersParams{ + Search: &validSearch, + } + resp, err = client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(len(validList.Items)).To(BeNumerically(">=", 0)) + Expect(len(resp.JSON200.Items)).To(BeNumerically(">=", 0)) } // TestClusterDuplicateNames tests that duplicate cluster names are rejected @@ -176,26 +212,26 @@ func TestClusterDuplicateNames(t *testing.T) { // Create first cluster with a specific name clusterInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "duplicate-name-test", Spec: map[string]interface{}{"test": "spec1"}, } - cluster1, resp, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(clusterInput).Execute() + resp, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(clusterInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - id1 := *cluster1.Id + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + id1 := *resp.JSON201.Id // Create second cluster with the SAME name // Names are unique, so this should return 409 Conflict - _, resp, err = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(clusterInput).Execute() - Expect(err).To(HaveOccurred(), "Expected 409 Conflict for duplicate name") - Expect(resp.StatusCode).To(Equal(http.StatusConflict)) + resp, err = client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(clusterInput), test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusConflict), "Expected 409 Conflict for duplicate name") // Verify first cluster still exists - retrieved1, _, err := client.DefaultAPI.GetClusterById(ctx, id1).Execute() + getResp, err := client.GetClusterByIdWithResponse(ctx, id1, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(retrieved1.Name).To(Equal("duplicate-name-test")) + Expect(getResp.JSON200.Name).To(Equal("duplicate-name-test")) } // TestClusterBoundaryValues tests boundary values for cluster fields @@ -212,38 +248,37 @@ func TestClusterBoundaryValues(t *testing.T) { } longNameInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: longName, Spec: map[string]interface{}{"test": "spec"}, } - longNameCluster, resp, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(longNameInput).Execute() + resp, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(longNameInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Should accept name up to 63 characters") - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(longNameCluster.Name).To(Equal(longName)) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + Expect(resp.JSON201.Name).To(Equal(longName)) // Test exceeding max length (64 characters should fail) tooLongName := longName + "a" tooLongInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: tooLongName, Spec: map[string]interface{}{"test": "spec"}, } - _, resp, err = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(tooLongInput).Execute() - Expect(err).To(HaveOccurred(), "Should reject name exceeding 63 characters") - Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) + resp, err = client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(tooLongInput), test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusBadRequest), "Should reject name exceeding 63 characters") // Test 2: Empty name emptyNameInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "", Spec: map[string]interface{}{"test": "spec"}, } - _, resp, err = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(emptyNameInput).Execute() - // Empty name should be rejected with 400 - Expect(err).To(HaveOccurred(), "Should reject empty name") - Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) + resp, err = client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(emptyNameInput), test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusBadRequest), "Should reject empty name") // Test 3: Large spec JSON (test with ~10KB JSON) largeSpec := make(map[string]interface{}) @@ -252,30 +287,30 @@ func TestClusterBoundaryValues(t *testing.T) { } largeSpecInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "large-spec-test", Spec: largeSpec, } - largeSpecCluster, resp, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(largeSpecInput).Execute() + resp, err = client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(largeSpecInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Should accept large spec JSON") - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) // Verify the spec was stored correctly - retrieved, _, err := client.DefaultAPI.GetClusterById(ctx, *largeSpecCluster.Id).Execute() + getResp, err := client.GetClusterByIdWithResponse(ctx, *resp.JSON201.Id, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(len(retrieved.Spec)).To(Equal(100)) + Expect(len(getResp.JSON200.Spec)).To(Equal(100)) // Test 4: Unicode in name (should be rejected - pattern only allows [a-z0-9-]) unicodeNameInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "テスト-δοκιμή-🚀", Spec: map[string]interface{}{"test": "spec"}, } - _, resp, err = client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(unicodeNameInput).Execute() - Expect(err).To(HaveOccurred(), "Should reject unicode in name (pattern is ^[a-z0-9-]+$)") - Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) + resp, err = client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(unicodeNameInput), test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusBadRequest), "Should reject unicode in name (pattern is ^[a-z0-9-]+$)") } // TestClusterSchemaValidation tests schema validation for cluster specs @@ -295,15 +330,15 @@ func TestClusterSchemaValidation(t *testing.T) { } validInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "schema-valid-test", Spec: validSpec, } - cluster, resp, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(validInput).Execute() + resp, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(validInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Valid spec should be accepted") - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*cluster.Id).NotTo(BeEmpty()) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + Expect(*resp.JSON201.Id).NotTo(BeEmpty()) // Test 2: Invalid spec type (spec must be object, not string) // This should fail even with base schema @@ -315,7 +350,7 @@ func TestClusterSchemaValidation(t *testing.T) { "spec": "invalid-string-spec" }` - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) resp2, _ := resty.R(). SetHeader("Content-Type", "application/json"). @@ -336,15 +371,15 @@ func TestClusterSchemaValidation(t *testing.T) { // Test 3: Empty spec (should be valid as spec is optional in base schema) emptySpecInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "schema-empty-spec", Spec: map[string]interface{}{}, } - cluster3, resp3, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(emptySpecInput).Execute() + resp3, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(emptySpecInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Empty spec should be accepted by base schema") - Expect(resp3.StatusCode).To(Equal(http.StatusCreated)) - Expect(*cluster3.Id).NotTo(BeEmpty()) + Expect(resp3.StatusCode()).To(Equal(http.StatusCreated)) + Expect(*resp3.JSON201.Id).NotTo(BeEmpty()) } // TestClusterSchemaValidationWithProviderSchema tests schema validation with a provider-specific schema @@ -378,18 +413,17 @@ func TestClusterSchemaValidationWithProviderSchema(t *testing.T) { } invalidInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: "provider-schema-invalid", Spec: invalidSpec, } - _, resp, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(invalidInput).Execute() - Expect(err).To(HaveOccurred(), "Should reject spec with missing required field") - Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) - defer func() { _ = resp.Body.Close() }() + resp, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(invalidInput), test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusBadRequest), "Should reject spec with missing required field") // Parse error response to verify field-level details - bodyBytes, err := io.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.HTTPResponse.Body) if err != nil { t.Fatalf("failed to read response body: %v", err) } @@ -405,10 +439,12 @@ func TestClusterSchemaValidationWithProviderSchema(t *testing.T) { // Verify details contain field path foundRegionError := false - for _, detail := range errorResponse.Details { - if detail.Field != nil && strings.Contains(*detail.Field, "region") { - foundRegionError = true - break + if errorResponse.Details != nil { + for _, detail := range *errorResponse.Details { + if detail.Field != nil && strings.Contains(*detail.Field, "region") { + foundRegionError = true + break + } } } Expect(foundRegionError).To(BeTrue(), "Error details should mention missing 'region' field") @@ -430,7 +466,7 @@ func TestClusterSchemaValidationErrorDetails(t *testing.T) { } body, _ := json.Marshal(invalidTypeRequest) - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) resp, err := resty.R(). SetHeader("Content-Type", "application/json"). @@ -481,22 +517,24 @@ func TestClusterList_DefaultSorting(t *testing.T) { var createdClusters []openapi.Cluster for i := 1; i <= 3; i++ { clusterInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: fmt.Sprintf("sort-test-%d-%s", i, strings.ToLower(h.NewID())), Spec: map[string]interface{}{"test": fmt.Sprintf("value-%d", i)}, } - cluster, _, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(clusterInput).Execute() + resp, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(clusterInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Failed to create cluster %d", i) - createdClusters = append(createdClusters, *cluster) + createdClusters = append(createdClusters, *resp.JSON201) // Add 100ms delay to ensure different created_time time.Sleep(100 * time.Millisecond) } // List clusters without orderBy parameter - should default to created_time desc - list, _, err := client.DefaultAPI.GetClusters(ctx).Execute() + listResp, err := client.GetClustersWithResponse(ctx, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Failed to list clusters") + list := listResp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(BeNumerically(">=", 3), "Should have at least 3 clusters") // Find our test clusters in the response @@ -538,18 +576,25 @@ func TestClusterList_OrderByName(t *testing.T) { for _, name := range names { clusterInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: name, Spec: map[string]interface{}{"test": "value"}, } - _, _, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(clusterInput).Execute() + _, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(clusterInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Failed to create cluster %s", name) } // List with orderBy=name asc - list, _, err := client.DefaultAPI.GetClusters(ctx).OrderBy("name asc").Execute() + orderByStr := "name asc" + orderBy := openapi.QueryParamsOrderBy(orderByStr) + params := &openapi.GetClustersParams{ + OrderBy: &orderBy, + } + listResp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Failed to list clusters with orderBy") + list := listResp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(BeNumerically(">=", 3), "Should have at least 3 clusters") // Find our test clusters in the response @@ -587,18 +632,24 @@ func TestClusterList_OrderByNameDesc(t *testing.T) { for _, name := range names { clusterInput := openapi.ClusterCreateRequest{ - Kind: "Cluster", + Kind: openapi.PtrString("Cluster"), Name: name, Spec: map[string]interface{}{"test": "value"}, } - _, _, err := client.DefaultAPI.PostCluster(ctx).ClusterCreateRequest(clusterInput).Execute() + _, err := client.PostClusterWithResponse(ctx, openapi.PostClusterJSONRequestBody(clusterInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Failed to create cluster %s", name) } // List with orderBy=name desc - list, _, err := client.DefaultAPI.GetClusters(ctx).OrderBy("name desc").Execute() + orderByStr := "name desc" + orderBy := openapi.QueryParamsOrderBy(orderByStr) + params := &openapi.GetClustersParams{ + OrderBy: &orderBy, + } + listResp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Failed to list clusters with orderBy desc") + list := listResp.JSON200 // Find our test clusters in the response var testClusters []openapi.Cluster @@ -624,7 +675,7 @@ func TestClusterPost_EmptyKind(t *testing.T) { account := h.NewRandAccount() ctx := h.NewAuthenticatedContext(account) - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) // Send request with empty kind invalidInput := `{ @@ -659,7 +710,7 @@ func TestClusterPost_WrongKind(t *testing.T) { account := h.NewRandAccount() ctx := h.NewAuthenticatedContext(account) - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) // Send request with wrong kind invalidInput := `{ diff --git a/test/integration/node_pools_test.go b/test/integration/node_pools_test.go index 62bbc9c..3680a71 100644 --- a/test/integration/node_pools_test.go +++ b/test/integration/node_pools_test.go @@ -58,22 +58,26 @@ func TestNodePoolPost(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // POST responses per openapi spec: 201, 409, 500 + kind := "NodePool" nodePoolInput := openapi.NodePoolCreateRequest{ - Kind: openapi.PtrString("NodePool"), + Kind: &kind, Name: "test-name", Spec: map[string]interface{}{"test": "spec"}, } // 201 Created - nodePoolOutput, resp, err := client.DefaultAPI.CreateNodePool(ctx, cluster.ID).NodePoolCreateRequest(nodePoolInput).Execute() + resp, err := client.CreateNodePoolWithResponse(ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(nodePoolInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error posting object: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + + nodePoolOutput := resp.JSON201 + Expect(nodePoolOutput).NotTo(BeNil()) Expect(*nodePoolOutput.Id).NotTo(BeEmpty(), "Expected ID assigned on creation") Expect(*nodePoolOutput.Kind).To(Equal("NodePool")) Expect(*nodePoolOutput.Href).To(Equal(fmt.Sprintf("/api/hyperfleet/v1/clusters/%s/nodepools/%s", cluster.ID, *nodePoolOutput.Id))) // 400 bad request. posting junk json is one way to trigger 400. - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) restyResp, err := resty.R(). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", fmt.Sprintf("Bearer %s", jwtToken)). @@ -99,15 +103,25 @@ func TestNodePoolPaging(t *testing.T) { _, err := h.Factories.NewNodePoolsList("Bronto", 20) Expect(err).NotTo(HaveOccurred()) - list, _, err := client.DefaultAPI.GetNodePools(ctx).Execute() + resp, err := client.GetNodePoolsWithResponse(ctx, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting nodePool list: %v", err) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(Equal(20)) Expect(list.Size).To(Equal(int32(20))) Expect(list.Total).To(Equal(int32(20))) Expect(list.Page).To(Equal(int32(1))) - list, _, err = client.DefaultAPI.GetNodePools(ctx).Page(2).PageSize(5).Execute() + page := openapi.QueryParamsPage(2) + pageSize := openapi.QueryParamsPageSize(5) + params := &openapi.GetNodePoolsParams{ + Page: &page, + PageSize: &pageSize, + } + resp, err = client.GetNodePoolsWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting nodePool list: %v", err) + list = resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(Equal(5)) Expect(list.Size).To(Equal(int32(5))) Expect(list.Total).To(Equal(int32(20))) @@ -123,9 +137,15 @@ func TestNodePoolListSearch(t *testing.T) { nodePools, err := h.Factories.NewNodePoolsList("bronto", 20) Expect(err).NotTo(HaveOccurred(), "Error creating test nodepools: %v", err) - search := fmt.Sprintf("id in ('%s')", nodePools[0].ID) - list, _, err := client.DefaultAPI.GetNodePools(ctx).Search(search).Execute() + searchStr := fmt.Sprintf("id in ('%s')", nodePools[0].ID) + search := openapi.SearchParams(searchStr) + params := &openapi.GetNodePoolsParams{ + Search: &search, + } + resp, err := client.GetNodePoolsWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting nodePool list: %v", err) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(len(list.Items)).To(Equal(1)) Expect(list.Total).To(Equal(int32(1))) Expect(*list.Items[0].Id).To(Equal(nodePools[0].ID)) @@ -148,10 +168,10 @@ func TestNodePoolsByClusterId(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Get nodepools by cluster ID - list, resp, err := client.DefaultAPI.GetNodePoolsByClusterId(ctx, cluster.ID).Execute() + resp, err := client.GetNodePoolsByClusterIdWithResponse(ctx, cluster.ID, nil, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting nodepools by cluster ID: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(list).NotTo(BeNil()) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + Expect(resp.JSON200).NotTo(BeNil()) // The list might be empty if nodepools aren't properly associated with the cluster // but the endpoint should work } @@ -167,44 +187,47 @@ func TestGetNodePoolByClusterIdAndNodePoolId(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Create a nodepool for this cluster using the API + kind := "NodePool" nodePoolInput := openapi.NodePoolCreateRequest{ - Kind: openapi.PtrString("NodePool"), + Kind: &kind, Name: "test-nodepool-get", Spec: map[string]interface{}{"instance_type": "m5.large", "replicas": 2}, } - nodePoolOutput, resp, err := client.DefaultAPI.CreateNodePool(ctx, cluster.ID).NodePoolCreateRequest(nodePoolInput).Execute() + createResp, err := client.CreateNodePoolWithResponse(ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(nodePoolInput), test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error creating nodepool: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusCreated)) - Expect(*nodePoolOutput.Id).NotTo(BeEmpty()) + Expect(createResp.StatusCode()).To(Equal(http.StatusCreated)) + Expect(*createResp.JSON201.Id).NotTo(BeEmpty()) - nodePoolID := *nodePoolOutput.Id + nodePoolID := *createResp.JSON201.Id // Test 1: Get the nodepool by cluster ID and nodepool ID (200 OK) - retrieved, resp, err := client.DefaultAPI.GetNodePoolById(ctx, cluster.ID, nodePoolID).Execute() + getResp, err := client.GetNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred(), "Error getting nodepool by cluster and nodepool ID: %v", err) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(getResp.StatusCode()).To(Equal(http.StatusOK)) + retrieved := getResp.JSON200 + Expect(retrieved).NotTo(BeNil()) Expect(*retrieved.Id).To(Equal(nodePoolID), "Retrieved nodepool ID should match") Expect(*retrieved.Kind).To(Equal("NodePool")) Expect(retrieved.Name).To(Equal("test-nodepool-get")) // Test 2: Try to get with non-existent nodepool ID (404) - _, resp, err = client.DefaultAPI.GetNodePoolById(ctx, cluster.ID, "non-existent-id").Execute() - Expect(err).To(HaveOccurred(), "Expected 404 for non-existent nodepool") - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + notFoundResp, err := client.GetNodePoolByIdWithResponse(ctx, cluster.ID, "non-existent-id", test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(notFoundResp.StatusCode()).To(Equal(http.StatusNotFound), "Expected 404 for non-existent nodepool") // Test 3: Try to get with non-existent cluster ID (404) - _, resp, err = client.DefaultAPI.GetNodePoolById(ctx, "non-existent-cluster", nodePoolID).Execute() - Expect(err).To(HaveOccurred(), "Expected 404 for non-existent cluster") - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + notFoundResp, err = client.GetNodePoolByIdWithResponse(ctx, "non-existent-cluster", nodePoolID, test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(notFoundResp.StatusCode()).To(Equal(http.StatusNotFound), "Expected 404 for non-existent cluster") // Test 4: Create another cluster and verify that nodepool is not accessible from wrong cluster cluster2, err := h.Factories.NewClusters(h.NewID()) Expect(err).NotTo(HaveOccurred()) - _, resp, err = client.DefaultAPI.GetNodePoolById(ctx, cluster2.ID, nodePoolID).Execute() - Expect(err).To(HaveOccurred(), "Expected 404 when accessing nodepool from wrong cluster") - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + wrongClusterResp, err := client.GetNodePoolByIdWithResponse(ctx, cluster2.ID, nodePoolID, test.WithAuthToken(ctx)) + Expect(err).NotTo(HaveOccurred()) + Expect(wrongClusterResp.StatusCode()).To(Equal(http.StatusNotFound), "Expected 404 when accessing nodepool from wrong cluster") } // TestNodePoolPost_EmptyKind tests that empty kind field returns 400 @@ -213,7 +236,7 @@ func TestNodePoolPost_EmptyKind(t *testing.T) { account := h.NewRandAccount() ctx := h.NewAuthenticatedContext(account) - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) // Create a cluster first cluster, err := h.Factories.NewClusters(h.NewID()) @@ -252,7 +275,7 @@ func TestNodePoolPost_WrongKind(t *testing.T) { account := h.NewRandAccount() ctx := h.NewAuthenticatedContext(account) - jwtToken := ctx.Value(openapi.ContextAccessToken) + jwtToken := test.GetAccessTokenFromContext(ctx) // Create a cluster first cluster, err := h.Factories.NewClusters(h.NewID()) diff --git a/test/integration/search_field_mapping_test.go b/test/integration/search_field_mapping_test.go index 2e4f044..04fe7de 100644 --- a/test/integration/search_field_mapping_test.go +++ b/test/integration/search_field_mapping_test.go @@ -31,11 +31,17 @@ func TestSearchStatusPhaseMapping(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Query NotReady clusters using user-friendly syntax - search := "status.phase='NotReady'" - list, resp, err := client.DefaultAPI.GetClusters(ctx).Search(search).Execute() + searchStr := "status.phase='NotReady'" + search := openapi.SearchParams(searchStr) + params := &openapi.GetClustersParams{ + Search: &search, + } + resp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(list.Total).To(BeNumerically(">=", 1)) // Verify all returned clusters are NotReady @@ -45,7 +51,7 @@ func TestSearchStatusPhaseMapping(t *testing.T) { foundNotReady = true // Status field structure depends on openapi.yaml // Assuming status.phase exists - Expect(item.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(item.Status.Phase).To(Equal(openapi.NotReady)) } // Should not contain readyCluster Expect(*item.Id).NotTo(Equal(readyCluster.ID)) @@ -77,11 +83,17 @@ func TestSearchStatusLastUpdatedTimeMapping(t *testing.T) { // Query clusters updated before 1 hour ago threshold := now.Add(-1 * time.Hour) - search := fmt.Sprintf("status.last_updated_time < '%s'", threshold.Format(time.RFC3339)) - list, resp, err := client.DefaultAPI.GetClusters(ctx).Search(search).Execute() + searchStr := fmt.Sprintf("status.last_updated_time < '%s'", threshold.Format(time.RFC3339)) + search := openapi.SearchParams(searchStr) + params := &openapi.GetClustersParams{ + Search: &search, + } + resp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) // Should return at least oldCluster Expect(list.Total).To(BeNumerically(">=", 1)) @@ -121,11 +133,17 @@ func TestSearchLabelsMapping(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Query production environment clusters using user-friendly syntax - search := "labels.environment='production'" - list, resp, err := client.DefaultAPI.GetClusters(ctx).Search(search).Execute() + searchStr := "labels.environment='production'" + search := openapi.SearchParams(searchStr) + params := &openapi.GetClustersParams{ + Search: &search, + } + resp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(list.Total).To(BeNumerically(">=", 1)) // Verify returned clusters have correct label @@ -154,12 +172,16 @@ func TestSearchSpecFieldRejected(t *testing.T) { ctx := h.NewAuthenticatedContext(account) // Attempt to query spec field (should be rejected) - search := "spec = '{}'" - _, resp, err := client.DefaultAPI.GetClusters(ctx).Search(search).Execute() + searchStr := "spec = '{}'" + search := openapi.SearchParams(searchStr) + params := &openapi.GetClustersParams{ + Search: &search, + } + resp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) // Should return error - Expect(err).To(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusBadRequest)) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode()).To(Equal(http.StatusBadRequest)) } // TestSearchCombinedQuery verifies that combined queries (AND/OR) @@ -205,11 +227,17 @@ func TestSearchCombinedQuery(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Query using combined AND condition - search := "status.phase='NotReady' and labels.region='us-east'" - list, resp, err := client.DefaultAPI.GetClusters(ctx).Search(search).Execute() + searchStr := "status.phase='NotReady' and labels.region='us-east'" + search := openapi.SearchParams(searchStr) + params := &openapi.GetClustersParams{ + Search: &search, + } + resp, err := client.GetClustersWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(list.Total).To(BeNumerically(">=", 1)) // Should only return matchCluster @@ -217,7 +245,7 @@ func TestSearchCombinedQuery(t *testing.T) { for _, item := range list.Items { if *item.Id == matchCluster.ID { foundMatch = true - Expect(item.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(item.Status.Phase).To(Equal(openapi.NotReady)) } // Should not contain wrongRegionCluster or wrongStatusCluster Expect(*item.Id).NotTo(Equal(wrongRegionCluster.ID)) @@ -244,11 +272,17 @@ func TestSearchNodePoolFieldMapping(t *testing.T) { Expect(err).NotTo(HaveOccurred()) // Query NotReady NodePools using user-friendly syntax - search := "status.phase='NotReady'" - list, resp, err := client.DefaultAPI.GetNodePools(ctx).Search(search).Execute() + searchStr := "status.phase='NotReady'" + search := openapi.SearchParams(searchStr) + params := &openapi.GetNodePoolsParams{ + Search: &search, + } + resp, err := client.GetNodePoolsWithResponse(ctx, params, test.WithAuthToken(ctx)) Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(resp.StatusCode()).To(Equal(http.StatusOK)) + list := resp.JSON200 + Expect(list).NotTo(BeNil()) Expect(list.Total).To(BeNumerically(">=", 1)) // Verify NotReady NodePool is in results @@ -256,7 +290,7 @@ func TestSearchNodePoolFieldMapping(t *testing.T) { for _, item := range list.Items { if *item.Id == notReadyNP.ID { foundNotReady = true - Expect(item.Status.Phase).To(Equal(openapi.NOT_READY)) + Expect(item.Status.Phase).To(Equal(openapi.NotReady)) } // Should not contain readyNP Expect(*item.Id).NotTo(Equal(readyNP.ID)) @@ -269,11 +303,17 @@ func TestSearchNodePoolFieldMapping(t *testing.T) { }) Expect(err).NotTo(HaveOccurred()) - searchLabels := "labels.environment='test'" - labelsList, labelsResp, labelsErr := client.DefaultAPI.GetNodePools(ctx).Search(searchLabels).Execute() + searchLabelsStr := "labels.environment='test'" + searchLabels := openapi.SearchParams(searchLabelsStr) + labelsParams := &openapi.GetNodePoolsParams{ + Search: &searchLabels, + } + labelsResp, labelsErr := client.GetNodePoolsWithResponse(ctx, labelsParams, test.WithAuthToken(ctx)) Expect(labelsErr).NotTo(HaveOccurred()) - Expect(labelsResp.StatusCode).To(Equal(http.StatusOK)) + Expect(labelsResp.StatusCode()).To(Equal(http.StatusOK)) + labelsList := labelsResp.JSON200 + Expect(labelsList).NotTo(BeNil()) foundLabeled := false for _, item := range labelsList.Items { diff --git a/test/registration.go b/test/registration.go index defd794..7555c31 100755 --- a/test/registration.go +++ b/test/registration.go @@ -10,7 +10,7 @@ import ( // RegisterIntegration Register a test // This should be run before every integration test -func RegisterIntegration(t *testing.T) (*Helper, *openapi.APIClient) { +func RegisterIntegration(t *testing.T) (*Helper, *openapi.ClientWithResponses) { // Register the test with gomega gm.RegisterTestingT(t) // Create a new helper