diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d6f78f6..affdfa7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,7 +13,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v5 with: - go-version: ^1.24.0 + go-version: ^1.24.2 - name: Check out code uses: actions/checkout@v4 @@ -35,6 +35,7 @@ jobs: - name: Build plugins run: | echo "Building plugins" + go mod tidy go mod download CGO_ENABLED=1 go build --buildmode=plugin ./gen3_writer CGO_ENABLED=1 go build --buildmode=plugin ./grip-js diff --git a/Dockerfile b/Dockerfile index 82695dd..6593015 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,12 @@ ENV PATH="/go/bin:${PATH}" ADD ./ /go/src/github.com/bmeg/grip-graphql WORKDIR /go/src/github.com/bmeg/grip-graphql +RUN ls -l middleware +RUN pwd + +RUN go mod tidy +RUN go mod download + RUN GRIP_VERSION=$(go list -m -json github.com/bmeg/grip | jq -r '.Version') && \ go install github.com/bmeg/grip@$GRIP_VERSION RUN make all @@ -18,5 +24,4 @@ COPY --from=build-env /go/src/github.com/bmeg/grip-graphql/gql-gen.so /data/ COPY --from=build-env /go/src/github.com/bmeg/grip-graphql/gen3_writer.so /data/ COPY --from=build-env /go/src/github.com/bmeg/grip-graphql/grip-js.so /data/ COPY --from=build-env /go/src/github.com/bmeg/grip-graphql/grip-js/config/gen3.js /data/config/ -COPY --from=build-env /go/src/github.com/bmeg/grip-graphql/mongo.yml /data/ COPY --from=build-env /go/bin/grip /app/ diff --git a/gen3_writer/handler.go b/gen3_writer/handler.go index e8d3c24..0eaf93a 100644 --- a/gen3_writer/handler.go +++ b/gen3_writer/handler.go @@ -88,7 +88,7 @@ func TokenAuthMiddleware(jwtHandler middleware.JWTHandler) gin.HandlerFunc { return } - anyList, err := jwtHandler.HandleJWTToken(Token, method) + anyList, err := jwtHandler.GetAllowedResources(Token, method, "*") if err != nil { RegError(c, c.Writer, c.Param("graph"), err) return @@ -128,7 +128,7 @@ func NewHTTPHandler(client gripql.Client, config map[string]string) (http.Handle // Since using Grip logging functions, log config needs to be set to properly format JSON data outputs logConfig := log.Logger{ - Level: "info", + Level: "debug", Formatter: "json", } log.ConfigureLogger(logConfig) diff --git a/go.mod b/go.mod index c8dd8b7..8db71f0 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,29 @@ module github.com/bmeg/grip-graphql -go 1.24 - -toolchain go1.24.2 +go 1.24.2 require ( github.com/99designs/gqlgen v0.17.57 - github.com/bmeg/grip v0.0.0-20250714173925-c1892d4f63b1 + github.com/bmeg/grip v0.0.0-20251202230911-d54d9a03cb7d github.com/dop251/goja v0.0.0-20240707163329-b1681fb2a2f5 github.com/gin-gonic/gin v1.8.1 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/uuid v1.6.0 github.com/graphql-go/graphql v0.8.1 github.com/graphql-go/handler v0.2.4 - github.com/patrickmn/go-cache v2.1.0+incompatible github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/vektah/gqlparser/v2 v2.5.19 golang.org/x/exp v0.0.0-20240707233637-46b078467d37 - google.golang.org/protobuf v1.36.5 + google.golang.org/protobuf v1.36.7 ) require ( github.com/agnivade/levenshtein v1.2.0 // indirect github.com/bmeg/jsonpath v0.0.0-20210207014051-cca5355553ad // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/dlclark/regexp2 v1.11.2 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -37,7 +37,7 @@ require ( github.com/goccy/go-json v0.10.3 // indirect github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -45,7 +45,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -63,20 +63,22 @@ require ( github.com/segmentio/ksuid v1.0.4 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sosodev/duration v1.3.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.7 // indirect github.com/urfave/cli/v2 v2.27.5 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/crypto v0.33.0 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sync v0.11.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/term v0.29.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/tools v0.28.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect - google.golang.org/grpc v1.70.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect + google.golang.org/grpc v1.71.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index c03964e..3cec8de 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/99designs/gqlgen v0.17.57 h1:Ak4p60BRq6QibxY0lEc0JnQhDurfhxA67sp02lMjmPc= github.com/99designs/gqlgen v0.17.57/go.mod h1:Jx61hzOSTcR4VJy/HFIgXiQ5rJ0Ypw8DxWLjbYDAUw0= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0= github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= @@ -12,10 +14,22 @@ github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/bmeg/grip v0.0.0-20250714173925-c1892d4f63b1 h1:19EoCYhIUMQO7eOYaveueecefJQUu7gkbi8oPBir5zE= -github.com/bmeg/grip v0.0.0-20250714173925-c1892d4f63b1/go.mod h1:laB5Md64qjhrb9spgvViXG/eeOfx+mNpLcsYJWbJyMs= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bmeg/grip v0.0.0-20251202230911-d54d9a03cb7d h1:qUd4kCVq2KG1Oz2s8O+pNdfGcbCM6sk2Wntf0P/P8Qo= +github.com/bmeg/grip v0.0.0-20251202230911-d54d9a03cb7d/go.mod h1:BxpaUuXbymKkEPvSDslziCzU17akkBo1ubu9nAFsI1A= github.com/bmeg/jsonpath v0.0.0-20210207014051-cca5355553ad h1:ICgBexeLB7iv/IQz4rsP+MimOXFZUwWSPojEypuOaQ8= github.com/bmeg/jsonpath v0.0.0-20210207014051-cca5355553ad/go.mod h1:ft96Irkp72C7ZrUWRenG7LrF0NKMxXdRvsypo5Njhm4= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -31,12 +45,18 @@ github.com/dop251/goja v0.0.0-20240707163329-b1681fb2a2f5 h1:ZRqTaoW9WZ2DqeOQGhK github.com/dop251/goja v0.0.0-20240707163329-b1681fb2a2f5/go.mod h1:o31y53rb/qiIAONF7w3FHJZRqqP3fzHUr1HqanthByw= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -53,14 +73,22 @@ github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TC github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -75,8 +103,8 @@ github.com/graphql-go/graphql v0.8.1 h1:p7/Ou/WpmulocJeEx7wjQy611rtXGQaAcXGqanuM github.com/graphql-go/graphql v0.8.1/go.mod h1:nKiHzRM0qopJEwCITUuIsxk9PlVlwIiiI8pnJEhordQ= github.com/graphql-go/handler v0.2.4 h1:gz9q11TUHPNUpqzV8LMa+rkqM5NUuH/nkE3oF2LS3rI= github.com/graphql-go/handler v0.2.4/go.mod h1:gsQlb4gDvURR0bgN8vWQEh+s5vJALM2lYL3n3cf6OxQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -92,11 +120,16 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -126,13 +159,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -146,6 +180,7 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= @@ -155,14 +190,21 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k 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/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= @@ -172,65 +214,134 @@ github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egpl github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +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/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20190911185100-cd5d95a43a6e/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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190422165155-953cdadca894/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-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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 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= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= -google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= -google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a h1:DMCgtIAIQGZqJXMVzJF4MV8BlWoJh2ZuFiRdAleyr58= +google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a/go.mod h1:y2yVLIE/CSMCPXaHnSKXxu1spLPnglFLegmgdY23uuE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/gql-gen/server.go b/gql-gen/server.go index 997d108..ea36ebd 100644 --- a/gql-gen/server.go +++ b/gql-gen/server.go @@ -81,7 +81,7 @@ func (gh *Handler) graphqlHandler(client gripql.Client, jwtHandler middleware.JW if os.Getenv("AUTH_ENABLED") == "true" { if val, ok := requestHeaders["Authorization"]; ok { Token := val[0] - anyList, err := jwtHandler.HandleJWTToken(Token, "read") + anyList, err := jwtHandler.GetAllowedResources(Token, "read", "*") if err != nil { RegError(c, c.Writer, c.Param("graph"), err) return diff --git a/grip-js/server/handler.go b/grip-js/server/handler.go index bd60b4f..88154c3 100644 --- a/grip-js/server/handler.go +++ b/grip-js/server/handler.go @@ -198,7 +198,7 @@ func (gh *GraphQLJS) ServeHTTP(writer http.ResponseWriter, request *http.Request log.Debug("Request Headers: ", requestHeaders) if val, ok := requestHeaders["Authorization"]; ok { Token := val[0] - resourceList, err := jwtHandler.HandleJWTToken(Token, "read") + resourceList, err := jwtHandler.GetAllowedResources(Token, "read", "*") log.Debugln("Resource List: ", resourceList) if err != nil { log.Debugln("HandleJWTToken Err: ", err) diff --git a/grip-js/server/js_client.go b/grip-js/server/js_client.go index d6cc1c9..cbc0864 100644 --- a/grip-js/server/js_client.go +++ b/grip-js/server/js_client.go @@ -45,7 +45,7 @@ func toInterface(qr *gripql.QueryResult) any { fmt.Println("QUERY RESULT: ", qr) if v := qr.GetVertex(); v != nil { data := v.GetDataMap() - data["id"] = v.Id + data["_id"] = v.Id return data } if e := qr.GetEdge(); e != nil { @@ -196,7 +196,7 @@ func (cw *JSClientWrapper) AddVertex(args ...goja.Value) goja.Value { Id: _id, Label: _label, } - vData["id"] = _id + vData["_id"] = _id vertex.SetDataMap(vData) log.Debugf("adding vertex: %#v graph: %s", vertex, cw.graph) diff --git a/middleware/authToken.go b/middleware/authToken.go new file mode 100644 index 0000000..7ae6115 --- /dev/null +++ b/middleware/authToken.go @@ -0,0 +1,84 @@ +package middleware + +import ( + "fmt" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +type MockJWTHandler struct{} +type ProdJWTHandler struct{} +type JWTHandler interface { + // This method assumes that you are checking method, service on resource paths of form + // /programs/[program_name]/projects/[project_name] + GetAllowedResources(token, method, service string) ([]any, error) + // This method is for checking exact permission for a specific resource + CheckResourceServiceAccess(token, method, service, resource string) (bool, error) +} + +func (m *MockJWTHandler) CheckResourceServiceAccess(token, method, service, resource string) (bool, error) { + return false, nil +} +func (m *MockJWTHandler) GetAllowedResources(token string, method string, service string) ([]any, error) { + expiration, err := GetExpiration(token) + if err != nil { + return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s Failed to get expiration from token %s", err, token)} + } + + if expiration.After(time.Now()) { + claims, err := m.decodeToken(token) + if err != nil { + return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("failed to parse token data %#v", token)} + } + subject, err := claims.GetSubject() + if err != nil { + return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("failed to parse token claims data %#v", claims)} + } + fmt.Println("SUBJECT:", subject, "") + if method == subject || "*" == subject { + return []any{"/programs/ohsu/projects/test"}, nil + } + return []any{}, nil + } + return nil, &ServerError{StatusCode: 401, Message: fmt.Sprintf("token %s has expired %s", token, expiration)} +} + +func (m *MockJWTHandler) decodeToken(tokenString string) (jwt.MapClaims, error) { + /* A Mock token decoder designed to decode mock tokens for testing purposes only */ + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte("foo-bar-signature"), nil + }) + if err != nil { + return nil, err + } + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + return claims, nil + } + return nil, fmt.Errorf("invalid token") +} + +func (m *ProdJWTHandler) CheckResourceServiceAccess(token, method, service, resource string) (bool, error) { + expiration, err := GetExpiration(token) + if err != nil { + return false, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s Failed to get expiration from token %s", err, token)} + } + if !expiration.After(time.Now()) { + return false, &ServerError{StatusCode: 401, Message: fmt.Sprintf("Token %s has expired %s", token, expiration)} + } + return IsProjectAllowedOnResource("http://arborist-service/auth/mapping", token, method, service, resource) +} + +func (j *ProdJWTHandler) GetAllowedResources(token string, method string, service string) ([]any, error) { + expiration, err := GetExpiration(token) + if err != nil { + return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s Failed to get expiration from token %s", err, token)} + } + if !expiration.After(time.Now()) { + return nil, &ServerError{StatusCode: 401, Message: fmt.Sprintf("Token %s has expired %s", token, expiration)} + } + return GetAllowedProjects("http://arborist-service/auth/mapping", token, method, service) +} diff --git a/middleware/gen3_errors.go b/middleware/errors.go similarity index 100% rename from middleware/gen3_errors.go rename to middleware/errors.go diff --git a/middleware/gen3_caching.go b/middleware/gen3_caching.go deleted file mode 100644 index fcd35be..0000000 --- a/middleware/gen3_caching.go +++ /dev/null @@ -1,138 +0,0 @@ -package middleware - -import ( - "fmt" - "strings" - "time" - - "github.com/bmeg/grip/log" - "github.com/golang-jwt/jwt/v5" - "github.com/patrickmn/go-cache" -) - -type TokenData struct { - Expiration time.Time - ResourceList []any -} - -var jwtCache *cache.Cache - -func init() { - jwtCache = cache.New(0, 0) -} - -func GetExpiration(tokenString string) (time.Time, error) { - // Also consider trimming the 'Bearer ' prefix too - tokenString = strings.TrimPrefix(tokenString, "bearer ") - token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) - if err != nil { - return time.Time{}, err - } - - // Parse and convert from float64 epoch time to time.Time - if claims, ok := token.Claims.(jwt.MapClaims); ok { - if exp, ok := claims["exp"].(float64); ok { - temp := int64(exp) - exp := time.Unix(temp, 0) - return exp, nil - } - } - return time.Time{}, fmt.Errorf("Expiration field 'exp' type float64 not found in token %v", token) -} - -type ProdJWTHandler struct{} -type JWTHandler interface { - HandleJWTToken(token, method string) ([]interface{}, error) -} -type MockJWTHandler struct{} - -func (m *MockJWTHandler) decodeToken(tokenString string) (jwt.MapClaims, error) { - /* A Mock token decoder designed to decode mock tokens for testing purposes only */ - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return []byte("foo-bar-signature"), nil - }) - if err != nil { - return nil, err - } - if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { - return claims, nil - } - return nil, fmt.Errorf("invalid token") -} - -func (m *MockJWTHandler) HandleJWTToken(token string, perm_method string) ([]any, error) { - expiration, err := GetExpiration(token) - if err != nil { - return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s Failed to get expiration from token %s", err, token)} - } - - if expiration.After(time.Now()) { - claims, err := m.decodeToken(token) - if err != nil { - return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("failed to parse token data %#v", token)} - } - subject, err := claims.GetSubject() - if err != nil { - return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("failed to parse token claims data %#v", claims)} - } - fmt.Println("SUBJECT:", subject, "") - if ((subject == "create-reader" || subject == "create") && perm_method == "create") || - ((subject == "reader" || subject == "create-reader") && perm_method == "read") { - return []any{"/programs/ohsu/projects/test"}, nil - } - - return []any{}, nil - } - return nil, &ServerError{StatusCode: 401, Message: fmt.Sprintf("token %s has expired %s", token, expiration)} -} - -func (j *ProdJWTHandler) HandleJWTToken(token string, perm_method string) ([]any, error) { - cachedData, found := jwtCache.Get(token) - - // If cache hit check expiration and return resourceList - if found { - tokenData, ok := cachedData.(TokenData) - if !ok { - return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("failed to parse token data %#v", cachedData)} - } - - if tokenData.Expiration.After(time.Now()) { - log.Infoln("Retrieved Cached token") - return tokenData.ResourceList, nil - } - jwtCache.Delete(token) - return nil, &ServerError{StatusCode: 401, Message: fmt.Sprintf("token %s has expired %s", token, tokenData.Expiration)} - } - - // Otherise check expiration, add token to cache and return resourceList - expiration, err := GetExpiration(token) - if err != nil { - return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s Failed to get expiration from token %s", err, token)} - } - - if expiration.After(time.Now()) { - resourceList, err := AddJWTToken(token, expiration, perm_method) - if err != nil { - return nil, err - } - return resourceList, nil - } - return nil, &ServerError{StatusCode: 401, Message: fmt.Sprintf("Token validation failed for token: %s", token)} -} - -func AddJWTToken(token string, expiration time.Time, method string) ([]any, error) { - resourceList, err := GetAllowedProjects("http://arborist-service/auth/mapping", token, method) - if err != nil { - return nil, err - } - - tokenData := TokenData{ - Expiration: expiration, - ResourceList: resourceList, - } - jwtCache.Set(token, tokenData, cache.NoExpiration) - return resourceList, nil -} diff --git a/middleware/gen3_resources.go b/middleware/resources.go similarity index 54% rename from middleware/gen3_resources.go rename to middleware/resources.go index 8f831fb..6fcbe5c 100644 --- a/middleware/gen3_resources.go +++ b/middleware/resources.go @@ -8,10 +8,15 @@ import ( "net/http" "regexp" "slices" + "strings" + "time" "github.com/bmeg/grip/log" + "github.com/golang-jwt/jwt/v5" ) +var HTTPClient = http.DefaultClient + func getAuthMappings(url string, token string) (any, error) { GetRequest, err := http.NewRequest("GET", url, nil) if err != nil { @@ -20,7 +25,7 @@ func getAuthMappings(url string, token string) (any, error) { } GetRequest.Header.Set("Authorization", token) GetRequest.Header.Set("Accept", "application/json") - fetchedData, err := http.DefaultClient.Do(GetRequest) + fetchedData, err := HTTPClient.Do(GetRequest) if err != nil { log.Error(err) return nil, err @@ -47,28 +52,27 @@ func getAuthMappings(url string, token string) (any, error) { return empty_map, err } -func hasPermission(permissions []any, method string) bool { +func hasPermission(permissions []any, method string, service string) bool { for _, permission := range permissions { permission := permission.(map[string]any) - if (permission["service"] == "*" || permission["service"] == "grip") && - (permission["method"] == "*" || permission["method"] == method) { - // fmt.Println("PERMISSIONS: ", permission) + if permission["service"] == service && + permission["method"] == method { return true } } return false } -func GetAllowedProjects(url string, token string, method string) ([]any, error) { +func GetAllowedProjects(url string, token string, method string, service string) ([]any, error) { var readAccessResources []string authMappings, err := getAuthMappings(url, token) if err != nil { return nil, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s", err)} } - // Iterate through /auth/mapping resultant dict checking for valid read permissions + // Iterate through /auth/mapping resultant dict checking for valid permissions for resourcePath, permissions := range authMappings.(map[string]any) { - if hasPermission(permissions.([]any), method) { + if hasPermission(permissions.([]any), method, service) { readAccessResources = append(readAccessResources, resourcePath) } } @@ -77,7 +81,7 @@ func GetAllowedProjects(url string, token string, method string) ([]any, error) pattern := regexp.MustCompile(`^/programs/[^/]+/projects/[^/]+$`) ProjectIds := filterProjects(readAccessResources, pattern) - s := make([]interface{}, len(ProjectIds)) + s := make([]any, len(ProjectIds)) for i, v := range ProjectIds { s[i] = v } @@ -94,3 +98,39 @@ func filterProjects(input []string, pattern *regexp.Regexp) []string { slices.Sort(filtered) return filtered } + +func IsProjectAllowedOnResource(url, token, method, service, resource string) (bool, error) { + authMappings, err := getAuthMappings(url, token) + if err != nil { + return false, &ServerError{StatusCode: 400, Message: fmt.Sprintf("%s", err)} + } + + // Iterate through /auth/mapping resultant dict checking for valid permissions + for resourcePath, permissions := range authMappings.(map[string]any) { + if resource == resourcePath { + if hasPermission(permissions.([]any), method, service) { + return true, nil + } + } + } + return false, nil +} + +func GetExpiration(tokenString string) (time.Time, error) { + // Also consider trimming the 'Bearer ' prefix too + tokenString = strings.TrimPrefix(tokenString, "bearer ") + token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) + if err != nil { + return time.Time{}, err + } + + // Parse and convert from float64 epoch time to time.Time + if claims, ok := token.Claims.(jwt.MapClaims); ok { + if exp, ok := claims["exp"].(float64); ok { + temp := int64(exp) + exp := time.Unix(temp, 0) + return exp, nil + } + } + return time.Time{}, fmt.Errorf("Expiration field 'exp' type float64 not found in token %v", token) +} diff --git a/tests/README.md b/tests/README.md index b2cd69d..3a8f4c8 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,7 +6,7 @@ Tests can be run locally by specifying that you want to turn on the plugin in te grip server -w writer=gen3_writer.so \ -w reader=grip-js.so \ -w graphql=gql-gen.so \ - -l reader:config=./config/gen3.js \ + -l reader:config=./grip-js/config/gen3.js \ -l reader:graph=TEST \ -l writer:test=true \ -l graphql:test=true \ @@ -19,7 +19,11 @@ then cd to tests directory and run: If the graph name is `TEST` and the config is setup for test=true, mock auth will be used, and these tests can be run locally outside of a gen3 instance. -Note: some tests that rely on the underlying data in the DB only work when all tests are run, since not all tests populate the grip with data +The tests leave 2 graphs in the DB, so to clean out the state after the tests are run, run +``` +grip drop JSONTEST +grip drop TEST +``` ## Running locally diff --git a/tests/fixtures/compbio-examples-fhir/ValidationErrors.ndjson b/tests/fixtures/compbio-examples-fhir/ValidationErrors.ndjson index d0a142c..15e8efa 100644 --- a/tests/fixtures/compbio-examples-fhir/ValidationErrors.ndjson +++ b/tests/fixtures/compbio-examples-fhir/ValidationErrors.ndjson @@ -10,7 +10,7 @@ {"resourceType":"Observation","id":"ab09b3de-50b9-4288-a445-912e4354b9a0","meta":{"versionId":"1","lastUpdated":"2023-01-26T14:21:56.658+00:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"6298-4","display":"Potassium"}],"text":"Potassium"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":4.54,"unit":"mmol/L","system":"http://unitsofmeasure.org","code":"mmol/L"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} {"resourceType":"Observation","id":"50cae0ee-896e-4dd0-b6f7-0f876353fa1f","meta":{"versionId":"1","lastUpdated":"2023-01-26T14:21:56.658+00:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"2069-3","display":"Chloride"}],"text":"Chloride"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":102.9,"unit":"mmol/L","system":"http://unitsofmeasure.org","code":"mmol/L"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} {"resourceType":"Observation","id":"53090ac3-4e76-4182-9009-ff1146795bec","meta":{"versionId":"1","lastUpdated":"2023-01-26T14:21:56.658+00:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"20565-8","display":"Carbon Dioxide"}],"text":"Carbon Dioxide"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":28.28,"unit":"mmol/L","system":"http://unitsofmeasure.org","code":"mmol/L"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} -{"resourceType":"Observation","id":"944bb3cb-743f-40b5-9e05-6cee3aba7eb4","meta":{"versionId":"1","lastUpdated":"2023-01-26T14:21:56.658+00:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"2339-0","display":"Glucose"}],"text":"Glucose"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":94.76,"unit":"mg/dL","system":"http://unitsofmeasure.org","code":"mg/dL"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} +{"resourceType":"Observation","id":"944bb3cb-743f-40b5-9e05-6cee3aba7eb4","meta":{"versionId":"1","lastUpdated":"2023-016.658+00:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"2339-0","display":"Glucose"}],"text":"Glucose"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":94.76,"unit":"mg/dL","system":"http://unitsofmeasure.org","code":"mg/dL"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} {"resourceType":"Observation","id":"be951e45-3e17-4924-80c9-17c6f2a6150a","meta":{"versionId":"1","lastUpdated":"2023-01-26T14:21:56.658+00:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"6299-2","display":"Urea Nitrogen"}],"text":"Urea Nitrogen"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":13.16,"unit":"mg/dL","system":"http://unitsofmeasure.org","code":"mg/dL"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} {"resourceType":"Observation","id":"0da73dc4-cc7d-463b-b5fe-94197d1ffe84","meta":{"versionId":"1","lastUpdated":":00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"38483-4","display":"Creatinine"}],"text":"Creatinine"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":0.91,"unit":"mg/dL","system":"http://unitsofmeasure.org","code":"mg/dL"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} {"resourceType":"Observation","id":"e124ee34-83ff-4b68-9d8a-5b3a52ffe496","meta":{"versionId":"1","lastUpdated":"2020:00","source":"#DmW9sueQ4yuQdyA9","profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"status":"final","category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"laboratory"}]}],"code":{"coding":[{"system":"http://loinc.org","code":"49765-1","display":"Calcium"}],"text":"Calcium"},"subject":{"reference":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"},"encounter":{"reference":"Encounter/bd62656a-5e43-46a5-9584-d2402786f7f5"},"effectiveDateTime":"2019-01-19T14:40:53-05:00","issued":"2019-01-19T14:40:53.188-05:00","valueQuantity":{"value":8.59,"unit":"mg/dL","system":"http://unitsofmeasure.org","code":"mg/dL"},"links":[{"rel":"subject_Patient","href":"Patient/45c11dad-2b38-4c8e-822e-7abff8a1ee1d"}]} diff --git a/tests/integration/gen3-writer_test.go b/tests/integration/gen3-writer_test.go index b0cfc9b..0257c28 100644 --- a/tests/integration/gen3-writer_test.go +++ b/tests/integration/gen3-writer_test.go @@ -17,75 +17,142 @@ import ( "github.com/bmeg/grip/gripql" ) -func Test_Writer_Load_Ok(t *testing.T) { +// --- Helper Functions for Test Isolation (Setup/Teardown) --- + +const ( + GraphTEST = "TEST" + GraphJSONTEST = "JSONTEST" + ProjectOHSU = "ohsu-test" + TestVertexID = "cec32723-9ede-5f24-ba63-63cb8c6a02cf" + TestEdgeID = "e2867c6d-db7e-5d6e-87d8-b1c293f5b47e" +) + +// deleteTestProject attempts to delete a project and logs the result. +func deleteTestProject(t *testing.T, graph, project string) { + t.Helper() + req := &integration.Request{ + Url: fmt.Sprintf("http://localhost:8201/writer/%s/proj-delete/%s", graph, project), + Method: "DELETE", + // Use a writer token for deletion + Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, + } + response, _ := integration.TemplateRequest(req, t) + status := response["status"].(float64) + + // We only log if deletion failed or was not found (which can happen if a previous test deleted it) + if status != 200 { + t.Logf("Project cleanup for %s/%s returned status %f: %v", graph, project, status, response) + } +} + +// loadTestProjectData creates the graph and bulk-loads the GRIP data. +func loadTestProjectData(t *testing.T, graph, project string) { + t.Helper() + // 1. Create Graph req := &integration.Request{ - Url: "http://localhost:8201/writer/add-graph/TEST/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/add-graph/%s/%s", graph, project), Method: "POST", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, } - // Inlined subtest logic response_json, pass := integration.TemplateRequest(req, t) if !pass { - t.Error("status is not 200") + t.Errorf("Failed to create graph %s/%s: %v", graph, project, response_json) + return } - t.Log("RESPONSE JSON: ", response_json, "STATUS: ", pass) + t.Logf("Created Graph %s/%s: %v", graph, project, response_json) - err, responses := integration.BulkLoad("http://localhost:8201/writer/TEST/bulk-load/ohsu-test", + // 2. Bulk Load Data + err, responses := integration.BulkLoad( + fmt.Sprintf("http://localhost:8201/writer/%s/bulk-load/%s", graph, project), "../fixtures/combio-examples-grip", integration.CreateToken(false, true, true), ) if err != nil { - t.Error(err) + t.Error("BulkLoad failed:", err) } for _, resp := range responses { if resp["status"].(float64) != 200 { - t.Error(resp) - } else { - t.Log(resp) + t.Errorf("BulkLoad response error: %v", resp) } } } -func Test_Writer_Json_Schema_Load_Ok(t *testing.T) { +// loadJSONSchemaData creates the JSONTEST graph, downloads the schema, and loads it. +func loadJSONSchemaData(t *testing.T) { + t.Helper() + + // 1. Create Graph JSONTEST/ohsu-test req := &integration.Request{ - Url: "http://localhost:8201/writer/add-graph/JSONTEST/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/add-graph/%s/%s", GraphJSONTEST, ProjectOHSU), Method: "POST", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, } - // Inlined subtest logic from "create_load_graph" response_json, pass := integration.TemplateRequest(req, t) if !pass { - t.Error("status is not 200") + t.Errorf("Failed to create graph %s/%s: %v", GraphJSONTEST, ProjectOHSU, response_json) + return } - t.Log("RESPONSE JSON: ", response_json, "STATUS: ", pass) + t.Logf("Created Graph %s/%s: %v", GraphJSONTEST, ProjectOHSU, response_json) + // 2. Download Schema (Existing logic) tempDir, err := os.MkdirTemp("", "jsonschema") if err != nil { - t.Errorf("Error creating temp directory: %v\n", err) - return + t.Fatalf("Error creating temp directory: %v", err) } - defer os.RemoveAll(tempDir) + t.Cleanup(func() { os.RemoveAll(tempDir) }) tempFilePath := fmt.Sprintf("%s/graph-fhir.json", tempDir) curlCmd := exec.Command("curl", "-o", tempFilePath, "https://raw.githubusercontent.com/bmeg/iceberg/refs/heads/main/schemas/graph/graph-fhir.json") if err := curlCmd.Run(); err != nil { - t.Errorf("Error running curl command: %v\n", err) - return + t.Fatalf("Error running curl command: %v", err) } + t.Logf("Downloaded schema to: %s", tempFilePath) - t.Logf("Downloaded schema to: %s\n", tempFilePath) - - err, response := integration.MultipartFormTest("http://localhost:8201/writer/JSONTEST/add-json-schema/ohsu-test", + // 3. Add JSON Schema + err, response := integration.MultipartFormTest( + fmt.Sprintf("http://localhost:8201/writer/%s/add-json-schema/%s", GraphJSONTEST, ProjectOHSU), tempFilePath, integration.CreateToken(false, true, true), ) if err != nil { - t.Error(err) + t.Error("Add JSON schema failed:", err) } t.Log("RESP: ", response) } +// --- Test Functions --- + +func Test_Writer_Load_Ok(t *testing.T) { + // Set up cleanup first: delete project regardless of test result + t.Cleanup(func() { + deleteTestProject(t, GraphTEST, ProjectOHSU) + }) + + // Perform the loading steps (now simplified by the helper) + loadTestProjectData(t, GraphTEST, ProjectOHSU) +} + +// This test now relies on the loadJSONSchemaData helper +func Test_Writer_Json_Schema_Load_Ok(t *testing.T) { + // Cleanup: delete project regardless of test result + t.Cleanup(func() { + deleteTestProject(t, GraphJSONTEST, ProjectOHSU) + }) + + loadJSONSchemaData(t) // This helper performs the create and load +} + func Test_Writer_Json_Load_Ok(t *testing.T) { - err, response := integration.MultipartFormTest("http://localhost:8201/writer/JSONTEST/bulk-load-raw/ohsu-test", + // This test needs the schema loaded, so we run the setup. + // Since Test_Writer_Json_Schema_Load_Ok handles cleanup, we just run the setup. + // Note: In a production test suite, you'd call a dedicated setup helper here, + // but we call the schema load one for simplicity. + loadJSONSchemaData(t) + // Add cleanup for the setup data in case the main schema test didn't run. + t.Cleanup(func() { deleteTestProject(t, GraphJSONTEST, ProjectOHSU) }) + + // Original assertions + err, response := integration.MultipartFormTest( + fmt.Sprintf("http://localhost:8201/writer/%s/bulk-load-raw/%s", GraphJSONTEST, ProjectOHSU), "../fixtures/compbio-examples-fhir/DocumentReference.ndjson", integration.CreateToken(false, true, true), ) @@ -97,8 +164,22 @@ func Test_Writer_Json_Load_Ok(t *testing.T) { } } +// ... (Test_Writer_Json_Schema_Not_Found remains largely the same, no data dependency) ... + func Test_Writer_Json_Schema_Not_Found(t *testing.T) { - err, response := integration.MultipartFormTest("http://localhost:8201/writer/JSONTEST/add-json-schema/ohsu-test", + // Note: This test still relies on the JSONTEST/ohsu-test project existing, + // but since it attempts to load an invalid schema file, it's mostly self-contained. + // We'll ensure the project exists and is cleaned up. + t.Cleanup(func() { deleteTestProject(t, GraphJSONTEST, ProjectOHSU) }) + req := &integration.Request{ + Url: fmt.Sprintf("http://localhost:8201/writer/add-graph/%s/%s", GraphJSONTEST, ProjectOHSU), + Method: "POST", + Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, + } + integration.TemplateRequest(req, t) + + err, response := integration.MultipartFormTest( + fmt.Sprintf("http://localhost:8201/writer/%s/add-json-schema/%s", GraphJSONTEST, ProjectOHSU), "../fixtures/not_found_schema.json", integration.CreateToken(false, true, true), ) @@ -108,15 +189,19 @@ func Test_Writer_Json_Schema_Not_Found(t *testing.T) { if response["status"].(float64) == 500 { t.Log("Expected outcome:", response["message"].(string)) } else { - t.Error("Status shouldn't equal 200", response["status"].(string)) + t.Error("Status shouldn't equal 500 or message suggests error was not about file not found", response["status"]) } } +// ... (Tests for validation/invalid JSON errors should also run loadJSONSchemaData first and add cleanup) ... func Test_Writer_Json_Load_Validation_Errors(t *testing.T) { + loadJSONSchemaData(t) + t.Cleanup(func() { deleteTestProject(t, GraphJSONTEST, ProjectOHSU) }) + + // Original logic... file, err := os.Open("../fixtures/compbio-examples-fhir/ValidationErrors.ndjson") if err != nil { - t.Error(err) - return + t.Errorf("Failed to open file: %v", err) } defer file.Close() @@ -128,15 +213,15 @@ func Test_Writer_Json_Load_Validation_Errors(t *testing.T) { writer.Close() req := &integration.Request{ - Url: "http://localhost:8201/writer/JSONTEST/bulk-load-raw/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/bulk-load-raw/%s", GraphJSONTEST, ProjectOHSU), Method: "POST", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, } + // ... (rest of the HTTP request execution logic remains the same) ... request, err := http.NewRequest(req.Method, req.Url, body) if err != nil { - t.Error("Error creating request:", err) - return + t.Errorf("Error creating request: %v", err) } for key, val := range req.Headers { request.Header.Set(key, val.(string)) @@ -146,38 +231,57 @@ func Test_Writer_Json_Load_Validation_Errors(t *testing.T) { client := &http.Client{} resp, err := client.Do(request) if err != nil { - t.Error("Error sending request:", err) + t.Errorf("Error sending request: %v", err) } - defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - t.Logf("server responded with status: %d", resp.StatusCode) + t.Logf("Server responded with status: %d", resp.StatusCode) } buf := new(bytes.Buffer) _, err = buf.ReadFrom(resp.Body) if err != nil { - t.Error("Error reading response:", err) - return + t.Errorf("Error reading response body: %v", err) } + resp.Body.Close() var data map[string]any errors := json.Unmarshal([]byte(buf.String()), &data) if errors != nil { - t.Error("Error: ", errors) - return + t.Errorf("Error unmarshalling response: %v\nResponse Body: %s", errors, buf.String()) } - t.Log(data) - if data["message"] == nil || len(data["message"].([]any)) != 2 { - t.Error("Expected return message of length 2") + // ... (rest of the assertions remain the same) ... + message, ok := data["message"].([]any) + if !ok { + t.Errorf("Message expected []any, got %T", data["message"]) + } else if len(message) != 3 { + t.Errorf("Expected validation message length to be 3 but got %d instead", len(message)) } - if data["status"].(float64) != 206 { - t.Error() + + t.Log("MESSAGE: ", message) + + status, ok := data["status"].(float64) + if !ok { + var actualType string + if rawStatus, found := data["status"]; found { + actualType = fmt.Sprintf("%T", rawStatus) + } else { + actualType = "missing" + } + t.Errorf("Validation Error: Expected 'status' field to be a float64, got %s", actualType) + } else if status != 206 { + t.Errorf("Validation Error: Expected status 206, got %f", status) + } else { + t.Log("Status field value OK.") } } func Test_Writer_Json_Load_Invalid_Json(t *testing.T) { + loadJSONSchemaData(t) + t.Cleanup(func() { deleteTestProject(t, GraphJSONTEST, ProjectOHSU) }) + + // Original logic... file, err := os.Open("../fixtures/compbio-examples-fhir/invalid_json.ndjson") if err != nil { t.Error(err) @@ -193,11 +297,12 @@ func Test_Writer_Json_Load_Invalid_Json(t *testing.T) { writer.Close() req := &integration.Request{ - Url: "http://localhost:8201/writer/JSONTEST/bulk-load-raw/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/bulk-load-raw/%s", GraphJSONTEST, ProjectOHSU), Method: "POST", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, } + // ... (rest of the HTTP request execution logic remains the same) ... request, err := http.NewRequest(req.Method, req.Url, body) if err != nil { t.Error("Error creating request:", err) @@ -233,14 +338,19 @@ func Test_Writer_Json_Load_Invalid_Json(t *testing.T) { return } t.Log(data) - if data["status"].(float64) != 206 { t.Error() } } +// --- Dependency-Free Tests (Access/Token Errors) --- +// These don't modify data, but still rely on the project existing for the request URL to be valid. + func Test_Writer_Load_Malformed_Token(t *testing.T) { - /* Server returns a 400 given an unparsable token */ + // No data setup needed, but a quick cleanup in case this is the first test run. + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + + // Server returns a 400 given an unparsable token err, responses := integration.BulkLoad("http://localhost:8201/writer/TEST/bulk-load/ohsu-test", "../fixtures/combio-examples-grip", integration.CreateToken(false, true, true)[2:50], @@ -258,7 +368,9 @@ func Test_Writer_Load_Malformed_Token(t *testing.T) { } func Test_Writer_Load_Expired_Token(t *testing.T) { - /* Server returns a 401 given an expired token */ + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + + // Server returns a 401 given an expired token err, responses := integration.BulkLoad("http://localhost:8201/writer/TEST/bulk-load/ohsu-test", "../fixtures/combio-examples-grip", integration.CreateToken(true, true, true), @@ -276,7 +388,9 @@ func Test_Writer_Load_Expired_Token(t *testing.T) { } func Test_Writer_Load_No_Writer_Perms(t *testing.T) { - /* Server returns a 403 given a token that represents a user with no writer perms on the specified project */ + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + + // Server returns a 403 given a token that represents a user with no writer perms on the specified project err, responses := integration.BulkLoad("http://localhost:8201/writer/TEST/bulk-load/ohsu-test", "../fixtures/combio-examples-grip", integration.CreateToken(false, false, true), @@ -293,10 +407,16 @@ func Test_Writer_Load_No_Writer_Perms(t *testing.T) { } } +// --- Data-Dependent Read Tests (Need data loaded) --- + func Test_Writer_Get_Vertex_No_Reader_Perms(t *testing.T) { - /* Server returns a 403 given a token that represents a user with no reader perms on the specified project */ + // Setup: Ensure data is loaded + loadTestProjectData(t, GraphTEST, ProjectOHSU) + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + + // Server returns a 403 given a token that represents a user with no reader perms on the specified project req := &integration.Request{ - Url: "http://localhost:8201/writer/TEST/get-vertex/cec32723-9ede-5f24-ba63-63cb8c6a02cf/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/get-vertex/%s/%s", GraphTEST, TestVertexID, ProjectOHSU), Method: "GET", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, false)}, } @@ -309,10 +429,13 @@ func Test_Writer_Get_Vertex_No_Reader_Perms(t *testing.T) { } func Test_Writer_Get_Vertex_Ok(t *testing.T) { - /* Given appropriate perms, vertex should be retrieved */ + // Setup: Ensure data is loaded + loadTestProjectData(t, GraphTEST, ProjectOHSU) + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + // Given appropriate perms, vertex should be retrieved req := &integration.Request{ - Url: "http://localhost:8201/writer/TEST/get-vertex/cec32723-9ede-5f24-ba63-63cb8c6a02cf/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/get-vertex/%s/%s", GraphTEST, TestVertexID, ProjectOHSU), Method: "GET", Headers: map[string]any{"Authorization": integration.CreateToken(false, false, true)}, } @@ -324,8 +447,12 @@ func Test_Writer_Get_Vertex_Ok(t *testing.T) { } func Test_Writer_Get_Project_Vertices_Ok(t *testing.T) { + // Setup: Ensure data is loaded + loadTestProjectData(t, GraphTEST, ProjectOHSU) + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + req := &integration.Request{ - Url: "http://localhost:8201/writer/TEST/get-vertices/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/get-vertices/%s", GraphTEST, ProjectOHSU), Method: "GET", Headers: map[string]any{"Authorization": integration.CreateToken(false, false, true)}, } @@ -352,6 +479,8 @@ func Test_Writer_Get_Project_Vertices_Ok(t *testing.T) { reader := bufio.NewReader(resp.Body) jum := gripql.NewFlattenMarshaler() + // ... (rest of the read loop logic remains the same) ... + count := 0 for { line, err := reader.ReadString('\n') if err != nil { @@ -375,13 +504,21 @@ func Test_Writer_Get_Project_Vertices_Ok(t *testing.T) { if mappedData["auth_resource_path"] != "/programs/ohsu/projects/test" { t.Error("Returned data should have resource path: /programs/ohsu/projects/test") } + count++ + } + if count == 0 { + t.Error("Expected to receive vertices, but none were returned.") } } func Test_Writer_Delete_Edge_Ok(t *testing.T) { - /* Regular delete should return 200 */ + // Setup: Ensure data is loaded (including edges) + loadTestProjectData(t, GraphTEST, ProjectOHSU) + t.Cleanup(func() { deleteTestProject(t, GraphTEST, ProjectOHSU) }) + + // Regular delete should return 200 req := &integration.Request{ - Url: "http://localhost:8201/writer/TEST/del-edge/e2867c6d-db7e-5d6e-87d8-b1c293f5b47e/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/del-edge/%s/%s", GraphTEST, TestEdgeID, ProjectOHSU), Method: "DELETE", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, } @@ -392,28 +529,35 @@ func Test_Writer_Delete_Edge_Ok(t *testing.T) { t.Log(status, response) } -func Test_Writer_Delete_Proj(t *testing.T) { - /* Delete everything from test graph project ohsu-test. Should return 200 */ +// FIX: Rewritten to be self-contained and run its own setup. +func Test_Writer_Delete_Proj_Endpoint_Ok(t *testing.T) { + // 1. SETUP: Guarantee the project exists immediately before attempting deletion. + loadTestProjectData(t, GraphTEST, ProjectOHSU) + + // 2. Delete everything from test graph project ohsu-test. Should return 200 req := &integration.Request{ - Url: "http://localhost:8201/writer/TEST/proj-delete/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/proj-delete/%s", GraphTEST, ProjectOHSU), Method: "DELETE", Headers: map[string]any{"Authorization": integration.CreateToken(false, true, true)}, } response, status := integration.TemplateRequest(req, t) + // IMPORTANT: Check for 200, which confirms the project was deleted. if !(response["status"].(float64) == 200) { - t.Error(response) + t.Error("Project deletion failed with status:", response) } t.Log(status, response) - // Look for the vertex that was retrieved earlier. It should be gone + // 3. Look for the vertex that was retrieved earlier. It should be gone (404) req = &integration.Request{ - Url: "http://localhost:8201/writer/TEST/get-vertex/cec32723-9ede-5f24-ba63-63cb8c6a02cf/ohsu-test", + Url: fmt.Sprintf("http://localhost:8201/writer/%s/get-vertex/%s/%s", GraphTEST, TestVertexID, ProjectOHSU), Method: "GET", Headers: map[string]any{"Authorization": integration.CreateToken(false, false, true)}, } response, status = integration.TemplateRequest(req, t) + t.Log("RESPONSE: ", response) if !(response["status"].(float64) == 404) { - t.Error(response) + t.Errorf("Vertex was unexpectedly found after project deletion. Status: %f, Response: %v", response["status"].(float64), response) } - t.Log("RESPONSE: ", response) + + // No t.Cleanup() is needed here, as the test successfully deleted the project. } diff --git a/tests/integration/grip-graphql_test.go b/tests/integration/grip-graphql_test.go index 2ddc3f5..3ca7e49 100644 --- a/tests/integration/grip-graphql_test.go +++ b/tests/integration/grip-graphql_test.go @@ -1,6 +1,7 @@ package integration_test import ( + "fmt" "testing" "github.com/bmeg/grip-graphql/tests/integration" @@ -24,6 +25,8 @@ func Test_Graphql_Query_Forbidden_Perms(t *testing.T) { Body: payload, } response, status := integration.TemplateRequest(req, t) + fmt.Println("RESP: ", response) + t.Log("RESP", response) if !(response["StatusCode"].(float64) == 403) { t.Error(response) } diff --git a/tests/integration/helpers.go b/tests/integration/helpers.go index 7790cf7..0224c9a 100644 --- a/tests/integration/helpers.go +++ b/tests/integration/helpers.go @@ -35,10 +35,10 @@ func CreateToken(expired bool, writer bool, reader bool) string { create = "create" } if reader { - create = "reader" + create = "read" } if writer && reader { - create = "create-reader" + create = "*" } token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ diff --git a/tests/integration/main_test.go b/tests/integration/main_test.go index 9b8a90f..0763a09 100644 --- a/tests/integration/main_test.go +++ b/tests/integration/main_test.go @@ -87,7 +87,7 @@ func runTests(m *testing.M) int { fmt.Println("Starting cleanup") cleanupT := &testing.T{} - Test_Writer_Delete_Proj(cleanupT) + Test_Writer_Delete_Proj_Endpoint_Ok(cleanupT) if cleanupT.Failed() { fmt.Println("Cleanup test failed") return 1