From d0a679c5ac0cf4fcd755cbdd32d5892605d886d6 Mon Sep 17 00:00:00 2001 From: "Adam T. Williams" Date: Thu, 25 Jul 2024 12:22:18 -0600 Subject: [PATCH 1/2] feat: client query negation --- client/handler.go | 4 ++-- client/manager.go | 18 ++++++++++++++++-- client/manager_test_helpers.go | 12 ++++++++++++ internal/httpclient/api/openapi.yaml | 4 ++-- persistence/sql/persister_client.go | 12 ++++++++++-- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/client/handler.go b/client/handler.go index a3bcdc90985..5b023787f9d 100644 --- a/client/handler.go +++ b/client/handler.go @@ -511,8 +511,8 @@ func (h *Handler) listOAuth2Clients(w http.ResponseWriter, r *http.Request, ps h filters := Filter{ Limit: itemsPerPage, Offset: page * itemsPerPage, - Name: r.URL.Query().Get("client_name"), - Owner: r.URL.Query().Get("owner"), + Name: field(r.URL.Query().Get("client_name")), + Owner: field(r.URL.Query().Get("owner")), } c, err := h.r.ClientManager().GetClients(r.Context(), filters) diff --git a/client/manager.go b/client/manager.go index b4c54cb3b2a..164286e9b2e 100644 --- a/client/manager.go +++ b/client/manager.go @@ -5,6 +5,7 @@ package client import ( "context" + "strings" "github.com/ory/fosite" ) @@ -21,11 +22,24 @@ type Filter struct { // The name of the clients to filter by. // in: query - Name string `json:"client_name"` + Name field `json:"client_name"` // The owner of the clients to filter by. // in: query - Owner string `json:"owner"` + Owner field `json:"owner"` +} + +type field string + +func (f field) Value() string { + if f.IsNegated() { + return string(f[2:]) + } + return string(f) +} + +func (f field) IsNegated() bool { + return strings.HasPrefix(string(f), "!=") } type Manager interface { diff --git a/client/manager_test_helpers.go b/client/manager_test_helpers.go index b47b78de88e..d1cb9f02f75 100644 --- a/client/manager_test_helpers.go +++ b/client/manager_test_helpers.go @@ -298,6 +298,12 @@ func TestHelperCreateGetUpdateDeleteClient(k string, connection *pop.Connection, assert.Len(t, ds, 1) assert.Equal(t, ds[0].Name, "name") + // get by name negated prefix + ds, err = t1.GetClients(ctx, Filter{Limit: 100, Offset: 0, Name: "!=name"}) + assert.NoError(t, err) + assert.Len(t, ds, 1) + assert.Equal(t, ds[0].GetID(), "2-1234") + // get by name not exist ds, err = t1.GetClients(ctx, Filter{Limit: 100, Offset: 0, Name: "bad name"}) assert.NoError(t, err) @@ -309,6 +315,12 @@ func TestHelperCreateGetUpdateDeleteClient(k string, connection *pop.Connection, assert.Len(t, ds, 1) assert.Equal(t, ds[0].Owner, "aeneas") + // get by owner negated prefix + ds, err = t1.GetClients(ctx, Filter{Limit: 100, Offset: 0, Owner: "!=aeneas"}) + assert.NoError(t, err) + assert.Len(t, ds, 1) + assert.Equal(t, ds[0].GetID(), "2-1234") + testHelperUpdateClient(t, ctx, t1, k) testHelperUpdateClient(t, ctx, t2, k) diff --git a/internal/httpclient/api/openapi.yaml b/internal/httpclient/api/openapi.yaml index e04f7e830cf..f2973c9421e 100644 --- a/internal/httpclient/api/openapi.yaml +++ b/internal/httpclient/api/openapi.yaml @@ -109,7 +109,7 @@ paths: minimum: 1 type: string style: form - - description: The name of the clients to filter by. + - description: The name of the clients to filter by. Prefix "!=" to search by negation. explode: true in: query name: client_name @@ -117,7 +117,7 @@ paths: schema: type: string style: form - - description: The owner of the clients to filter by. + - description: The owner of the clients to filter by. Prefix "!=" to search by negation. explode: true in: query name: owner diff --git a/persistence/sql/persister_client.go b/persistence/sql/persister_client.go index c85893c1df8..d2cb41f9ebc 100644 --- a/persistence/sql/persister_client.go +++ b/persistence/sql/persister_client.go @@ -147,10 +147,18 @@ func (p *Persister) GetClients(ctx context.Context, filters client.Filter) (_ [] Order("id") if filters.Name != "" { - query.Where("client_name = ?", filters.Name) + stmt := "client_name = ?" + if filters.Name.IsNegated() { + stmt = "client_name != ?" + } + query.Where(stmt, filters.Name.Value()) } if filters.Owner != "" { - query.Where("owner = ?", filters.Owner) + stmt := "owner = ?" + if filters.Owner.IsNegated() { + stmt = "owner != ?" + } + query.Where(stmt, filters.Owner.Value()) } if err := query.All(&cs); err != nil { From aca1c6602bf29de1e5e8cf9e27f7fa571e4988d1 Mon Sep 17 00:00:00 2001 From: "Adam T. Williams" Date: Mon, 29 Jul 2024 08:47:11 -0600 Subject: [PATCH 2/2] doc: describe negative matching --- internal/httpclient/api/openapi.yaml | 4 ++-- internal/httpclient/docs/OAuth2API.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/httpclient/api/openapi.yaml b/internal/httpclient/api/openapi.yaml index f2973c9421e..756003acca2 100644 --- a/internal/httpclient/api/openapi.yaml +++ b/internal/httpclient/api/openapi.yaml @@ -109,7 +109,7 @@ paths: minimum: 1 type: string style: form - - description: The name of the clients to filter by. Prefix "!=" to search by negation. + - description: The name of the clients to filter by. Prefix with "!=" to filter with negative matching. explode: true in: query name: client_name @@ -117,7 +117,7 @@ paths: schema: type: string style: form - - description: The owner of the clients to filter by. Prefix "!=" to search by negation. + - description: The owner of the clients to filter by. Prefix with "!=" to filter with negative matching. explode: true in: query name: owner diff --git a/internal/httpclient/docs/OAuth2API.md b/internal/httpclient/docs/OAuth2API.md index a98efff4b77..becc8dd29ec 100644 --- a/internal/httpclient/docs/OAuth2API.md +++ b/internal/httpclient/docs/OAuth2API.md @@ -960,8 +960,8 @@ Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **pageSize** | **int64** | Items per Page This is the number of items per page to return. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination). | [default to 250] **pageToken** | **string** | Next Page Token The next page token. For details on pagination please head over to the [pagination documentation](https://www.ory.sh/docs/ecosystem/api-design#pagination). | [default to "1"] - **clientName** | **string** | The name of the clients to filter by. | - **owner** | **string** | The owner of the clients to filter by. | + **clientName** | **string** | The name of the clients to filter by. Prefix with "!=" to filter with negative matching. | + **owner** | **string** | The owner of the clients to filter by. Prefix with "!=" to filter with negative matching. | ### Return type