├── .github
└── workflows
│ └── build.yaml
├── .gitignore
├── LICENSE
├── README.md
├── assets
├── append-block-example.png
├── example-database.png
├── internal-integration-token.png
├── page-in-page-example.png
└── public-integration-details.png
├── examples
└── version1
│ ├── README.md
│ ├── append-block-children-example.go
│ ├── create-page-example.go
│ ├── go.mod
│ ├── go.sum
│ ├── list-databases-example.go
│ ├── list-users-example.go
│ ├── oauth2-flow-part1-example.go
│ ├── oauth2-flow-part2-example.go
│ ├── query-database-example.go
│ ├── retrieve-block-children-example.go
│ ├── retrieve-database-example.go
│ ├── retrieve-page-example.go
│ ├── retrieve-user-example.go
│ ├── search-example.go
│ ├── testing.go
│ └── update-page-properties-example.go
├── go.mod
├── go.sum
└── notion
└── version1
├── auth.go
├── auth_test.go
├── blocks.go
├── blocks_test.go
├── common.go
├── databases.go
├── databases_test.go
├── errors.go
├── go.mod
├── go.sum
├── notion.go
├── notion_test.go
├── pages.go
├── pages_test.go
├── search.go
├── search_test.go
├── users.go
└── users_test.go
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 | name: Build
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Set up Go 1.x
15 | uses: actions/setup-go@v2
16 | with:
17 | go-version: ^1.13
18 | id: go
19 |
20 | - name: Check out code into the Go module directory
21 | uses: actions/checkout@v2
22 |
23 | - name: Get dependencies
24 | run: |
25 | go get -v -t -d ./notion/verson1/...
26 | if [ -f Gopkg.toml ]; then
27 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
28 | dep ensure
29 | fi
30 |
31 | - name: Test
32 | run: cd notion/version1 && go test -v -race -coverprofile=coverage.txt -covermode=atomic .
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/go,intellij+all
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,intellij+all
4 |
5 | ### Go ###
6 | # Binaries for programs and plugins
7 | *.exe
8 | *.exe~
9 | *.dll
10 | *.so
11 | *.dylib
12 |
13 | # Test binary, built with `go test -c`
14 | *.test
15 |
16 | # Output of the go coverage tool, specifically when used with LiteIDE
17 | *.out
18 |
19 | # Dependency directories (remove the comment below to include it)
20 | # vendor/
21 |
22 | ### Go Patch ###
23 | /vendor/
24 | /Godeps/
25 |
26 | ### Intellij+all ###
27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
29 |
30 | # User-specific stuff
31 | .idea/**/workspace.xml
32 | .idea/**/tasks.xml
33 | .idea/**/usage.statistics.xml
34 | .idea/**/dictionaries
35 | .idea/**/shelf
36 |
37 | # Generated files
38 | .idea/**/contentModel.xml
39 |
40 | # Sensitive or high-churn files
41 | .idea/**/dataSources/
42 | .idea/**/dataSources.ids
43 | .idea/**/dataSources.local.xml
44 | .idea/**/sqlDataSources.xml
45 | .idea/**/dynamic.xml
46 | .idea/**/uiDesigner.xml
47 | .idea/**/dbnavigator.xml
48 |
49 | # Gradle
50 | .idea/**/gradle.xml
51 | .idea/**/libraries
52 |
53 | # Gradle and Maven with auto-import
54 | # When using Gradle or Maven with auto-import, you should exclude module files,
55 | # since they will be recreated, and may cause churn. Uncomment if using
56 | # auto-import.
57 | # .idea/artifacts
58 | # .idea/compiler.xml
59 | # .idea/jarRepositories.xml
60 | # .idea/modules.xml
61 | # .idea/*.iml
62 | # .idea/modules
63 | # *.iml
64 | # *.ipr
65 |
66 | # CMake
67 | cmake-build-*/
68 |
69 | # Mongo Explorer plugin
70 | .idea/**/mongoSettings.xml
71 |
72 | # File-based project format
73 | *.iws
74 |
75 | # IntelliJ
76 | out/
77 |
78 | # mpeltonen/sbt-idea plugin
79 | .idea_modules/
80 |
81 | # JIRA plugin
82 | atlassian-ide-plugin.xml
83 |
84 | # Cursive Clojure plugin
85 | .idea/replstate.xml
86 |
87 | # Crashlytics plugin (for Android Studio and IntelliJ)
88 | com_crashlytics_export_strings.xml
89 | crashlytics.properties
90 | crashlytics-build.properties
91 | fabric.properties
92 |
93 | # Editor-based Rest Client
94 | .idea/httpRequests
95 |
96 | # Android studio 3.1+ serialized cache file
97 | .idea/caches/build_file_checksums.ser
98 |
99 | ### Intellij+all Patch ###
100 | # Ignores the whole .idea folder and all .iml files
101 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
102 |
103 | .idea/
104 |
105 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
106 |
107 | *.iml
108 | modules.xml
109 | .idea/misc.xml
110 | *.ipr
111 |
112 | # Sonarlint plugin
113 | .idea/sonarlint
114 |
115 | # End of https://www.toptal.com/developers/gitignore/api/go,intellij+all
116 |
117 | # General
118 | .DS_Store
119 | .AppleDouble
120 | .LSOverride
121 |
122 | examples/version1/testing.go
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-notion (beta) [](https://github.com/oyekanmiayo/go-notion/actions?query=workflow%3ABuild)
2 |
3 | go-notion is a minimal Go client library for [Notion's v1 API](https://developers.notion.com/). Check the [usage](#usage) or
4 | [examples](examples/version1) to see how to access Notion's v1 API.
5 |
6 | ***NB**: Notion's v1 API is still in beta; this integration will change as they update it.*
7 |
8 | ## Table of Contents
9 |
10 | * [Installation](#installation)
11 | * [Authentication](#authentication)
12 | * [Internal Integration](#internal-integration)
13 | * [Public Integration](#public-integration)
14 | * [Usage](#usage)
15 | * [Databases](#databases)
16 | * [Pages](#pages)
17 | * [Blocks](#blocks)
18 | * [Users](#users)
19 | * [Search](#search)
20 | * [Contributing](#contributing)
21 | * [License](#license)
22 | * [Authors](#authors)
23 |
24 | ## Installation
25 |
26 | ```
27 | go get github.com/oyekanmiayo/go-notion/notion/version1
28 | ```
29 |
30 | ## Authentication
31 |
32 | HTTP requests to Notion's API must contain a bearer token in the `Authorization` header to be successful. Currently,
33 | Notion allows two types of integrations: Internal and Public. Getting the bearer token is different for each case.
34 |
35 | ### Internal Integration
36 |
37 | When you create an internal integration on Notion's developer portal, you are given something called an "Internal
38 | Integration Token" - this is the bearer token. All you need to do is simply copy it out and store it as an environment
39 | variable (preferred) or use it directly in your code.
40 |
41 |
42 |
43 | NB: Internal Integration Token, Bearer Token and Access Token mean the same things here.
44 |
45 | ```go
46 | import (
47 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
48 | "net/http"
49 | "os"
50 | )
51 |
52 | func main() {
53 | accessToken := os.Getenv("NOTION_BEARER_TOKEN")
54 |
55 | // Notion Client
56 | client := notion.NewClient(http.DefaultClient, accessToken)
57 | }
58 | ```
59 |
60 | ### Public Integration
61 |
62 | This integration receives bearer tokens each time a user completes the [OAuth flow](https://developers.notion.com/docs/authorization#authorizing-public-integrations).
63 | Basically, this is in 3 steps:
64 | 1. **The client** requests authorization from a user
65 | 2. **Notion** calls a callback url (a.k.a `redirect_uri`) with `state` and `code` as query params
66 | 3. **The client** exchanges the code for a token
67 |
68 | ```go
69 | import (
70 | "flag"
71 | "fmt"
72 | "golang.org/x/oauth2"
73 | "log"
74 | "os"
75 | "net/http"
76 | )
77 |
78 | func main() {
79 |
80 | flags := flag.NewFlagSet("notion-example", flag.ExitOnError)
81 | clientID := flags.String("client-id", "", "Client ID")
82 | redirectURL := flags.String("url", "", "Redirect url")
83 | err := flags.Parse(os.Args[1:])
84 | if err != nil {
85 | log.Fatalf("error: %v", err)
86 | }
87 |
88 | c := oauth2.Config{
89 | ClientID: *clientID,
90 | ClientSecret: "",
91 | Endpoint: oauth2.Endpoint{
92 | AuthURL: "https://api.notion.com/v1/oauth/authorize",
93 | TokenURL: "https://api.notion.com/v1/oauth/token",
94 | },
95 | RedirectURL: *redirectURL,
96 | }
97 |
98 | client := notion.AuthClient(http.DefaultClient)
99 |
100 | // Generate the authorization url
101 | authURL := client.Auth.AuthorizationURL(&c, "")
102 | fmt.Println(authURL)
103 |
104 | // The authorization url should be sent back to the client as a redirect
105 | // The user will see an option to give the integration access to their workspace
106 | // If the user approves the request, notion will call the call the callback url (a.k.a `redirect_uri`) with `state` and `code` as query params
107 |
108 | // Use the code here to get an access token that can now be used with go-notion's client
109 | resp, _ := client.Auth.AccessToken(&c, *code)
110 | fmt.Println(resp)
111 |
112 | client := notion.NewClient(http.DefaultClient, resp.AcessToken)
113 | }
114 | ```
115 |
116 | The `clientID` and `clientSecret` in the snippet above can be found on the developer portal:
117 |
118 |
119 |
120 | ## Usage
121 |
122 | ### Databases
123 |
124 | Read more about the Database endpoints [here](https://developers.notion.com/reference/database).
125 |
126 | #### Retrieve a database
127 |
128 | This retrieves a Notion database based on a specified ID. Read more [here](https://developers.notion.com/reference/get-database).
129 |
130 | ```go
131 | client := notion.NewClient(http.DefaultClient, *accessToken)
132 |
133 | // Retrieve DB
134 | db, _, err := client.Databases.RetrieveDatabase(databaseID)
135 | if err != nil {
136 | fmt.Printf("Err %v\n", err)
137 | }
138 | ```
139 |
140 | See full code example [here](examples/version1/retrieve-database-example.go).
141 |
142 | #### Query a database
143 |
144 | This gets a list of [Pages](https://developers.notion.com/reference/page) contained in a database, filtered and ordered according to the filter conditions and sort
145 | criteria provided in the request. Filters can single filters or compound filters. Read more [here](https://developers.notion.com/reference/post-database-query).
146 |
147 | ```go
148 | client := notion.NewClient(http.DefaultClient, *accessToken)
149 |
150 | // Query DB with SingleFilter
151 | params := ¬ion.QueryDatabaseBodyParams{
152 | Filter: ¬ion.SingleFilter{
153 | Property: "Tags",
154 | MultiSelect: ¬ion.MultiSelectCondition{
155 | Contains: "Tag1",
156 | },
157 | },
158 | }
159 | resp, _, err := client.Databases.QueryDatabase(*databaseID, params)
160 | if err != nil {
161 | fmt.Printf("Err %v\n", err)
162 | }
163 | ```
164 |
165 | See full code example [here](examples/version1/query-database-example.go) - it also contains a compound filter example :)
166 |
167 | #### List databases
168 |
169 | More than one database can be shared with a Notion integration. This endpoint lists all the databases shared with an
170 | authenticated integration. Read more [here](https://developers.notion.com/reference/get-databases).
171 |
172 | ```go
173 | client := notion.NewClient(http.DefaultClient, *accessToken)
174 |
175 | // List all DBs in a workspace
176 | params := ¬ion.ListDatabasesQueryParams{
177 | PageSize: 20,
178 | }
179 | resp, _, err := client.Databases.ListDatabases(params)
180 | if err != nil {
181 | fmt.Printf("Err %v\n", err)
182 | }
183 | ```
184 |
185 | See full code example [here](examples/version1/list-databases-example.go).
186 |
187 | ### Pages
188 |
189 | Read more about Page endpoints [here](https://developers.notion.com/reference/page).
190 |
191 | #### Retrieve a page
192 |
193 | This retrieves a Notion page based on a specified ID. Read more [here](https://developers.notion.com/reference/get-page).
194 |
195 | ```go
196 | client := notion.NewClient(http.DefaultClient, *accessToken)
197 |
198 | // Retrieve a page using its pageID
199 | db, _, err := client.Pages.RetrievePage(*pageID)
200 | if err != nil {
201 | fmt.Printf("Err %v\n", err)
202 | }
203 | ```
204 |
205 | See full code example [here](examples/version1/retrieve-page-example.go).
206 |
207 | #### Create a page
208 |
209 | Pages in Notion can be created within a database or within another page. This endpoint creates a page as a child of the
210 | parent (database or page) specified. Read more [here](https://developers.notion.com/reference/post-page).
211 |
212 | ```go
213 | client := notion.NewClient(http.DefaultClient, *accessToken)
214 |
215 | params := ¬ion.CreatePageBodyParams{
216 | Parent: ¬ion.DatabaseParent{
217 | DatabaseID: *databaseID,
218 | },
219 | Properties: map[string]notion.PageProperty{
220 | "Name": {
221 | Title: []notion.RichText{
222 | {
223 | Text: ¬ion.Text{
224 | Content: "Creating Page Sample",
225 | },
226 | },
227 | },
228 | },
229 | "Tags": {
230 | MultiSelect: []notion.MultiSelectPropertyOpts{
231 | {
232 | Name: "Tag1",
233 | },
234 | {
235 | Name: "Tag3",
236 | },
237 | },
238 | },
239 | "Recommended": {
240 | Checkbox: true,
241 | },
242 | },
243 | }
244 | resp, _, err := client.Pages.CreatePage(params)
245 | if err != nil {
246 | fmt.Printf("Err %v\n", err)
247 | }
248 | ```
249 |
250 | The example above creates a page within a database. The body of the request is determined by the structure of the
251 | database. See full code example [here](examples/version1/create-page-example.go) along with an example to create a page within another page.
252 |
253 | #### Update page properties
254 |
255 | Updates page property values for the specified page. Properties that are not set via the "properties" parameter will
256 | remain unchanged. Read more [here](https://developers.notion.com/reference/patch-page).
257 |
258 | ```go
259 | client := notion.NewClient(http.DefaultClient, *accessToken)
260 |
261 | // Update the title of a page
262 | params := ¬ion.UpdatePagePropertiesBodyParams{
263 | // first keys are the names or ids of the properties :)
264 | // id for title is "title"
265 | // See examples here: https://developers.notion.com/reference/page#page-property-value
266 | Properties: map[string]notion.PageProperty{
267 | "Name": {
268 | Title: []notion.RichText{
269 | {
270 | Type: "text",
271 | Text: ¬ion.Text{
272 | Content: "Jamaican Cuisines III",
273 | },
274 | },
275 | },
276 | },
277 | "Recommended": {
278 | Checkbox: true,
279 | },
280 | },
281 | }
282 | resp, _, err := client.Pages.UpdatePageProperties(*pageID, params)
283 | if err != nil {
284 | fmt.Printf("Err %v\n", err)
285 | }
286 | ```
287 |
288 | See full code example [here](examples/version1/update-page-properties-example.go).
289 |
290 | ### Blocks
291 |
292 | A block object represents content within Notion. Blocks can be text, lists, media, and more. A page is a type of block,
293 | too! Read more about Block endpoints [here](https://developers.notion.com/reference/block).
294 |
295 | #### Retrieve block children
296 |
297 | Returns a paginated array of child block objects contained in the block using the ID specified. In order to receive a
298 | complete representation of a block, you may need to recursively retrieve the block children of child blocks. Read more [here](https://developers.notion.com/reference/get-block-children).
299 |
300 | ```go
301 | client := notion.NewClient(http.DefaultClient, *accessToken)
302 |
303 | // Retrieve the block children for a block
304 | params := ¬ion.RetrieveBlockChildrenParams{}
305 | db, _, err := client.Blocks.RetrieveBlockChildren(*blockID, params)
306 | if err != nil {
307 | fmt.Printf("Err %v\n", err)
308 | }
309 | ```
310 |
311 | See full code example [here](examples/version1/retrieve-block-children-example.go).
312 |
313 | #### Append block children
314 |
315 | Creates and appends new children blocks to the block using the ID specified. Returns the Block object which contains the
316 | new children. Read more [here](https://developers.notion.com/reference/patch-block-children).
317 |
318 | ```go
319 | client := notion.NewClient(http.DefaultClient, *accessToken)
320 |
321 | // Append block children (Header Two & Paragraph) to a block
322 | params := ¬ion.AppendBlockChildrenBodyParams{
323 | Children: []notion.Block{
324 | {
325 | Object: "block",
326 | Type: "heading_2",
327 | HeadingTwo: ¬ion.HeadingTwo{
328 | Text: []notion.RichText{
329 | {
330 | Type: "text",
331 | Text: ¬ion.Text{
332 | Content: "Header Two Test",
333 | },
334 | },
335 | },
336 | },
337 | },
338 | {
339 | Object: "block",
340 | Type: "paragraph",
341 | Paragraph: ¬ion.Paragraph{
342 | Text: []notion.RichText{
343 | {
344 | Type: "text",
345 | Text: ¬ion.Text{
346 | Content: "Paragraph Test",
347 | },
348 | },
349 | },
350 | },
351 | },
352 | },
353 | }
354 |
355 | db, _, err := client.Blocks.AppendBlockChildren(*blockID, params)
356 | if err != nil {
357 | fmt.Printf("Err %v\n", err)
358 | }
359 | ```
360 |
361 | See full code example [here](examples/version1/append-block-children-example.go).
362 |
363 | ### Users
364 |
365 | The User object represents a user in a Notion workspace. Users include guests, full workspace members, and bots. Read
366 | more about User endpoints [here](https://developers.notion.com/reference/user).
367 |
368 | #### Retrieve a user
369 |
370 | This retrieves a Notion user based on a specified ID. Read more [here](https://developers.notion.com/reference/get-user).
371 |
372 | ```go
373 | client := notion.NewClient(http.DefaultClient, *accessToken)
374 |
375 | // Retrieve a user by userID
376 | db, _, err := client.Users.RetrieveUser(*userID)
377 | if err != nil {
378 | fmt.Printf("Err %v\n", err)
379 | }
380 | ```
381 |
382 | See full code example [here](examples/version1/retrieve-user-example.go).
383 |
384 | #### List all users
385 |
386 | Returns a paginated list of users for the workspace. Read more [here](https://developers.notion.com/reference/get-users).
387 |
388 | ```go
389 | client := notion.NewClient(http.DefaultClient, *accessToken)
390 |
391 | // List all users in workspace
392 | params := ¬ion.ListUsersQueryParams{
393 | PageSize: 20,
394 | }
395 | db, _, err := client.Users.ListUsers(params)
396 | if err != nil {
397 | fmt.Printf("Err %v\n", err)
398 | }
399 | ```
400 |
401 | See full code example [here](examples/version1/list-users-example.go).
402 |
403 | ### Search
404 |
405 | Searches all pages and child pages that are shared with the integration and returns a list of databases and pages which
406 | have titles that contain the `query` parameter. Other parameters like `sort` and `filter` also affect the output. Read
407 | more about it [here](https://developers.notion.com/reference/post-search).
408 |
409 | ```go
410 | client := notion.NewClient(http.DefaultClient, *accessToken)
411 |
412 | // Search the workspace and return pages that have titles containing "Yurts"
413 | // The result should be sorted in descending order of "last_edited_time"
414 | params := ¬ion.SearchBodyParams{
415 | Query: "Yurts",
416 | Sort: ¬ion.Sort{
417 | Direction: "descending",
418 | Timestamp: "last_edited_time",
419 | },
420 | Filter: ¬ion.SearchFilter{
421 | Value: "page",
422 | Property: "object",
423 | },
424 | }
425 | db, _, err := client.Search.Search(params)
426 | if err != nil {
427 | fmt.Printf("Err %v\n", err)
428 | }
429 | ```
430 |
431 | See full code example [here](examples/version1/search-example.go).
432 |
433 | ### Contributing
434 |
435 | * Code Contributions won't be accepted until Notion's v1 API is out of beta
436 | * Open an issue if you find a bug or missing integration
437 |
438 | ### License
439 |
440 | [Apache 2.0 License](LICENSE)
441 |
442 | ### Authors
443 | * [Ayomide Oyekanmi](https://twitter.com/_alternatewolf)
444 |
--------------------------------------------------------------------------------
/assets/append-block-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyekanmiayo/go-notion/f800055d9888d516d72a67ea926e6d4c9bc1513d/assets/append-block-example.png
--------------------------------------------------------------------------------
/assets/example-database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyekanmiayo/go-notion/f800055d9888d516d72a67ea926e6d4c9bc1513d/assets/example-database.png
--------------------------------------------------------------------------------
/assets/internal-integration-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyekanmiayo/go-notion/f800055d9888d516d72a67ea926e6d4c9bc1513d/assets/internal-integration-token.png
--------------------------------------------------------------------------------
/assets/page-in-page-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyekanmiayo/go-notion/f800055d9888d516d72a67ea926e6d4c9bc1513d/assets/page-in-page-example.png
--------------------------------------------------------------------------------
/assets/public-integration-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyekanmiayo/go-notion/f800055d9888d516d72a67ea926e6d4c9bc1513d/assets/public-integration-details.png
--------------------------------------------------------------------------------
/examples/version1/README.md:
--------------------------------------------------------------------------------
1 | # go-notion examples
2 |
3 | This README helps to provide useful information to help people understand the examples in this directory.
4 |
5 | ## Examples
6 |
7 | * [Databases](#databases)
8 | * [Pages](#pages)
9 | * [Blocks](#blocks)
10 | * [Users](#users)
11 | * [Search](#search)
12 |
13 | ### Databases
14 |
15 | The image below represents the structure of the database that was used in the examples.
16 |
17 |
18 |
19 | #### Examples
20 |
21 | * [Retrieve a database](retrieve-database-example.go)
22 | * [Query a database](query-database-example.go)
23 | * [List databases](list-databases-example.go)
24 |
25 | ### Pages
26 |
27 | Pages can be added within databases or within other pages. If a page's parent is a database, it is added as an entry to
28 | that database. The rows in the "Example DB" image above represents pages within the DB. If a page's parent is another
29 | page, then it looks like the image below:
30 |
31 |
32 |
33 | #### Examples
34 |
35 | * [Retrieve a page](retrieve-page-example.go)
36 | * [Create a page](create-page-example.go)
37 | * [Update page properties](update-page-properties-example.go)
38 |
39 | ### Blocks
40 |
41 | Read more about blocks [here](../../README.md#blocks). In the append-block example, I demo-ed adding a header and
42 | paragraph to a page. It'll look like the image below.
43 |
44 |
45 |
46 | #### Examples
47 |
48 | * [Retrieve block children](retrieve-block-children-example.go)
49 | * [Append block children](append-block-children-example.go)
50 |
51 | ### Users
52 |
53 | #### Examples
54 |
55 | * [Retrieve a user](retrieve-user-example.go)
56 | * [List all users](list-users-example.go)
57 |
58 | ### Search
59 |
60 | I couldn't figure out how to combine the two calls below into one call. Ideally, we should be able to make a call to the
61 | Notion server that can contains an array of pages and databases. See the model below
62 |
63 | ```
64 | type SearchResponse struct {
65 | Object string `json:"object,omitempty"`
66 | Results []interface{} `json:"results,omitempty"`
67 | NextCursor string `json:"next_cursor,omitempty"`
68 | HasMore bool `json:"has_more,omitempty"`
69 | }
70 | ```
71 |
72 | The `Results` field cannot be `[]Page`, neither can it be `[]Database` because the results can be an array of both
73 | models. So, for now, the calls are split (with its own model). I'll add the things I've tried later, but if you think of
74 | something feel free to add as an issue to the repo :).
75 |
76 | #### Examples
77 |
78 | * [Search a page](search-example.go)
79 | * [Search a database](search-example.go)
--------------------------------------------------------------------------------
/examples/version1/append-block-children-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | blockID := flags.String("block-id", "", "Page ID")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | client := notion.NewClient(http.DefaultClient, *accessToken)
24 |
25 | // Append block children to a block
26 | // Sample command: go run append-block-children-example.go --access-token= --block-id=
27 | params := ¬ion.AppendBlockChildrenBodyParams{
28 | Children: []notion.Block{
29 | {
30 | Object: "block",
31 | Type: "heading_2",
32 | HeadingTwo: ¬ion.HeadingTwoBlock{
33 | Text: []notion.RichText{
34 | {
35 | Type: "text",
36 | Text: ¬ion.Text{
37 | Content: "Header Two Test",
38 | },
39 | },
40 | },
41 | },
42 | },
43 | {
44 | Object: "block",
45 | Type: "paragraph",
46 | Paragraph: ¬ion.ParagraphBlock{
47 | Text: []notion.RichText{
48 | {
49 | Type: "text",
50 | Text: ¬ion.Text{
51 | Content: "Paragraph Test",
52 | },
53 | },
54 | },
55 | },
56 | },
57 | },
58 | }
59 |
60 |
61 | res, _, err := client.Blocks.AppendBlockChildren(*blockID, params)
62 | if err != nil {
63 | fmt.Printf("Err %v\n", err)
64 | }
65 |
66 | jsonBody, _ := json.Marshal(res)
67 | fmt.Println()
68 | fmt.Println(string(jsonBody))
69 | }
70 |
--------------------------------------------------------------------------------
/examples/version1/create-page-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
8 | "log"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | func main() {
14 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
15 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
16 | pageID := flags.String("page-id", "", "Page ID")
17 | databaseID := flags.String("db-id", "", "DB ID")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | client := notion.NewClient(http.DefaultClient, *accessToken)
24 |
25 | // Create a page in a database
26 | // Sample command: go run create-page-example.go --access-token= --page-id= --db-id=
27 | params := ¬ion.CreatePageBodyParams{
28 | Parent: ¬ion.DatabaseParent{
29 | DatabaseID: *databaseID,
30 | },
31 | Properties: map[string]notion.PageProperty{
32 | "Name": {
33 | Title: []notion.RichText{
34 | {
35 | Text: ¬ion.Text{
36 | Content: "Creating Page Sample",
37 | },
38 | },
39 | },
40 | },
41 | "Tags": {
42 | MultiSelect: []notion.MultiSelectPropertyOpts{
43 | {
44 | Name: "Tag1",
45 | },
46 | {
47 | Name: "Tag3",
48 | },
49 | },
50 | },
51 | "Recommended": {
52 | Checkbox: true,
53 | },
54 | },
55 | }
56 | resp, _, err := client.Pages.CreatePage(params)
57 | if err != nil {
58 | fmt.Printf("Err %v\n", err)
59 | }
60 |
61 | jsonBody, _ := json.Marshal(resp)
62 | fmt.Println(string(jsonBody))
63 |
64 | // Create a page within a page
65 | params = ¬ion.CreatePageBodyParams{
66 | Parent: ¬ion.PageParent{
67 | PageID: *pageID,
68 | },
69 | Properties: ¬ion.PageProperty{
70 | Title: []notion.RichText{
71 | {
72 | Text: ¬ion.Text{
73 | Content: "Creating Page Within A Page Sample",
74 | },
75 | },
76 | },
77 | },
78 | }
79 |
80 | resp, _, err = client.Pages.CreatePage(params)
81 | if err != nil {
82 | fmt.Printf("Err %v\n", err)
83 | }
84 |
85 | jsonBody, _ = json.Marshal(resp)
86 | fmt.Println(string(jsonBody))
87 | }
88 |
--------------------------------------------------------------------------------
/examples/version1/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/oyekanmiayo/go-notion/examples/version1
2 |
3 | go 1.15
4 |
5 | replace github.com/oyekanmiayo/go-notion/notion/version1 => ../../notion/version1
6 |
7 | require (
8 | github.com/dghubble/sling v1.3.0 // indirect
9 | github.com/oyekanmiayo/go-notion/notion/version1 v0.0.0-00010101000000-000000000000
10 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
11 | )
12 |
--------------------------------------------------------------------------------
/examples/version1/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
34 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
35 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
36 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
37 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
38 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
39 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
40 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
41 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
42 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43 | github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=
44 | github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
45 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
46 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
47 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
48 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
49 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
50 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
51 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
52 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
53 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
54 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
55 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
56 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
57 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
58 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
59 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
60 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
61 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
62 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
63 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
64 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
65 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
66 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
67 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
68 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
69 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
70 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
71 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
72 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
73 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
74 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
75 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
76 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
77 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
78 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
79 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
80 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
81 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
82 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
83 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
84 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
85 | github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
86 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
87 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
88 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
89 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
90 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
91 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
92 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
93 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
94 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
95 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
96 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
97 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
98 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
99 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
100 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
101 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
102 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
103 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
104 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
105 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
106 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
107 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
108 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
109 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
110 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
111 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
112 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
113 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
114 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
115 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
116 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
117 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
118 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
119 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
120 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
121 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
122 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
123 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
124 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
125 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
126 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
127 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
128 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
129 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
130 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
131 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
132 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
133 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
134 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
135 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
136 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
137 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
138 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
139 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
140 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
141 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
142 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
143 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
144 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
145 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
146 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
147 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
148 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
149 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
150 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
151 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
152 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
153 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
154 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
155 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
156 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
157 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
158 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
159 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
160 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
161 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
162 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
163 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
164 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
165 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
166 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
167 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
168 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
169 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
170 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
171 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
172 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
173 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
174 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
175 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
176 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
177 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
178 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
179 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
180 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
181 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
182 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
183 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
184 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
185 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
186 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
187 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
188 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
189 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
190 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
191 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
192 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
193 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
194 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
195 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
196 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
197 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
198 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
199 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
200 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
201 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
202 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
203 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
204 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
205 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
206 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
207 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
208 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
209 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
210 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
211 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
212 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
213 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
214 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
215 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
216 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
217 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
218 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
219 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
220 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
221 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
222 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
223 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
224 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
225 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
226 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
227 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
228 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
229 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
230 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
231 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
232 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
233 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
234 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
235 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
236 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
237 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
238 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
239 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
240 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
241 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
242 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
243 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
244 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
245 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
246 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
247 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
248 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
249 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
250 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
251 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
252 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
253 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
254 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
255 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
256 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
257 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
258 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
259 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
260 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
261 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
262 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
263 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
264 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
265 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
266 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
267 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
268 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
269 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
270 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
271 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
272 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
273 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
274 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
275 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
276 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
277 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
278 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
279 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
280 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
281 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
282 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
283 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
284 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
285 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
286 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
287 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
288 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
289 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
290 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
291 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
292 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
293 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
294 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
295 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
296 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
297 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
298 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
299 | google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
300 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
301 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
302 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
303 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
304 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
305 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
306 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
307 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
308 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
309 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
310 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
311 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
312 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
313 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
314 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
315 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
316 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
317 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
318 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
319 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
320 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
321 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
322 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
323 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
324 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
325 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
326 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
327 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
328 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
329 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
330 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
331 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
332 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
333 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
334 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
335 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
336 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
337 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
338 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
339 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
340 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
341 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
342 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
343 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
344 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
345 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
346 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
347 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
348 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
349 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
350 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
351 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
352 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
353 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
354 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
355 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
356 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
357 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
358 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
359 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
360 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
361 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
362 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
363 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
364 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
365 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
366 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
367 |
--------------------------------------------------------------------------------
/examples/version1/list-databases-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | err := flags.Parse(os.Args[1:])
18 | if err != nil {
19 | log.Fatalf("error: %v", err)
20 | }
21 |
22 | client := notion.NewClient(http.DefaultClient, *accessToken)
23 |
24 | // List all DBs in a workspace
25 | // Sample command: go run list-databases-example.go --access-token=
26 | params := ¬ion.ListDatabasesQueryParams{
27 | PageSize: 20,
28 | }
29 | resp, _, err := client.Databases.ListDatabases(params)
30 | if err != nil {
31 | fmt.Printf("Err %v\n", err)
32 | }
33 |
34 | jsonBody, _ := json.Marshal(resp)
35 | fmt.Println(string(jsonBody))
36 | }
37 |
--------------------------------------------------------------------------------
/examples/version1/list-users-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | err := flags.Parse(os.Args[1:])
18 | if err != nil {
19 | log.Fatalf("error: %v", err)
20 | }
21 |
22 | client := notion.NewClient(http.DefaultClient, *accessToken)
23 |
24 | // List all users in workspace
25 | // Sample command: go run list-users-example.go --access-token= --db-id=
26 | params := ¬ion.ListUsersQueryParams{
27 | PageSize: 20,
28 | }
29 | db, _, err := client.Users.ListUsers(params)
30 | if err != nil {
31 | fmt.Printf("Err %v\n", err)
32 | }
33 |
34 | jsonBody, _ := json.Marshal(db)
35 | fmt.Println(string(jsonBody))
36 | }
37 |
--------------------------------------------------------------------------------
/examples/version1/oauth2-flow-part1-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
7 | "golang.org/x/oauth2"
8 | "log"
9 | "os"
10 | )
11 |
12 | // go run oauth2-flow-part1-example.go --client-id= --url=
13 | func main() {
14 |
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | clientID := flags.String("client-id", "", "Client ID")
17 | redirectURL := flags.String("url", "", "Redirect url")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | c := oauth2.Config{
24 | ClientID: *clientID,
25 | ClientSecret: "",
26 | Endpoint: oauth2.Endpoint{
27 | AuthURL: "https://api.notion.com/v1/oauth/authorize",
28 | TokenURL: "https://api.notion.com/v1/oauth/token",
29 | },
30 | RedirectURL: *redirectURL,
31 | }
32 |
33 | client := notion.AuthClient(nil)
34 |
35 | authURL := client.Auth.AuthorizationURL(&c, "")
36 | fmt.Println(authURL)
37 | }
38 |
--------------------------------------------------------------------------------
/examples/version1/oauth2-flow-part2-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
7 | "golang.org/x/oauth2"
8 | "log"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | // go run oauth2-flow-part2-example.go --client-id= --client-secret= --code= --url=
14 | func main() {
15 |
16 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
17 | clientID := flags.String("client-id", "", "Client ID")
18 | clientSecret := flags.String("client-secret", "", "Client ID")
19 | code := flags.String("code", "", "Auth Code from Notion")
20 | redirectURL := flags.String("url", "", "Redirect url")
21 | err := flags.Parse(os.Args[1:])
22 | if err != nil {
23 | log.Fatalf("error: %v", err)
24 | }
25 |
26 | c := oauth2.Config{
27 | ClientID: *clientID,
28 | ClientSecret: *clientSecret,
29 | Endpoint: oauth2.Endpoint{
30 | AuthURL: "https://api.notion.com/v1/oauth/authorize",
31 | TokenURL: "https://api.notion.com/v1/oauth/token",
32 | },
33 | RedirectURL: *redirectURL,
34 | }
35 |
36 | fmt.Println("Hello")
37 | client := notion.AuthClient(http.DefaultClient)
38 |
39 | // This code is sent in as a query param to the redirect_uri after the user has authorized notion
40 | //?code=&state=
41 | resp, _, _ := client.Auth.AccessToken(&c, *code)
42 | fmt.Println(resp)
43 | }
44 |
--------------------------------------------------------------------------------
/examples/version1/query-database-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | databaseID := flags.String("db-id", "", "Notion Database ID")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | client := notion.NewClient(http.DefaultClient, *accessToken)
24 |
25 | // Query DB with SingleFilter
26 | // Sample command: go run query-database-example.go --access-token= --db-id=
27 | params := ¬ion.QueryDatabaseBodyParams{
28 | Filter: ¬ion.SingleFilter{
29 | Property: "Tags",
30 | MultiSelect: ¬ion.MultiSelectCondition{
31 | Contains: "Tag1",
32 | },
33 | },
34 | }
35 | resp, _, err := client.Databases.QueryDatabase(*databaseID, params)
36 | if err != nil {
37 | fmt.Printf("Err %v\n", err)
38 | }
39 |
40 | jsonBody, _ := json.Marshal(resp)
41 | fmt.Println(string(jsonBody))
42 |
43 | fmt.Println()
44 |
45 | // Query DB with CompoundFilter
46 | params = ¬ion.QueryDatabaseBodyParams{
47 | Filter: ¬ion.CompoundFilter{
48 | AND: []notion.SingleFilter{
49 | {
50 | Property: "Tags",
51 | MultiSelect: ¬ion.MultiSelectCondition{
52 | Contains: "Tag1",
53 | },
54 | },
55 | {
56 | Property: "Recommended",
57 | Checkbox: ¬ion.CheckboxCondition{
58 | Equals: true,
59 | },
60 | },
61 | },
62 | },
63 | }
64 | resp, _, err = client.Databases.QueryDatabase(*databaseID, params)
65 | if err != nil {
66 | fmt.Printf("Err %v\n", err)
67 | }
68 |
69 | jsonBody, _ = json.Marshal(resp)
70 | fmt.Println(string(jsonBody))
71 | }
72 |
--------------------------------------------------------------------------------
/examples/version1/retrieve-block-children-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | blockID := flags.String("block-id", "", "Page ID")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | client := notion.NewClient(http.DefaultClient, *accessToken)
24 |
25 | // Retrieve the block children for a block
26 | // Sample command: go run retrieve-block-children-example.go --access-token= --block-id=
27 | params := ¬ion.RetrieveBlockChildrenParams{}
28 | db, _, err := client.Blocks.RetrieveBlockChildren(*blockID, params)
29 | if err != nil {
30 | fmt.Printf("Err %v\n", err)
31 | }
32 |
33 | jsonBody, _ := json.Marshal(db)
34 | fmt.Println(string(jsonBody))
35 | }
36 |
--------------------------------------------------------------------------------
/examples/version1/retrieve-database-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | databaseID := flags.String("db-id", "", "Notion Database ID")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | client := notion.NewClient(http.DefaultClient, *accessToken)
24 |
25 | // Retrieve DB
26 | // Sample command: go run retrieve-database-example.go --access-token= --db-id=
27 | db, _, err := client.Databases.RetrieveDatabase(*databaseID)
28 | if err != nil {
29 | fmt.Printf("Err %v\n", err)
30 | }
31 |
32 | // Print column names and type for DB table
33 | for k, v := range db.Properties {
34 | fmt.Printf("%v, %v", k, v.Type)
35 | fmt.Println()
36 | }
37 |
38 | // View response in json
39 | jsonBody, _ := json.Marshal(db)
40 | fmt.Println(string(jsonBody))
41 | }
42 |
--------------------------------------------------------------------------------
/examples/version1/retrieve-page-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
8 | "log"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | func main() {
14 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
15 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
16 | pageID := flags.String("page-id", "", "Page ID")
17 | err := flags.Parse(os.Args[1:])
18 | if err != nil {
19 | log.Fatalf("error: %v", err)
20 | }
21 |
22 | client := notion.NewClient(http.DefaultClient, *accessToken)
23 |
24 | // Retrieve a page using its pageID
25 | // Sample command: go run retrieve-page-example.go --access-token= --page-id=
26 | page, _, err := client.Pages.RetrievePage(*pageID)
27 | if err != nil {
28 | fmt.Printf("Err %v\n", err)
29 | }
30 |
31 | // Print page response as json
32 | jsonBody, _ := json.Marshal(page)
33 | fmt.Println(string(jsonBody))
34 | }
35 |
--------------------------------------------------------------------------------
/examples/version1/retrieve-user-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 |
11 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
12 | )
13 |
14 | func main() {
15 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
16 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
17 | userID := flags.String("user-id", "", "User ID")
18 | err := flags.Parse(os.Args[1:])
19 | if err != nil {
20 | log.Fatalf("error: %v", err)
21 | }
22 |
23 | client := notion.NewClient(http.DefaultClient, *accessToken)
24 |
25 | // Retrieve a user by userID
26 | // Sample command: go run retrieve-user-example.go --access-token= --user-id=
27 | db, _, err := client.Users.RetrieveUser(*userID)
28 | if err != nil {
29 | fmt.Printf("Err %v\n", err)
30 | }
31 |
32 | jsonBody, _ := json.Marshal(db)
33 | fmt.Println(string(jsonBody))
34 | }
35 |
--------------------------------------------------------------------------------
/examples/version1/search-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
8 | "log"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | func main() {
14 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
15 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
16 | err := flags.Parse(os.Args[1:])
17 | if err != nil {
18 | log.Fatalf("error: %v", err)
19 | }
20 |
21 | client := notion.NewClient(http.DefaultClient, *accessToken)
22 |
23 | // Search the workspace for pages with titles that contain this
24 | // Sample command: go run search-example.go --access-token=
25 | params := ¬ion.SearchBodyParams{
26 | Query: "Yurts",
27 | Sort: ¬ion.Sort{
28 | Direction: "descending",
29 | Timestamp: "last_edited_time",
30 | },
31 | }
32 | res, _, err := client.Search.SearchPage(params)
33 | if err != nil {
34 | fmt.Printf("Err %v\n", err)
35 | }
36 |
37 | jsonBody, _ := json.Marshal(res)
38 | fmt.Println(string(jsonBody))
39 |
40 | fmt.Println("<==============================>")
41 |
42 | // Search the workspace for databases with titles that contain this
43 | paramsII := ¬ion.SearchBodyParams{
44 | Query: "Yurts",
45 | Sort: ¬ion.Sort{
46 | Direction: "descending",
47 | Timestamp: "last_edited_time",
48 | },
49 | }
50 | resII, _, err := client.Search.SearchDatabase(paramsII)
51 | if err != nil {
52 | fmt.Printf("Err %v\n", err)
53 | }
54 |
55 | jsonBodyII, _ := json.Marshal(resII)
56 | fmt.Println(string(jsonBodyII))
57 | }
58 |
--------------------------------------------------------------------------------
/examples/version1/testing.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
7 | )
8 |
9 | func main() {
10 |
11 | params := ¬ion.SearchDatabaseResponse{
12 | Object: "list",
13 | Results: []notion.Database{
14 | {
15 | Object: "database",
16 | ID: "123",
17 | CreatedTime: "2021-05-23T07:41:16.751Z",
18 | LastEditedTime: "2021-05-23T07:41:00.000Z",
19 | Title: []notion.RichText{
20 | {
21 | Type: "text",
22 | Text: ¬ion.Text{
23 | Content: "Jamboree",
24 | },
25 | },
26 | },
27 | Properties: map[string]notion.PropertyObj{
28 | "Name": {
29 | ID: "title",
30 | Type: "title",
31 | Title: map[string]interface{}{},
32 | },
33 | },
34 | },
35 | },
36 | }
37 |
38 | res, _ := json.Marshal(params)
39 | fmt.Println(string(res))
40 | }
41 |
--------------------------------------------------------------------------------
/examples/version1/update-page-properties-example.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
8 | "log"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | func main() {
14 | flags := flag.NewFlagSet("notion-databases-example", flag.ExitOnError)
15 | accessToken := flags.String("access-token", "", "Notion API Key / Notion Access Token")
16 | pageID := flags.String("page-id", "", "Page ID")
17 | err := flags.Parse(os.Args[1:])
18 | if err != nil {
19 | log.Fatalf("error: %v", err)
20 | }
21 |
22 | client := notion.NewClient(http.DefaultClient, *accessToken)
23 |
24 | // Sample command: go run update-page-properties-example.go --access-token= --page-id=
25 | params := ¬ion.UpdatePagePropertiesBodyParams{
26 | // first keys are the names or ids of the properties :)
27 | // id for title is "title"
28 | // See examples here: https://developers.notion.com/reference/page#page-property-value
29 | Properties: map[string]notion.PageProperty{
30 | "Name": {
31 | Title: []notion.RichText{
32 | {
33 | Type: "text",
34 | Text: ¬ion.Text{
35 | Content: "Jamaican Cuisines III",
36 | },
37 | },
38 | },
39 | },
40 | "Recommended": {
41 | Checkbox: true,
42 | },
43 | },
44 | }
45 | resp, _, err := client.Pages.UpdatePageProperties(*pageID, params)
46 | if err != nil {
47 | fmt.Printf("Err %v\n", err)
48 | }
49 |
50 | jsonBody, _ := json.Marshal(resp)
51 | fmt.Println(string(jsonBody))
52 | }
53 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/oyekanmiayo/go-notion
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/dghubble/sling v1.3.0 // indirect
7 | github.com/stretchr/testify v1.7.0 // indirect
8 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
9 | )
10 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
34 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
35 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
36 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
37 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
38 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
39 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
40 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
41 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
42 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
43 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
44 | github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU=
45 | github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY=
46 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
47 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
48 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
49 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
50 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
51 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
52 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
53 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
54 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
55 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
56 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
57 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
58 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
59 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
60 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
61 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
62 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
63 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
64 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
65 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
66 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
67 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
68 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
69 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
70 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
71 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
72 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
73 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
74 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
75 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
76 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
77 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
78 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
79 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
80 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
81 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
82 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
83 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
84 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
85 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
86 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
87 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
88 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
89 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
90 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
91 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
92 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
93 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
94 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
95 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
96 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
97 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
98 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
99 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
100 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
101 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
102 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
103 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
104 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
105 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
106 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
107 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
108 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
109 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
110 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
111 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
112 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
113 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
114 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
115 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
116 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
117 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
118 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
119 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
120 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
121 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
122 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
123 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
124 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
125 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
126 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
127 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
128 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
129 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
130 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
131 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
132 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
133 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
134 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
135 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
136 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
137 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
138 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
139 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
140 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
141 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
142 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
143 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
144 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
145 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
146 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
147 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
148 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
149 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
150 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
151 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
152 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
153 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
154 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
155 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
156 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
157 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
158 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
159 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
160 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
161 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
162 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
163 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
164 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
165 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
166 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
167 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
168 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
169 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
170 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
171 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
172 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
173 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
174 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
175 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
176 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
177 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
178 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
179 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
180 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
181 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
182 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
183 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
184 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
185 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
186 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
187 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
188 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
189 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
190 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
191 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
192 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
193 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
194 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
195 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
196 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
197 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
198 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
199 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
200 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
201 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
202 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
203 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
204 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
205 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
206 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
207 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
208 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
209 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
210 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
211 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
212 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
213 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
214 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
215 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
216 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
217 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
218 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
219 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
220 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
221 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
222 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
223 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
224 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
225 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
226 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
227 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
228 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
229 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
230 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
231 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
232 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
233 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
234 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
235 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
236 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
237 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
238 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
239 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
240 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
241 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
242 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
243 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
244 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
245 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
246 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
247 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
248 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
249 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
250 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
251 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
252 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
253 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
254 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
255 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
256 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
257 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
258 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
259 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
260 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
261 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
262 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
263 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
264 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
265 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
266 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
267 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
268 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
269 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
270 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
271 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
272 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
273 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
274 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
275 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
276 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
277 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
278 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
279 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
280 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
281 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
282 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
283 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
284 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
285 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
286 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
287 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
288 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
289 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
290 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
291 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
292 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
293 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
294 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
295 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
296 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
297 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
298 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
299 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
300 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
301 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
302 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
303 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
304 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
305 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
306 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
307 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
308 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
309 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
310 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
311 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
312 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
313 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
314 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
315 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
316 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
317 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
318 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
319 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
320 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
321 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
322 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
323 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
324 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
325 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
326 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
327 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
328 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
329 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
330 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
331 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
332 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
333 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
334 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
335 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
336 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
337 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
338 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
339 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
340 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
341 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
342 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
343 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
344 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
345 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
346 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
347 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
348 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
349 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
350 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
351 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
352 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
353 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
354 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
355 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
356 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
357 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
358 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
359 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
360 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
361 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
362 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
363 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
364 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
365 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
366 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
367 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
368 |
--------------------------------------------------------------------------------
/notion/version1/auth.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "encoding/base64"
5 | "github.com/dghubble/sling"
6 | "golang.org/x/oauth2"
7 | "net/http"
8 | )
9 |
10 | type AuthService struct {
11 | sling *sling.Sling
12 | }
13 |
14 | // newAuthService returns a new AuthService.
15 | func newAuthService(sling *sling.Sling) *AuthService {
16 | return &AuthService{
17 | sling: sling.Path("oauth/"),
18 | }
19 | }
20 |
21 | func (a *AuthService) AuthorizationURL(c *oauth2.Config, state string) string {
22 | return c.AuthCodeURL("")
23 | }
24 |
25 | type AccessTokenRequest struct {
26 | GrantType string `json:"grant_type"`
27 | Code string `json:"code"`
28 | RedirectURI string `json:"redirect_uri"`
29 | }
30 |
31 | type AccessTokenResponse struct {
32 | AccessToken string `json:"access_token"`
33 | WorkspaceName string `json:"workspace_name"`
34 | WorkspaceIcon string `json:"workspace_icon"`
35 | BotID string `json:"bot_id"`
36 | }
37 |
38 | func (a *AuthService) AccessToken(c *oauth2.Config, authCode string) (*AccessTokenResponse, *http.Response, error) {
39 |
40 | tokenResponse := new(AccessTokenResponse)
41 | apiError := new(APIError)
42 |
43 | tokenRequest := AccessTokenRequest{
44 | GrantType: "authorization_code",
45 | Code: authCode,
46 | RedirectURI: c.RedirectURL,
47 | }
48 |
49 | resp, err := a.sling.New().Post("token").
50 | BodyJSON(tokenRequest).Add("Content-Type", "application/json").
51 | Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(c.ClientID+":"+c.ClientSecret))).
52 | Receive(tokenResponse, apiError)
53 |
54 | return tokenResponse, resp, relevantError(err, *apiError)
55 | }
56 |
--------------------------------------------------------------------------------
/notion/version1/auth_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "fmt"
5 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
6 | "github.com/stretchr/testify/assert"
7 | "golang.org/x/oauth2"
8 | "net/http"
9 | "testing"
10 | )
11 |
12 | var (
13 | testAuthorizationURL = "https://api.notion.com/v1/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8081&response_type=code"
14 | testAccessTokenReqJSON = `{"grant_type":"authorization_code","code":"1234","redirect_uri":"http://localhost:8081"}` + "\n"
15 | testAccessTokenResJSON = `{"access_token": "abc123def", "workspace_name": "workspace", "workspace_icon": "icon", "bot_id": "987"}`
16 | testAccessTokenRes = notion.AccessTokenResponse{
17 | AccessToken: "abc123def",
18 | WorkspaceName: "workspace",
19 | WorkspaceIcon: "icon",
20 | BotID: "987",
21 | }
22 | )
23 |
24 | func TestAuthService_AuthorizationURL(t *testing.T) {
25 | config := oauth2.Config{
26 | ClientID: "client_id",
27 | ClientSecret: "",
28 | Endpoint: oauth2.Endpoint{
29 | AuthURL: "https://api.notion.com/v1/oauth/authorize",
30 | TokenURL: "https://api.notion.com/v1/oauth/token",
31 | },
32 | RedirectURL: "http://localhost:8081",
33 | }
34 | client := notion.AuthClient(nil)
35 | authURL := client.Auth.AuthorizationURL(&config, "")
36 | assert.Equal(t, testAuthorizationURL, authURL)
37 | }
38 |
39 | func TestAuthService_AccessToken(t *testing.T) {
40 | httpClient, mux, server := testServer()
41 | defer server.Close()
42 |
43 | mux.HandleFunc("/v1/oauth/token", func(w http.ResponseWriter, r *http.Request) {
44 | assertMethod(t, "POST", r)
45 | assertPostJSON(t, testAccessTokenReqJSON, r)
46 | w.Header().Set("Content-Type", "application/json")
47 | fmt.Fprintf(w, testAccessTokenResJSON)
48 | })
49 |
50 | config := oauth2.Config{
51 | ClientID: "client_id",
52 | ClientSecret: "client_sec",
53 | Endpoint: oauth2.Endpoint{
54 | AuthURL: "https://api.notion.com/v1/oauth/authorize",
55 | TokenURL: "https://api.notion.com/v1/oauth/token",
56 | },
57 | RedirectURL: "http://localhost:8081",
58 | }
59 |
60 | client := notion.AuthClient(httpClient)
61 | resp, _, err := client.Auth.AccessToken(&config, "1234")
62 | assert.Nil(t, err)
63 | assert.Equal(t, &testAccessTokenRes, resp)
64 | }
65 |
--------------------------------------------------------------------------------
/notion/version1/blocks.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "github.com/dghubble/sling"
5 | "net/http"
6 | )
7 |
8 | type BlockService struct {
9 | sling *sling.Sling
10 | }
11 |
12 | // newBlockService returns a new BlockService.
13 | func newBlockService(sling *sling.Sling) *BlockService {
14 | return &BlockService{
15 | sling: sling.Path("blocks/"),
16 | }
17 | }
18 |
19 | // https://developers.notion.com/reference/block
20 | // Object is always "block"
21 | // Type must be one of "paragraph", "heading_1", "heading_2", "heading_3", "bulleted_list_item",
22 | // "numbered_list_item", "to_do", "toggle", "child_page", and "unsupported"
23 | type Block struct {
24 | Object string `json:"object,omitempty"`
25 | ID string `json:"id,omitempty"`
26 | Type string `json:"type,omitempty"`
27 | CreatedTime string `json:"created_time,omitempty"`
28 | LastEditedTime string `json:"last_edited_time,omitempty"`
29 | HasChildren bool `json:"has_children,omitempty"`
30 | Paragraph *ParagraphBlock `json:"paragraph,omitempty"`
31 | HeadingOne *HeadingOneBlock `json:"heading_1,omitempty"`
32 | HeadingTwo *HeadingTwoBlock `json:"heading_2,omitempty"`
33 | HeadingThree *HeadingThreeBlock `json:"heading_3,omitempty"`
34 | BulletedListItem *BulletedListItemBlock `json:"bulleted_list_item,omitempty"`
35 | NumberedListItem *NumberedListItemBlock `json:"numbered_list_item,omitempty"`
36 | ToDo *ToDoBlock `json:"to_do,omitempty"`
37 | Toggle *ToggleBlock `json:"toggle,omitempty"`
38 | ChildPage *ChildPageBlock `json:"child_page,omitempty"`
39 | }
40 |
41 | // https://developers.notion.com/reference/block#paragraph-blocks
42 | type ParagraphBlock struct {
43 | Text []RichText `json:"text,omitempty"`
44 | Children []Block `json:"children,omitempty"`
45 | }
46 |
47 | // https://developers.notion.com/reference/block#heading-one-blocks
48 | type HeadingOneBlock struct {
49 | Text []RichText `json:"text,omitempty"`
50 | }
51 |
52 | // https://developers.notion.com/reference/block#heading-two-blocks
53 | type HeadingTwoBlock struct {
54 | Text []RichText `json:"text,omitempty"`
55 | }
56 |
57 | // https://developers.notion.com/reference/block#heading-three-blocks
58 | type HeadingThreeBlock struct {
59 | Text []RichText `json:"text,omitempty"`
60 | }
61 |
62 | // https://developers.notion.com/reference/block#bulleted-list-item-blocks
63 | type BulletedListItemBlock struct {
64 | Text []RichText `json:"text,omitempty"`
65 | Children []Block `json:"children,omitempty"`
66 | }
67 |
68 | // https://developers.notion.com/reference/block#numbered-list-item-blocks
69 | type NumberedListItemBlock struct {
70 | Text []RichText `json:"text,omitempty"`
71 | Children []Block `json:"children,omitempty"`
72 | }
73 |
74 | // https://developers.notion.com/reference/block#to-do-blocks
75 | type ToDoBlock struct {
76 | Text []RichText `json:"text,omitempty"`
77 | Checked bool `json:"checked,omitempty"`
78 | Children []Block `json:"children,omitempty"`
79 | }
80 |
81 | // https://developers.notion.com/reference/block#toggle-blocks
82 | type ToggleBlock struct {
83 | Text []RichText `json:"text,omitempty"`
84 | Children []Block `json:"children,omitempty"`
85 | }
86 |
87 | // https://developers.notion.com/reference/block#child-page-blocks
88 | type ChildPageBlock struct {
89 | Title string `json:"title,omitempty"`
90 | }
91 |
92 | type RetrieveBlockChildrenParams struct {
93 | StartCursor string `url:"start_cursor,omitempty"`
94 | PageSize int32 `url:"page_size,omitempty"`
95 | }
96 |
97 | type RetrieveBlockChildrenResponse struct {
98 | Object string `json:"object,omitempty"`
99 | Results []Block `json:"results,omitempty"`
100 | NextCursor string `json:"next_cursor,omitempty"`
101 | HasMore bool `json:"has_more,omitempty"`
102 | }
103 |
104 | // https://developers.notion.com/reference/get-block-children
105 | func (b *BlockService) RetrieveBlockChildren(blockID string,
106 | params *RetrieveBlockChildrenParams) (*RetrieveBlockChildrenResponse, *http.Response, error) {
107 | response := new(RetrieveBlockChildrenResponse)
108 | apiError := new(APIError)
109 | resp, err := b.sling.New().Get(blockID+"/children").QueryStruct(params).Receive(response, apiError)
110 |
111 | return response, resp, relevantError(err, *apiError)
112 | }
113 |
114 | type AppendBlockChildrenBodyParams struct {
115 | Children []Block `json:"children"`
116 | }
117 |
118 | // https://developers.notion.com/reference/patch-block-children
119 | // NB: Blocks cannot be modified currently. Once a block is appended as a child of another block,
120 | // it cannot be updated or deleted.
121 | func (b *BlockService) AppendBlockChildren(blockID string, params *AppendBlockChildrenBodyParams) (*Block, *http.Response, error) {
122 | block := new(Block)
123 | apiError := new(APIError)
124 | resp, err := b.sling.New().Patch(blockID+"/children").BodyJSON(params).Receive(block, apiError)
125 |
126 | return block, resp, relevantError(err, *apiError)
127 | }
128 |
--------------------------------------------------------------------------------
/notion/version1/blocks_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "fmt"
5 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
6 | "github.com/stretchr/testify/assert"
7 | "net/http"
8 | "testing"
9 | )
10 |
11 | var (
12 | testRetrieveBlockChildrenResJSON = `{"object":"list","results":[{"object":"block","type":"to_do","to_do":{"text":[{"plain_text":"Sample Task","type":""}]}}]}`
13 | testRetrieveBlockChildrenRes = ¬ion.RetrieveBlockChildrenResponse{
14 | Object: "list",
15 | Results: []notion.Block{
16 | {
17 | Object: "block",
18 | Type: "to_do",
19 | ToDo: ¬ion.ToDoBlock{
20 | Text: []notion.RichText{
21 | {
22 | PlainText: "Sample Task",
23 | },
24 | },
25 | },
26 | },
27 | },
28 | }
29 |
30 | testAppendBlockChildrenBodyParamsJSON = `{"children":[{"object":"block","type":"heading_2","heading_2":{"text":[{"type":"text","text":{"content":"Header Two Test"}}]}}]}` + "\n"
31 | testAppendBlockChildrenBodyParams = ¬ion.AppendBlockChildrenBodyParams{
32 | Children: []notion.Block{
33 | {
34 | Object: "block",
35 | Type: "heading_2",
36 | HeadingTwo: ¬ion.HeadingTwoBlock{
37 | Text: []notion.RichText{
38 | {
39 | Type: "text",
40 | Text: ¬ion.Text{
41 | Content: "Header Two Test",
42 | },
43 | },
44 | },
45 | },
46 | },
47 | },
48 | }
49 | testAppendBlockChildrenResJSON = `
50 | {
51 | "object": "block",
52 | "id": "123",
53 | "type": "child_page",
54 | "created_time": "2021-05-16T14:35:46.713Z",
55 | "last_edited_time": "2021-05-23T07:09:07.935Z",
56 | "has_children": true,
57 | "child_page": {
58 | "title": "Yurts in Big Surr, California 2 "
59 | }
60 | }`
61 | testAppendBlockChildrenRes = ¬ion.Block{
62 | Object: "block",
63 | ID: "123",
64 | Type: "child_page",
65 | CreatedTime: "2021-05-16T14:35:46.713Z",
66 | LastEditedTime: "2021-05-23T07:09:07.935Z",
67 | HasChildren: true,
68 | ChildPage: ¬ion.ChildPageBlock{
69 | Title: "Yurts in Big Surr, California 2 ",
70 | },
71 | }
72 | )
73 |
74 | func TestBlockService_RetrieveBlockChildren(t *testing.T) {
75 | httpClient, mux, server := testServer()
76 | defer server.Close()
77 |
78 | mux.HandleFunc("/v1/blocks/123/children", func(w http.ResponseWriter, r *http.Request) {
79 | assertMethod(t, "GET", r)
80 | w.Header().Set("Content-Type", "application/json")
81 | fmt.Fprintf(w, testRetrieveBlockChildrenResJSON)
82 | })
83 |
84 | client := notion.NewClient(httpClient, "0000")
85 | params := notion.RetrieveBlockChildrenParams{}
86 | resp, _, err := client.Blocks.RetrieveBlockChildren("123", ¶ms)
87 | assert.Nil(t, err)
88 | assert.Equal(t, testRetrieveBlockChildrenRes, resp)
89 | }
90 |
91 | func TestBlockService_AppendBlockChildren(t *testing.T) {
92 | httpClient, mux, server := testServer()
93 | defer server.Close()
94 |
95 | mux.HandleFunc("/v1/blocks/123/children", func(w http.ResponseWriter, r *http.Request) {
96 | assertMethod(t, "PATCH", r)
97 | assertPostJSON(t, testAppendBlockChildrenBodyParamsJSON, r)
98 | w.Header().Set("Content-Type", "application/json")
99 | fmt.Fprintf(w, testAppendBlockChildrenResJSON)
100 | })
101 |
102 | client := notion.NewClient(httpClient, "0000")
103 | resp, _, err := client.Blocks.AppendBlockChildren("123", testAppendBlockChildrenBodyParams)
104 | assert.Nil(t, err)
105 | assert.Equal(t, testAppendBlockChildrenRes, resp)
106 | }
107 |
--------------------------------------------------------------------------------
/notion/version1/common.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | // Type must be either "text", "mention" or "equation"
4 | // Use Mention for Mention
5 | type RichText struct {
6 | PlainText string `json:"plain_text,omitempty"`
7 | Href string `json:"href,omitempty"`
8 | Annotations *Annotations `json:"annotations,omitempty"`
9 | Type string `json:"type,omitempty"`
10 | Text *Text `json:"text,omitempty"`
11 | Mention *Mention `json:"mention,omitempty"`
12 | Equation *Equation `json:"equation,omitempty"`
13 | }
14 |
15 | // Color must be either "default", "gray", "brown", "orange", "yellow", "green", "blue", "purple", "pink", "red",
16 | // "gray_background", "brown_background", "orange_background", "yellow_background", "green_background",
17 | // "blue_background", "purple_background", "pink_background", or "red_background"
18 | type Annotations struct {
19 | Bold bool `json:"bold,omitempty"`
20 | Italic bool `json:"italic,omitempty"`
21 | Strikethrough bool `json:"strikethrough,omitempty"`
22 | Underline bool `json:"underline,omitempty"`
23 | Code bool `json:"code,omitempty"`
24 | Color string `json:"color,omitempty"`
25 | }
26 |
27 | type Text struct {
28 | Content string `json:"content,omitempty"`
29 | Link *Link `json:"link,omitempty"`
30 | }
31 |
32 | // If, Link != nil, Type is always "url" and URL is always a web address.
33 | // https://developers.notion.com/reference/rich-text#link-objects
34 | type Link struct {
35 | Type string `json:"type,omitempty"`
36 | URL string `json:"url,omitempty"`
37 | }
38 |
39 | // Use PageMention for PageMention
40 | // Use DatabaseMention for DatabaseMention
41 | // Use Date for Date
42 | type Mention struct {
43 | Type string `json:"type"`
44 | UserMention *User `json:"user,omitempty"`
45 | PageMention *PageMention `json:"page,omitempty"`
46 | DatabaseMention *DatabaseMention `json:"database,omitempty"`
47 | Date *DateProperty `json:"date,omitempty"`
48 | }
49 |
50 | type PageMention struct {
51 | ID string `json:"id,omitempty"`
52 | }
53 |
54 | type DatabaseMention struct {
55 | ID string `json:"id,omitempty"`
56 | }
57 |
58 | type Equation struct {
59 | Expression string `json:"expression,omitempty"`
60 | }
61 |
--------------------------------------------------------------------------------
/notion/version1/databases.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "github.com/dghubble/sling"
5 | "net/http"
6 | )
7 |
8 | type DatabaseService struct {
9 | sling *sling.Sling
10 | }
11 |
12 | // newDatabaseService returns a new DatabaseService.
13 | func newDatabaseService(sling *sling.Sling) *DatabaseService {
14 | return &DatabaseService{
15 | sling: sling.Path("databases/"),
16 | }
17 | }
18 |
19 | type Database struct {
20 | Object string `json:"object,omitempty"`
21 | ID string `json:"id,omitempty"`
22 | CreatedTime string `json:"created_time,omitempty"`
23 | LastEditedTime string `json:"last_edited_time,omitempty"`
24 | Title []RichText `json:"title,omitempty"`
25 | Properties map[string]PropertyObj `json:"properties,omitempty"`
26 | }
27 |
28 | // Type can only be one of "title", "rich_text", "number", "select", "multi_select", "date", "people", "file",
29 | // "checkbox", "url", "email", "phone_number", "formula", "relation", "rollup", "created_time", "created_by",
30 | // "last_edited_time", "last_edited_by".
31 | type PropertyObj struct {
32 | ID string `json:"id,omitempty"`
33 | Type string `json:"type,omitempty"`
34 | Title interface{} `json:"title,omitempty"`
35 | Text interface{} `json:"text,omitempty"`
36 | RichText interface{} `json:"rich_text,omitempty"`
37 | Number *NumberConfig `json:"number,omitempty"`
38 | Select *SelectConfig `json:"select,omitempty"`
39 | MultiSelect *MultiSelectConfig `json:"multi_select,omitempty"`
40 | Date interface{} `json:"date,omitempty"`
41 | People interface{} `json:"people,omitempty"`
42 | File interface{} `json:"file,omitempty"`
43 | Checkbox interface{} `json:"checkbox,omitempty"`
44 | URL interface{} `json:"url,omitempty"`
45 | Email interface{} `json:"email,omitempty"`
46 | PhoneNumber interface{} `json:"phone_number,omitempty"`
47 | Formula *FormulaConfig `json:"formula,omitempty"`
48 | Relation *RelationConfig `json:"relation,omitempty"`
49 | Rollup *RollupConfig `json:"rollup,omitempty"`
50 | CreatedTime interface{} `json:"created_time,omitempty"`
51 | CreatedBy interface{} `json:"created_by,omitempty"`
52 | LastEditedTime interface{} `json:"last_edited_time,omitempty"`
53 | LastEditedBy interface{} `json:"last_edited_by,omitempty"`
54 | }
55 |
56 | // Format can only be one of number, number_with_commas, percent, dollar, euro, pound, yen, ruble, rupee, won, yuan.
57 | type NumberConfig struct {
58 | Format string `json:"format,omitempty"`
59 | }
60 |
61 | type SelectConfig struct {
62 | Options []SelectOption `json:"options,omitempty"`
63 | }
64 |
65 | // Color can only be one of default, gray, brown, orange, yellow, green, blue, purple, pink, red.
66 | type SelectOption struct {
67 | Name string `json:"name,omitempty"`
68 | ID string `json:"id,omitempty"`
69 | Color string `json:"color,omitempty"`
70 | }
71 |
72 | type MultiSelectConfig struct {
73 | Options []MultiSelectOption `json:"options,omitempty"`
74 | }
75 |
76 | // Color can only be one of default, gray, brown, orange, yellow, green, blue, purple, pink, red.
77 | type MultiSelectOption struct {
78 | Name string `json:"name,omitempty"`
79 | ID string `json:"id,omitempty"`
80 | Color string `json:"color,omitempty"`
81 | }
82 |
83 | type FormulaConfig struct {
84 | Expression string `json:"expression,omitempty"`
85 | }
86 |
87 | type RelationConfig struct {
88 | DatabaseID string `json:"database_id,omitempty"`
89 | SyncedPropertyName string `json:"synced_property_name,omitempty"`
90 | SyncedPropertyID string `json:"synced_property_id,omitempty"`
91 | }
92 |
93 | // Function can be one of count_all, count_values, count_unique_values, count_empty, count_not_empty, percent_empty,
94 | // percent_not_empty, sum, average, median, min, max, range
95 | type RollupConfig struct {
96 | RelationPropertyName string `json:"relation_property_name,omitempty"`
97 | RelationPropertyID string `json:"relation_property_id,omitempty"`
98 | RollupPropertyName string `json:"rollup_property_name,omitempty"`
99 | RollupPropertyID string `json:"rollup_property_id,omitempty"`
100 | Function string `json:"function,omitempty"`
101 | }
102 |
103 | // https://developers.notion.com/reference/get-database
104 | func (d *DatabaseService) RetrieveDatabase(databaseID string) (*Database, *http.Response, error) {
105 | database := new(Database)
106 | apiError := new(APIError)
107 | resp, err := d.sling.New().Get(databaseID).Receive(database, apiError)
108 |
109 | return database, resp, relevantError(err, *apiError)
110 | }
111 |
112 | // Filter can be either SingleFilter or CompoundFilter
113 | type QueryDatabaseBodyParams struct {
114 | Filter interface{} `json:"filter,omitempty"`
115 | Sorts []SortCriteria `json:"sorts,omitempty"`
116 | StartCursor string `json:"start_cursor,omitempty"`
117 | PageSize int32 `json:"page_size,omitempty"`
118 | }
119 |
120 | // Timestamp must either be "created_time" or "last_edited_time"
121 | // Direction must either be "ascending" or "descending"
122 | type SortCriteria struct {
123 | Property string `json:"property,omitempty"`
124 | Timestamp string `json:"timestamp,omitempty"`
125 | Direction string `json:"direction,omitempty"`
126 | }
127 |
128 | // OR and AND can either be []SingleFilter or []CompoundFilter (this is only valid once).
129 | type CompoundFilter struct {
130 | OR interface{} `json:"or,omitempty"`
131 | AND interface{} `json:"and,omitempty"`
132 | }
133 |
134 | type SingleFilter struct {
135 | Property string `json:"property,omitempty"`
136 | Title *TextCondition `json:"title,omitempty"`
137 | RichText *TextCondition `json:"rich_text,omitempty"`
138 | URL *TextCondition `json:"url,omitempty"`
139 | Email *TextCondition `json:"email,omitempty"`
140 | Phone *TextCondition `json:"phone,omitempty"`
141 | Number *NumberCondition `json:"number,omitempty"`
142 | Checkbox *CheckboxCondition `json:"checkbox,omitempty"`
143 | Select *SelectCondition `json:"select,omitempty"`
144 | MultiSelect *MultiSelectCondition `json:"multi_select,omitempty"`
145 | CreatedTime *DateCondition `json:"created_time,omitempty"`
146 | LastEditedTime *DateCondition `json:"last_edited_time,omitempty"`
147 | Date *DateCondition `json:"date,omitempty"`
148 | CreatedBy *PeopleCondition `json:"created_by,omitempty"`
149 | LastEditedBy *PeopleCondition `json:"last_edited_by,omitempty"`
150 | Files *FileCondition `json:"files,omitempty"`
151 | Relation *RelationCondition `json:"relation,omitempty"`
152 | Formula *FormulaCondition `json:"formula,omitempty"`
153 | }
154 |
155 | type TextCondition struct {
156 | Equals string `json:"equals,omitempty"`
157 | DoesNotEqual string `json:"does_not_equal,omitempty"`
158 | Contains string `json:"contains,omitempty"`
159 | DoesNotContain string `json:"does_not_contain,omitempty"`
160 | StartsWith string `json:"starts_with,omitempty"`
161 | EndsWith string `json:"ends_with,omitempty"`
162 | IsEmpty bool `json:"is_empty,omitempty"`
163 | isNotEmpty bool `json:"is_not_empty,omitempty"`
164 | }
165 |
166 | type NumberCondition struct {
167 | Equals int32 `json:"equals,omitempty"`
168 | DoesNotEqual int32 `json:"does_not_equal,omitempty"`
169 | GreaterThan int32 `json:greater_than, omitempty`
170 | LessThan int32 `json:"less_than,omitempty"`
171 | GreaterThanOrEqualTo int32 `json:"greater_than_or_equal_to,omitempty"`
172 | LessThanOrEqualTo int32 `json:"less_than_or_equal_to,omitempty"`
173 | IsEmpty bool `json:"is_empty,omitempty"`
174 | isNotEmpty bool `json:"is_not_empty,omitempty"`
175 | }
176 |
177 | type CheckboxCondition struct {
178 | Equals bool `json:"equals,omitempty"`
179 | DoesNotEqual bool `json:"does_not_equal,omitempty"`
180 | }
181 |
182 | type SelectCondition struct {
183 | Equals string `json:"equals,omitempty"`
184 | DoesNotEqual string `json:"does_not_equal,omitempty"`
185 | IsEmpty bool `json:"is_empty,omitempty"`
186 | isNotEmpty bool `json:"is_not_empty,omitempty"`
187 | }
188 |
189 | type MultiSelectCondition struct {
190 | Contains string `json:"contains,omitempty"`
191 | DoesNotContain string `json:"does_not_contain,omitempty"`
192 | IsEmpty bool `json:"is_empty,omitempty"`
193 | isNotEmpty bool `json:"is_not_empty,omitempty"`
194 | }
195 |
196 | type DateCondition struct {
197 | Equals string `json:"equals,omitempty"`
198 | Before string `json:"before,omitempty"`
199 | After string `json:"after,omitempty"`
200 | OnOrBefore string `json:"on_or_before,omitempty"`
201 | IsEmpty bool `json:"is_empty,omitempty"`
202 | IsNotEmpty bool `json:"is_not_empty,omitempty"`
203 | OnOrAfter string `json:"on_or_after,omitempty"`
204 | PastWeek interface{} `json:"past_week,omitempty"`
205 | PastMonth interface{} `json:"past_month,omitempty"`
206 | PastYear interface{} `json:"past_year,omitempty"`
207 | NextWeek interface{} `json:"next_week,omitempty"`
208 | NextMonth interface{} `json:"next_month,omitempty"`
209 | NextYear interface{} `json:"next_year,omitempty"`
210 | }
211 |
212 | type PeopleCondition struct {
213 | Contains string `json:"contains,omitempty"`
214 | DoesNotContain string `json:"does_not_contain,omitempty"`
215 | IsEmpty bool `json:"is_empty,omitempty"`
216 | IsNotEmpty bool `json:"is_not_empty,omitempty"`
217 | }
218 |
219 | type FileCondition struct {
220 | IsEmpty bool `json:"is_empty,omitempty"`
221 | IsNotEmpty bool `json:"is_not_empty,omitempty"`
222 | }
223 |
224 | type RelationCondition struct {
225 | Contains string `json:"contains,omitempty"`
226 | DoesNotContains string `json:"does_not_contains,omitempty"`
227 | IsEmpty string `json:"is_empty,omitempty"`
228 | IsNotEmpty string `json:"is_not_empty,omitempty"`
229 | }
230 |
231 | type FormulaCondition struct {
232 | Text *TextCondition `json:"text,omitempty"`
233 | Checkbox *CheckboxCondition `json:"checkbox,omitempty"`
234 | Number *NumberCondition `json:"number,omitempty"`
235 | Date *DateCondition `json:"date,omitempty"`
236 | }
237 |
238 | type QueryDatabaseResponse struct {
239 | Object string `json:"object,omitempty"`
240 | Results []Page `json:"results,omitempty"`
241 | NextCursor string `json:"next_cursor,omitempty"`
242 | HasMore bool `json:"has_more,omitempty"`
243 | }
244 |
245 | // https://developers.notion.com/reference/post-database-query
246 | func (d *DatabaseService) QueryDatabase(databaseID string,
247 | params *QueryDatabaseBodyParams) (*QueryDatabaseResponse, *http.Response, error) {
248 |
249 | response := new(QueryDatabaseResponse)
250 | apiError := new(APIError)
251 | resp, err := d.sling.New().Post(databaseID+"/query").BodyJSON(params).Receive(response, apiError)
252 |
253 | return response, resp, relevantError(err, *apiError)
254 | }
255 |
256 | type ListDatabasesQueryParams struct {
257 | StartCursor string `url:"start_cursor,omitempty"`
258 | PageSize int32 `url:"page_size,omitempty"`
259 | }
260 |
261 | type ListDatabasesResponse struct {
262 | Results []Database `json:"results,omitempty"`
263 | NextCursor string `json:"next_cursor,omitempty"`
264 | HasMore bool `json:"has_more,omitempty"`
265 | }
266 |
267 | // https://developers.notion.com/reference/get-databases
268 | func (d *DatabaseService) ListDatabases(params *ListDatabasesQueryParams) (*ListDatabasesResponse, *http.Response, error) {
269 | response := new(ListDatabasesResponse)
270 | apiError := new(APIError)
271 | resp, err := d.sling.New().Get("").QueryStruct(params).Receive(response, apiError)
272 |
273 | return response, resp, relevantError(err, *apiError)
274 | }
275 |
--------------------------------------------------------------------------------
/notion/version1/databases_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "fmt"
5 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
6 | "github.com/stretchr/testify/assert"
7 | "net/http"
8 | "testing"
9 | )
10 |
11 | var (
12 | testDB = ¬ion.Database{
13 | Object: "database",
14 | ID: "123",
15 | CreatedTime: "2021-05-23T07:41:16.751Z",
16 | LastEditedTime: "2021-05-23T07:41:00.000Z",
17 | Title: []notion.RichText{
18 | {
19 | PlainText: "TestDB",
20 | Annotations: ¬ion.Annotations{
21 | Color: "default",
22 | },
23 | Type: "text",
24 | Text: ¬ion.Text{
25 | Content: "TestDB",
26 | },
27 | },
28 | },
29 | Properties: map[string]notion.PropertyObj{
30 | "Name": {
31 | ID: "title",
32 | Type: "title",
33 | Title: map[string]interface{}{},
34 | },
35 | },
36 | }
37 |
38 | testDatabaseResJSON = `{"object":"database","id":"123","created_time":"2021-05-23T07:41:16.751Z","last_edited_time":"2021-05-23T07:41:00.000Z","title":[{"plain_text":"TestDB","annotations":{"color":"default"},"type":"text","text":{"content":"TestDB"}}],"properties":{"Name":{"id":"title","type":"title","title":{}}}}` + "\n"
39 | testDatabaseRes = testDB
40 |
41 | testSingleFilterParamsJSON = `{"filter":{"property":"Tags","multi_select":{"contains":"TagTest"}}}` + "\n"
42 | testSingleFilterParams = ¬ion.QueryDatabaseBodyParams{
43 | Filter: ¬ion.SingleFilter{
44 | Property: "Tags",
45 | MultiSelect: ¬ion.MultiSelectCondition{
46 | Contains: "TagTest",
47 | },
48 | },
49 | }
50 | testFilterResJSON = `{"object":"list","results":[{"object":"page","id":"5678","created_time":"2021-05-14T01:06:32.845Z","last_edited_time":"2021-05-23T08:02:00.000Z","parent":{"database_id":"38923","type":"database_id"},"properties":{"Name":{"id":"title","type":"title","title":[{"plain_text":"Jamboree"}]},"Recommended":{"id":"EZMA","type":"checkbox","checkbox":true},"Tags":{"id":"VSvn","type":"multi_select","multi_select":[{"id":"44645","name":"TagTest","color":"purple"}]}}}]}` + "\n"
51 | testFilterRes = ¬ion.QueryDatabaseResponse{
52 | Object: "list",
53 | Results: []notion.Page{
54 | {
55 | Object: "page",
56 | ID: "5678",
57 | CreatedTime: "2021-05-14T01:06:32.845Z",
58 | LastEditedTime: "2021-05-23T08:02:00.000Z",
59 | // Technically this is &DatabaseParent but Go will see type as map[string]interface{}
60 | // Haven't found a workaround yet :)
61 | Parent: map[string]interface{}{
62 | "database_id": "38923",
63 | "type": "database_id",
64 | },
65 | Properties: map[string]notion.PageProperty{
66 | "Name": {
67 | ID: "title",
68 | Type: "title",
69 | Title: []notion.RichText{
70 | {
71 | PlainText: "Jamboree",
72 | },
73 | },
74 | },
75 | "Recommended": {
76 | ID: "EZMA",
77 | Type: "checkbox",
78 | Checkbox: true,
79 | },
80 | "Tags": {
81 | ID: "VSvn",
82 | Type: "multi_select",
83 | MultiSelect: []notion.MultiSelectPropertyOpts{
84 | {
85 | ID: "44645",
86 | Name: "TagTest",
87 | Color: "purple",
88 | },
89 | },
90 | },
91 | },
92 | },
93 | },
94 | }
95 |
96 | testCompFilterParamsJSON = `{"filter":{"and":[{"property":"Tags","multi_select":{"contains":"TagTest"}},{"property":"Name","title":{"contains":"Jamboree"}}]}}` + "\n"
97 | testCompFilterParams = ¬ion.QueryDatabaseBodyParams{
98 | Filter: ¬ion.CompoundFilter{
99 | AND: []notion.SingleFilter{
100 | {
101 | Property: "Tags",
102 | MultiSelect: ¬ion.MultiSelectCondition{
103 | Contains: "TagTest",
104 | },
105 | },
106 | {
107 | Property: "Name",
108 | Title: ¬ion.TextCondition{
109 | Contains: "Jamboree",
110 | },
111 | },
112 | },
113 | },
114 | }
115 |
116 | testListDBResJSON = `{"results":[{"object":"database","id":"f170fb7c-af53-4173-a0b2-c4cfc8da7789","created_time":"2021-05-23T07:41:16.751Z","last_edited_time":"2021-05-23T08:04:00.000Z","title":[{"plain_text":"DB for Testing"}],"properties":{"Name":{"id":"title","type":"title","title":{}},"Tags":{"id":"M}hZ","type":"multi_select","multi_select":{"options":[{"name":"TagTest","id":"f3bbc99e-f70b-4585-a1a4-f3f895e01104","color":"gray"}]}}}}]}` + "\n"
117 | testListDBRes = ¬ion.ListDatabasesResponse{
118 | Results: []notion.Database{
119 | {
120 | Object: "database",
121 | ID: "f170fb7c-af53-4173-a0b2-c4cfc8da7789",
122 | CreatedTime: "2021-05-23T07:41:16.751Z",
123 | LastEditedTime: "2021-05-23T08:04:00.000Z",
124 | Title: []notion.RichText{
125 | {
126 | PlainText: "DB for Testing",
127 | },
128 | },
129 | Properties: map[string]notion.PropertyObj{
130 | "Name": {
131 | ID: "title",
132 | Type: "title",
133 | Title: map[string]interface{}{},
134 | },
135 | "Tags": {
136 | ID: "M}hZ",
137 | Type: "multi_select",
138 | MultiSelect: ¬ion.MultiSelectConfig{
139 | Options: []notion.MultiSelectOption{
140 | {
141 | Name: "TagTest",
142 | ID: "f3bbc99e-f70b-4585-a1a4-f3f895e01104",
143 | Color: "gray",
144 | },
145 | },
146 | },
147 | },
148 | },
149 | },
150 | },
151 | }
152 | )
153 |
154 | func TestDatabaseService_RetrieveDatabase(t *testing.T) {
155 | httpClient, mux, server := testServer()
156 | defer server.Close()
157 |
158 | mux.HandleFunc("/v1/databases/123", func(w http.ResponseWriter, r *http.Request) {
159 | assertMethod(t, "GET", r)
160 | w.Header().Set("Content-Type", "application/json")
161 | fmt.Fprintf(w, testDatabaseResJSON)
162 | })
163 |
164 | client := notion.NewClient(httpClient, "0000")
165 | resp, _, err := client.Databases.RetrieveDatabase("123")
166 | assert.Nil(t, err)
167 | assert.Equal(t, testDatabaseRes, resp)
168 | }
169 |
170 | func TestDatabaseService_QueryDatabase_SingleFilter(t *testing.T) {
171 | httpClient, mux, server := testServer()
172 | defer server.Close()
173 |
174 | mux.HandleFunc("/v1/databases/123/query", func(w http.ResponseWriter, r *http.Request) {
175 | assertMethod(t, "POST", r)
176 | assertPostJSON(t, testSingleFilterParamsJSON, r)
177 | w.Header().Set("Content-Type", "application/json")
178 | fmt.Fprintf(w, testFilterResJSON)
179 | })
180 |
181 | client := notion.NewClient(httpClient, "0000")
182 | resp, _, err := client.Databases.QueryDatabase("123", testSingleFilterParams)
183 | assert.Nil(t, err)
184 | assert.Equal(t, testFilterRes, resp)
185 |
186 | }
187 |
188 | func TestDatabaseService_QueryDatabase_CompoundFilter(t *testing.T) {
189 | httpClient, mux, server := testServer()
190 | defer server.Close()
191 |
192 | mux.HandleFunc("/v1/databases/123/query", func(w http.ResponseWriter, r *http.Request) {
193 | assertMethod(t, "POST", r)
194 | assertPostJSON(t, testCompFilterParamsJSON, r)
195 | w.Header().Set("Content-Type", "application/json")
196 | fmt.Fprintf(w, testFilterResJSON)
197 | })
198 |
199 | client := notion.NewClient(httpClient, "0000")
200 | resp, _, err := client.Databases.QueryDatabase("123", testCompFilterParams)
201 | assert.Nil(t, err)
202 | assert.Equal(t, testFilterRes, resp)
203 |
204 | }
205 |
206 | func TestDatabaseService_ListDatabases(t *testing.T) {
207 | httpClient, mux, server := testServer()
208 | defer server.Close()
209 |
210 | mux.HandleFunc("/v1/databases/", func(w http.ResponseWriter, r *http.Request) {
211 | assertMethod(t, "GET", r)
212 | w.Header().Set("Content-Type", "application/json")
213 | fmt.Fprintf(w, testListDBResJSON)
214 | })
215 |
216 | client := notion.NewClient(httpClient, "0000")
217 | resp, _, err := client.Databases.ListDatabases(¬ion.ListDatabasesQueryParams{})
218 | assert.Nil(t, err)
219 | assert.Equal(t, testListDBRes, resp)
220 | }
221 |
--------------------------------------------------------------------------------
/notion/version1/errors.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import "fmt"
4 |
5 | // APIError represents a Notion API response
6 | // https://developers.notion.com/reference/errors
7 | // Object is always "error"
8 | type APIError struct {
9 | Object string `json:"object,omitempty"`
10 | Status int32 `json:"status,omitempty"`
11 | Code string `json:"code,omitempty"`
12 | Message string `json:"message,omitempty"`
13 | }
14 |
15 | func (e APIError) Error() string {
16 | return fmt.Sprintf("code: %v, message: %v", e.Code, e.Message)
17 | }
18 |
19 | // relevantError returns any http-related error if it exists
20 | // if http-related errors don't exist, it returns apiError if it exists
21 | // else it returns nil
22 | func relevantError(httpError error, apiError APIError) error {
23 | if httpError != nil {
24 | return httpError
25 | }
26 |
27 | if (APIError{}) == apiError {
28 | return nil
29 | }
30 |
31 | return apiError
32 | }
33 |
--------------------------------------------------------------------------------
/notion/version1/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/oyekanmiayo/go-notion/notion/version1
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/dghubble/sling v1.3.0
7 | github.com/stretchr/testify v1.4.0
8 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
9 | )
10 |
--------------------------------------------------------------------------------
/notion/version1/notion.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "github.com/dghubble/sling"
5 | "net/http"
6 | )
7 |
8 | const notionAPI = "https://api.notion.com/v1/"
9 | const notionVersion = "2021-05-13"
10 |
11 | type Client struct {
12 | sling *sling.Sling
13 |
14 | // Notion API Services
15 | Users *UserService
16 | Databases *DatabaseService
17 | Pages *PageService
18 | Blocks *BlockService
19 | Search *SearchService
20 | Auth *AuthService
21 | }
22 |
23 | func AuthClient(client *http.Client) *Client {
24 | base := sling.New().Client(client).Base(notionAPI)
25 |
26 | return &Client{
27 | Auth: newAuthService(base.New()),
28 | }
29 | }
30 |
31 | func NewClient(client *http.Client, accessToken string) *Client {
32 | base := sling.New().Client(client).Base(notionAPI)
33 | base.Add("Authorization", "Bearer "+accessToken)
34 | base.Add("Notion-Version", notionVersion)
35 |
36 | return &Client{
37 | sling: base,
38 | Users: newUserService(base.New()),
39 | Databases: newDatabaseService(base.New()),
40 | Pages: newPageService(base.New()),
41 | Blocks: newBlockService(base.New()),
42 | Search: newSearchService(base.New()),
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/notion/version1/notion_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "github.com/stretchr/testify/assert"
5 | "io/ioutil"
6 | "net/http"
7 | "net/http/httptest"
8 | "net/url"
9 | "testing"
10 | )
11 |
12 | // testServer returns an http Client, ServeMux, and Server. The client proxies
13 | // requests to the server and handlers can be registered on the mux to handle
14 | // requests. The caller must close the test server.
15 | func testServer() (*http.Client, *http.ServeMux, *httptest.Server) {
16 | mux := http.NewServeMux()
17 | server := httptest.NewServer(mux)
18 | transport := &RewriteTransport{&http.Transport{
19 | Proxy: func(req *http.Request) (*url.URL, error) {
20 | return url.Parse(server.URL)
21 | },
22 | }}
23 | client := &http.Client{Transport: transport}
24 | return client, mux, server
25 | }
26 |
27 | // RewriteTransport rewrites https requests to http to avoid TLS cert issues
28 | // during testing.
29 | type RewriteTransport struct {
30 | Transport http.RoundTripper
31 | }
32 |
33 | // RoundTrip rewrites the request scheme to http and calls through to the
34 | // composed RoundTripper or if it is nil, to the http.DefaultTransport.
35 | func (t *RewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {
36 | req.URL.Scheme = "http"
37 | if t.Transport == nil {
38 | return http.DefaultTransport.RoundTrip(req)
39 | }
40 | return t.Transport.RoundTrip(req)
41 | }
42 |
43 | func assertMethod(t *testing.T, expectedMethod string, req *http.Request) {
44 | assert.Equal(t, expectedMethod, req.Method)
45 | }
46 |
47 | // assertPostJSON tests that the Request has the expected JSON body.
48 | func assertPostJSON(t *testing.T, expected string, req *http.Request) {
49 | data, err := ioutil.ReadAll(req.Body)
50 | assert.Nil(t, err)
51 | assert.Equal(t, expected, string(data))
52 | }
53 |
--------------------------------------------------------------------------------
/notion/version1/pages.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "github.com/dghubble/sling"
5 | "net/http"
6 | )
7 |
8 | type PageService struct {
9 | sling *sling.Sling
10 | }
11 |
12 | // newPageService returns a new PageService.
13 | func newPageService(sling *sling.Sling) *PageService {
14 | return &PageService{
15 | sling: sling.Path("pages/"),
16 | }
17 | }
18 |
19 | // https://developers.notion.com/reference/page#all-pages
20 | // Object must always be "page"
21 | // For Properties,
22 | // - If parent.type is "page_id" or "workspace", then the only valid key is title.
23 | // - If parent.type is "database_id", then the keys and values of this field are determined by the properties
24 | // of the database this page belongs to.
25 | // Page can be *DatabaseParent, *PageParent or *WorkspaceParent
26 | type Page struct {
27 | Object string `json:"object,omitempty"`
28 | ID string `json:"id,omitempty"`
29 | CreatedTime string `json:"created_time,omitempty"`
30 | LastEditedTime string `json:"last_edited_time,omitempty"`
31 | Parent interface{} `json:"parent,omitempty"`
32 | Archived bool `json:"archived,omitempty"`
33 | Properties map[string]PageProperty `json:"properties,omitempty"`
34 | }
35 |
36 | // Type is always "database_id" for Database Parent
37 | type DatabaseParent struct {
38 | Type string `json:"type,omitempty"`
39 | DatabaseID string `json:"database_id,omitempty"`
40 | }
41 |
42 | // Type is always "page_id
43 | type PageParent struct {
44 | Type string `json:"type,omitempty"`
45 | PageID string `json:"page_id,omitempty"`
46 | }
47 |
48 | // Type is always "workspace"
49 | type WorkspaceParent struct {
50 | Type string `json:"type,omitempty"`
51 | }
52 |
53 | // Type can be only one of "rich_text", "number", "select", "multi_select", "date", "formula",
54 | // "relation", "rollup", "title", "people", "files", "checkbox", "url", "email", "phone_number",
55 | // "created_time", "created_by", "last_edited_time", and "last_edited_by".
56 | // https://developers.notion.com/reference/page#all-property-values
57 | type PageProperty struct {
58 | ID string `json:"id,omitempty"`
59 | Type string `json:"type,omitempty"`
60 | Title []RichText `json:"title,omitempty"`
61 | RichText []RichText `json:"rich_text,omitempty"`
62 | Number int64 `json:"number,omitempty"`
63 | Select *SelectProperty `json:"select,omitempty"`
64 | URL string `json:"url,omitempty"`
65 | Email string `json:"email,omitempty"`
66 | Phone interface{} `json:"phone,omitempty"`
67 | Checkbox bool `json:"checkbox,omitempty"`
68 | MultiSelect []MultiSelectPropertyOpts `json:"multi_select,omitempty"`
69 | CreatedTime string `json:"created_time,omitempty"`
70 | LastEditedTime string `json:"last_edited_time,omitempty"`
71 | Date *DateProperty `json:"date,omitempty"`
72 | CreatedBy *User `json:"created_by,omitempty"`
73 | LastEditedBy *User `json:"last_edited_by,omitempty"`
74 | Files []FileReferenceProperty `json:"files,omitempty"`
75 | Relation []PageReferenceProperty `json:"relation,omitempty"`
76 | Formula *FormulaProperty `json:"formula,omitempty"`
77 | Rollup *RollupProperty `json:"rollup,omitempty"`
78 | People []User `json:"people,omitempty"`
79 | PhoneNumber string `json:"phone_number,omitempty"`
80 | }
81 |
82 | type NumberProperty struct {
83 | Number int32 `json:"number,omitempty"`
84 | }
85 |
86 | // Color can only be "default", "gray", "brown", "red", "orange", "yellow", "green",
87 | // "blue", "purple" or "pink"
88 | type SelectProperty struct {
89 | ID string `json:"id,omitempty"`
90 | Name string `json:"name,omitempty"`
91 | Color string `json:"color,omitempty"`
92 | }
93 |
94 | // When updating a multi-select property, you can use either name or id.
95 | // Setting a multi-select option by name will only update the page if the multi-select database
96 | // property has an option by that name.
97 | // Color can only be "default", "gray", "brown", "red", "orange", "yellow", "green",
98 | // "blue", "purple" or "pink"
99 | // https://developers.notion.com/reference/page#multi-select-property-values
100 | type MultiSelectPropertyOpts struct {
101 | ID string `json:"id,omitempty"`
102 | Name string `json:"name,omitempty"`
103 | Color string `json:"color,omitempty"`
104 | }
105 |
106 | // If End is not defined, this Date struct isn't considered a range
107 | // https://developers.notion.com/reference/page#date-property-values
108 | type DateProperty struct {
109 | Start string `json:"start,omitempty"`
110 | End string `json:"end,omitempty"`
111 | }
112 |
113 | // Type must be one of "string", "number", "boolean", and "date".
114 | // https://developers.notion.com/reference/page#formula-property-values
115 | // Use *Date for Date
116 | type FormulaProperty struct {
117 | Type string `json:"type"`
118 | String string `json:"string,omitempty"`
119 | Number int32 `json:"number,omitempty"`
120 | Boolean bool `json:"boolean,omitempty"`
121 | Date *DateProperty `json:"date,omitempty"`
122 | }
123 |
124 | // https://developers.notion.com/reference/page#relation-property-values
125 | type PageReferenceProperty struct {
126 | ID string `json:"id,omitempty"`
127 | }
128 |
129 | // https://developers.notion.com/reference/page#rollup-property-values
130 | // Use *DateProperty for Date
131 | // Use for Array
132 | type RollupProperty struct {
133 | Type string `json:"type,omitempty"`
134 | Number int32 `json:"number,omitempty"`
135 | Date *DateProperty `json:"date,omitempty"`
136 | Array []RollupPropertyElement `json:"array,omitempty"`
137 | }
138 |
139 | // https://developers.notion.com/reference/page#rollup-property-value-element
140 | // Type is one of rich_text", "number", "select", "multi_select", "date", "formula", "relation",
141 | // "rollup", "title", "people", "files", "checkbox", "url", "email", "phone_number", "created_time",
142 | // "created_by", "last_edited_time", or "last_edited_by"
143 | type RollupPropertyElement struct {
144 | Type string `json:"type"`
145 | Title []RichText `json:"title,omitempty"`
146 | RichText []RichText `json:"rich_text,omitempty"`
147 | Number int64 `json:"number,omitempty"`
148 | Select *SelectProperty `json:"select,omitempty"`
149 | URL string `json:"url,omitempty"`
150 | Email string `json:"email,omitempty"`
151 | Phone interface{} `json:"phone,omitempty"`
152 | Checkbox bool `json:"checkbox,omitempty"`
153 | MultiSelect []MultiSelectPropertyOpts `json:"multi_select,omitempty"`
154 | CreatedTime string `json:"created_time,omitempty"`
155 | LastEditedTime string `json:"last_edited_time,omitempty"`
156 | Date *DateProperty `json:"date,omitempty"`
157 | CreatedBy *User `json:"created_by,omitempty"`
158 | LastEditedBy *User `json:"last_edited_by,omitempty"`
159 | Files []FileReferenceProperty `json:"files,omitempty"`
160 | Relation interface{} `json:"relation,omitempty"`
161 | Formula *FormulaProperty `json:"formula,omitempty"`
162 | Rollup interface{} `json:"rollup,omitempty"`
163 | People []User `json:"people,omitempty"`
164 | PhoneNumber string `json:"phone_number,omitempty"`
165 | }
166 |
167 | // https://developers.notion.com/reference/page#files-property-values
168 | type FileReferenceProperty struct {
169 | Name string `json:"name,omitempty"`
170 | }
171 |
172 | // https://developers.notion.com/reference/get-page
173 | func (p *PageService) RetrievePage(pageID string) (*Page, *http.Response, error) {
174 | page := new(Page)
175 | apiError := new(APIError)
176 | resp, err := p.sling.New().Get(pageID).Receive(page, apiError)
177 |
178 | return page, resp, relevantError(err, *apiError)
179 | }
180 |
181 | // Use *DatabaseParent or *PageParent for Parent
182 | // Read https://developers.notion.com/reference/post-page before using this
183 | type CreatePageBodyParams struct {
184 | Parent interface{} `json:"parent,omitempty"`
185 | Properties interface{} `json:"properties,omitempty"`
186 | Children []Block `json:"children,omitempty"`
187 | }
188 |
189 | // https://developers.notion.com/reference/post-page
190 | func (p *PageService) CreatePage(params *CreatePageBodyParams) (*Page, *http.Response, error) {
191 | page := new(Page)
192 | apiError := new(APIError)
193 | resp, err := p.sling.New().Post("").BodyJSON(params).Receive(page, apiError)
194 |
195 | return page, resp, relevantError(err, *apiError)
196 | }
197 |
198 | type UpdatePagePropertiesBodyParams struct {
199 | Properties map[string]PageProperty `json:"properties,omitempty"`
200 | }
201 |
202 | // https://developers.notion.com/reference/patch-page
203 | func (p *PageService) UpdatePageProperties(pageID string,
204 | params *UpdatePagePropertiesBodyParams) (*Page, *http.Response, error) {
205 | page := new(Page)
206 | apiError := new(APIError)
207 | resp, err := p.sling.New().Patch(pageID).BodyJSON(params).Receive(page, apiError)
208 |
209 | return page, resp, relevantError(err, *apiError)
210 | }
211 |
--------------------------------------------------------------------------------
/notion/version1/pages_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "fmt"
5 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
6 | "github.com/stretchr/testify/assert"
7 | "net/http"
8 | "testing"
9 | )
10 |
11 | //TODO: Clean up tests. Extract reused names.
12 | var (
13 | testPageJSON = `{"object":"page","id":"5678","created_time":"2021-05-14T01:06:32.845Z","last_edited_time":"2021-05-23T08:02:00.000Z","parent":{"database_id":"38923","type":"database_id"},"properties":{"Name":{"id":"title","type":"title","title":[{"plain_text":"Jamboree"}]},"Recommended":{"id":"EZMA","type":"checkbox","checkbox":true},"Tags":{"id":"VSvn","type":"multi_select","multi_select":[{"id":"44645","name":"TagTest","color":"purple"}]}}}` + "\n"
14 | testPage = ¬ion.Page{
15 | Object: "page",
16 | ID: "5678",
17 | CreatedTime: "2021-05-14T01:06:32.845Z",
18 | LastEditedTime: "2021-05-23T08:02:00.000Z",
19 | // Technically this is &DatabaseParent but Go will see type as map[string]interface{}
20 | // Haven't found a workaround yet :)
21 | Parent: map[string]interface{}{
22 | "database_id": "38923",
23 | "type": "database_id",
24 | },
25 | Properties: map[string]notion.PageProperty{
26 | "Name": {
27 | ID: "title",
28 | Type: "title",
29 | Title: []notion.RichText{
30 | {
31 | PlainText: "Jamboree",
32 | },
33 | },
34 | },
35 | "Recommended": {
36 | ID: "EZMA",
37 | Type: "checkbox",
38 | Checkbox: true,
39 | },
40 | "Tags": {
41 | ID: "VSvn",
42 | Type: "multi_select",
43 | MultiSelect: []notion.MultiSelectPropertyOpts{
44 | {
45 | ID: "44645",
46 | Name: "TagTest",
47 | Color: "purple",
48 | },
49 | },
50 | },
51 | },
52 | }
53 | testRetrievePageResJSON = testPageJSON
54 | testRetrievePageRes = testPage
55 |
56 | testPageProperty = map[string]notion.PageProperty{
57 | "Name": {
58 | Title: []notion.RichText{
59 | {
60 | Text: ¬ion.Text{
61 | Content: "Jamboree",
62 | },
63 | },
64 | },
65 | },
66 | "Tags": {
67 | MultiSelect: []notion.MultiSelectPropertyOpts{
68 | {
69 | Name: "TagTest",
70 | },
71 | },
72 | },
73 | "Recommended": {
74 | Checkbox: true,
75 | },
76 | }
77 |
78 | testCreatePageBodyJSON = `{"parent":{"database_id":"7d6410f1-0c2d-4c75-8199-3fd7d90ff4ff"},"properties":{"Name":{"title":[{"text":{"content":"Jamboree"}}]},"Recommended":{"checkbox":true},"Tags":{"multi_select":[{"name":"TagTest"}]}}}` + "\n"
79 | testCreatePageBody = ¬ion.CreatePageBodyParams{
80 | Parent: ¬ion.DatabaseParent{
81 | DatabaseID: "7d6410f1-0c2d-4c75-8199-3fd7d90ff4ff",
82 | },
83 | Properties: testPageProperty,
84 | }
85 | testCreatePageResJSON = testPageJSON
86 | testCreatePageRes = testPage
87 |
88 | testUpdatePropertiesBodyJSON = `{"properties":{"Name":{"title":[{"text":{"content":"Jamboree"}}]},"Recommended":{"checkbox":true},"Tags":{"multi_select":[{"name":"TagTest"}]}}}` + "\n"
89 | testUpdatePropertiesBody = ¬ion.UpdatePagePropertiesBodyParams{
90 | Properties: testPageProperty,
91 | }
92 | testUpdatePropertiesResJSON = testPageJSON
93 | testUpdatePropertiesRes = testPage
94 | )
95 |
96 | func TestPageService_RetrievePage(t *testing.T) {
97 | httpClient, mux, server := testServer()
98 | defer server.Close()
99 |
100 | mux.HandleFunc("/v1/pages/123", func(w http.ResponseWriter, r *http.Request) {
101 | assertMethod(t, "GET", r)
102 | w.Header().Set("Content-Type", "application/json")
103 | fmt.Fprintf(w, testRetrievePageResJSON)
104 | })
105 |
106 | client := notion.NewClient(httpClient, "0000")
107 | resp, _, err := client.Pages.RetrievePage("123")
108 | assert.Nil(t, err)
109 | assert.Equal(t, testRetrievePageRes, resp)
110 | }
111 |
112 | func TestPageService_CreatePage(t *testing.T) {
113 | httpClient, mux, server := testServer()
114 | defer server.Close()
115 |
116 | mux.HandleFunc("/v1/pages/", func(w http.ResponseWriter, r *http.Request) {
117 | assertMethod(t, "POST", r)
118 | assertPostJSON(t, testCreatePageBodyJSON, r)
119 | w.Header().Set("Content-Type", "application/json")
120 | fmt.Fprintf(w, testCreatePageResJSON)
121 | })
122 |
123 | client := notion.NewClient(httpClient, "0000")
124 | resp, _, err := client.Pages.CreatePage(testCreatePageBody)
125 | assert.Nil(t, err)
126 | assert.Equal(t, testCreatePageRes, resp)
127 | }
128 |
129 | func TestPageService_UpdatePageProperties(t *testing.T) {
130 | httpClient, mux, server := testServer()
131 | defer server.Close()
132 |
133 | mux.HandleFunc("/v1/pages/123", func(w http.ResponseWriter, r *http.Request) {
134 | assertMethod(t, "PATCH", r)
135 | assertPostJSON(t, testUpdatePropertiesBodyJSON, r)
136 | w.Header().Set("Content-Type", "application/json")
137 | fmt.Fprintf(w, testUpdatePropertiesResJSON)
138 | })
139 |
140 | client := notion.NewClient(httpClient, "0000")
141 | resp, _, err := client.Pages.UpdatePageProperties("123", testUpdatePropertiesBody)
142 | assert.Nil(t, err)
143 | assert.Equal(t, testUpdatePropertiesRes, resp)
144 | }
145 |
--------------------------------------------------------------------------------
/notion/version1/search.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "github.com/dghubble/sling"
5 | "net/http"
6 | )
7 |
8 | type SearchService struct {
9 | sling *sling.Sling
10 | }
11 |
12 | // newSearchService returns a new SearchService.
13 | func newSearchService(sling *sling.Sling) *SearchService {
14 | return &SearchService{
15 | sling: sling.Path("search/"),
16 | }
17 | }
18 |
19 | // From my tests it looks like the api checks if page titles contain the query
20 | type SearchBodyParams struct {
21 | Query string `json:"query,omitempty"`
22 | Sort *Sort `json:"sort,omitempty"`
23 | Filter *SearchFilter `json:"filter,omitempty"`
24 | StartCursor string `json:"start_cursor,omitempty"`
25 | PageSize int32 `json:"page_size,omitempty"`
26 | }
27 |
28 | // Direction is either "ascending" or "descending"
29 | // Timestamp is always "last_edited_time"
30 | type Sort struct {
31 | Direction string `json:"direction,omitempty"`
32 | Timestamp string `json:"timestamp,omitempty"`
33 | }
34 |
35 | // Value is either "page" or "database"
36 | // Property can only be "object"
37 | type SearchFilter struct {
38 | Value string `json:"value,omitempty"`
39 | Property string `json:"property,omitempty"`
40 | }
41 |
42 | // TODO: Find a way to unify responses and make them accessible.
43 | // Use json.RawMessage & https://play.golang.org/p/e6kvxtOeTCc
44 | // One approach: Unmarshal into a random struct to get the type of
45 | // each entry, and use that to Unnmarshal into the right struct.
46 |
47 | type SearchPageResponse struct {
48 | Object string `json:"object,omitempty"`
49 | Results []Page `json:"results,omitempty"`
50 | NextCursor string `json:"next_cursor,omitempty"`
51 | HasMore bool `json:"has_more,omitempty"`
52 | }
53 |
54 | func (s *SearchService) SearchPage(params *SearchBodyParams) (*SearchPageResponse, *http.Response, error) {
55 | sResponse := new(SearchPageResponse)
56 | apiError := new(APIError)
57 |
58 | params.Filter = &SearchFilter{
59 | Property: "object",
60 | Value: "page",
61 | }
62 |
63 | httpResponse, httpError := s.sling.New().Post("").BodyJSON(params).Receive(sResponse, apiError)
64 |
65 | return sResponse, httpResponse, relevantError(httpError, *apiError)
66 | }
67 |
68 | type SearchDatabaseResponse struct {
69 | Object string `json:"object,omitempty"`
70 | Results []Database `json:"results,omitempty"`
71 | NextCursor string `json:"next_cursor,omitempty"`
72 | HasMore bool `json:"has_more,omitempty"`
73 | }
74 |
75 | func (s *SearchService) SearchDatabase(params *SearchBodyParams) (*SearchDatabaseResponse, *http.Response, error) {
76 | sResponse := new(SearchDatabaseResponse)
77 | apiError := new(APIError)
78 |
79 | params.Filter = &SearchFilter{
80 | Property: "object",
81 | Value: "database",
82 | }
83 | httpResponse, httpError := s.sling.New().Post("").BodyJSON(params).Receive(sResponse, apiError)
84 |
85 | return sResponse, httpResponse, relevantError(httpError, *apiError)
86 | }
87 |
--------------------------------------------------------------------------------
/notion/version1/search_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "fmt"
5 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
6 | "github.com/stretchr/testify/assert"
7 | "net/http"
8 | "testing"
9 | )
10 |
11 | var (
12 | testSearchPageBodyJSON = `{"query":"Jamboree","sort":{"direction":"descending","timestamp":"last_edited_time"},"filter":{"value":"page","property":"object"}}` + "\n"
13 | testSearchBody = ¬ion.SearchBodyParams{
14 | Query: "Jamboree",
15 | Sort: ¬ion.Sort{
16 | Direction: "descending",
17 | Timestamp: "last_edited_time",
18 | },
19 | }
20 | testSearchPageResJSON = `{"object":"list","results":[{"object":"page","id":"5678","created_time":"2021-05-14T01:06:32.845Z","last_edited_time":"2021-05-23T08:02:00.000Z","parent":{"database_id":"38923","type":"database_id"},"properties":{"Name":{"id":"title","type":"title","title":[{"plain_text":"Jamboree"}]},"Recommended":{"id":"EZMA","type":"checkbox","checkbox":true},"Tags":{"id":"VSvn","type":"multi_select","multi_select":[{"id":"44645","name":"TagTest","color":"purple"}]}}}]}` + "\n"
21 | testSearchPageRes = ¬ion.SearchPageResponse{
22 | Object: "list",
23 | Results: []notion.Page{
24 | *testPage,
25 | },
26 | }
27 |
28 | testSearchDBBodyJSON = `{"query":"Jamboree","sort":{"direction":"descending","timestamp":"last_edited_time"},"filter":{"value":"database","property":"object"}}` + "\n"
29 | testSearchDBResJSON = `{"object":"list","results":[{"object":"database","id":"123","created_time":"2021-05-23T07:41:16.751Z","last_edited_time":"2021-05-23T07:41:00.000Z","title":[{"type":"text","text":{"content":"Jamboree"}}],"properties":{"Name":{"id":"title","type":"title","title":{}}}}]}` + "\n"
30 | testSearchDBRes = ¬ion.SearchDatabaseResponse{
31 | Object: "list",
32 | Results: []notion.Database{
33 | {
34 | Object: "database",
35 | ID: "123",
36 | CreatedTime: "2021-05-23T07:41:16.751Z",
37 | LastEditedTime: "2021-05-23T07:41:00.000Z",
38 | Title: []notion.RichText{
39 | {
40 | Type: "text",
41 | Text: ¬ion.Text{
42 | Content: "Jamboree",
43 | },
44 | },
45 | },
46 | Properties: map[string]notion.PropertyObj{
47 | "Name": {
48 | ID: "title",
49 | Type: "title",
50 | Title: map[string]interface{}{},
51 | },
52 | },
53 | },
54 | },
55 | }
56 | )
57 |
58 | func TestSearchService_SearchPage(t *testing.T) {
59 | httpClient, mux, server := testServer()
60 | defer server.Close()
61 |
62 | mux.HandleFunc("/v1/search/", func(w http.ResponseWriter, r *http.Request) {
63 | assertMethod(t, "POST", r)
64 | assertPostJSON(t, testSearchPageBodyJSON, r)
65 |
66 | w.Header().Set("Content-Type", "application/json")
67 | fmt.Fprintf(w, testSearchPageResJSON)
68 | })
69 |
70 | client := notion.NewClient(httpClient, "0000")
71 | resp, _, err := client.Search.SearchPage(testSearchBody)
72 | assert.Nil(t, err)
73 | assert.Equal(t, testSearchPageRes, resp)
74 | }
75 |
76 | func TestSearchService_SearchDatabase(t *testing.T) {
77 | httpClient, mux, server := testServer()
78 | defer server.Close()
79 |
80 | mux.HandleFunc("/v1/search/", func(w http.ResponseWriter, r *http.Request) {
81 | assertMethod(t, "POST", r)
82 | assertPostJSON(t, testSearchDBBodyJSON, r)
83 |
84 | w.Header().Set("Content-Type", "application/json")
85 | fmt.Fprintf(w, testSearchDBResJSON)
86 | })
87 |
88 | client := notion.NewClient(httpClient, "0000")
89 | resp, _, err := client.Search.SearchDatabase(testSearchBody)
90 | assert.Nil(t, err)
91 | assert.Equal(t, testSearchDBRes, resp)
92 | }
93 |
--------------------------------------------------------------------------------
/notion/version1/users.go:
--------------------------------------------------------------------------------
1 | package version1
2 |
3 | import (
4 | "github.com/dghubble/sling"
5 | "net/http"
6 | )
7 |
8 | type UserService struct {
9 | sling *sling.Sling
10 | }
11 |
12 | // newUserService returns a new UserService.
13 | func newUserService(sling *sling.Sling) *UserService {
14 | return &UserService{
15 | sling: sling.Path("users/"),
16 | }
17 | }
18 |
19 | // https://developers.notion.com/reference/user
20 | // Updateable properties: ID
21 | // Display-only properties: Object, Type, Name, AvatarURL
22 | // Object is always "user" and it
23 | // Type is either "person" or "bot"
24 | type User struct {
25 | Object string `json:"object,omitempty"`
26 | ID string `json:"id,omitempty"`
27 | Type string `json:"type,omitempty"`
28 | Name string `json:"name,omitempty"`
29 | AvatarURL string `json:"avatar_url,omitempty"`
30 | Person *Person `json:"person,omitempty"`
31 | Bot interface{} `json:"bot,omitempty"`
32 | }
33 |
34 | // https://developers.notion.com/reference/user#people
35 | // Display-only properties: Person, PersonEmail
36 | type Person struct {
37 | PersonEmail string `json:"email,omitempty"`
38 | }
39 |
40 | func (u *UserService) RetrieveUser(userID string) (*User, *http.Response, error) {
41 | user := new(User)
42 | apiError := new(APIError)
43 | resp, err := u.sling.New().Get(userID).Receive(user, apiError)
44 |
45 | return user, resp, relevantError(err, *apiError)
46 | }
47 |
48 | type ListUsersQueryParams struct {
49 | StartCursor string `url:"start_cursor,omitempty"`
50 | PageSize int32 `url:"page_size,omitempty"`
51 | }
52 |
53 | type ListUsersResponse struct {
54 | Object string `json:"object,omitempty"`
55 | Results []User `json:"results"`
56 | NextCursor string `json:"next_cursor"`
57 | HasMore bool `json:"has_more"`
58 | }
59 |
60 | // https://developers.notion.com/reference/get-users
61 | // See https://developers.notion.com/reference/pagination to understand
62 | // how to iterate through paginated responses
63 | func (u *UserService) ListUsers(params *ListUsersQueryParams) (*ListUsersResponse, *http.Response, error) {
64 | response := new(ListUsersResponse)
65 | apiError := new(APIError)
66 | resp, err := u.sling.New().Get("").QueryStruct(params).Receive(response, apiError)
67 |
68 | return response, resp, relevantError(err, *apiError)
69 | }
70 |
--------------------------------------------------------------------------------
/notion/version1/users_test.go:
--------------------------------------------------------------------------------
1 | package version1_test
2 |
3 | import (
4 | "fmt"
5 | notion "github.com/oyekanmiayo/go-notion/notion/version1"
6 | "github.com/stretchr/testify/assert"
7 | "net/http"
8 | "testing"
9 | )
10 |
11 | var (
12 | testUserJSON = `{"object":"user","id":"123abc","type":"person","name":"John Doe","avatar_url":"https://test.com/test","person":{"email":"test@gmail.com"}}` + "\n"
13 | testUser = ¬ion.User{
14 | Object: "user",
15 | ID: "123abc",
16 | Type: "person",
17 | Name: "John Doe",
18 | AvatarURL: "https://test.com/test",
19 | Person: ¬ion.Person{
20 | PersonEmail: "test@gmail.com",
21 | },
22 | }
23 |
24 | testBot = ¬ion.User{
25 | Object: "user",
26 | ID: "456def",
27 | Type: "bot",
28 | Name: "Test Integration",
29 | Bot: map[string]interface{}{},
30 | }
31 |
32 | testListUsersJSON = `{"object":"list","results":[{"object":"user","id":"123abc","type":"person","name":"John Doe","avatar_url":"https://test.com/test","person":{"email":"test@gmail.com"}},{"object":"user","id":"456def","type":"bot","name":"Test Integration","bot":{}}]}` + "\n"
33 | testListUsers = ¬ion.ListUsersResponse{
34 | Object: "list",
35 | Results: []notion.User{
36 | *testUser,
37 | *testBot,
38 | },
39 | }
40 | )
41 |
42 | func TestUserService_RetrieveUser(t *testing.T) {
43 | httpClient, mux, server := testServer()
44 | defer server.Close()
45 |
46 | mux.HandleFunc("/v1/users/123", func(w http.ResponseWriter, r *http.Request) {
47 | assertMethod(t, "GET", r)
48 | w.Header().Set("Content-Type", "application/json")
49 | fmt.Fprintf(w, testUserJSON)
50 | })
51 |
52 | client := notion.NewClient(httpClient, "0000")
53 | resp, _, err := client.Users.RetrieveUser("123")
54 | assert.Nil(t, err)
55 | assert.Equal(t, testUser, resp)
56 | }
57 |
58 | func TestUserService_ListUsers(t *testing.T) {
59 | httpClient, mux, server := testServer()
60 | defer server.Close()
61 |
62 | mux.HandleFunc("/v1/users/", func(w http.ResponseWriter, r *http.Request) {
63 | assertMethod(t, "GET", r)
64 | w.Header().Set("Content-Type", "application/json")
65 | fmt.Fprintf(w, testListUsersJSON)
66 | })
67 |
68 | client := notion.NewClient(httpClient, "0000")
69 | resp, _, err := client.Users.ListUsers(¬ion.ListUsersQueryParams{})
70 | assert.Nil(t, err)
71 | assert.Equal(t, testListUsers, resp)
72 | }
73 |
--------------------------------------------------------------------------------