grist 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +45 -15
- data/docker-compose.yml +11 -0
- data/examples/main.rb +83 -0
- data/examples/update.rb +56 -0
- data/lib/grist/accessible.rb +10 -0
- data/lib/grist/api_routes.rb +79 -0
- data/lib/grist/response.rb +39 -0
- data/lib/grist/rest.rb +95 -0
- data/lib/grist/searchable.rb +17 -0
- data/lib/grist/type/access.rb +69 -0
- data/lib/grist/type/base.rb +72 -0
- data/lib/grist/type/column.rb +25 -0
- data/lib/grist/type/doc.rb +99 -0
- data/lib/grist/type/organization.rb +111 -0
- data/lib/grist/type/record.rb +22 -0
- data/lib/grist/type/table.rb +132 -0
- data/lib/grist/type/workspace.rb +88 -0
- data/lib/grist/version.rb +3 -1
- data/lib/grist.rb +56 -4
- metadata +20 -37
- data/lib/grist/client.rb +0 -20
- data/lib/grist/endpoint/organization.rb +0 -28
- data/lib/grist/endpoint/workspace.rb +0 -38
- data/lib/grist/endpoint.rb +0 -11
- data/lib/grist/http.rb +0 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 294b7958d818a5594d09d7f87847e50c0d58bf4d8056693cbb89b4f83be35cc7
|
|
4
|
+
data.tar.gz: 903319626e595ac69d98cfd35762ec17bd5ff266817eb607139a7e8baed16f3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d252f900b40170b0e939ad50b8f8f88bcafe937a6c9ad1056d13a332d662de18e70d0e896b28a1e87d2c6f4c2a60f09a52b7be0ab94f971017f1e8c16210cf77
|
|
7
|
+
data.tar.gz: 8f9b32899648337b388cdb9894ac78d852c98edb5f21fbcbded7559e7f9ffb4ee57ada91455dc5fc69c66e670be9d722f4014dc4c778e5ab52255a7807cb35ec
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
3.2.0
|
data/README.md
CHANGED
|
@@ -1,38 +1,68 @@
|
|
|
1
|
-
# Grist
|
|
1
|
+
# Grist::Ruby
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This gem provides a Ruby client for the Grist API.
|
|
4
|
+
|
|
5
|
+
⚠️ Library isn't ready to use, not actively maintained
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
Add this line to your application's Gemfile:
|
|
8
10
|
|
|
9
11
|
```ruby
|
|
10
|
-
|
|
12
|
+
gem 'grist'
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
And install the gem:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
$ bundle install
|
|
19
|
+
```
|
|
14
20
|
|
|
15
|
-
##
|
|
21
|
+
## Usage
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
See `examples/main.rb` for some examples, here is how to use them:
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
* First example will create 1 Workspace, with 2 Documents, with 3 tables into document 'demo'.
|
|
26
|
+
how to use it
|
|
27
|
+
```bash
|
|
28
|
+
$ GRIST_API_KEY=<GRIST_API_KEY> GRIST_API_URL=http://localhost:8484/api bundle exec examples/main.r
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
* `GRIST_API_URL` base url must contain `/api`
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
* Second example will update records
|
|
34
|
+
```bash
|
|
35
|
+
GRIST_ORG_NAME=<ORGANIZATION_NAME> GRIST_API_KEY=<GRIST_API_KEY> GRIST_API_URL=http://localhost:8484/api bundle exec examples/update.rb
|
|
25
36
|
```
|
|
26
37
|
|
|
38
|
+
* `ORGANIZATION_NAME` is case-sensitive
|
|
39
|
+
|
|
40
|
+
|
|
27
41
|
## Development
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
1. Start a Grist instance locally
|
|
44
|
+
```bash
|
|
45
|
+
$ docker compose up -d
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Playground using IRB
|
|
49
|
+
Then start the ruby console
|
|
50
|
+
```
|
|
51
|
+
$ GRIST_API_KEY=<GRIST_API_KEY> GRIST_API_URL=http://localhost:8484/api bundle exec bin/console
|
|
52
|
+
```
|
|
53
|
+
* GRIST_API_KEY can be found directly into your account at : http://localhost:8484/o/docs/account
|
|
54
|
+
|
|
55
|
+
You can now interact with Grist, example : `$ Grist::Type::Organization.all`
|
|
56
|
+
|
|
57
|
+
## Push a new release to RubyGems
|
|
58
|
+
|
|
59
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
30
60
|
|
|
31
|
-
To
|
|
61
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
32
62
|
|
|
33
63
|
## Contributing
|
|
34
64
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
65
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/grist-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/grist-ruby/blob/main/CODE_OF_CONDUCT.md).
|
|
36
66
|
|
|
37
67
|
## License
|
|
38
68
|
|
|
@@ -40,4 +70,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
40
70
|
|
|
41
71
|
## Code of Conduct
|
|
42
72
|
|
|
43
|
-
Everyone interacting in the Grist project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/grist/blob/main/CODE_OF_CONDUCT.md).
|
|
73
|
+
Everyone interacting in the Grist::Ruby project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/grist-ruby/blob/main/CODE_OF_CONDUCT.md).
|
data/docker-compose.yml
ADDED
data/examples/main.rb
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "grist"
|
|
6
|
+
require "byebug"
|
|
7
|
+
|
|
8
|
+
raise ArgumentError, "You must provide env var : 'GRIST_API_KEY'" if ENV.fetch("GRIST_API_KEY", "").empty?
|
|
9
|
+
raise ArgumentError, "You must provide env var : 'GRIST_API_URL'" if ENV.fetch("GRIST_API_URL", "").empty?
|
|
10
|
+
|
|
11
|
+
orgs = Grist::Type::Organization.all
|
|
12
|
+
org = orgs.last
|
|
13
|
+
|
|
14
|
+
if org.nil?
|
|
15
|
+
puts "No organization present"
|
|
16
|
+
exit 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
puts "Org: #{org.name} - ID: #{org.id}"
|
|
20
|
+
|
|
21
|
+
ws_name = "Workspace N°#{rand(1_000)}"
|
|
22
|
+
puts "Creating workspace #{ws_name}..."
|
|
23
|
+
ws = org.create_workspace({ name: ws_name })
|
|
24
|
+
|
|
25
|
+
puts "Creating document 'demo' into workspace '#{ws_name}'..."
|
|
26
|
+
doc = ws.create_doc({
|
|
27
|
+
name: "Demo",
|
|
28
|
+
isPinned: true
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
puts "Creating document 'Github' into workspace '#{ws_name}'"
|
|
32
|
+
ws.create_doc({
|
|
33
|
+
name: "Github",
|
|
34
|
+
isPinned: false
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
puts "Creating tables for document 'demo' into workspace '#{ws_name}'..."
|
|
38
|
+
doc.create_tables({
|
|
39
|
+
"tables" => [
|
|
40
|
+
{ "id" => "Community gems",
|
|
41
|
+
"columns" => [
|
|
42
|
+
{ "id" => "name", "fields" => { "label" => "Gem name" } },
|
|
43
|
+
{ "id" => "description", "fields" => { "label" => "Description" } },
|
|
44
|
+
{ "id" => "link", "fields" => { "label" => "RubyGems link" } }
|
|
45
|
+
] }
|
|
46
|
+
]
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
puts "Creating tables for document 'demo' into workspace '#{ws_name}'..."
|
|
50
|
+
doc.create_tables({
|
|
51
|
+
"tables" => [
|
|
52
|
+
{ "id" => "Standard library",
|
|
53
|
+
"columns" => [
|
|
54
|
+
{ "id" => "name", "fields" => { "label" => "Library name" } },
|
|
55
|
+
{ "id" => "url", "fields" => { "label" => "Source code public URL" } },
|
|
56
|
+
{ "id" => "version", "fields" => { "label" => "Latest version" } }
|
|
57
|
+
] }
|
|
58
|
+
]
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
doc = ws.docs.first
|
|
62
|
+
tables = doc.tables
|
|
63
|
+
|
|
64
|
+
puts "Tables:"
|
|
65
|
+
tables.each do |table|
|
|
66
|
+
puts table.id
|
|
67
|
+
puts "Total records : #{table.records.count}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
table = tables.last
|
|
71
|
+
|
|
72
|
+
puts "Creating records into table from doc 'demo' into workspace '#{ws_name}'..."
|
|
73
|
+
table.create_records("records" => [
|
|
74
|
+
{
|
|
75
|
+
"fields" => {
|
|
76
|
+
"name" => "did_you_mean",
|
|
77
|
+
"url" => "https://github.com/ruby/did_you_mean",
|
|
78
|
+
"version" => "v2.0.0"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
])
|
|
82
|
+
|
|
83
|
+
puts "Terminated, go check at http://localhost:8484/o/demo"
|
data/examples/update.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "grist"
|
|
6
|
+
require "byebug"
|
|
7
|
+
|
|
8
|
+
raise ArgumentError, "You must provide env var : 'GRIST_API_KEY'" if ENV.fetch("GRIST_API_KEY", "").empty?
|
|
9
|
+
raise ArgumentError, "You must provide env var : 'GRIST_API_URL'" if ENV.fetch("GRIST_API_URL", "").empty?
|
|
10
|
+
|
|
11
|
+
TABLE_NAME = "Open_source_software"
|
|
12
|
+
|
|
13
|
+
org_name = ENV.fetch("GRIST_ORG_NAME", "")
|
|
14
|
+
orgs = Grist::Type::Organization.all
|
|
15
|
+
org = orgs.find { |org| org.name == org_name }
|
|
16
|
+
|
|
17
|
+
if org&.workspaces&.empty?
|
|
18
|
+
puts "No workspaces found in organization #{org.name}. Creating a new one..."
|
|
19
|
+
ws = org.create_workspace({ name: "Grist Ruby" })
|
|
20
|
+
puts "Workspace created: #{ws.name} - ID: #{ws.id}"
|
|
21
|
+
else
|
|
22
|
+
ws = org&.workspaces&.last
|
|
23
|
+
puts "Workspaces found in organization #{org.name}. Using the last one '#{ws.name}'..."
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
puts "Workspace ID: #{ws.id}"
|
|
27
|
+
puts "Workspace Name: #{ws.name}"
|
|
28
|
+
|
|
29
|
+
if ws.docs.empty?
|
|
30
|
+
puts "No docs found in workspace #{ws.name}. Creating a new one..."
|
|
31
|
+
doc = ws.create_doc({ name: "Getting started" })
|
|
32
|
+
puts "Doc created: #{doc.name} - ID: #{doc.id}"
|
|
33
|
+
else
|
|
34
|
+
doc = ws.docs.last
|
|
35
|
+
puts "Docs found in workspace #{ws.name}. Using the last one '#{doc.name}'..."
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts "Doc ID: #{doc.id}"
|
|
39
|
+
puts "Doc Name: #{doc.name}"
|
|
40
|
+
|
|
41
|
+
table = doc.tables.find { |t| t.id == TABLE_NAME }
|
|
42
|
+
if !table.nil?
|
|
43
|
+
puts "Table found in doc #{doc.name}. Using the last one '#{table.id}'..."
|
|
44
|
+
puts "Table ID: #{table.id}"
|
|
45
|
+
else
|
|
46
|
+
puts "No tables found in doc #{doc.name}. Creating a new one..."
|
|
47
|
+
new_table = Grist::Type::Table.new("id" => TABLE_NAME, "columns" => [
|
|
48
|
+
{ "id" => "name", "fields" => { "label" => "Software name" } },
|
|
49
|
+
{ "id" => "description", "fields" => { "label" => "Description" } },
|
|
50
|
+
{ "id" => "link", "fields" => { "label" => "Source code link" } }
|
|
51
|
+
])
|
|
52
|
+
table = doc.create_tables("tables" => [new_table.to_hash])[0]
|
|
53
|
+
puts "Table created ID: #{table&.id}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# TODO: WIP
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
module ApiRoutes
|
|
5
|
+
### ORGANIZATIONS
|
|
6
|
+
ORG_ALL = "https://{gristhost}/api/orgs"
|
|
7
|
+
ORG_FIND = "https://{gristhost}/api/orgs/{orgId}"
|
|
8
|
+
ORG_UPDATE = "https://{gristhost}/api/orgs/{orgId}"
|
|
9
|
+
ORG_DELETE = "https://{gristhost}/api/orgs/{orgId}"
|
|
10
|
+
# ORG ACCESS
|
|
11
|
+
ORG_ACCESS_ALL = "https://{gristhost}/api/orgs/{orgId}/access"
|
|
12
|
+
ORG_ACCESS_UPDATE = "https://{gristhost}/api/orgs/{orgId}/access"
|
|
13
|
+
# ORG WORKSPACE
|
|
14
|
+
WORKSPACE_ALL = "https://{gristhost}/api/orgs/{orgId}/workspaces"
|
|
15
|
+
WORKSPACE_CREATE = "https://{gristhost}/api/orgs/{orgId}/workspaces"
|
|
16
|
+
|
|
17
|
+
### WORKSPACES
|
|
18
|
+
WORKSPACE_FIND = "https://{gristhost}/api/workspaces/{workspaceId}"
|
|
19
|
+
WORKSPACE_UPDATE = "https://{gristhost}/api/workspaces/{workspaceId}"
|
|
20
|
+
WORKSPACE_DELETE = "https://{gristhost}/api/workspaces/{workspaceId}"
|
|
21
|
+
# WORKSPACE ACCESS
|
|
22
|
+
WORKSPACE_ACCESS_ALL = "https://{gristhost}/api/workspaces/{orgId}/access"
|
|
23
|
+
WORKSPACE_ACCESS_UPDATE = "https://{gristhost}/api/workspaces/{orgId}/access"
|
|
24
|
+
# WORKSPACE DOCUMENTS
|
|
25
|
+
DOC_CREATE = "https://{gristhost}/api/workspaces/{workspaceId}/docs"
|
|
26
|
+
|
|
27
|
+
### DOCUMENTS
|
|
28
|
+
DOC_FIND = "https://{gristhost}/api/docs/{docId}"
|
|
29
|
+
DOC_UPDATE = "https://{gristhost}/api/docs/{docId}"
|
|
30
|
+
DOC_DELETE = "https://{gristhost}/api/docs/{docId}"
|
|
31
|
+
DOC_MOVE = "https://{gristhost}/api/docs/{docId}/move"
|
|
32
|
+
# DOC ACCESS
|
|
33
|
+
DOC_ACCESS_ALL = "https://{gristhost}/api/docs/{docId}/access"
|
|
34
|
+
DOC_ACCESS_UPDATE = "https://{gristhost}/api/docs/{docId}/access"
|
|
35
|
+
# DOC ACCESS
|
|
36
|
+
DOC_DL_SQL = "https://{gristhost}/api/docs/{docId}/download"
|
|
37
|
+
DOC_DL_EXCEL = "https://{gristhost}/api/docs/{docId}/download/xlsx"
|
|
38
|
+
DOC_DL_CSV = "https://{gristhost}/api/docs/{docId}/download/csv"
|
|
39
|
+
DOC_DL_TABLE_SCHEMA = "https://{gristhost}/api/docs/{docId}/download/table-schema"
|
|
40
|
+
DOC_TRUNCATE_HISTORY = "https://{gristhost}/api/docs/{docId}/states/remove"
|
|
41
|
+
DOC_FORCE_RELOAD = "https://{gristhost}/api/docs/{docId}/force-reload"
|
|
42
|
+
DOC_CLEAR_QUEUE = "https://{gristhost}/api/docs/{docId}/webhooks/queue"
|
|
43
|
+
|
|
44
|
+
### RECORDS
|
|
45
|
+
RECORD_ALL = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/records"
|
|
46
|
+
RECORD_CREATE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/records"
|
|
47
|
+
RECORD_UPDATE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/records"
|
|
48
|
+
RECORD_ADD_OR_UPDATE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/records"
|
|
49
|
+
|
|
50
|
+
### TABLES
|
|
51
|
+
TABLE_ALL = "https://{gristhost}/api/docs/{docId}/tables"
|
|
52
|
+
TABLE_CREATE = "https://{gristhost}/api/docs/{docId}/tables"
|
|
53
|
+
TABLE_UPDATE = "https://{gristhost}/api/docs/{docId}/tables"
|
|
54
|
+
|
|
55
|
+
### COLUMNS
|
|
56
|
+
COLUMN_ALL = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/columns"
|
|
57
|
+
COLUMN_CREATE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/columns"
|
|
58
|
+
COLUMN_UPDATE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/columns"
|
|
59
|
+
COLUMN_ADD_OR_UPDATE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/columns"
|
|
60
|
+
COLUMN_DELETE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/columns/{colId}"
|
|
61
|
+
|
|
62
|
+
### ROWS
|
|
63
|
+
ROW_DELETE = "https://{gristhost}/api/docs/{docId}/tables/{tableId}/data/delete"
|
|
64
|
+
|
|
65
|
+
### ATTACHMENTS
|
|
66
|
+
ATTACHMENT_ALL = "https://{gristhost}/api/docs/{docId}/attachments"
|
|
67
|
+
ATTACHMENT_UPDATE = "https://{gristhost}/api/docs/{docId}/attachments"
|
|
68
|
+
ATTACHMENT_METADATA = "https://{gristhost}/api/docs/{docId}/attachments/{attachmentId}"
|
|
69
|
+
ATTACHMENT_DL_CONTENT = "https://{gristhost}/api/docs/{docId}/attachments/{attachmentId}/download"
|
|
70
|
+
|
|
71
|
+
### WEBHOOKS
|
|
72
|
+
WEBHOOK_ALL = "https://{gristhost}/api/docs/{docId}/webhooks"
|
|
73
|
+
WEBHOOK_CREATE = "https://{gristhost}/api/docs/{docId}/webhooks"
|
|
74
|
+
WEBHOOK_UPDATE = "https://{gristhost}/api/docs/{docId}/webhooks/{webhookId}"
|
|
75
|
+
WEBHOOK_DELETE = "https://{gristhost}/api/docs/{docId}/webhooks/{webhookId}"
|
|
76
|
+
|
|
77
|
+
USER_DELETE = "https://{gristhost}/api/users/{userId}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
class Response
|
|
5
|
+
attr_reader :data, :error, :code, :type
|
|
6
|
+
|
|
7
|
+
def initialize(data: nil, error: nil, code: nil, type: nil)
|
|
8
|
+
@data = data
|
|
9
|
+
@error = error
|
|
10
|
+
@type = type
|
|
11
|
+
@code = code&.to_i
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success?
|
|
15
|
+
error.nil? && (@code >= 200 && @code < 400)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def error?
|
|
19
|
+
!error.nil? || !(@code >= 200 && @code < 400)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def not_found?
|
|
23
|
+
@code == 404
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def print_error
|
|
27
|
+
err = "#{@type}: #{@error}"
|
|
28
|
+
err += " (code: #{@code})" if @code
|
|
29
|
+
err += "\n#{@type.help}" if @type.respond_to?(:help) && !@type.help.nil?
|
|
30
|
+
err
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def log_error
|
|
34
|
+
return unless error?
|
|
35
|
+
|
|
36
|
+
Grist.logger.error("Grist API Error: #{@type} (code: #{@code})- #{@error}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/grist/rest.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
module Rest
|
|
5
|
+
def request(method, endpoint, params = {})
|
|
6
|
+
uri = URI("#{::Grist.base_api_url}#{endpoint}")
|
|
7
|
+
uri.query = URI.encode_www_form(params) if method == :get
|
|
8
|
+
|
|
9
|
+
http = ::Net::HTTP.new(uri.host, uri.port)
|
|
10
|
+
http.use_ssl = !::Grist.localhost?
|
|
11
|
+
|
|
12
|
+
request = ::Net::HTTP.const_get(method.capitalize).new(uri)
|
|
13
|
+
request["Authorization"] = ::Grist.token_auth
|
|
14
|
+
request["Content-Type"] = "application/json"
|
|
15
|
+
request.body = params.to_json unless method == :get
|
|
16
|
+
|
|
17
|
+
response = http.request(request)
|
|
18
|
+
|
|
19
|
+
raise InvalidApiKey, "Invalid API key" if response.is_a?(Net::HTTPUnauthorized)
|
|
20
|
+
raise NotFound, "Resource not found at : #{request.uri}" if response.is_a?(Net::HTTPNotFound)
|
|
21
|
+
|
|
22
|
+
data = response_body(response.body)
|
|
23
|
+
|
|
24
|
+
Grist::Response.new(data: data, code: response.code)
|
|
25
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, SocketError => e
|
|
26
|
+
res = Grist::Response.new(code: response&.code, error: "Grist endpoint is unreachable at #{request.uri}",
|
|
27
|
+
type: e.class)
|
|
28
|
+
res.log_error
|
|
29
|
+
raise NetworkError, res.print_error
|
|
30
|
+
rescue APIError => e
|
|
31
|
+
res = Grist::Response.new(code: response&.code, error: e.message, type: e.class)
|
|
32
|
+
res.log_error
|
|
33
|
+
res
|
|
34
|
+
rescue StandardError, Grist::NotFound => e
|
|
35
|
+
res = Grist::Response.new(code: response&.code, error: e.message, type: e.class)
|
|
36
|
+
res.log_error
|
|
37
|
+
res
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def path
|
|
41
|
+
self.class::PATH
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def list(params = {})
|
|
45
|
+
request(:get, path, params)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get(id)
|
|
49
|
+
request(:get, "#{path}/#{id}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def create(data)
|
|
53
|
+
grist_res = request(:post, path, data)
|
|
54
|
+
puts "Creating #{path} with data: #{data}"
|
|
55
|
+
puts puts grist_res.inspect
|
|
56
|
+
return unless grist_res.success?
|
|
57
|
+
|
|
58
|
+
data.each_key do |key|
|
|
59
|
+
instance_variable_set("@#{key}", data[key])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def update(data)
|
|
66
|
+
id = instance_variable_get("@id")
|
|
67
|
+
grist_res = request(:patch, "#{path}/#{id}", data)
|
|
68
|
+
return unless grist_res.success?
|
|
69
|
+
|
|
70
|
+
data.each_key do |key|
|
|
71
|
+
instance_variable_set("@#{key}", data[key])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def delete
|
|
78
|
+
id = instance_variable_get("@id")
|
|
79
|
+
grist_res = request(:delete, "#{path}/#{id}")
|
|
80
|
+
return unless grist_res.success?
|
|
81
|
+
|
|
82
|
+
instance_variable_set("@deleted", true)
|
|
83
|
+
|
|
84
|
+
self
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def response_body(str)
|
|
90
|
+
return {} if str.nil? || str.empty?
|
|
91
|
+
|
|
92
|
+
JSON.parse(str)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
module Searchable
|
|
5
|
+
# Finds a record based on the given parameters.
|
|
6
|
+
# @param params [Hash] The parameters to search for
|
|
7
|
+
# @return [Array] The records that match the given parameters
|
|
8
|
+
def self.find_by(*params)
|
|
9
|
+
objs = all
|
|
10
|
+
objs.select do |obj|
|
|
11
|
+
params.all? do |key, value|
|
|
12
|
+
obj.instance_variable_get("@#{key}") == value
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
module Type
|
|
5
|
+
# Defines a Grist Organization
|
|
6
|
+
class Access < Grist::Type::Base
|
|
7
|
+
PATH = "/access"
|
|
8
|
+
KEYS = %w[
|
|
9
|
+
id
|
|
10
|
+
email
|
|
11
|
+
name
|
|
12
|
+
picture
|
|
13
|
+
ref
|
|
14
|
+
access
|
|
15
|
+
isMember
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
attr_accessor(*KEYS)
|
|
19
|
+
|
|
20
|
+
# List all organizations
|
|
21
|
+
# @return [Array] Array of organizations
|
|
22
|
+
def self.all
|
|
23
|
+
grist_res = new.list
|
|
24
|
+
return [] unless grist_res&.data.is_a?(Array)
|
|
25
|
+
|
|
26
|
+
grist_res.data&.map { |org| Organization.new(org) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Finds an organization by ID
|
|
30
|
+
# @param id [Integer] The ID of the organization to find
|
|
31
|
+
# return [self | nil] The organization or nil if not found
|
|
32
|
+
def self.find(id)
|
|
33
|
+
grist_res = new.get(id)
|
|
34
|
+
return unless grist_res.success? && grist_res.data
|
|
35
|
+
|
|
36
|
+
new(grist_res.data)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Updates the organization
|
|
40
|
+
# @param id [Integer] The ID of the organization to delete
|
|
41
|
+
# @param data [Hash] The data to update the organization with
|
|
42
|
+
# @return [self | nil] The updated organization or nil if not found
|
|
43
|
+
def self.update(id, data)
|
|
44
|
+
org = find(id)
|
|
45
|
+
return unless org
|
|
46
|
+
|
|
47
|
+
org.update(data)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Deletes the organization
|
|
51
|
+
# @param id [Integer] The ID of the organization to delete
|
|
52
|
+
# @return [self | nil] The deleted organization or nil if not found
|
|
53
|
+
def self.delete(id)
|
|
54
|
+
org = find(id)
|
|
55
|
+
return unless org
|
|
56
|
+
|
|
57
|
+
org.delete
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.access(id)
|
|
61
|
+
org = find(id)
|
|
62
|
+
grist_res = org.access
|
|
63
|
+
return unless grist_res.success? && grist_res.data
|
|
64
|
+
|
|
65
|
+
grist_res.data
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
module Type
|
|
5
|
+
class Base
|
|
6
|
+
include Rest
|
|
7
|
+
include Accessible
|
|
8
|
+
include Searchable
|
|
9
|
+
|
|
10
|
+
PATH = ""
|
|
11
|
+
KEYS = %w[].freeze
|
|
12
|
+
attr_accessor(*KEYS)
|
|
13
|
+
|
|
14
|
+
def initialize(params = {})
|
|
15
|
+
keys.each do |key|
|
|
16
|
+
instance_variable_set("@#{key}", params[key])
|
|
17
|
+
end
|
|
18
|
+
@deleted = params.delete(:deleted) || false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def keys
|
|
22
|
+
self.class::KEYS
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def deleted?
|
|
26
|
+
!!@deleted
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# List all items
|
|
30
|
+
# @return [Array] Array of items
|
|
31
|
+
def self.all
|
|
32
|
+
grist_res = new.list
|
|
33
|
+
|
|
34
|
+
return [] unless grist_res&.data.is_a?(Array)
|
|
35
|
+
return [] unless grist_res&.data&.any?
|
|
36
|
+
|
|
37
|
+
grist_res.data.map { |org| new(org) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Finds an item by ID
|
|
41
|
+
# @param id [Integer] The ID of the item to find
|
|
42
|
+
# return [Grist::Type::Base, nil] The item or nil if not found
|
|
43
|
+
def self.find(id)
|
|
44
|
+
grist_res = new.get(id)
|
|
45
|
+
return unless grist_res.success? && grist_res.data
|
|
46
|
+
|
|
47
|
+
new(grist_res.data)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Updates the item
|
|
51
|
+
# @param id [Integer] The ID of the item to delete
|
|
52
|
+
# @param data [Hash] The data to update the item with
|
|
53
|
+
# @return [Grist::Type::Base, nil] The updated item or nil if not found
|
|
54
|
+
def self.update(id, data)
|
|
55
|
+
org = find(id)
|
|
56
|
+
return unless org
|
|
57
|
+
|
|
58
|
+
org.update(data)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Deletes the item
|
|
62
|
+
# @param id [Integer] The ID of the item to delete
|
|
63
|
+
# @return [Grist::Type::Base, nil] The deleted item or nil if not found
|
|
64
|
+
def self.delete(id)
|
|
65
|
+
org = find(id)
|
|
66
|
+
return unless org
|
|
67
|
+
|
|
68
|
+
org.delete
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grist
|
|
4
|
+
module Type
|
|
5
|
+
# Defines a Grist Workspace
|
|
6
|
+
class Column
|
|
7
|
+
include Rest
|
|
8
|
+
|
|
9
|
+
PATH = "/docs"
|
|
10
|
+
KEYS = %w[
|
|
11
|
+
id
|
|
12
|
+
fields
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
attr_accessor(*KEYS)
|
|
16
|
+
|
|
17
|
+
def initialize(params = {})
|
|
18
|
+
@doc_id = params[:doc_id]
|
|
19
|
+
KEYS.each do |key|
|
|
20
|
+
instance_variable_set("@#{key}", params[key])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|