~netlandish/links-dev

links: Simple cleanup. v1 APPLIED

Peter Sanchez: 1
 Simple cleanup.

 6 files changed, 147 insertions(+), 150 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.code.netlandish.com/~netlandish/links-dev/patches/172/mbox | git am -3
Learn more about email & git

[PATCH links] Simple cleanup. Export this patch

- Removing interface{} from codebase
- Modernizing some loops
---
 api/directives/directives.go |  10 +-
 api/graph/model/cursor.go    |   2 +-
 cmd/global.go                |   2 +-
 core/routes.go               |   2 +-
 pinboard/routes_test.go      | 256 +++++++++++++++++------------------
 valid/valid.go               |  25 ++--
 6 files changed, 147 insertions(+), 150 deletions(-)

diff --git a/api/directives/directives.go b/api/directives/directives.go
index 3faabcf..fd58eb8 100644
--- a/api/directives/directives.go
+++ b/api/directives/directives.go
@@ -10,7 +10,7 @@ import (
)

// Admin ...
func Admin(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) {
func Admin(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
	tokenUser := oauth2.ForContext(ctx)
	if tokenUser == nil {
		return nil, fmt.Errorf("Admin auth access denied")
@@ -22,7 +22,7 @@ func Admin(ctx context.Context, obj interface{}, next graphql.Resolver) (interfa
}

// Internal ...
func Internal(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) {
func Internal(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
	tokenUser := oauth2.ForContext(ctx)
	if tokenUser == nil {
		return nil, fmt.Errorf("Internal auth access denied")
@@ -34,7 +34,7 @@ func Internal(ctx context.Context, obj interface{}, next graphql.Resolver) (inte
}

// Private ...
func Private(ctx context.Context, obj interface{}, next graphql.Resolver) (interface{}, error) {
func Private(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
	tokenUser := oauth2.ForContext(ctx)
	if tokenUser == nil {
		return nil, fmt.Errorf("Private auth access denied")
@@ -53,8 +53,8 @@ func Private(ctx context.Context, obj interface{}, next graphql.Resolver) (inter
}

// Access ...
func Access(ctx context.Context, obj interface{}, next graphql.Resolver,
	scope string, kind string) (interface{}, error) {
func Access(ctx context.Context, obj any, next graphql.Resolver,
	scope string, kind string) (any, error) {

	tokenUser := oauth2.ForContext(ctx)
	if tokenUser == nil {
diff --git a/api/graph/model/cursor.go b/api/graph/model/cursor.go
index ed571b3..644c56e 100644
--- a/api/graph/model/cursor.go
+++ b/api/graph/model/cursor.go
@@ -20,7 +20,7 @@ type Cursor struct {
	Limit  int
}

func (c *Cursor) UnmarshalGQL(v interface{}) error {
func (c *Cursor) UnmarshalGQL(v any) error {
	enc, ok := v.(string)
	if !ok {
		return fmt.Errorf("cursor must be strings")
diff --git a/cmd/global.go b/cmd/global.go
index 1b07240..e965707 100644
--- a/cmd/global.go
+++ b/cmd/global.go
@@ -27,7 +27,7 @@ func RunMigrations(t *testing.T, db *sql.DB) {
	)

	engine := migrate.NewEngine(db, migrations, migrate.DOLLAR, false)
	engine.Printf = func(format string, args ...interface{}) (int, error) {
	engine.Printf = func(format string, args ...any) (int, error) {
		t.Logf(format, args...)
		return 0, nil
	}
diff --git a/core/routes.go b/core/routes.go
index 163f504..5e8fd53 100644
--- a/core/routes.go
+++ b/core/routes.go
@@ -2159,7 +2159,7 @@ func (s *Service) OrgLinksList(c echo.Context) error {
	cloudType := model.CloudTypeLinks
	if c.Path() != c.Echo().Reverse(s.RouteName("recent_link_list")) &&
		c.Path() != c.Echo().Reverse(s.RouteName("recent_link_list_rss")) {
		// This means that we want to see a specific org (private or businnes)
		// This means that we want to see a specific org (private or business)
		// and not all the recent links
		slug = links.PullOrgSlug(c)
		op.Var("slug", slug)
diff --git a/pinboard/routes_test.go b/pinboard/routes_test.go
index b08294b..07bea5b 100644
--- a/pinboard/routes_test.go
+++ b/pinboard/routes_test.go
@@ -106,9 +106,9 @@ func TestPinboardHandlers(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"addLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"addLink": map[string]any{
						"hash": "abc123def456",
					},
				},
@@ -163,9 +163,9 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"addLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"addLink": map[string]any{
						"hash": "abc123def456",
					},
				},
@@ -203,10 +203,10 @@ func TestPinboardHandlers(t *testing.T) {
				callCount++
				if callCount == 1 {
					// First call: find the link
					return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
						"data": map[string]interface{}{
							"getOrgLinks": map[string]interface{}{
								"result": []map[string]interface{}{
					return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
						"data": map[string]any{
							"getOrgLinks": map[string]any{
								"result": []map[string]any{
									{
										"hash": "abc123",
										"url":  "https://example.com",
@@ -217,9 +217,9 @@ func TestPinboardHandlers(t *testing.T) {
					})
				}
				// Second call: delete the link
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"deleteLink": map[string]interface{}{
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"deleteLink": map[string]any{
							"success": true,
						},
					},
@@ -250,10 +250,10 @@ func TestPinboardHandlers(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{},
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{},
					},
				},
			}))
@@ -281,10 +281,10 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          1,
								"title":       "Example Title",
@@ -294,7 +294,7 @@ func TestPinboardHandlers(t *testing.T) {
								"visibility":  "PUBLIC",
								"unread":      false,
								"starred":     false,
								"tags": []map[string]interface{}{
								"tags": []map[string]any{
									{"name": "tag1"},
									{"name": "tag2"},
								},
@@ -327,10 +327,10 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          1,
								"title":       "Example Title",
@@ -340,7 +340,7 @@ func TestPinboardHandlers(t *testing.T) {
								"visibility":  "PRIVATE",
								"unread":      true,
								"starred":     false,
								"tags":        []map[string]interface{}{},
								"tags":        []map[string]any{},
								"createdOn":   "2024-01-15T10:30:00Z",
								"updatedOn":   "2024-01-15T10:30:00Z",
							},
@@ -376,8 +376,8 @@ func TestPinboardHandlers(t *testing.T) {
				body, _ := io.ReadAll(req.Body)
				// Parse the GraphQL request
				var gqlReq struct {
					Query     string                 `json:"query"`
					Variables map[string]interface{} `json:"variables"`
					Query     string         `json:"query"`
					Variables map[string]any `json:"variables"`
				}
				if err := json.Unmarshal(body, &gqlReq); err == nil {
					if limit, ok := gqlReq.Variables["limit"]; ok {
@@ -386,10 +386,10 @@ func TestPinboardHandlers(t *testing.T) {
						}
					}
				}
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"getOrgLinks": map[string]interface{}{
							"result": []map[string]interface{}{},
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"getOrgLinks": map[string]any{
							"result": []map[string]any{},
						},
					},
				})
@@ -417,8 +417,8 @@ func TestPinboardHandlers(t *testing.T) {
				body, _ := io.ReadAll(req.Body)
				// Parse the GraphQL request
				var gqlReq struct {
					Query     string                 `json:"query"`
					Variables map[string]interface{} `json:"variables"`
					Query     string         `json:"query"`
					Variables map[string]any `json:"variables"`
				}
				if err := json.Unmarshal(body, &gqlReq); err == nil {
					if limit, ok := gqlReq.Variables["limit"]; ok {
@@ -427,10 +427,10 @@ func TestPinboardHandlers(t *testing.T) {
						}
					}
				}
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"getOrgLinks": map[string]interface{}{
							"result": []map[string]interface{}{},
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"getOrgLinks": map[string]any{
							"result": []map[string]any{},
						},
					},
				})
@@ -452,10 +452,10 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{"createdOn": "2024-01-15T10:30:00Z"},
							{"createdOn": "2024-01-15T11:30:00Z"},
							{"createdOn": "2024-01-14T09:00:00Z"},
@@ -487,10 +487,10 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          1,
								"title":       "Old Link",
@@ -500,7 +500,7 @@ func TestPinboardHandlers(t *testing.T) {
								"visibility":  "PUBLIC",
								"unread":      false,
								"starred":     false,
								"tags":        []map[string]interface{}{},
								"tags":        []map[string]any{},
								"createdOn":   "2024-01-10T10:30:00Z",
								"updatedOn":   "2024-01-10T10:30:00Z",
							},
@@ -513,7 +513,7 @@ func TestPinboardHandlers(t *testing.T) {
								"visibility":  "PUBLIC",
								"unread":      false,
								"starred":     false,
								"tags":        []map[string]interface{}{},
								"tags":        []map[string]any{},
								"createdOn":   "2024-01-20T10:30:00Z",
								"updatedOn":   "2024-01-20T10:30:00Z",
							},
@@ -542,10 +542,10 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          100,
								"title":       "My First Note",
@@ -590,9 +590,9 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLink": map[string]any{
						"id":          100,
						"title":       "My First Note",
						"description": "This is the full content of the note.\n\nIt has multiple paragraphs.",
@@ -626,9 +626,9 @@ func TestPinboardHandlers(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLink": map[string]any{
						"id":          100,
						"title":       "A Link",
						"description": "Not a note",
@@ -690,9 +690,9 @@ func TestTagConversion(t *testing.T) {
						}
					}
				}
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"addLink": map[string]interface{}{
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"addLink": map[string]any{
							"hash": "abc123",
						},
					},
@@ -727,10 +727,10 @@ func TestTagConversion(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          1,
								"title":       "Test",
@@ -740,7 +740,7 @@ func TestTagConversion(t *testing.T) {
								"visibility":  "PUBLIC",
								"unread":      false,
								"starred":     false,
								"tags": []map[string]interface{}{
								"tags": []map[string]any{
									{"name": "simple"},
									{"name": "tag with spaces"},
									{"name": "another"},
@@ -821,11 +821,11 @@ func TestErrorHandling(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"errors": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"errors": []map[string]any{
					{
						"message": "Internal server error",
						"extensions": map[string]interface{}{
						"extensions": map[string]any{
							"code": "INTERNAL_SERVER_ERROR",
						},
					},
@@ -871,10 +871,10 @@ func TestResponseFormats(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          1,
								"title":       "Test",
@@ -884,7 +884,7 @@ func TestResponseFormats(t *testing.T) {
								"visibility":  "PUBLIC",
								"unread":      false,
								"starred":     false,
								"tags":        []map[string]interface{}{},
								"tags":        []map[string]any{},
								"createdOn":   "2024-01-15T10:30:00Z",
								"updatedOn":   "2024-01-15T10:30:00Z",
							},
@@ -921,10 +921,10 @@ func TestResponseFormats(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getOrgLinks": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getOrgLinks": map[string]any{
						"result": []map[string]any{
							{
								"id":          1,
								"title":       "Test",
@@ -934,7 +934,7 @@ func TestResponseFormats(t *testing.T) {
								"visibility":  "PUBLIC",
								"unread":      false,
								"starred":     false,
								"tags":        []map[string]interface{}{},
								"tags":        []map[string]any{},
								"createdOn":   "2024-01-15T10:30:00Z",
								"updatedOn":   "2024-01-15T10:30:00Z",
							},
@@ -958,16 +958,16 @@ func TestResponseFormats(t *testing.T) {
		c.NoError(err)

		// Parse JSON to verify structure
		var result map[string]interface{}
		var result map[string]any
		err = json.Unmarshal(recorder.Body.Bytes(), &result)
		c.NoError(err)
		c.Contains(result, "date")
		c.Contains(result, "user")
		c.Contains(result, "posts")

		posts := result["posts"].([]interface{})
		posts := result["posts"].([]any)
		c.Len(posts, 1)
		post := posts[0].(map[string]interface{})
		post := posts[0].(map[string]any)
		c.Equal("https://test.com", post["href"])
	})
}
@@ -1003,8 +1003,8 @@ func TestOrganizationSelection(t *testing.T) {
			func(req *http.Request) (*http.Response, error) {
				body, _ := io.ReadAll(req.Body)
				var gqlReq struct {
					Query     string                 `json:"query"`
					Variables map[string]interface{} `json:"variables"`
					Query     string         `json:"query"`
					Variables map[string]any `json:"variables"`
				}
				if err := json.Unmarshal(body, &gqlReq); err == nil {
					if orgSlug, ok := gqlReq.Variables["orgSlug"]; ok {
@@ -1013,10 +1013,10 @@ func TestOrganizationSelection(t *testing.T) {
						}
					}
				}
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"getOrgLinks": map[string]interface{}{
							"result": []map[string]interface{}{},
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"getOrgLinks": map[string]any{
							"result": []map[string]any{},
						},
					},
				})
@@ -1043,8 +1043,8 @@ func TestOrganizationSelection(t *testing.T) {
			func(req *http.Request) (*http.Response, error) {
				body, _ := io.ReadAll(req.Body)
				var gqlReq struct {
					Query     string                 `json:"query"`
					Variables map[string]interface{} `json:"variables"`
					Query     string         `json:"query"`
					Variables map[string]any `json:"variables"`
				}
				if err := json.Unmarshal(body, &gqlReq); err == nil {
					if orgSlug, ok := gqlReq.Variables["orgSlug"]; ok {
@@ -1053,10 +1053,10 @@ func TestOrganizationSelection(t *testing.T) {
						}
					}
				}
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"getOrgLinks": map[string]interface{}{
							"result": []map[string]interface{}{},
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"getOrgLinks": map[string]any{
							"result": []map[string]any{},
						},
					},
				})
@@ -1065,7 +1065,7 @@ func TestOrganizationSelection(t *testing.T) {
		// Create a test group with Pinboard auth middleware
		authGroup := e.Group("/test")
		authGroup.Use(pinboard.PinboardAuthMiddleware())
		

		authGroup.GET("/posts/get", func(ctx echo.Context) error {
			// Forward to the actual handler
			return pinboardService.PostsGet(ctx)
@@ -1138,9 +1138,9 @@ func TestEdgeCases(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"addLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"addLink": map[string]any{
						"hash": "abc123",
					},
				},
@@ -1172,9 +1172,9 @@ func TestEdgeCases(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"addLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"addLink": map[string]any{
						"hash": "abc123",
					},
				},
@@ -1205,9 +1205,9 @@ func TestEdgeCases(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"addLink": map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"addLink": map[string]any{
						"hash": "abc123",
					},
				},
@@ -1240,9 +1240,9 @@ func TestEdgeCases(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		// Create exactly 10 links
		links := make([]map[string]interface{}, 10)
		for i := 0; i < 10; i++ {
			links[i] = map[string]interface{}{
		links := make([]map[string]any, 10)
		for i := range 10 {
			links[i] = map[string]any{
				"id":          i,
				"title":       fmt.Sprintf("Link %d", i),
				"description": "Test",
@@ -1251,7 +1251,7 @@ func TestEdgeCases(t *testing.T) {
				"visibility":  "PUBLIC",
				"unread":      false,
				"starred":     false,
				"tags":        []map[string]interface{}{},
				"tags":        []map[string]any{},
				"createdOn":   "2024-01-15T10:30:00Z",
				"updatedOn":   "2024-01-15T10:30:00Z",
			}
@@ -1261,13 +1261,13 @@ func TestEdgeCases(t *testing.T) {
		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			func(req *http.Request) (*http.Response, error) {
				// Parse the GraphQL query to check the limit
				var gqlReq map[string]interface{}
				var gqlReq map[string]any
				json.NewDecoder(req.Body).Decode(&gqlReq)

				// Return all links, handler will paginate
				return httpmock.NewJsonResponse(http.StatusOK, map[string]interface{}{
					"data": map[string]interface{}{
						"getOrgLinks": map[string]interface{}{
				return httpmock.NewJsonResponse(http.StatusOK, map[string]any{
					"data": map[string]any{
						"getOrgLinks": map[string]any{
							"result": links,
						},
					},
@@ -1349,10 +1349,10 @@ func TestTagEndpoints(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getTags": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getTags": map[string]any{
						"result": []map[string]any{
							{
								"name":  "golang",
								"count": 42,
@@ -1394,10 +1394,10 @@ func TestTagEndpoints(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getTags": map[string]interface{}{
						"result": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getTags": map[string]any{
						"result": []map[string]any{
							{
								"name":  "test",
								"count": 10,
@@ -1429,10 +1429,10 @@ func TestTagEndpoints(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"data": map[string]interface{}{
					"getTags": map[string]interface{}{
						"result": []map[string]interface{}{},
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"data": map[string]any{
					"getTags": map[string]any{
						"result": []map[string]any{},
					},
				},
			}))
@@ -1490,11 +1490,11 @@ func TestTagEndpoints(t *testing.T) {
		defer httpmock.DeactivateAndReset()

		httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query",
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]interface{}{
				"errors": []map[string]interface{}{
			httpmock.NewJsonResponderOrPanic(http.StatusOK, map[string]any{
				"errors": []map[string]any{
					{
						"message": "getTags query failed",
						"extensions": map[string]interface{}{
						"extensions": map[string]any{
							"code": "INTERNAL_SERVER_ERROR",
						},
					},
diff --git a/valid/valid.go b/valid/valid.go
index 0122ff4..30210d4 100644
--- a/valid/valid.go
+++ b/valid/valid.go
@@ -25,7 +25,7 @@ var (
// Validation ...
type Validation struct {
	ctx   context.Context
	input map[string]interface{}
	input map[string]any
}

// ValidationError ...
@@ -39,18 +39,18 @@ func Error(ctx context.Context, field string, msg string) error {
	return &gqlerror.Error{
		Message: msg,
		Path:    graphql.GetPath(ctx),
		Extensions: map[string]interface{}{
		Extensions: map[string]any{
			"field": field,
		},
	}
}

// Errorf Returns a new GraphQL error attached to the given field.
func Errorf(ctx context.Context, field string, msg string, items ...interface{}) error {
func Errorf(ctx context.Context, field string, msg string, items ...any) error {
	return &gqlerror.Error{
		Message: fmt.Sprintf(msg, items...),
		Path:    graphql.GetPath(ctx),
		Extensions: map[string]interface{}{
		Extensions: map[string]any{
			"field": field,
		},
	}
@@ -64,7 +64,7 @@ func New(ctx context.Context) *Validation {
}

// WithInput Adds an input map to a validation context.
func (valid *Validation) WithInput(input map[string]interface{}) *Validation {
func (valid *Validation) WithInput(input map[string]any) *Validation {
	valid.input = input
	return valid
}
@@ -78,7 +78,7 @@ func (valid *Validation) Ok() bool {
// registered. If the field is not present, the callback is not run. Otherwise,
// the function is called with the value for the user to conduct further
// validation with.
func (valid *Validation) Optional(name string, fn func(i interface{})) {
func (valid *Validation) Optional(name string, fn func(i any)) {
	if valid.input == nil {
		panic("Attempted to validate fields without input")
	}
@@ -175,8 +175,7 @@ func (valid *Validation) OptionalBool(name string, fn func(b bool)) {
}

// Creates a validation error unconditionally.
func (valid *Validation) Error(msg string,
	items ...interface{}) *ValidationError {
func (valid *Validation) Error(msg string, items ...any) *ValidationError {
	err := &gqlerror.Error{
		Path:    graphql.GetPath(valid.ctx),
		Message: fmt.Sprintf(msg, items...),
@@ -190,8 +189,7 @@ func (valid *Validation) Error(msg string,

// Expect Asserts that a condition is true, recording a GraphQL error with the given
// message if not.
func (valid *Validation) Expect(cond bool,
	msg string, items ...interface{}) *ValidationError {
func (valid *Validation) Expect(cond bool, msg string, items ...any) *ValidationError {
	if cond {
		return &ValidationError{valid: valid}
	}
@@ -204,7 +202,7 @@ func (err *ValidationError) WithField(field string) *ValidationError {
		return err
	}
	if err.err.Extensions == nil {
		err.err.Extensions = make(map[string]interface{})
		err.err.Extensions = make(map[string]any)
	}
	err.err.Extensions["field"] = field
	return err
@@ -216,7 +214,7 @@ func (err *ValidationError) WithCode(code int) *ValidationError {
		return err
	}
	if err.err.Extensions == nil {
		err.err.Extensions = make(map[string]interface{})
		err.err.Extensions = make(map[string]any)
	}
	err.err.Extensions["code"] = code
	return err
@@ -225,8 +223,7 @@ func (err *ValidationError) WithCode(code int) *ValidationError {
// And Composes another assertion onto the same validation context which initially
// created an error. Short-circuiting is used, such that if the earlier
// condition failed, the new condition is not considered.
func (err *ValidationError) And(cond bool,
	msg string, items ...interface{}) *ValidationError {
func (err *ValidationError) And(cond bool, msg string, items ...any) *ValidationError {
	if err.err != nil {
		return err
	}
-- 
2.49.1
Applied.

To git@git.code.netlandish.com:~netlandish/links
   d329c96..8038d7f  master -> master