From 5656f578fcb26a3e05454b62428fe515634f892c Mon Sep 17 00:00:00 2001 From: Dylan Moreland Date: Thu, 9 Jul 2026 15:37:42 -0400 Subject: [PATCH] chore(dq): consume tokenclaims from dauth instead of token-exchange-api token-exchange-api has been replaced by dauth; dq now imports github.com/DIMO-Network/dauth/pkg/tokenclaims (v0.0.1), which carries the identical claim shape and permission constants, so token parsing behavior is unchanged. gqlgen Privilege enum bindings updated and code regenerated. cloudevent moves to v1.1.0. dauth required v1.0.4, a ghost tag deleted from GitHub but immortal in the module proxy, whose content predates the v0.2.x line dq depends on (StoredEvent, VoidsID, DataIndexKey, raweventid); v1.1.0 was tagged from the current line to restore semver ordering. The gqlgen regen also flushed the permissionsFromCtx helper out of base.resolvers.go into privilege_filter.go, where helpers survive regen. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01DNqJMcyn2p3oKosj3KFdQR --- go.mod | 16 +++++----- go.sum | 42 ++++++++++----------------- gqlgen.yml | 14 ++++----- internal/auth/claim.go | 2 +- internal/auth/grpc.go | 2 +- internal/auth/grpc_test.go | 2 +- internal/auth/jwt.go | 4 +-- internal/fetch/rpc/authz_test.go | 2 +- internal/graph/auth_helpers.go | 2 +- internal/graph/base.resolvers.go | 9 ------ internal/graph/base_resolvers_test.go | 2 +- internal/graph/generated.go | 2 +- internal/graph/mcp_tools_gen.go | 2 +- internal/graph/privilege_filter.go | 11 ++++++- tests/local_e2e_186612_fetch_test.go | 2 +- 15 files changed, 52 insertions(+), 62 deletions(-) diff --git a/go.mod b/go.mod index 655837a..c2db032 100644 --- a/go.mod +++ b/go.mod @@ -6,23 +6,23 @@ toolchain go1.26.4 require ( github.com/99designs/gqlgen v0.17.89 - github.com/DIMO-Network/cloudevent v0.2.13 + github.com/DIMO-Network/cloudevent v1.1.0 + github.com/DIMO-Network/dauth v0.0.1 github.com/DIMO-Network/model-garage v1.0.11 - github.com/DIMO-Network/server-garage v0.1.1 - github.com/DIMO-Network/shared v1.1.7 - github.com/DIMO-Network/token-exchange-api v0.4.0 + github.com/DIMO-Network/server-garage v0.4.0 + github.com/DIMO-Network/shared v1.1.9 github.com/auth0/go-jwt-middleware/v2 v2.3.0 github.com/aws/aws-sdk-go-v2 v1.41.5 github.com/aws/aws-sdk-go-v2/credentials v1.19.14 github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 github.com/aws/smithy-go v1.24.2 github.com/duckdb/duckdb-go/v2 v2.10504.0 - github.com/ethereum/go-ethereum v1.17.1 - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 + github.com/ethereum/go-ethereum v1.17.3 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 github.com/modelcontextprotocol/go-sdk v1.4.1 github.com/parquet-go/parquet-go v0.30.1 github.com/prometheus/client_golang v1.23.2 - github.com/rs/zerolog v1.34.0 + github.com/rs/zerolog v1.35.1 github.com/stretchr/testify v1.11.1 github.com/uber/h3-go/v4 v4.3.0 github.com/vektah/gqlparser/v2 v2.5.32 @@ -95,7 +95,7 @@ require ( github.com/sosodev/duration v1.4.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/teslamotors/fleet-telemetry v0.9.0 // indirect - github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/gjson v1.19.0 // indirect github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/twpayne/go-geom v1.6.1 // indirect diff --git a/go.sum b/go.sum index f8bf4d3..26c3735 100644 --- a/go.sum +++ b/go.sum @@ -4,16 +4,16 @@ github.com/99designs/gqlgen v0.17.89 h1:KzEcxPiMgQoMw3m/E85atUEHyZyt0PbAflMia5Kw github.com/99designs/gqlgen v0.17.89/go.mod h1:GFqruTVGB7ZTdrf1uzOagpXbY7DrEt1pIxnTdhIbWvQ= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/DIMO-Network/cloudevent v0.2.13 h1:Lyf3gq7/SR29dzDGkJvJniulKgb+nhKJcG37dYrwR0o= -github.com/DIMO-Network/cloudevent v0.2.13/go.mod h1:I/9NcpMozV5Fw194WimhbkAsJtKVZf5UKYJ9hgc8Cdg= +github.com/DIMO-Network/cloudevent v1.1.0 h1:pDJxwO3/Zom/U8BOZqKtMQ05Ye6Pgn6LAh/Dlr8VwWA= +github.com/DIMO-Network/cloudevent v1.1.0/go.mod h1:I/9NcpMozV5Fw194WimhbkAsJtKVZf5UKYJ9hgc8Cdg= +github.com/DIMO-Network/dauth v0.0.1 h1:ZDPNOfCRci1+RhpgMz/2LXUywXjYdSeXRDl+J3M+/5A= +github.com/DIMO-Network/dauth v0.0.1/go.mod h1:RWy1bbPI0KWJ/Q5eS5teKo7jP6sNPWLomK0SXKkhdoo= github.com/DIMO-Network/model-garage v1.0.11 h1:aLvIyeo58p9pVgz+d3DnU5k5Fxvxh6mq/jE2s3LxXoc= github.com/DIMO-Network/model-garage v1.0.11/go.mod h1:oi7EGKQVxFVpXRsu2H+YbizbKcx06aQg2N1Yu4GqOp8= -github.com/DIMO-Network/server-garage v0.1.1 h1:EYmyy+Fgi2BNW0Bufn04BViDtb8BCWaN7C7BbEuoI5s= -github.com/DIMO-Network/server-garage v0.1.1/go.mod h1:Z3A1KDUsXey+XhrPhmw/wyCidfrQvmEdWp7nShno7ZM= -github.com/DIMO-Network/shared v1.1.7 h1:5Ex8bZ6BpOjcLj4u7n5Kih1Ho6b9BVJsKpKn4iU2EaM= -github.com/DIMO-Network/shared v1.1.7/go.mod h1:lDHUKwwT2LW6Zvd42Nb33dXklRNTmfyOlbUNx2dQfGY= -github.com/DIMO-Network/token-exchange-api v0.4.0 h1:EayDrw9VdyAfc6rbpdnDxFhlN3lMhbonUJoouKZu35g= -github.com/DIMO-Network/token-exchange-api v0.4.0/go.mod h1:cldgAyDGLMNk3YIf2mr6vGohcO0ANlZ88fCbI+v/wEY= +github.com/DIMO-Network/server-garage v0.4.0 h1:3ukXvtldIhLldn9AtbtQBooBjT2gicvB3WphrsjNQho= +github.com/DIMO-Network/server-garage v0.4.0/go.mod h1:Z3A1KDUsXey+XhrPhmw/wyCidfrQvmEdWp7nShno7ZM= +github.com/DIMO-Network/shared v1.1.9 h1:vwTPHZ3m1PjyKQpOA/zxB/IkySqhl0SpSxILwwxDRvo= +github.com/DIMO-Network/shared v1.1.9/go.mod h1:dVum9AzLJejZyKrQqknoKkRVZV2Zm4fCOLkBjy1w/pA= github.com/DIMO-Network/yaml v0.1.0 h1:KQ3oKHUZETchR6Pxbmmol3e4ewrPv/q8cEwqxfwyZbU= github.com/DIMO-Network/yaml v0.1.0/go.mod h1:KkiehcbkVzH8Pf8f9dja8B2aW81gYYZSqfwzSj9yN68= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -74,7 +74,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -93,8 +92,8 @@ github.com/duckdb/duckdb-go-bindings/lib/windows-amd64 v0.10504.0 h1:8O774uudYee github.com/duckdb/duckdb-go-bindings/lib/windows-amd64 v0.10504.0/go.mod h1:K25pJL26ARblGDeuAkrdblFvUen92+CwksLtPEHRqqQ= github.com/duckdb/duckdb-go/v2 v2.10504.0 h1:bnkcNQpz3EaJmMmfOhFRFJ3ELVDHW4u3PDhZ78ZTCMY= github.com/duckdb/duckdb-go/v2 v2.10504.0/go.mod h1:DQ8TxrUb0RGyTTFTwPNlpQ9FFx+ocJjoZGDkuOktYp8= -github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= -github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= +github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q5LDd4Q= +github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= @@ -107,7 +106,6 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v2 v2.52.13 h1:TOKP64iqC9b5P49VrBW5tHhUOvDyrtJ0xePEfzJbCbk= github.com/gofiber/fiber/v2 v2.52.13/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= @@ -126,8 +124,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -148,11 +146,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= @@ -191,9 +186,8 @@ github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEy github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= +github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0= @@ -212,12 +206,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/teslamotors/fleet-telemetry v0.9.0 h1:3qz6JAaMF15+HdUzq5eH+LPelH6qiUf+6x5pHG9uzbg= github.com/teslamotors/fleet-telemetry v0.9.0/go.mod h1:0pr+vwxzHPxJjB2LNhwCuG+FvIgpoM70GICHJB3MMlk= -github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= +github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= @@ -270,9 +262,7 @@ golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa h1:efT73AJZfAAUV7SOip6pWGkwJDzIGiKBZGVzHYa+ve4= diff --git a/gqlgen.yml b/gqlgen.yml index fdeca41..4faba72 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -38,19 +38,19 @@ models: model: github.com/99designs/gqlgen/graphql.String enum_values: VEHICLE_NON_LOCATION_DATA: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionGetNonLocationHistory + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionGetNonLocationHistory VEHICLE_COMMANDS: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionExecuteCommands + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionExecuteCommands VEHICLE_CURRENT_LOCATION: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionGetCurrentLocation + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionGetCurrentLocation VEHICLE_ALL_TIME_LOCATION: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionGetLocationHistory + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionGetLocationHistory VEHICLE_VIN_CREDENTIAL: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionGetVINCredential + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionGetVINCredential VEHICLE_APPROXIMATE_LOCATION: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionGetApproximateLocation + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionGetApproximateLocation VEHICLE_RAW_DATA: - value: github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims.PermissionGetRawData + value: github.com/DIMO-Network/dauth/pkg/tokenclaims.PermissionGetRawData Time: model: - github.com/99designs/gqlgen/graphql.Time diff --git a/internal/auth/claim.go b/internal/auth/claim.go index 82dbee2..629a04f 100644 --- a/internal/auth/claim.go +++ b/internal/auth/claim.go @@ -4,7 +4,7 @@ package auth import ( "context" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" jwtmiddleware "github.com/auth0/go-jwt-middleware/v2" ) diff --git a/internal/auth/grpc.go b/internal/auth/grpc.go index 0564516..72be67c 100644 --- a/internal/auth/grpc.go +++ b/internal/auth/grpc.go @@ -5,7 +5,7 @@ import ( "slices" "strings" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" "github.com/auth0/go-jwt-middleware/v2/validator" "github.com/rs/zerolog" "google.golang.org/grpc" diff --git a/internal/auth/grpc_test.go b/internal/auth/grpc_test.go index 877eb08..9b062e1 100644 --- a/internal/auth/grpc_test.go +++ b/internal/auth/grpc_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" "github.com/stretchr/testify/assert" "google.golang.org/grpc/metadata" ) diff --git a/internal/auth/jwt.go b/internal/auth/jwt.go index e95f8c7..52add35 100644 --- a/internal/auth/jwt.go +++ b/internal/auth/jwt.go @@ -14,7 +14,7 @@ import ( "github.com/rs/zerolog" ) -// newValidator builds the token-exchange JWT validator (RS256, dimo.zone +// newValidator builds the dauth JWT validator (RS256, dimo.zone // audience, *DQClaim custom claims) shared by the HTTP middleware and the gRPC // fetch interceptor so both enforce identical token semantics. func newValidator(issuer, jwksURI string) (*validator.Validator, error) { @@ -43,7 +43,7 @@ func newValidator(issuer, jwksURI string) (*validator.Validator, error) { ) } -// NewJWTMiddleware creates JWT middleware with the given token-exchange issuer and JWKS URI. +// NewJWTMiddleware creates JWT middleware with the given dauth issuer and JWKS URI. func NewJWTMiddleware(issuer, jwksURI string) (*jwtmiddleware.JWTMiddleware, error) { jwtValidator, err := newValidator(issuer, jwksURI) if err != nil { diff --git a/internal/fetch/rpc/authz_test.go b/internal/fetch/rpc/authz_test.go index f6f35eb..309ad52 100644 --- a/internal/fetch/rpc/authz_test.go +++ b/internal/fetch/rpc/authz_test.go @@ -7,7 +7,7 @@ import ( "github.com/DIMO-Network/dq/internal/auth" "github.com/DIMO-Network/dq/pkg/grpc" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" diff --git a/internal/graph/auth_helpers.go b/internal/graph/auth_helpers.go index a1056c1..1f8449a 100644 --- a/internal/graph/auth_helpers.go +++ b/internal/graph/auth_helpers.go @@ -8,7 +8,7 @@ import ( "github.com/DIMO-Network/cloudevent" "github.com/DIMO-Network/dq/internal/graph/model" "github.com/DIMO-Network/dq/pkg/grpc" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" ) const ( diff --git a/internal/graph/base.resolvers.go b/internal/graph/base.resolvers.go index e27e290..8530d9a 100644 --- a/internal/graph/base.resolvers.go +++ b/internal/graph/base.resolvers.go @@ -12,7 +12,6 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/DIMO-Network/dq/internal/graph/model" "github.com/DIMO-Network/dq/internal/repositories" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" ) // Signals is the resolver for the signals field. @@ -33,14 +32,6 @@ func (r *queryResolver) SignalsLatest(ctx context.Context, subject string, filte return r.SignalRepo.GetSignalLatest(ctx, latestArgs) } -// permissionsFromCtx returns the caller's token permissions, or nil when unauthenticated. -func permissionsFromCtx(ctx context.Context) []string { - if tok, _ := ctx.Value(ClaimsContextKey{}).(*tokenclaims.Token); tok != nil { - return tok.Permissions - } - return nil -} - // AvailableSignals is the resolver for the availableSignals field. func (r *queryResolver) AvailableSignals(ctx context.Context, subject string, filter *model.SignalFilter) ([]string, error) { names, err := r.SignalRepo.GetAvailableSignals(ctx, subject, filter) diff --git a/internal/graph/base_resolvers_test.go b/internal/graph/base_resolvers_test.go index 3d68999..12977dc 100644 --- a/internal/graph/base_resolvers_test.go +++ b/internal/graph/base_resolvers_test.go @@ -5,7 +5,7 @@ import ( "github.com/DIMO-Network/dq/internal/graph/model" "github.com/DIMO-Network/dq/internal/repositories" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" ) func TestHasPrivilegesForSignal(t *testing.T) { diff --git a/internal/graph/generated.go b/internal/graph/generated.go index 5c6bbb1..18f66d0 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -14,8 +14,8 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" "github.com/DIMO-Network/cloudevent" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" "github.com/DIMO-Network/dq/internal/graph/model" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" gqlparser "github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2/ast" ) diff --git a/internal/graph/mcp_tools_gen.go b/internal/graph/mcp_tools_gen.go index 392cc68..a2e6890 100644 --- a/internal/graph/mcp_tools_gen.go +++ b/internal/graph/mcp_tools_gen.go @@ -195,4 +195,4 @@ var MCPTools = []mcpserver.ToolDefinition{ }, } -var CondensedSchema = "scalar Address # A 20-byte Ethereum address, encoded as a checksummed hex string with 0x prefix.\nscalar JSON # Arbitrary JSON value; serialized as raw JSON (object/array), not an escaped string.\nscalar Map\nscalar Time # A point in time, encoded per RFC-3999.\nscalar Uint64 # A 64-bit unsigned integer.\n\n# ═══ SIGNAL FIELDS (117 total) ═══\n# All signals below exist on every signal type. Calling convention per type:\n# SignalAggregations:\n# fieldName(agg: LocationAggregation!): Location\n# fieldName(agg: FloatAggregation!, filter: SignalFloatFilter): Float\n# fieldName(agg: LocationAggregation!, filter: SignalLocationFilter): Location\n# fieldName(agg: StringAggregation!): String\n# SignalCollection:\n# fieldName(): SignalLocation\n# fieldName(): SignalFloat\n# fieldName(): SignalString\n# Float is the default type. Location: currentLocationApproximateCoordinates, currentLocationCoordinates. String: obdDTCList, obdFuelTypeName, powertrainCombustionEngineEngineOilLevel, powertrainFuelSystemSupportedFuelTypes, powertrainTransmissionRetarderTorqueMode, powertrainType.\n# | Signal | Unit | Description |\n# |--------|------|-------------|\n# Shared descriptions (blank rows below use these):\n# - Is item open or closed? True = Fully or partially open\n# - Is the belt engaged\n# - Measured Load on axle row 3\n# ── CURRENT (privilege: VEHICLE_ALL_TIME_LOCATION) ──\n# | currentLocationApproximateCoordinates | | Approximate location of the vehicle in WGS 84 coordinates (privilege: VEHICLE_APPROXIMATE_LOCATION VEHICLE_ALL_TIME_LOCATION) |\n# | currentLocationAltitude | m | Current altitude relative to WGS 84 reference ellipsoid, as measured at the position of GNSS receiver antenna |\n# | currentLocationCoordinates | | Current location of the vehicle in WGS 84 coordinates |\n# | currentLocationHeading | degrees | Current heading relative to geographic north |\n# ── OTHER (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | angularVelocityYaw | degrees/s | Vehicle rotation rate along Z (vertical) |\n# | connectivityCellularIsJammingDetected | | Indicates whether cellular radio signal jamming or interference is detected that prevents normal communication |\n# | exteriorAirTemperature | celsius | Air temperature outside the vehicle |\n# | isIgnitionOn | | Vehicle ignition status |\n# | lowVoltageBatteryCurrentVoltage | V | |\n# | speed | km/h | |\n# ── BODY (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | bodyLightsIsAirbagWarningOn | | Indicates whether the airbag/SRS warning telltale is active |\n# | bodyLockIsLocked | | Indicates whether the vehicle is locked via the central locking system |\n# | bodyTrunkFrontIsOpen | | |\n# | bodyTrunkRearIsOpen | | |\n# ── CABIN (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | cabinDoorRow1DriverSideIsOpen | | |\n# | cabinDoorRow1DriverSideWindowIsOpen | | |\n# | cabinDoorRow1PassengerSideIsOpen | | |\n# | cabinDoorRow1PassengerSideWindowIsOpen | | |\n# | cabinDoorRow2DriverSideIsOpen | | |\n# | cabinDoorRow2DriverSideWindowIsOpen | | |\n# | cabinDoorRow2PassengerSideIsOpen | | |\n# | cabinDoorRow2PassengerSideWindowIsOpen | | |\n# | cabinSeatRow1DriverSideIsBelted | | |\n# | cabinSeatRow1PassengerSideIsBelted | | |\n# | cabinSeatRow2DriverSideIsBelted | | |\n# | cabinSeatRow2MiddleIsBelted | | |\n# | cabinSeatRow2PassengerSideIsBelted | | |\n# | cabinSeatRow3DriverSideIsBelted | | |\n# | cabinSeatRow3PassengerSideIsBelted | | |\n# ── CHASSIS (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# shared: Rotational speed of a vehicle's wheel\n# shared: Pneumatic pressure in the service brake circuit or reservoir\n# | chassisAxleRow1WheelLeftSpeed | km/h | |\n# | chassisAxleRow1WheelLeftTirePressure | kPa | |\n# | chassisAxleRow1WheelRightSpeed | km/h | |\n# | chassisAxleRow1WheelRightTirePressure | kPa | |\n# | chassisAxleRow2WheelLeftTirePressure | kPa | |\n# | chassisAxleRow2WheelRightTirePressure | kPa | |\n# | chassisAxleRow3Weight | kg | |\n# | chassisAxleRow4Weight | kg | |\n# | chassisAxleRow5Weight | kg | |\n# | chassisBrakeABSIsWarningOn | | Indicates whether the ABS warning telltale is active (any non-off state) |\n# | chassisBrakeCircuit1PressurePrimary | kPa | |\n# | chassisBrakeCircuit2PressurePrimary | kPa | |\n# | chassisBrakeIsPedalPressed | | Indicates whether the brake pedal is pressed |\n# | chassisBrakePedalPosition | percent | Brake pedal position as percent |\n# | chassisParkingBrakeIsEngaged | | |\n# | chassisTireSystemIsWarningOn | | Indicates whether the tire system warning telltale is active |\n# ── OBD (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# shared: PID 2x (byte CD) - Voltage for wide range/band oxygen sensor\n# | obdBarometricPressure | kPa | PID 33 - Barometric pressure |\n# | obdCommandedEGR | percent | PID 2C - Commanded exhaust gas recirculation (EGR) |\n# | obdCommandedEVAP | percent | PID 2E - Commanded evaporative purge (EVAP) valve |\n# | obdDTCList | | List of currently active DTCs formatted according OBD II (SAE-J2012DA_201812) standard ([P|C|B|U]XXXXX ) |\n# | obdDistanceSinceDTCClear | km | PID 31 - Distance traveled since codes cleared |\n# | obdDistanceWithMIL | km | PID 21 - Distance traveled with MIL on |\n# | obdEngineLoad | percent | PID 04 - Engine load in percent - 0 = no load, 100 = full load |\n# | obdEthanolPercent | percent | PID 52 - Percentage of ethanol in the fuel |\n# | obdFuelPressure | kPa | PID 0A - Fuel pressure |\n# | obdFuelRailPressure | kPa | |\n# | obdFuelRate | l/h | PID 5E - Engine fuel rate |\n# | obdFuelTypeName | | Fuel type names decoded from PID 51 |\n# | obdIntakeTemp | celsius | PID 0F - Intake temperature |\n# | obdIsEngineBlocked | | Engine block status, 0 = engine unblocked, 1 = engine blocked |\n# | obdIsPTOActive | | PID 1E - Auxiliary input status (power take off) |\n# | obdIsPluggedIn | | Aftermarket device plugged in status |\n# | obdLongTermFuelTrim1 | percent | PID 07 - Long Term (learned) Fuel Trim - Bank 1 - negative percent leaner, positive percent richer |\n# | obdLongTermFuelTrim2 | percent | PID 09 - Long Term (learned) Fuel Trim - Bank 2 - negative percent leaner, positive percent richer |\n# | obdMAP | kPa | PID 0B - Intake manifold pressure |\n# | obdMaxMAF | g/s | PID 50 - Maximum flow for mass air flow sensor |\n# | obdO2WRSensor1Voltage | V | |\n# | obdO2WRSensor2Voltage | V | |\n# | obdOilTemperature | celsius | PID 5C - Engine oil temperature |\n# | obdRunTime | s | PID 1F - Engine run time |\n# | obdShortTermFuelTrim1 | percent | PID 06 - Short Term (immediate) Fuel Trim - Bank 1 - negative percent leaner, positive percent richer |\n# | obdStatusDTCCount | | Number of Diagnostic Trouble Codes (DTC) |\n# | obdThrottlePosition | percent | PID 11 - Throttle position - 0 = closed throttle, 100 = open throttle |\n# | obdWarmupsSinceDTCClear | | PID 30 - Number of warm-ups since codes cleared |\n# ── POWERTRAIN (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | powertrainCombustionEngineDieselExhaustFluidCapacity | l | Capacity in liters of the Diesel Exhaust Fluid Tank |\n# | powertrainCombustionEngineDieselExhaustFluidLevel | percent | Level of the Diesel Exhaust Fluid tank as percent of capacity |\n# | powertrainCombustionEngineECT | celsius | Engine coolant temperature |\n# | powertrainCombustionEngineEOP | kPa | Engine oil pressure |\n# | powertrainCombustionEngineEOT | celsius | Engine oil temperature |\n# | powertrainCombustionEngineEngineOilLevel | | |\n# | powertrainCombustionEngineEngineOilRelativeLevel | percent | Engine oil level as a percentage |\n# | powertrainCombustionEngineMAF | g/s | Grams of air drawn into engine per second |\n# | powertrainCombustionEngineSpeed | rpm | Engine speed measured as rotations per minute |\n# | powertrainCombustionEngineTPS | percent | Current throttle position |\n# | powertrainCombustionEngineTorque | Nm | |\n# | powertrainCombustionEngineTorquePercent | percent | Actual engine output torque as a percentage of reference engine torque (FMS / J1939 parameter SPN 513) |\n# | powertrainFuelSystemAbsoluteLevel | l | Current available fuel in the fuel tank expressed in liters |\n# | powertrainFuelSystemAccumulatedConsumption | l | Accumulated fuel consumption (totalized) reported by the vehicle (FMS SPN 250) |\n# | powertrainFuelSystemRelativeLevel | percent | Level in fuel tank as percent of capacity |\n# | powertrainFuelSystemSupportedFuelTypes | | High level information of fuel types supported |\n# | powertrainRange | km | Remaining range in kilometers using all energy sources available in the vehicle |\n# | powertrainTractionBatteryChargingAddedEnergy | kWh | Amount of charge added to the high voltage battery during the current charging session, expressed in kilowatt-hours |\n# | powertrainTractionBatteryChargingChargeCurrentAC | A | Current AC charging current (rms) at inlet |\n# | powertrainTractionBatteryChargingChargeLimit | percent | Target charge limit (state of charge) for battery |\n# | powertrainTractionBatteryChargingChargeVoltageUnknownType | V | Current charging voltage at inlet |\n# | powertrainTractionBatteryChargingIsCharging | | True if charging is ongoing |\n# | powertrainTractionBatteryChargingIsChargingCableConnected | | Indicates if a charging cable is physically connected to the vehicle or not |\n# | powertrainTractionBatteryChargingPower | kW | Instantaneous charging power recorded during a charging event |\n# | powertrainTractionBatteryCurrentPower | W | Current electrical energy flowing in/out of battery |\n# | powertrainTractionBatteryCurrentVoltage | V | |\n# | powertrainTractionBatteryGrossCapacity | kWh | |\n# | powertrainTractionBatteryRange | km | Remaining range in kilometers using only battery |\n# | powertrainTractionBatteryStateOfChargeCurrent | percent | Physical state of charge of the high voltage battery, relative to net capacity |\n# | powertrainTractionBatteryStateOfChargeCurrentEnergy | kWh | Physical state of charge of high voltage battery expressed in kWh |\n# | powertrainTractionBatteryStateOfHealth | percent | Calculated battery state of health at standard conditions |\n# | powertrainTractionBatteryTemperatureAverage | celsius | Current average temperature of the battery cells |\n# | powertrainTransmissionActualGear | | Actual transmission gear currently engaged |\n# | powertrainTransmissionActualGearRatio | | |\n# | powertrainTransmissionCurrentGear | | |\n# | powertrainTransmissionIsClutchSwitchOperated | | Indicates if the Clutch switch is operated, so engine and transmission are partially or fully decoupled |\n# | powertrainTransmissionRetarderActualTorque | percent | Actual retarder torque as a percentage (FMS / J1939 SPN 520) |\n# | powertrainTransmissionRetarderTorqueMode | | Active engine torque mode |\n# | powertrainTransmissionSelectedGear | | |\n# | powertrainTransmissionTemperature | celsius | The current gearbox temperature |\n# | powertrainTransmissionTravelledDistance | km | Odometer reading, total distance travelled during the lifetime of the transmission |\n# | powertrainType | | Defines the powertrain type of the vehicle |\n# ── SERVICE (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | serviceDistanceToService | km | Remaining distance to service (of any kind) |\n# | serviceTimeToService | s | Remaining time to service (of any kind) |\n\ntype Query {\n \"signals returns a collection of signals for a given vehicle DID in a given time range.\"\n signals(\n subject: String!\n \"\"\"\n interval is a time span that used for aggregatting the data with. A duration\n string is a sequence of decimal numbers, each with optional fraction and a unit\n suffix, such as \"300ms\" or \"2h45m\". Valid time units are \"ms\", \"s\", \"m\", \"h\"\n \"\"\"\n interval: String!\n from: Time!\n to: Time!\n filter: SignalFilter\n ): [SignalAggregations!]\n # Example - Hourly average speed over a time range:\n # query TimeSeries($subject:String!,$from:Time!,$to:Time!) { signals(subject:$subject,interval:\"1h\",from:$from,to:$to) { timestamp speed(agg:AVG) } }\n\n \"SignalsLatest returns the latest signals for a given vehicle DID.\"\n signalsLatest(subject: String!, filter: SignalFilter): SignalCollection\n # Example - Latest speed and battery charge:\n # query Latest($subject:String!) { signalsLatest(subject:$subject) { lastSeen speed{timestamp value} powertrainTractionBatteryStateOfChargeCurrent{timestamp value} } }\n\n \"availableSignals returns a list of queryable signal names that have stored data for a given vehicle DID.\"\n availableSignals(subject: String!, filter: SignalFilter): [String!]\n\n \"data summary of all signals for a given vehicle DID\"\n dataSummary(subject: String!, filter: SignalFilter): DataSummary\n\n \"signalsSnapshot returns the latest value of every available signal for a vehicle, along with when any signal was last seen.\"\n signalsSnapshot(subject: String!, filter: SignalFilter): SignalsSnapshotResponse\n # Example - Full snapshot of all signals for a vehicle:\n # query Snapshot($subject:String!) { signalsSnapshot(subject:$subject) { lastSeen signals { name timestamp valueNumber valueString valueLocation { latitude longitude hdop } } } }\n\n \"Latest full cloud event matching the given subject DID and optional filter.\"\n latestCloudEvent(subject: String!, filter: CloudEventFilter): CloudEvent!\n\n cloudEvents(subject: String!, limit: Int = 10, filter: CloudEventFilter): [CloudEvent!]!\n availableCloudEventTypes(subject: String!, filter: CloudEventFilter): [CloudEventTypeSummary!]!\n \"events returns a list of events for a given vehicle DID in a given time range.\"\n events(\n \"subject is the vehicle DID to get events for.\"\n subject: String!\n \"from is the start time of the event.\"\n from: Time!\n \"to is the end time of the event.\"\n to: Time!\n \"filter is the filter to apply to the events.\"\n filter: EventFilter\n ): [Event!]\n\n \"\"\"\n Returns vehicle usage segments detected using the specified mechanism. Maximum\n date range: 31 days.\n Detection mechanisms:\n - IGNITION_DETECTION: Uses 'isIgnitionOn' signal with configurable debouncing\n - FREQUENCY_ANALYSIS: Analyzes signal update frequency to detect activity\n periods\n - CHANGE_POINT_DETECTION: CUSUM-based regime change detection\n - IDLING: Idling segments (engine rpm idle)\n - REFUEL: Refueling segments (fuel level increased)\n - RECHARGE: Charging segments (battery SoC increased)\n Segment IDs are stable and consistent across queries as long as the segment\n start is captured in the underlying data source.\n Each segment includes summary: signals, start/end location, and (when requested)\n eventCounts. A default set of signal requests is always applied (e.g. speed,\n odometer; for refuel/recharge also the level signal at start and end). When\n signalRequests is provided, those requests are added on top of the default set;\n duplicates (same name and agg) are omitted.\n \"\"\"\n segments(\n subject: String!\n from: Time!\n to: Time!\n mechanism: DetectionMechanism!\n config: SegmentConfig\n signalRequests: [SegmentSignalRequest!]\n eventRequests: [SegmentEventRequest!]\n \"Maximum number of segments to return. Default 100, max 200.\"\n limit: Int = 100\n after: Time\n ): [Segment!]!\n # Example - Trip segments with start/end locations and signal aggregates:\n # query Trips($subject:String!,$from:Time!,$to:Time!) { segments(subject:$subject,from:$from,to:$to,mechanism:FREQUENCY_ANALYSIS) { start{timestamp value{latitude longitude}} end{timestamp value{latitude longitude}} duration isOngoing signals{name agg value} eventCounts{name count} } }\n\n \"\"\"\n Returns one record per calendar day in the date range. Mechanism must be\n IGNITION_DETECTION, FREQUENCY_ANALYSIS, or CHANGE_POINT_DETECTION (IDLING,\n REFUEL, and RECHARGE not allowed). Maximum date range: 31 days.\n \"\"\"\n dailyActivity(subject: String!, from: Time!, to: Time!, mechanism: DetectionMechanism!, config: SegmentConfig, signalRequests: [SegmentSignalRequest!], eventRequests: [SegmentEventRequest!], timezone: String): [DailyActivity!]!\n # Example - Daily activity summaries:\n # query Daily($subject:String!,$from:Time!,$to:Time!) { dailyActivity(subject:$subject,from:$from,to:$to,mechanism:FREQUENCY_ANALYSIS) { segmentCount duration signals{name agg value} eventCounts{name count} } }\n}\n\ntype CloudEvent { header: CloudEventHeader!, data: JSON, dataBase64: String, dataUrl: String }\n\ninput CloudEventFilter { id: String, type: String, types: [String!], dataversion: String, source: String, producer: String, before: Time, after: Time }\n\ntype CloudEventHeader { specversion: String!, type: String!, source: String!, subject: String!, id: String!, time: Time!, datacontenttype: String, dataschema: String, dataversion: String, producer: String!, signature: String, raweventid: String, tags: [String!]! }\n\ntype CloudEventTypeSummary { type: String!, count: Int!, firstSeen: Time!, lastSeen: Time! }\n\ntype DailyActivity { start: SignalLocation, end: SignalLocation, segmentCount: Int!, duration: Int!, signals: [SignalAggregationValue!]!, eventCounts: [EventCount!]! }\n\ntype DataSummary { numberOfSignals: Uint64!, availableSignals: [String!]!, firstSeen: Time!, lastSeen: Time!, signalDataSummary: [SignalDataSummary!]!, eventDataSummary: [EventDataSummary!]! }\n\nenum DetectionMechanism {\n \"Ignition-based detection: Segments are identified by isIgnitionOn state transitions. Most reliable for vehicles with proper ignition signal support.\"\n IGNITION_DETECTION\n \"Frequency analysis: Segments are detected by analyzing signal update patterns. Uses pre-computed materialized view for optimal performance. Ideal for real-time APIs and bulk queries.\"\n FREQUENCY_ANALYSIS\n \"\"\"\n Change point detection: Uses CUSUM algorithm to detect statistical regime\n changes. Monitors cumulative deviation in signal frequency via materialized\n view. Excellent noise resistance with 100% accuracy match to ignition baseline.\n Best alternative when ignition signal is unavailable - same accuracy, same speed\n as frequency analysis.\n \"\"\"\n CHANGE_POINT_DETECTION\n \"Idling: Segments are contiguous periods where engine RPM remains in idle range.\"\n IDLING\n \"Refuel: Detects where fuel level rises significantly.\"\n REFUEL\n \"Recharge: Hybrid detection. Uses charging signals and state of charge for detection.\"\n RECHARGE\n}\n\ntype Event { timestamp: Time!, name: String!, source: String!, durationNs: Int!, metadata: String }\n\ntype EventCount { name: String!, count: Int! }\n\ntype EventDataSummary { name: String!, numberOfEvents: Uint64!, firstSeen: Time!, lastSeen: Time! }\n\ninput EventFilter {\n name: StringValueFilter\n \"source is the name of the source connection that created the event.\"\n source: StringValueFilter\n tags: StringArrayFilter\n}\n\ninput FilterLocation {\n \"Latitude in the range [-90, 90].\"\n latitude: Float!\n \"Longitude in the range [-180, 180].\"\n longitude: Float!\n}\n\nenum FloatAggregation { AVG, MED, MAX, MIN, RAND, FIRST, LAST }\n\ninput InCircleFilter {\n center: FilterLocation!\n \"Radius of the circle around the center, in kilometers (km).\"\n radius: Float!\n}\n\ntype LatestSignal { name: String!, timestamp: Time!, valueNumber: Float, valueString: String, valueLocation: Location }\n\ntype Location { latitude: Float!, longitude: Float!, hdop: Float! }\n\nenum LocationAggregation { AVG, RAND, FIRST, LAST }\n\nenum Privilege { VEHICLE_NON_LOCATION_DATA, VEHICLE_COMMANDS, VEHICLE_CURRENT_LOCATION, VEHICLE_ALL_TIME_LOCATION, VEHICLE_VIN_CREDENTIAL, VEHICLE_APPROXIMATE_LOCATION, VEHICLE_RAW_DATA }\n\ntype Segment { start: SignalLocation!, end: SignalLocation, duration: Int!, isOngoing: Boolean!, startedBeforeRange: Boolean!, signals: [SignalAggregationValue!], eventCounts: [EventCount!] }\n\ninput SegmentConfig {\n \"\"\"\n Maximum gap (seconds) between data points before a segment is split. For\n IGNITION_DETECTION: filters noise from brief ignition OFF events. For\n FREQUENCY_ANALYSIS: maximum gap between active windows to merge. Default: 300 (5\n minutes), Min: 60, Max: 3600\n \"\"\"\n maxGapSeconds: Int = 300\n \"Minimum segment duration (seconds) to include in results. Filters very short segments (testing, engine cycling). Default: 240 (4 minutes), Min: 60, Max: 3600\"\n minSegmentDurationSeconds: Int = 240\n \"\"\"\n [FREQUENCY_ANALYSIS] Minimum signal count per window for activity detection.\n [IDLING] Minimum samples per window to consider it idle (same semantics). Higher\n values = more conservative. Lower values = more sensitive. Default: 10, Min: 1,\n Max: 3600\n \"\"\"\n signalCountThreshold: Int = 10\n \"[IDLING only] Upper bound for idle RPM. Windows with max(RPM) <= this are considered idle. Default: 1000, Min: 300, Max: 3000\"\n maxIdleRpm: Int = 1000\n \"[REFUEL and RECHARGE only] Minimum percent increase within a window to consider it a level-increase window.\"\n minIncreasePercent: Int = 15\n}\n\ninput SegmentEventRequest { name: String! }\n\ninput SegmentSignalRequest { name: String!, agg: FloatAggregation! }\n\ntype SignalAggregationValue { name: String!, agg: String!, value: Float! }\n\ntype SignalAggregations {\n \"Timestamp of the aggregated data.\"\n timestamp: Time!\n # + 117 signal fields (see SIGNAL FIELDS table above)\n}\n\ntype SignalCollection {\n \"The last time any signal was seen matching the filter.\"\n lastSeen: Time\n # + 117 signal fields (see SIGNAL FIELDS table above)\n}\n\ntype SignalDataSummary { name: String!, numberOfSignals: Uint64!, firstSeen: Time!, lastSeen: Time! }\n\ninput SignalFilter {\n \"\"\"\n Filter signals by source using an ethr DID. Example:\n \"did:ethr:137:0xcd445F4c6bDAD32b68a2939b912150Fe3C88803E\"\n \"\"\"\n source: String\n}\n\ntype SignalFloat { timestamp: Time!, value: Float! }\n\ninput SignalFloatFilter { eq: Float, neq: Float, gt: Float, lt: Float, gte: Float, lte: Float, notIn: [Float!], in: [Float!], or: [SignalFloatFilter!] }\n\ntype SignalLocation { timestamp: Time!, value: Location! }\n\ninput SignalLocationFilter {\n \"Filter for locations within a polygon. The vertices should be ordered clockwise or counterclockwise, and there must be at least 3. May produce inaccurate results around the poles and the antimeridian.\"\n inPolygon: [FilterLocation!]\n \"Filter for locations within a given distance of a given point. Distances are computed using WGS 84, and points that are exactly a distance `radius` from the `center` will be included.\"\n inCircle: InCircleFilter\n}\n\ntype SignalString { timestamp: Time!, value: String! }\n\ntype SignalsSnapshotResponse { lastSeen: Time, signals: [LatestSignal!]! }\n\nenum StringAggregation {\n \"Randomly select a value from the group.\"\n RAND\n \"Select the most frequently occurring value in the group.\"\n TOP\n \"Return a list of unique values in the group.\"\n UNIQUE\n \"Return value in group associated with the minimum time value.\"\n FIRST\n \"Return value in group associated with the maximum time value.\"\n LAST\n}\n\ninput StringArrayFilter {\n containsAny: [String!]\n containsAll: [String!]\n \"notContainsAny array of strings does not contain any of the strings in the array\"\n notContainsAny: [String!]\n \"notContainsAll array of strings does not contain all of the strings in the array\"\n notContainsAll: [String!]\n or: [StringArrayFilter!]\n}\n\ninput StringValueFilter {\n eq: String\n neq: String\n notIn: [String!]\n in: [String!]\n \"startsWith matches strings that begin with the given prefix.\"\n startsWith: String\n or: [StringValueFilter!]\n}\n" +var CondensedSchema = "scalar JSON # Arbitrary JSON value; serialized as raw JSON (object/array), not an escaped string.\nscalar Map\nscalar Time # A point in time, encoded per RFC-3999.\nscalar Uint64 # A 64-bit unsigned integer.\n\n# ═══ SIGNAL FIELDS (117 total) ═══\n# All signals below exist on every signal type. Calling convention per type:\n# SignalAggregations:\n# fieldName(agg: LocationAggregation!): Location\n# fieldName(agg: FloatAggregation!, filter: SignalFloatFilter): Float\n# fieldName(agg: LocationAggregation!, filter: SignalLocationFilter): Location\n# fieldName(agg: StringAggregation!): String\n# SignalCollection:\n# fieldName(): SignalLocation\n# fieldName(): SignalFloat\n# fieldName(): SignalString\n# Float is the default type. Location: currentLocationApproximateCoordinates, currentLocationCoordinates. String: obdDTCList, obdFuelTypeName, powertrainCombustionEngineEngineOilLevel, powertrainFuelSystemSupportedFuelTypes, powertrainTransmissionRetarderTorqueMode, powertrainType.\n# | Signal | Unit | Description |\n# |--------|------|-------------|\n# Shared descriptions (blank rows below use these):\n# - Is item open or closed? True = Fully or partially open\n# - Is the belt engaged\n# - Measured Load on axle row 3\n# ── CURRENT (privilege: VEHICLE_ALL_TIME_LOCATION) ──\n# | currentLocationApproximateCoordinates | | Approximate location of the vehicle in WGS 84 coordinates (privilege: VEHICLE_APPROXIMATE_LOCATION VEHICLE_ALL_TIME_LOCATION) |\n# | currentLocationAltitude | m | Current altitude relative to WGS 84 reference ellipsoid, as measured at the position of GNSS receiver antenna |\n# | currentLocationCoordinates | | Current location of the vehicle in WGS 84 coordinates |\n# | currentLocationHeading | degrees | Current heading relative to geographic north |\n# ── OTHER (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | angularVelocityYaw | degrees/s | Vehicle rotation rate along Z (vertical) |\n# | connectivityCellularIsJammingDetected | | Indicates whether cellular radio signal jamming or interference is detected that prevents normal communication |\n# | exteriorAirTemperature | celsius | Air temperature outside the vehicle |\n# | isIgnitionOn | | Vehicle ignition status |\n# | lowVoltageBatteryCurrentVoltage | V | |\n# | speed | km/h | |\n# ── BODY (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | bodyLightsIsAirbagWarningOn | | Indicates whether the airbag/SRS warning telltale is active |\n# | bodyLockIsLocked | | Indicates whether the vehicle is locked via the central locking system |\n# | bodyTrunkFrontIsOpen | | |\n# | bodyTrunkRearIsOpen | | |\n# ── CABIN (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | cabinDoorRow1DriverSideIsOpen | | |\n# | cabinDoorRow1DriverSideWindowIsOpen | | |\n# | cabinDoorRow1PassengerSideIsOpen | | |\n# | cabinDoorRow1PassengerSideWindowIsOpen | | |\n# | cabinDoorRow2DriverSideIsOpen | | |\n# | cabinDoorRow2DriverSideWindowIsOpen | | |\n# | cabinDoorRow2PassengerSideIsOpen | | |\n# | cabinDoorRow2PassengerSideWindowIsOpen | | |\n# | cabinSeatRow1DriverSideIsBelted | | |\n# | cabinSeatRow1PassengerSideIsBelted | | |\n# | cabinSeatRow2DriverSideIsBelted | | |\n# | cabinSeatRow2MiddleIsBelted | | |\n# | cabinSeatRow2PassengerSideIsBelted | | |\n# | cabinSeatRow3DriverSideIsBelted | | |\n# | cabinSeatRow3PassengerSideIsBelted | | |\n# ── CHASSIS (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# shared: Rotational speed of a vehicle's wheel\n# shared: Pneumatic pressure in the service brake circuit or reservoir\n# | chassisAxleRow1WheelLeftSpeed | km/h | |\n# | chassisAxleRow1WheelLeftTirePressure | kPa | |\n# | chassisAxleRow1WheelRightSpeed | km/h | |\n# | chassisAxleRow1WheelRightTirePressure | kPa | |\n# | chassisAxleRow2WheelLeftTirePressure | kPa | |\n# | chassisAxleRow2WheelRightTirePressure | kPa | |\n# | chassisAxleRow3Weight | kg | |\n# | chassisAxleRow4Weight | kg | |\n# | chassisAxleRow5Weight | kg | |\n# | chassisBrakeABSIsWarningOn | | Indicates whether the ABS warning telltale is active (any non-off state) |\n# | chassisBrakeCircuit1PressurePrimary | kPa | |\n# | chassisBrakeCircuit2PressurePrimary | kPa | |\n# | chassisBrakeIsPedalPressed | | Indicates whether the brake pedal is pressed |\n# | chassisBrakePedalPosition | percent | Brake pedal position as percent |\n# | chassisParkingBrakeIsEngaged | | |\n# | chassisTireSystemIsWarningOn | | Indicates whether the tire system warning telltale is active |\n# ── OBD (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# shared: PID 2x (byte CD) - Voltage for wide range/band oxygen sensor\n# | obdBarometricPressure | kPa | PID 33 - Barometric pressure |\n# | obdCommandedEGR | percent | PID 2C - Commanded exhaust gas recirculation (EGR) |\n# | obdCommandedEVAP | percent | PID 2E - Commanded evaporative purge (EVAP) valve |\n# | obdDTCList | | List of currently active DTCs formatted according OBD II (SAE-J2012DA_201812) standard ([P|C|B|U]XXXXX ) |\n# | obdDistanceSinceDTCClear | km | PID 31 - Distance traveled since codes cleared |\n# | obdDistanceWithMIL | km | PID 21 - Distance traveled with MIL on |\n# | obdEngineLoad | percent | PID 04 - Engine load in percent - 0 = no load, 100 = full load |\n# | obdEthanolPercent | percent | PID 52 - Percentage of ethanol in the fuel |\n# | obdFuelPressure | kPa | PID 0A - Fuel pressure |\n# | obdFuelRailPressure | kPa | |\n# | obdFuelRate | l/h | PID 5E - Engine fuel rate |\n# | obdFuelTypeName | | Fuel type names decoded from PID 51 |\n# | obdIntakeTemp | celsius | PID 0F - Intake temperature |\n# | obdIsEngineBlocked | | Engine block status, 0 = engine unblocked, 1 = engine blocked |\n# | obdIsPTOActive | | PID 1E - Auxiliary input status (power take off) |\n# | obdIsPluggedIn | | Aftermarket device plugged in status |\n# | obdLongTermFuelTrim1 | percent | PID 07 - Long Term (learned) Fuel Trim - Bank 1 - negative percent leaner, positive percent richer |\n# | obdLongTermFuelTrim2 | percent | PID 09 - Long Term (learned) Fuel Trim - Bank 2 - negative percent leaner, positive percent richer |\n# | obdMAP | kPa | PID 0B - Intake manifold pressure |\n# | obdMaxMAF | g/s | PID 50 - Maximum flow for mass air flow sensor |\n# | obdO2WRSensor1Voltage | V | |\n# | obdO2WRSensor2Voltage | V | |\n# | obdOilTemperature | celsius | PID 5C - Engine oil temperature |\n# | obdRunTime | s | PID 1F - Engine run time |\n# | obdShortTermFuelTrim1 | percent | PID 06 - Short Term (immediate) Fuel Trim - Bank 1 - negative percent leaner, positive percent richer |\n# | obdStatusDTCCount | | Number of Diagnostic Trouble Codes (DTC) |\n# | obdThrottlePosition | percent | PID 11 - Throttle position - 0 = closed throttle, 100 = open throttle |\n# | obdWarmupsSinceDTCClear | | PID 30 - Number of warm-ups since codes cleared |\n# ── POWERTRAIN (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | powertrainCombustionEngineDieselExhaustFluidCapacity | l | Capacity in liters of the Diesel Exhaust Fluid Tank |\n# | powertrainCombustionEngineDieselExhaustFluidLevel | percent | Level of the Diesel Exhaust Fluid tank as percent of capacity |\n# | powertrainCombustionEngineECT | celsius | Engine coolant temperature |\n# | powertrainCombustionEngineEOP | kPa | Engine oil pressure |\n# | powertrainCombustionEngineEOT | celsius | Engine oil temperature |\n# | powertrainCombustionEngineEngineOilLevel | | |\n# | powertrainCombustionEngineEngineOilRelativeLevel | percent | Engine oil level as a percentage |\n# | powertrainCombustionEngineMAF | g/s | Grams of air drawn into engine per second |\n# | powertrainCombustionEngineSpeed | rpm | Engine speed measured as rotations per minute |\n# | powertrainCombustionEngineTPS | percent | Current throttle position |\n# | powertrainCombustionEngineTorque | Nm | |\n# | powertrainCombustionEngineTorquePercent | percent | Actual engine output torque as a percentage of reference engine torque (FMS / J1939 parameter SPN 513) |\n# | powertrainFuelSystemAbsoluteLevel | l | Current available fuel in the fuel tank expressed in liters |\n# | powertrainFuelSystemAccumulatedConsumption | l | Accumulated fuel consumption (totalized) reported by the vehicle (FMS SPN 250) |\n# | powertrainFuelSystemRelativeLevel | percent | Level in fuel tank as percent of capacity |\n# | powertrainFuelSystemSupportedFuelTypes | | High level information of fuel types supported |\n# | powertrainRange | km | Remaining range in kilometers using all energy sources available in the vehicle |\n# | powertrainTractionBatteryChargingAddedEnergy | kWh | Amount of charge added to the high voltage battery during the current charging session, expressed in kilowatt-hours |\n# | powertrainTractionBatteryChargingChargeCurrentAC | A | Current AC charging current (rms) at inlet |\n# | powertrainTractionBatteryChargingChargeLimit | percent | Target charge limit (state of charge) for battery |\n# | powertrainTractionBatteryChargingChargeVoltageUnknownType | V | Current charging voltage at inlet |\n# | powertrainTractionBatteryChargingIsCharging | | True if charging is ongoing |\n# | powertrainTractionBatteryChargingIsChargingCableConnected | | Indicates if a charging cable is physically connected to the vehicle or not |\n# | powertrainTractionBatteryChargingPower | kW | Instantaneous charging power recorded during a charging event |\n# | powertrainTractionBatteryCurrentPower | W | Current electrical energy flowing in/out of battery |\n# | powertrainTractionBatteryCurrentVoltage | V | |\n# | powertrainTractionBatteryGrossCapacity | kWh | |\n# | powertrainTractionBatteryRange | km | Remaining range in kilometers using only battery |\n# | powertrainTractionBatteryStateOfChargeCurrent | percent | Physical state of charge of the high voltage battery, relative to net capacity |\n# | powertrainTractionBatteryStateOfChargeCurrentEnergy | kWh | Physical state of charge of high voltage battery expressed in kWh |\n# | powertrainTractionBatteryStateOfHealth | percent | Calculated battery state of health at standard conditions |\n# | powertrainTractionBatteryTemperatureAverage | celsius | Current average temperature of the battery cells |\n# | powertrainTransmissionActualGear | | Actual transmission gear currently engaged |\n# | powertrainTransmissionActualGearRatio | | |\n# | powertrainTransmissionCurrentGear | | |\n# | powertrainTransmissionIsClutchSwitchOperated | | Indicates if the Clutch switch is operated, so engine and transmission are partially or fully decoupled |\n# | powertrainTransmissionRetarderActualTorque | percent | Actual retarder torque as a percentage (FMS / J1939 SPN 520) |\n# | powertrainTransmissionRetarderTorqueMode | | Active engine torque mode |\n# | powertrainTransmissionSelectedGear | | |\n# | powertrainTransmissionTemperature | celsius | The current gearbox temperature |\n# | powertrainTransmissionTravelledDistance | km | Odometer reading, total distance travelled during the lifetime of the transmission |\n# | powertrainType | | Defines the powertrain type of the vehicle |\n# ── SERVICE (privilege: VEHICLE_NON_LOCATION_DATA) ──\n# | serviceDistanceToService | km | Remaining distance to service (of any kind) |\n# | serviceTimeToService | s | Remaining time to service (of any kind) |\n\ntype Query {\n \"signals returns a collection of signals for a given vehicle DID in a given time range.\"\n signals(\n subject: String!\n \"\"\"\n interval is a time span that used for aggregatting the data with. A duration\n string is a sequence of decimal numbers, each with optional fraction and a unit\n suffix, such as \"300ms\" or \"2h45m\". Valid time units are \"ms\", \"s\", \"m\", \"h\"\n \"\"\"\n interval: String!\n from: Time!\n to: Time!\n filter: SignalFilter\n ): [SignalAggregations!]\n # Example - Hourly average speed over a time range:\n # query TimeSeries($subject:String!,$from:Time!,$to:Time!) { signals(subject:$subject,interval:\"1h\",from:$from,to:$to) { timestamp speed(agg:AVG) } }\n\n \"SignalsLatest returns the latest signals for a given vehicle DID.\"\n signalsLatest(subject: String!, filter: SignalFilter): SignalCollection\n # Example - Latest speed and battery charge:\n # query Latest($subject:String!) { signalsLatest(subject:$subject) { lastSeen speed{timestamp value} powertrainTractionBatteryStateOfChargeCurrent{timestamp value} } }\n\n \"availableSignals returns a list of queryable signal names that have stored data for a given vehicle DID.\"\n availableSignals(subject: String!, filter: SignalFilter): [String!]\n\n \"data summary of all signals for a given vehicle DID\"\n dataSummary(subject: String!, filter: SignalFilter): DataSummary\n\n \"signalsSnapshot returns the latest value of every available signal for a vehicle, along with when any signal was last seen.\"\n signalsSnapshot(subject: String!, filter: SignalFilter): SignalsSnapshotResponse\n # Example - Full snapshot of all signals for a vehicle:\n # query Snapshot($subject:String!) { signalsSnapshot(subject:$subject) { lastSeen signals { name timestamp valueNumber valueString valueLocation { latitude longitude hdop } } } }\n\n \"Latest full cloud event matching the given subject DID and optional filter.\"\n latestCloudEvent(subject: String!, filter: CloudEventFilter): CloudEvent!\n\n cloudEvents(subject: String!, limit: Int = 10, filter: CloudEventFilter): [CloudEvent!]!\n availableCloudEventTypes(subject: String!, filter: CloudEventFilter): [CloudEventTypeSummary!]!\n \"events returns a list of events for a given vehicle DID in a given time range.\"\n events(\n \"subject is the vehicle DID to get events for.\"\n subject: String!\n \"from is the start time of the event.\"\n from: Time!\n \"to is the end time of the event.\"\n to: Time!\n \"filter is the filter to apply to the events.\"\n filter: EventFilter\n ): [Event!]\n\n \"\"\"\n Returns vehicle usage segments detected using the specified mechanism. Maximum\n date range: 31 days.\n Detection mechanisms:\n - IGNITION_DETECTION: Uses 'isIgnitionOn' signal with configurable debouncing\n - FREQUENCY_ANALYSIS: Analyzes signal update frequency to detect activity\n periods\n - CHANGE_POINT_DETECTION: CUSUM-based regime change detection\n - IDLING: Idling segments (engine rpm idle)\n - REFUEL: Refueling segments (fuel level increased)\n - RECHARGE: Charging segments (battery SoC increased)\n Segment IDs are stable and consistent across queries as long as the segment\n start is captured in the underlying data source.\n Each segment includes summary: signals, start/end location, and (when requested)\n eventCounts. A default set of signal requests is always applied (e.g. speed,\n odometer; for refuel/recharge also the level signal at start and end). When\n signalRequests is provided, those requests are added on top of the default set;\n duplicates (same name and agg) are omitted.\n \"\"\"\n segments(\n subject: String!\n from: Time!\n to: Time!\n mechanism: DetectionMechanism!\n config: SegmentConfig\n signalRequests: [SegmentSignalRequest!]\n eventRequests: [SegmentEventRequest!]\n \"Maximum number of segments to return. Default 100, max 200.\"\n limit: Int = 100\n after: Time\n ): [Segment!]!\n # Example - Trip segments with start/end locations and signal aggregates:\n # query Trips($subject:String!,$from:Time!,$to:Time!) { segments(subject:$subject,from:$from,to:$to,mechanism:FREQUENCY_ANALYSIS) { start{timestamp value{latitude longitude}} end{timestamp value{latitude longitude}} duration isOngoing signals{name agg value} eventCounts{name count} } }\n\n \"\"\"\n Returns one record per calendar day in the date range. Mechanism must be\n IGNITION_DETECTION, FREQUENCY_ANALYSIS, or CHANGE_POINT_DETECTION (IDLING,\n REFUEL, and RECHARGE not allowed). Maximum date range: 31 days.\n \"\"\"\n dailyActivity(subject: String!, from: Time!, to: Time!, mechanism: DetectionMechanism!, config: SegmentConfig, signalRequests: [SegmentSignalRequest!], eventRequests: [SegmentEventRequest!], timezone: String): [DailyActivity!]!\n # Example - Daily activity summaries:\n # query Daily($subject:String!,$from:Time!,$to:Time!) { dailyActivity(subject:$subject,from:$from,to:$to,mechanism:FREQUENCY_ANALYSIS) { segmentCount duration signals{name agg value} eventCounts{name count} } }\n}\n\ntype CloudEvent { header: CloudEventHeader!, data: JSON, dataBase64: String, dataUrl: String }\n\ninput CloudEventFilter { id: String, type: String, types: [String!], dataversion: String, source: String, producer: String, before: Time, after: Time }\n\ntype CloudEventHeader { specversion: String!, type: String!, source: String!, subject: String!, id: String!, time: Time!, datacontenttype: String, dataschema: String, dataversion: String, producer: String!, signature: String, raweventid: String, tags: [String!]! }\n\ntype CloudEventTypeSummary { type: String!, count: Int!, firstSeen: Time!, lastSeen: Time! }\n\ntype DailyActivity { start: SignalLocation, end: SignalLocation, segmentCount: Int!, duration: Int!, signals: [SignalAggregationValue!]!, eventCounts: [EventCount!]! }\n\ntype DataSummary { numberOfSignals: Uint64!, availableSignals: [String!]!, firstSeen: Time!, lastSeen: Time!, signalDataSummary: [SignalDataSummary!]!, eventDataSummary: [EventDataSummary!]! }\n\nenum DetectionMechanism {\n \"Ignition-based detection: Segments are identified by isIgnitionOn state transitions. Most reliable for vehicles with proper ignition signal support.\"\n IGNITION_DETECTION\n \"Frequency analysis: Segments are detected by analyzing signal update patterns. Uses pre-computed materialized view for optimal performance. Ideal for real-time APIs and bulk queries.\"\n FREQUENCY_ANALYSIS\n \"\"\"\n Change point detection: Uses CUSUM algorithm to detect statistical regime\n changes. Monitors cumulative deviation in signal frequency via materialized\n view. Excellent noise resistance with 100% accuracy match to ignition baseline.\n Best alternative when ignition signal is unavailable - same accuracy, same speed\n as frequency analysis.\n \"\"\"\n CHANGE_POINT_DETECTION\n \"Idling: Segments are contiguous periods where engine RPM remains in idle range.\"\n IDLING\n \"Refuel: Detects where fuel level rises significantly.\"\n REFUEL\n \"Recharge: Hybrid detection. Uses charging signals and state of charge for detection.\"\n RECHARGE\n}\n\ntype Event { timestamp: Time!, name: String!, source: String!, durationNs: Int!, metadata: String }\n\ntype EventCount { name: String!, count: Int! }\n\ntype EventDataSummary { name: String!, numberOfEvents: Uint64!, firstSeen: Time!, lastSeen: Time! }\n\ninput EventFilter {\n name: StringValueFilter\n \"source is the name of the source connection that created the event.\"\n source: StringValueFilter\n tags: StringArrayFilter\n}\n\ninput FilterLocation {\n \"Latitude in the range [-90, 90].\"\n latitude: Float!\n \"Longitude in the range [-180, 180].\"\n longitude: Float!\n}\n\nenum FloatAggregation { AVG, MED, MAX, MIN, RAND, FIRST, LAST }\n\ninput InCircleFilter {\n center: FilterLocation!\n \"Radius of the circle around the center, in kilometers (km).\"\n radius: Float!\n}\n\ntype LatestSignal { name: String!, timestamp: Time!, valueNumber: Float, valueString: String, valueLocation: Location }\n\ntype Location { latitude: Float!, longitude: Float!, hdop: Float! }\n\nenum LocationAggregation { AVG, RAND, FIRST, LAST }\n\nenum Privilege { VEHICLE_NON_LOCATION_DATA, VEHICLE_COMMANDS, VEHICLE_CURRENT_LOCATION, VEHICLE_ALL_TIME_LOCATION, VEHICLE_VIN_CREDENTIAL, VEHICLE_APPROXIMATE_LOCATION, VEHICLE_RAW_DATA }\n\ntype Segment { start: SignalLocation!, end: SignalLocation, duration: Int!, isOngoing: Boolean!, startedBeforeRange: Boolean!, signals: [SignalAggregationValue!], eventCounts: [EventCount!] }\n\ninput SegmentConfig {\n \"\"\"\n Maximum gap (seconds) between data points before a segment is split. For\n IGNITION_DETECTION: filters noise from brief ignition OFF events. For\n FREQUENCY_ANALYSIS: maximum gap between active windows to merge. Default: 300 (5\n minutes), Min: 60, Max: 3600\n \"\"\"\n maxGapSeconds: Int = 300\n \"Minimum segment duration (seconds) to include in results. Filters very short segments (testing, engine cycling). Default: 240 (4 minutes), Min: 60, Max: 3600\"\n minSegmentDurationSeconds: Int = 240\n \"\"\"\n [FREQUENCY_ANALYSIS] Minimum signal count per window for activity detection.\n [IDLING] Minimum samples per window to consider it idle (same semantics). Higher\n values = more conservative. Lower values = more sensitive. Default: 10, Min: 1,\n Max: 3600\n \"\"\"\n signalCountThreshold: Int = 10\n \"[IDLING only] Upper bound for idle RPM. Windows with max(RPM) <= this are considered idle. Default: 1000, Min: 300, Max: 3000\"\n maxIdleRpm: Int = 1000\n \"[REFUEL and RECHARGE only] Minimum percent increase within a window to consider it a level-increase window.\"\n minIncreasePercent: Int = 15\n}\n\ninput SegmentEventRequest { name: String! }\n\ninput SegmentSignalRequest { name: String!, agg: FloatAggregation! }\n\ntype SignalAggregationValue { name: String!, agg: String!, value: Float! }\n\ntype SignalAggregations {\n \"Timestamp of the aggregated data.\"\n timestamp: Time!\n # + 117 signal fields (see SIGNAL FIELDS table above)\n}\n\ntype SignalCollection {\n \"The last time any signal was seen matching the filter.\"\n lastSeen: Time\n # + 117 signal fields (see SIGNAL FIELDS table above)\n}\n\ntype SignalDataSummary { name: String!, numberOfSignals: Uint64!, firstSeen: Time!, lastSeen: Time! }\n\ninput SignalFilter {\n \"\"\"\n Filter signals by source using an ethr DID. Example:\n \"did:ethr:137:0xcd445F4c6bDAD32b68a2939b912150Fe3C88803E\"\n \"\"\"\n source: String\n}\n\ntype SignalFloat { timestamp: Time!, value: Float! }\n\ninput SignalFloatFilter { eq: Float, neq: Float, gt: Float, lt: Float, gte: Float, lte: Float, notIn: [Float!], in: [Float!], or: [SignalFloatFilter!] }\n\ntype SignalLocation { timestamp: Time!, value: Location! }\n\ninput SignalLocationFilter {\n \"Filter for locations within a polygon. The vertices should be ordered clockwise or counterclockwise, and there must be at least 3. May produce inaccurate results around the poles and the antimeridian.\"\n inPolygon: [FilterLocation!]\n \"Filter for locations within a given distance of a given point. Distances are computed using WGS 84, and points that are exactly a distance `radius` from the `center` will be included.\"\n inCircle: InCircleFilter\n}\n\ntype SignalString { timestamp: Time!, value: String! }\n\ntype SignalsSnapshotResponse { lastSeen: Time, signals: [LatestSignal!]! }\n\nenum StringAggregation {\n \"Randomly select a value from the group.\"\n RAND\n \"Select the most frequently occurring value in the group.\"\n TOP\n \"Return a list of unique values in the group.\"\n UNIQUE\n \"Return value in group associated with the minimum time value.\"\n FIRST\n \"Return value in group associated with the maximum time value.\"\n LAST\n}\n\ninput StringArrayFilter {\n containsAny: [String!]\n containsAll: [String!]\n \"notContainsAny array of strings does not contain any of the strings in the array\"\n notContainsAny: [String!]\n \"notContainsAll array of strings does not contain all of the strings in the array\"\n notContainsAll: [String!]\n or: [StringArrayFilter!]\n}\n\ninput StringValueFilter {\n eq: String\n neq: String\n notIn: [String!]\n in: [String!]\n \"startsWith matches strings that begin with the given prefix.\"\n startsWith: String\n or: [StringValueFilter!]\n}\n" diff --git a/internal/graph/privilege_filter.go b/internal/graph/privilege_filter.go index b852077..4a00b57 100644 --- a/internal/graph/privilege_filter.go +++ b/internal/graph/privilege_filter.go @@ -1,11 +1,12 @@ package graph import ( + "context" "slices" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" "github.com/DIMO-Network/dq/internal/graph/model" "github.com/DIMO-Network/dq/internal/repositories" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" ) // privilegeEnumToPermission maps GraphQL Privilege enum values (as they appear @@ -42,3 +43,11 @@ func hasPrivilegesForSignal(repo *repositories.Repository, name string, permissi } return true } + +// permissionsFromCtx returns the caller's token permissions, or nil when unauthenticated. +func permissionsFromCtx(ctx context.Context) []string { + if tok, _ := ctx.Value(ClaimsContextKey{}).(*tokenclaims.Token); tok != nil { + return tok.Permissions + } + return nil +} diff --git a/tests/local_e2e_186612_fetch_test.go b/tests/local_e2e_186612_fetch_test.go index 4800536..8afea87 100644 --- a/tests/local_e2e_186612_fetch_test.go +++ b/tests/local_e2e_186612_fetch_test.go @@ -23,7 +23,7 @@ import ( "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/DIMO-Network/dq/internal/graph" "github.com/DIMO-Network/dq/internal/service/duck" - "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims" + "github.com/DIMO-Network/dauth/pkg/tokenclaims" "github.com/stretchr/testify/require" )