Received: from mail.netlandish.com (mail.netlandish.com [174.136.98.166]) by code.netlandish.com (Postfix) with ESMTP id BA24BE1 for <~netlandish/links-dev@lists.code.netlandish.com>; Wed, 13 Aug 2025 14:07:39 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=209.85.222.47; helo=mail-ua1-f47.google.com; envelope-from=peter@netlandish.com; receiver= Authentication-Results: mail.netlandish.com; dkim=pass (1024-bit key; unprotected) header.d=netlandish.com header.i=@netlandish.com header.b=iMnczEeR Received: from mail-ua1-f47.google.com (mail-ua1-f47.google.com [209.85.222.47]) by mail.netlandish.com (Postfix) with ESMTP id 8AAAF1D643F for <~netlandish/links-dev@lists.code.netlandish.com>; Wed, 13 Aug 2025 14:08:30 +0000 (UTC) Received: by mail-ua1-f47.google.com with SMTP id a1e0cc1a2514c-88e373c07f8so1035436241.1 for <~netlandish/links-dev@lists.code.netlandish.com>; Wed, 13 Aug 2025 07:08:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=netlandish.com; s=google; t=1755094109; x=1755698909; darn=lists.code.netlandish.com; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=apBUavlaDZ0rwPPiQsNAFltbHnZyAb8vzi7y7+g9bhM=; b=iMnczEeR5Z2G/ZUdC8ziOFBY3FnexrQWhph4+zZqYRivWrwsid5ZZH3fUOXnD5wIWN UOQFBkiYiALTbnxgRxYjPCT6UFbDUSjwrouHramX6uoRhZqf9rDRIZOT+zZHTzZCkcEx f1IaPh6FIuD0z+cfMgS6B35A0NFf0PoIgHq68= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1755094109; x=1755698909; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=apBUavlaDZ0rwPPiQsNAFltbHnZyAb8vzi7y7+g9bhM=; b=Z3jCnuVUyOL/VB9oKraveaMCnn+KTIuYk1US+PqIcwQ6EEz3g4Etp/PCumoLNyo5t9 +um3O3k/aoOZeDZZd4dxBOETpM0BlXNnIieI3NojrziBuzITyzZOm0+PCC8ZaMysmNS6 vxUuPU+t8E77OagxGRfv5+m9eaMoIwv2aB4Q4XEuTgO7RLjJBLN9LmyhOvVQlt5sH9Hx b7brzjkMszfbcxMnC3tXaSKD4ozBsGSkAp0OVczqGdVSVU7rlGqsxk/1DvPexjVG6B1e PjgWDZS1BIl4tbH0piWbTKCpp2x8s+axxGPaVZpY8jU2q/05UKSLTu2NtLdR68/PNvMe rmmw== X-Gm-Message-State: AOJu0Yzle6UPvVeGVF53u8IqgmfuNZ/YB/oxVpbObbKGTqGAqdFljCxk B258d6hDnsnb3x5ySGYhYkeBQOjNMc5nl9I8yrlbb39HHI6R5zlR0MWgknrS1js0BR52IKjPl1l 0cmahV+ZbhQ== X-Gm-Gg: ASbGnctYnfpWkEUQJ+kdPAWXoZWz/Nt5nputoGTos4AuXaChhxr6Ryb4BvMx6AsHAtx D3AnMlXwICdC7ejxeAnhGFNr88mRysMOLK9Mk9FHAWwUxoQCadYzH4FlooFYnGT05/Ee7CwbCE2 UpFu9zf25A9gQ2jG+ihXIPZXg8FZGIW0afIJbV7s3E+wsmOw+43zMDGOej9hvd31uDr73nWziFo wplkepZEBtJwH4HnXyWhyf2slOBU+Tup6cWRyxmReHZb4FCFvYcJl8BTexLFf5Vk40KN/bIdhHG atwH4ErGJyGnnyxFK+05g6FTH4s0l7uKt/2qkT7ToEH6xdCGrM/JMyXWZgCMRsV9dOonw4+tIMB NtQpzOrAT/71xIRcEqDGE7Ao= X-Google-Smtp-Source: AGHT+IEP7OPgTy/bY40g4S7HKrJGzpQLqDw6UGeNw/kbH7zr4s5Gim48NfvhXed167heLEtbyEZDNw== X-Received: by 2002:a05:6122:178d:b0:530:6bcb:c97f with SMTP id 71dfb90a1353d-53b0b5c5462mr1144505e0c.8.1755094109212; Wed, 13 Aug 2025 07:08:29 -0700 (PDT) Received: from localhost ([2803:2d60:1107:87f:2c43:bb71:23ab:6116]) by smtp.gmail.com with UTF8SMTPSA id 71dfb90a1353d-539b02a4dd8sm3906227e0c.28.2025.08.13.07.08.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Aug 2025 07:08:28 -0700 (PDT) From: Peter Sanchez To: ~netlandish/links-dev@lists.code.netlandish.com Cc: Peter Sanchez Subject: [PATCH links] Fetch all tags, via cursor pagintion, for the Pinboard API bridge. Date: Wed, 13 Aug 2025 08:08:23 -0600 Message-ID: <20250813140825.32737-1-peter@netlandish.com> X-Mailer: git-send-email 2.49.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Changelog-updated: The `/pinboard/tags/get` endpoint will now fetch all tags via cursor pagination --- pinboard/routes.go | 58 +++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/pinboard/routes.go b/pinboard/routes.go index 69c1311..5393056 100644 --- a/pinboard/routes.go +++ b/pinboard/routes.go @@ -781,31 +781,57 @@ func (s *Service) TagsGet(c echo.Context) error { // Prepare GraphQL query type GraphQLResponse struct { GetTags struct { - Result []*models.Tag `json:"result"` + Result []*models.Tag `json:"result"` + PageInfo struct { + Cursor string `json:"cursor"` + HasNextPage bool `json:"hasNextPage"` + } `json:"pageInfo"` } `json:"getTags"` } - var result GraphQLResponse - op := gqlclient.NewOperation( - `query GetTags($orgSlug: String!) { - getTags(input: {orgSlug: $orgSlug}) { - result { - name - count + // Collect all tags across all pages + var allTags []*models.Tag + var afterCursor *string + + // Loop through all pages + for { + var result GraphQLResponse + op := gqlclient.NewOperation( + `query GetTags($orgSlug: String!, $limit: Int, $after: Cursor) { + getTags(input: {orgSlug: $orgSlug, limit: $limit, after: $after}) { + result { + name + count + } + pageInfo { + cursor + hasNextPage + } } - } - }`) + }`) - op.Var("orgSlug", org.Slug) + op.Var("orgSlug", org.Slug) + op.Var("limit", 250) // Maximum tags per call + if afterCursor != nil { + op.Var("after", *afterCursor) + } - err = links.Execute(c.Request().Context(), op, &result) - if err != nil { - return formatError(c, err.Error()) + err = links.Execute(c.Request().Context(), op, &result) + if err != nil { + return formatError(c, err.Error()) + } + + allTags = append(allTags, result.GetTags.Result...) + + if !result.GetTags.PageInfo.HasNextPage { + break + } + afterCursor = &result.GetTags.PageInfo.Cursor } // Convert to Pinboard format - tags := make([]PinboardTag, len(result.GetTags.Result)) - for i, tag := range result.GetTags.Result { + tags := make([]PinboardTag, len(allTags)) + for i, tag := range allTags { tags[i] = PinboardTag{ Tag: tag.Name, Count: tag.Count, -- 2.49.1