monday_ruby 1.0.0 → 1.2.0
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/.env +1 -1
- data/.rspec +0 -1
- data/.rubocop.yml +19 -0
- data/.simplecov +1 -0
- data/CHANGELOG.md +49 -0
- data/CONTRIBUTING.md +165 -0
- data/README.md +167 -88
- data/docs/.vitepress/config.mjs +255 -0
- data/docs/.vitepress/theme/index.js +4 -0
- data/docs/.vitepress/theme/style.css +43 -0
- data/docs/README.md +80 -0
- data/docs/explanation/architecture.md +507 -0
- data/docs/explanation/best-practices/errors.md +478 -0
- data/docs/explanation/best-practices/performance.md +1084 -0
- data/docs/explanation/best-practices/rate-limiting.md +630 -0
- data/docs/explanation/best-practices/testing.md +820 -0
- data/docs/explanation/column-values.md +857 -0
- data/docs/explanation/design.md +795 -0
- data/docs/explanation/graphql.md +356 -0
- data/docs/explanation/migration/v1.md +808 -0
- data/docs/explanation/pagination.md +447 -0
- data/docs/guides/advanced/batch.md +1274 -0
- data/docs/guides/advanced/complex-queries.md +1114 -0
- data/docs/guides/advanced/errors.md +818 -0
- data/docs/guides/advanced/pagination.md +934 -0
- data/docs/guides/advanced/rate-limiting.md +981 -0
- data/docs/guides/authentication.md +286 -0
- data/docs/guides/boards/create.md +386 -0
- data/docs/guides/boards/delete.md +405 -0
- data/docs/guides/boards/duplicate.md +511 -0
- data/docs/guides/boards/query.md +530 -0
- data/docs/guides/boards/update.md +453 -0
- data/docs/guides/columns/create.md +452 -0
- data/docs/guides/columns/metadata.md +492 -0
- data/docs/guides/columns/query.md +455 -0
- data/docs/guides/columns/update-multiple.md +459 -0
- data/docs/guides/columns/update-values.md +509 -0
- data/docs/guides/files/add-to-column.md +40 -0
- data/docs/guides/files/add-to-update.md +37 -0
- data/docs/guides/files/clear-column.md +33 -0
- data/docs/guides/first-request.md +285 -0
- data/docs/guides/folders/manage.md +750 -0
- data/docs/guides/groups/items.md +626 -0
- data/docs/guides/groups/manage.md +501 -0
- data/docs/guides/installation.md +169 -0
- data/docs/guides/items/create.md +493 -0
- data/docs/guides/items/delete.md +514 -0
- data/docs/guides/items/query.md +605 -0
- data/docs/guides/items/subitems.md +483 -0
- data/docs/guides/items/update.md +699 -0
- data/docs/guides/updates/manage.md +619 -0
- data/docs/guides/use-cases/dashboard.md +1421 -0
- data/docs/guides/use-cases/import.md +1962 -0
- data/docs/guides/use-cases/task-management.md +1381 -0
- data/docs/guides/workspaces/manage.md +502 -0
- data/docs/index.md +69 -0
- data/docs/package-lock.json +2468 -0
- data/docs/package.json +13 -0
- data/docs/reference/client.md +540 -0
- data/docs/reference/configuration.md +586 -0
- data/docs/reference/errors.md +693 -0
- data/docs/reference/resources/account.md +208 -0
- data/docs/reference/resources/activity-log.md +369 -0
- data/docs/reference/resources/board-view.md +359 -0
- data/docs/reference/resources/board.md +393 -0
- data/docs/reference/resources/column.md +543 -0
- data/docs/reference/resources/file.md +236 -0
- data/docs/reference/resources/folder.md +386 -0
- data/docs/reference/resources/group.md +507 -0
- data/docs/reference/resources/item.md +348 -0
- data/docs/reference/resources/subitem.md +267 -0
- data/docs/reference/resources/update.md +259 -0
- data/docs/reference/resources/workspace.md +213 -0
- data/docs/reference/response.md +560 -0
- data/docs/tutorial/first-integration.md +713 -0
- data/lib/monday/client.rb +41 -2
- data/lib/monday/configuration.rb +13 -0
- data/lib/monday/deprecation.rb +23 -0
- data/lib/monday/error.rb +5 -2
- data/lib/monday/request.rb +19 -1
- data/lib/monday/resources/base.rb +4 -0
- data/lib/monday/resources/board.rb +52 -0
- data/lib/monday/resources/column.rb +6 -0
- data/lib/monday/resources/file.rb +56 -0
- data/lib/monday/resources/folder.rb +55 -0
- data/lib/monday/resources/group.rb +66 -0
- data/lib/monday/resources/item.rb +62 -0
- data/lib/monday/util.rb +33 -1
- data/lib/monday/version.rb +1 -1
- data/lib/monday_ruby.rb +1 -0
- metadata +92 -11
- data/monday_ruby.gemspec +0 -39
data/lib/monday/client.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "uri"
|
|
4
4
|
require "net/http"
|
|
5
|
+
require "net/http/post/multipart"
|
|
5
6
|
require "json"
|
|
6
7
|
|
|
7
8
|
require_relative "configuration"
|
|
@@ -26,7 +27,25 @@ module Monday
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def make_request(body)
|
|
29
|
-
response = Request.post(
|
|
30
|
+
response = Request.post(
|
|
31
|
+
uri,
|
|
32
|
+
body,
|
|
33
|
+
request_headers,
|
|
34
|
+
open_timeout: @config.open_timeout,
|
|
35
|
+
read_timeout: @config.read_timeout
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
handle_response(Response.new(response))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def make_file_request(query, variables)
|
|
42
|
+
response = Request.post_multipart(
|
|
43
|
+
files_uri,
|
|
44
|
+
{ query: query, variables: variables },
|
|
45
|
+
request_multipart_headers,
|
|
46
|
+
open_timeout: @config.open_timeout,
|
|
47
|
+
read_timeout: @config.read_timeout
|
|
48
|
+
)
|
|
30
49
|
|
|
31
50
|
handle_response(Response.new(response))
|
|
32
51
|
end
|
|
@@ -43,6 +62,10 @@ module Monday
|
|
|
43
62
|
URI(@config.host)
|
|
44
63
|
end
|
|
45
64
|
|
|
65
|
+
def files_uri
|
|
66
|
+
URI(@config.files_host)
|
|
67
|
+
end
|
|
68
|
+
|
|
46
69
|
def request_headers
|
|
47
70
|
{
|
|
48
71
|
"Content-Type": "application/json",
|
|
@@ -50,6 +73,13 @@ module Monday
|
|
|
50
73
|
}
|
|
51
74
|
end
|
|
52
75
|
|
|
76
|
+
def request_multipart_headers
|
|
77
|
+
{
|
|
78
|
+
"Content-Type": "multipart/form-data",
|
|
79
|
+
Authorization: @config.token
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
|
|
53
83
|
def handle_response(response)
|
|
54
84
|
return response if response.success?
|
|
55
85
|
|
|
@@ -63,7 +93,7 @@ module Monday
|
|
|
63
93
|
end
|
|
64
94
|
|
|
65
95
|
def response_exception(response)
|
|
66
|
-
error_code = response
|
|
96
|
+
error_code = response_error_code(response)
|
|
67
97
|
|
|
68
98
|
return Error.new(response: response) if error_code.nil?
|
|
69
99
|
|
|
@@ -71,6 +101,15 @@ module Monday
|
|
|
71
101
|
exception_klass.new(message: error_code, response: response, code: code)
|
|
72
102
|
end
|
|
73
103
|
|
|
104
|
+
def response_error_code(response)
|
|
105
|
+
error_code = response.body["error_code"]
|
|
106
|
+
return error_code unless error_code.nil?
|
|
107
|
+
|
|
108
|
+
return unless response.body["errors"].is_a?(Array) && !response.body["errors"].empty?
|
|
109
|
+
|
|
110
|
+
response.body.dig("errors", 0, "extensions", "code") || response.body.dig("errors", 0, "extensions", "error_code")
|
|
111
|
+
end
|
|
112
|
+
|
|
74
113
|
def default_exception(response)
|
|
75
114
|
Util.status_code_exceptions_mapping(response.status).new(response: response)
|
|
76
115
|
end
|
data/lib/monday/configuration.rb
CHANGED
|
@@ -7,15 +7,22 @@ module Monday
|
|
|
7
7
|
#
|
|
8
8
|
# token: used to authenticate the requests
|
|
9
9
|
# host: defaults to https://api.monday.com/v2
|
|
10
|
+
# files_host: defaults to https://api.monday.com/v2/files
|
|
10
11
|
class Configuration
|
|
11
12
|
DEFAULT_HOST = "https://api.monday.com/v2"
|
|
13
|
+
DEFAULT_FILES_HOST = "#{DEFAULT_HOST}/file"
|
|
12
14
|
DEFAULT_TOKEN = nil
|
|
13
15
|
DEFAULT_VERSION = "2023-07"
|
|
16
|
+
DEFAULT_OPEN_TIMEOUT = 10
|
|
17
|
+
DEFAULT_READ_TIMEOUT = 30
|
|
14
18
|
|
|
15
19
|
CONFIGURATION_FIELDS = %i[
|
|
16
20
|
token
|
|
17
21
|
host
|
|
22
|
+
files_host
|
|
18
23
|
version
|
|
24
|
+
open_timeout
|
|
25
|
+
read_timeout
|
|
19
26
|
].freeze
|
|
20
27
|
|
|
21
28
|
attr_accessor(*CONFIGURATION_FIELDS)
|
|
@@ -25,8 +32,11 @@ module Monday
|
|
|
25
32
|
raise ArgumentError, "Unknown arguments: #{invalid_keys}" unless invalid_keys.empty?
|
|
26
33
|
|
|
27
34
|
@host = DEFAULT_HOST
|
|
35
|
+
@files_host = DEFAULT_FILES_HOST
|
|
28
36
|
@token = DEFAULT_TOKEN
|
|
29
37
|
@version = DEFAULT_VERSION
|
|
38
|
+
@open_timeout = DEFAULT_OPEN_TIMEOUT
|
|
39
|
+
@read_timeout = DEFAULT_READ_TIMEOUT
|
|
30
40
|
|
|
31
41
|
config_args.each do |key, value|
|
|
32
42
|
public_send("#{key}=", value)
|
|
@@ -36,7 +46,10 @@ module Monday
|
|
|
36
46
|
def reset
|
|
37
47
|
@token = DEFAULT_TOKEN
|
|
38
48
|
@host = DEFAULT_HOST
|
|
49
|
+
@files_host = DEFAULT_FILES_HOST
|
|
39
50
|
@version = DEFAULT_VERSION
|
|
51
|
+
@open_timeout = DEFAULT_OPEN_TIMEOUT
|
|
52
|
+
@read_timeout = DEFAULT_READ_TIMEOUT
|
|
40
53
|
end
|
|
41
54
|
end
|
|
42
55
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Monday
|
|
4
|
+
# Utility module for handling deprecation warnings
|
|
5
|
+
module Deprecation
|
|
6
|
+
# Issues a deprecation warning to stderr
|
|
7
|
+
#
|
|
8
|
+
# @param method_name [String] The name of the deprecated method
|
|
9
|
+
# @param removal_version [String] The version in which the method will be removed
|
|
10
|
+
# @param alternative [String, nil] The recommended alternative method
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# Deprecation.warn(method_name: "items", removal_version: "2.0.0", alternative: "items_page")
|
|
14
|
+
# # => [DEPRECATION] `items` is deprecated and will be removed in v2.0.0. Use `items_page` instead.
|
|
15
|
+
def self.warn(method_name:, removal_version:, alternative: nil)
|
|
16
|
+
message = "[DEPRECATION] `#{method_name}` is deprecated and will be removed in v#{removal_version}."
|
|
17
|
+
message += " Use `#{alternative}` instead." if alternative
|
|
18
|
+
|
|
19
|
+
# Output to stderr so it doesn't interfere with normal output
|
|
20
|
+
Kernel.warn message
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/monday/error.rb
CHANGED
|
@@ -56,13 +56,16 @@ module Monday
|
|
|
56
56
|
# AuthorizationError is raised when the request returns
|
|
57
57
|
# a 401 or 403 status code.
|
|
58
58
|
#
|
|
59
|
-
# It is also raised when the body returns the following
|
|
60
|
-
# UserUnauthorizedException
|
|
59
|
+
# It is also raised when the body returns the following error_codes:
|
|
60
|
+
# UserUnauthorizedException, USER_UNAUTHORIZED
|
|
61
61
|
class AuthorizationError < Error
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# RateLimitError is raised when the request returns
|
|
65
65
|
# a 429 status code.
|
|
66
|
+
#
|
|
67
|
+
# It is also raised when the body returns the following error_codes:
|
|
68
|
+
# ComplexityException, COMPLEXITY_BUDGET_EXHAUSTED
|
|
66
69
|
class RateLimitError < Error
|
|
67
70
|
end
|
|
68
71
|
|
data/lib/monday/request.rb
CHANGED
|
@@ -4,9 +4,12 @@ module Monday
|
|
|
4
4
|
# Defines the HTTP request methods.
|
|
5
5
|
class Request
|
|
6
6
|
# Performs a POST request
|
|
7
|
-
def self.post(uri, query, headers)
|
|
7
|
+
def self.post(uri, query, headers, open_timeout: 10, read_timeout: 30)
|
|
8
8
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
9
9
|
http.use_ssl = true
|
|
10
|
+
http.open_timeout = open_timeout
|
|
11
|
+
http.read_timeout = read_timeout
|
|
12
|
+
|
|
10
13
|
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
|
11
14
|
|
|
12
15
|
request.body = {
|
|
@@ -15,5 +18,20 @@ module Monday
|
|
|
15
18
|
|
|
16
19
|
http.request(request)
|
|
17
20
|
end
|
|
21
|
+
|
|
22
|
+
def self.post_multipart(uri, body, headers, open_timeout: 10, read_timeout: 30)
|
|
23
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
24
|
+
http.use_ssl = true
|
|
25
|
+
http.open_timeout = open_timeout
|
|
26
|
+
http.read_timeout = read_timeout
|
|
27
|
+
|
|
28
|
+
params = {
|
|
29
|
+
"query" => body[:query],
|
|
30
|
+
"variables[file]" => body[:variables][:file]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
request = Net::HTTP::Post::Multipart.new(uri.request_uri, params, headers)
|
|
34
|
+
http.request(request)
|
|
35
|
+
end
|
|
18
36
|
end
|
|
19
37
|
end
|
|
@@ -7,6 +7,7 @@ module Monday
|
|
|
7
7
|
# Represents Monday.com's board resource.
|
|
8
8
|
class Board < Base
|
|
9
9
|
DEFAULT_SELECT = %w[id name description].freeze
|
|
10
|
+
DEFAULT_PAGINATED_SELECT = %w[id name].freeze
|
|
10
11
|
|
|
11
12
|
# Retrieves all the boards.
|
|
12
13
|
#
|
|
@@ -79,11 +80,62 @@ module Monday
|
|
|
79
80
|
# Allows customizing the values to retrieve using the select option.
|
|
80
81
|
# By default, returns the deleted subscriber IDs.
|
|
81
82
|
def delete_subscribers(board_id, user_ids, select: ["id"])
|
|
83
|
+
Deprecation.warn(
|
|
84
|
+
method_name: "delete_subscribers",
|
|
85
|
+
removal_version: "2.0.0",
|
|
86
|
+
alternative: "user.delete_from_board"
|
|
87
|
+
)
|
|
88
|
+
|
|
82
89
|
query = "mutation{delete_subscribers_from_board(" \
|
|
83
90
|
"board_id: #{board_id}, user_ids: #{user_ids}){#{Util.format_select(select)}}}"
|
|
84
91
|
|
|
85
92
|
make_request(query)
|
|
86
93
|
end
|
|
94
|
+
|
|
95
|
+
# Retrieves paginated items from a board.
|
|
96
|
+
#
|
|
97
|
+
# Uses cursor-based pagination for efficient data retrieval.
|
|
98
|
+
# The items_page field is the modern replacement for the deprecated items field.
|
|
99
|
+
#
|
|
100
|
+
# @param board_id [Integer] The ID of the board
|
|
101
|
+
# @param limit [Integer] Number of items to retrieve per page (default: 25, max: 500)
|
|
102
|
+
# @param cursor [String, nil] Pagination cursor for fetching next page (expires after 60 minutes)
|
|
103
|
+
# @param query_params [Hash, nil] Query parameters for filtering items with rules and operators
|
|
104
|
+
# @param args [Hash] Additional board query arguments
|
|
105
|
+
# @param select [Array] Fields to retrieve for each item
|
|
106
|
+
# @return [Monday::Response] Response containing items and cursor
|
|
107
|
+
#
|
|
108
|
+
# @example Fetch first page of items
|
|
109
|
+
# response = client.board.items_page(board_id: 123, limit: 50)
|
|
110
|
+
# items = response.dig("data", "boards", 0, "items_page", "items")
|
|
111
|
+
# cursor = response.dig("data", "boards", 0, "items_page", "cursor")
|
|
112
|
+
#
|
|
113
|
+
# @example Fetch next page using cursor
|
|
114
|
+
# response = client.board.items_page(board_id: 123, cursor: cursor)
|
|
115
|
+
#
|
|
116
|
+
# @example Filter items by column value
|
|
117
|
+
# response = client.board.items_page(
|
|
118
|
+
# board_id: 123,
|
|
119
|
+
# limit: 100,
|
|
120
|
+
# query_params: {
|
|
121
|
+
# rules: [{ column_id: "status", compare_value: [1] }],
|
|
122
|
+
# operator: :and
|
|
123
|
+
# }
|
|
124
|
+
# )
|
|
125
|
+
def items_page(board_ids:, limit: 25, cursor: nil, query_params: nil, select: DEFAULT_PAGINATED_SELECT)
|
|
126
|
+
items_args_parts = ["limit: #{limit}"]
|
|
127
|
+
items_args_parts << "cursor: \"#{cursor}\"" if cursor
|
|
128
|
+
items_args_parts << "query_params: #{Util.format_graphql_object(query_params)}" if query_params
|
|
129
|
+
|
|
130
|
+
items_args_string = items_args_parts.empty? ? "" : "(#{items_args_parts.join(", ")})"
|
|
131
|
+
|
|
132
|
+
items_page_select = "items_page#{items_args_string}{cursor items{#{Util.format_select(select)}}}"
|
|
133
|
+
|
|
134
|
+
board_args = { ids: board_ids.is_a?(Array) ? board_ids : [board_ids] }
|
|
135
|
+
request_query = "query{boards#{Util.format_args(board_args)}{#{items_page_select}}}"
|
|
136
|
+
|
|
137
|
+
make_request(request_query)
|
|
138
|
+
end
|
|
87
139
|
end
|
|
88
140
|
end
|
|
89
141
|
end
|
|
@@ -25,6 +25,12 @@ module Monday
|
|
|
25
25
|
# Allows customizing the values to retrieve using the select option.
|
|
26
26
|
# By default, ID, title and description fields are retrieved.
|
|
27
27
|
def column_values(board_ids = [], item_ids = [], select: DEFAULT_SELECT)
|
|
28
|
+
Deprecation.warn(
|
|
29
|
+
method_name: "column_values",
|
|
30
|
+
removal_version: "2.0.0",
|
|
31
|
+
alternative: "item.column_values"
|
|
32
|
+
)
|
|
33
|
+
|
|
28
34
|
board_args = board_ids.empty? ? "" : "ids: #{board_ids}"
|
|
29
35
|
item_args = item_ids.empty? ? "" : "ids: #{item_ids}"
|
|
30
36
|
query = "query{boards(#{board_args}){items(#{item_args})" \
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Monday
|
|
4
|
+
module Resources
|
|
5
|
+
# Represents Monday.com's file asset resource.
|
|
6
|
+
class File < Base
|
|
7
|
+
DEFAULT_SELECT = %w[id].freeze
|
|
8
|
+
|
|
9
|
+
# Adds a file to a file type column for an item.
|
|
10
|
+
#
|
|
11
|
+
# Allows customizing the column update using the args option.
|
|
12
|
+
# Allows customizing the values to retrieve using the select option.
|
|
13
|
+
# By default, The ID is retrieved.
|
|
14
|
+
def add_file_to_column(args: {}, select: DEFAULT_SELECT)
|
|
15
|
+
cloned_args = args.clone
|
|
16
|
+
variables = { file: cloned_args.delete(:file) }
|
|
17
|
+
cloned_args.merge!(file: "$file")
|
|
18
|
+
query = <<~QUERY
|
|
19
|
+
mutation add_file($file: File!) {
|
|
20
|
+
add_file_to_column#{Util.format_args(cloned_args)} {#{Util.format_select(select)}}
|
|
21
|
+
}
|
|
22
|
+
QUERY
|
|
23
|
+
make_file_request(query, variables)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Adds a file to an update for an item.
|
|
27
|
+
#
|
|
28
|
+
# Allows customizing the update creation using the args option.
|
|
29
|
+
# Allows customizing the values to retrieve using the select option.
|
|
30
|
+
# By default, The ID is retrieved.
|
|
31
|
+
def add_file_to_update(args: {}, select: DEFAULT_SELECT)
|
|
32
|
+
cloned_args = args.clone
|
|
33
|
+
variables = { file: cloned_args.delete(:file) }
|
|
34
|
+
cloned_args.merge!(file: "$file")
|
|
35
|
+
query = <<~QUERY
|
|
36
|
+
mutation ($file: File!) {
|
|
37
|
+
add_file_to_update#{Util.format_args(cloned_args)} {#{Util.format_select(select)}}
|
|
38
|
+
}
|
|
39
|
+
QUERY
|
|
40
|
+
make_file_request(query, variables)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Clear an item's files column. This is a helper method for files
|
|
44
|
+
# and you could also use the column.change_value to clear the column as well.
|
|
45
|
+
#
|
|
46
|
+
# Allows customizing the update creation using the args option.
|
|
47
|
+
# Allows customizing the values to retrieve using the select option.
|
|
48
|
+
# By default, ID is retrieved.
|
|
49
|
+
def clear_file_column(args: {}, select: DEFAULT_SELECT)
|
|
50
|
+
merged_args = args.merge(value: '{\"clear_all\": true}')
|
|
51
|
+
query = "mutation { change_column_value#{Util.format_args(merged_args)} {#{Util.format_select(select)}}}"
|
|
52
|
+
make_request(query)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Monday
|
|
6
|
+
module Resources
|
|
7
|
+
# Represents Monday.com's board resource.
|
|
8
|
+
class Folder < Base
|
|
9
|
+
DEFAULT_SELECT = %w[id name].freeze
|
|
10
|
+
|
|
11
|
+
# Retrieves all the folders.
|
|
12
|
+
#
|
|
13
|
+
# Allows filtering folders using the args option.
|
|
14
|
+
# Allows customizing the values to retrieve using the select option.
|
|
15
|
+
# By default, ID and name fields are retrieved.
|
|
16
|
+
def query(args: {}, select: DEFAULT_SELECT)
|
|
17
|
+
request_query = "query{folders#{Util.format_args(args)}{#{Util.format_select(select)}}}"
|
|
18
|
+
|
|
19
|
+
make_request(request_query)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Create a new folder.
|
|
23
|
+
#
|
|
24
|
+
# Allows customizing creating a folder using the args option.
|
|
25
|
+
# Allows customizing the values to retrieve using the select option.
|
|
26
|
+
# By default, ID and name fields are retrieved.
|
|
27
|
+
def create(args: {}, select: DEFAULT_SELECT)
|
|
28
|
+
query = "mutation{create_folder#{Util.format_args(args)}{#{Util.format_select(select)}}}"
|
|
29
|
+
|
|
30
|
+
make_request(query)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Update a folder.
|
|
34
|
+
#
|
|
35
|
+
# Allows customizing updating the folder using the args option.
|
|
36
|
+
# By default, returns the ID of the updated folder.
|
|
37
|
+
def update(args: {}, select: ["id"])
|
|
38
|
+
query = "mutation{update_folder#{Util.format_args(args)}{#{Util.format_select(select)}}}"
|
|
39
|
+
|
|
40
|
+
make_request(query)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Delete a folder.
|
|
44
|
+
#
|
|
45
|
+
# Requires folder_id in args option to delete the folder.
|
|
46
|
+
# Allows customizing the values to retrieve using the select option.
|
|
47
|
+
# By default, returns the ID of the folder deleted.
|
|
48
|
+
def delete(args: {}, select: ["id"])
|
|
49
|
+
query = "mutation{delete_folder#{Util.format_args(args)}{#{Util.format_select(select)}}}"
|
|
50
|
+
|
|
51
|
+
make_request(query)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -7,6 +7,7 @@ module Monday
|
|
|
7
7
|
# Represents Monday.com's group resource.
|
|
8
8
|
class Group < Base
|
|
9
9
|
DEFAULT_SELECT = %w[id title].freeze
|
|
10
|
+
DEFAULT_PAGINATED_SELECT = %w[id name].freeze
|
|
10
11
|
|
|
11
12
|
# Retrieves all the groups.
|
|
12
13
|
#
|
|
@@ -83,6 +84,71 @@ module Monday
|
|
|
83
84
|
|
|
84
85
|
make_request(query)
|
|
85
86
|
end
|
|
87
|
+
|
|
88
|
+
# Retrieves paginated items from a group.
|
|
89
|
+
#
|
|
90
|
+
# Uses cursor-based pagination for efficient data retrieval.
|
|
91
|
+
# The items_page field is the modern replacement for the deprecated items field.
|
|
92
|
+
#
|
|
93
|
+
# @param board_ids [Integer, Array<Integer>] The ID(s) of the board(s) containing the group
|
|
94
|
+
# @param group_ids [String, Array<String>] The ID(s) of the group(s)
|
|
95
|
+
# @param limit [Integer] Number of items to retrieve per page (default: 25, max: 500)
|
|
96
|
+
# @param cursor [String, nil] Pagination cursor for fetching next page (expires after 60 minutes)
|
|
97
|
+
# @param query_params [Hash, nil] Query parameters for filtering items with rules and operators
|
|
98
|
+
# @param select [Array] Fields to retrieve for each item
|
|
99
|
+
# @return [Monday::Response] Response containing items and cursor
|
|
100
|
+
#
|
|
101
|
+
# @example Fetch first page of items from a group
|
|
102
|
+
# response = client.group.items_page(board_ids: 123, group_ids: "group_1", limit: 50)
|
|
103
|
+
# items = response.dig("data", "boards", 0, "groups", 0, "items_page", "items")
|
|
104
|
+
# cursor = response.dig("data", "boards", 0, "groups", 0, "items_page", "cursor")
|
|
105
|
+
#
|
|
106
|
+
# @example Fetch next page using cursor
|
|
107
|
+
# response = client.group.items_page(board_ids: 123, group_ids: "group_1", cursor: cursor)
|
|
108
|
+
#
|
|
109
|
+
# @example Filter items by column value
|
|
110
|
+
# response = client.group.items_page(
|
|
111
|
+
# board_ids: 123,
|
|
112
|
+
# group_ids: "group_1",
|
|
113
|
+
# limit: 100,
|
|
114
|
+
# query_params: {
|
|
115
|
+
# rules: [{ column_id: "status", compare_value: [1] }],
|
|
116
|
+
# operator: :and
|
|
117
|
+
# }
|
|
118
|
+
# )
|
|
119
|
+
def items_page(
|
|
120
|
+
board_ids:, group_ids:, limit: 25, cursor: nil, query_params: nil, select: DEFAULT_PAGINATED_SELECT
|
|
121
|
+
)
|
|
122
|
+
items_page_query = build_items_page_query(limit, cursor, query_params, select)
|
|
123
|
+
request_query = build_groups_items_page_request(board_ids, group_ids, items_page_query)
|
|
124
|
+
|
|
125
|
+
make_request(request_query)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def build_items_page_query(limit, cursor, query_params, select)
|
|
131
|
+
args_parts = ["limit: #{limit}"]
|
|
132
|
+
args_parts << "cursor: \"#{cursor}\"" if cursor
|
|
133
|
+
args_parts << "query_params: #{Util.format_graphql_object(query_params)}" if query_params
|
|
134
|
+
|
|
135
|
+
args_string = "(#{args_parts.join(", ")})"
|
|
136
|
+
"items_page#{args_string}{cursor items{#{Util.format_select(select)}}}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def build_groups_items_page_request(board_ids, group_ids, items_page_query)
|
|
140
|
+
board_args = { ids: board_ids.is_a?(Array) ? board_ids : [board_ids] }
|
|
141
|
+
group_ids_formatted = format_group_ids(group_ids)
|
|
142
|
+
|
|
143
|
+
boards_part = "boards#{Util.format_args(board_args)}"
|
|
144
|
+
groups_part = "groups(ids: #{group_ids_formatted})"
|
|
145
|
+
"query{#{boards_part}{#{groups_part}{#{items_page_query}}}}"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def format_group_ids(group_ids)
|
|
149
|
+
ids_array = group_ids.is_a?(Array) ? group_ids : [group_ids]
|
|
150
|
+
"[#{ids_array.map { |id| "\"#{id}\"" }.join(", ")}]"
|
|
151
|
+
end
|
|
86
152
|
end
|
|
87
153
|
end
|
|
88
154
|
end
|
|
@@ -7,6 +7,7 @@ module Monday
|
|
|
7
7
|
# Represents Monday.com's item resource.
|
|
8
8
|
class Item < Base
|
|
9
9
|
DEFAULT_SELECT = %w[id name created_at].freeze
|
|
10
|
+
DEFAULT_PAGINATED_SELECT = %w[id name].freeze
|
|
10
11
|
|
|
11
12
|
# Retrieves all the items for the boards.
|
|
12
13
|
#
|
|
@@ -63,6 +64,67 @@ module Monday
|
|
|
63
64
|
|
|
64
65
|
make_request(query)
|
|
65
66
|
end
|
|
67
|
+
|
|
68
|
+
# Retrieves paginated items filtered by column values.
|
|
69
|
+
#
|
|
70
|
+
# Enables searching for items based on specific column values.
|
|
71
|
+
# Uses cursor-based pagination for efficient data retrieval.
|
|
72
|
+
#
|
|
73
|
+
# @param board_id [Integer] The ID of the board
|
|
74
|
+
# @param columns [Array<Hash>, nil] Column filtering criteria (mutually exclusive with cursor)
|
|
75
|
+
# Each hash should contain :column_id and :column_values
|
|
76
|
+
# @param limit [Integer] Number of items to retrieve per page (default: 25, max: 500)
|
|
77
|
+
# @param cursor [String, nil] Pagination cursor for fetching next page (mutually exclusive with columns)
|
|
78
|
+
# @param select [Array] Fields to retrieve for each item
|
|
79
|
+
# @return [Monday::Response] Response containing items and cursor
|
|
80
|
+
#
|
|
81
|
+
# @example Search items by column values
|
|
82
|
+
# response = client.item.page_by_column_values(
|
|
83
|
+
# board_id: 123,
|
|
84
|
+
# columns: [
|
|
85
|
+
# { column_id: "status", column_values: ["Done", "Working on it"] },
|
|
86
|
+
# { column_id: "text", column_values: ["High Priority"] }
|
|
87
|
+
# ],
|
|
88
|
+
# limit: 50
|
|
89
|
+
# )
|
|
90
|
+
# items = response.dig("data", "items_page_by_column_values", "items")
|
|
91
|
+
# cursor = response.dig("data", "items_page_by_column_values", "cursor")
|
|
92
|
+
#
|
|
93
|
+
# @example Fetch next page using cursor
|
|
94
|
+
# response = client.item.page_by_column_values(
|
|
95
|
+
# board_id: 123,
|
|
96
|
+
# cursor: cursor
|
|
97
|
+
# )
|
|
98
|
+
#
|
|
99
|
+
# @note Supported column types: Checkbox, Country, Date, Dropdown, Email, Hour, Link,
|
|
100
|
+
# Long Text, Numbers, People, Phone, Status, Text, Timeline, World Clock
|
|
101
|
+
# @note Columns use AND logic; values within a column use ANY_OF logic
|
|
102
|
+
def page_by_column_values(board_id:, columns: nil, limit: 25, cursor: nil, select: DEFAULT_PAGINATED_SELECT)
|
|
103
|
+
query_string = build_items_page_by_column_values_query(board_id, columns, limit, cursor, select)
|
|
104
|
+
make_request(query_string)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def build_items_page_by_column_values_query(board_id, columns, limit, cursor, select)
|
|
110
|
+
args_parts = ["board_id: #{board_id}", "limit: #{limit}"]
|
|
111
|
+
args_parts << "cursor: \"#{cursor}\"" if cursor
|
|
112
|
+
args_parts << "columns: #{format_columns(columns)}" if columns
|
|
113
|
+
|
|
114
|
+
args_string = "(#{args_parts.join(", ")})"
|
|
115
|
+
"query{items_page_by_column_values#{args_string}{cursor items{#{Util.format_select(select)}}}}"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def format_columns(columns)
|
|
119
|
+
return nil unless columns
|
|
120
|
+
|
|
121
|
+
column_strings = columns.map do |col|
|
|
122
|
+
values = col[:column_values].map { |v| "\"#{v}\"" }.join(", ")
|
|
123
|
+
"{column_id: \"#{col[:column_id]}\", column_values: [#{values}]}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
"[#{column_strings.join(", ")}]"
|
|
127
|
+
end
|
|
66
128
|
end
|
|
67
129
|
end
|
|
68
130
|
end
|
data/lib/monday/util.rb
CHANGED
|
@@ -29,6 +29,18 @@ module Monday
|
|
|
29
29
|
values
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Formats a hash as a GraphQL input object (not JSON string)
|
|
33
|
+
# Used for complex input types like ItemsQuery
|
|
34
|
+
#
|
|
35
|
+
# input: { rules: [{ column_id: "status", compare_value: [1] }], operator: :and }
|
|
36
|
+
# output: { rules: [{ column_id: "status", compare_value: [1] }], operator: and }
|
|
37
|
+
def format_graphql_object(hash)
|
|
38
|
+
formatted = hash.map do |key, value|
|
|
39
|
+
"#{key}: #{format_graphql_value(value)}"
|
|
40
|
+
end.join(", ")
|
|
41
|
+
"{#{formatted}}"
|
|
42
|
+
end
|
|
43
|
+
|
|
32
44
|
def status_code_exceptions_mapping(status_code)
|
|
33
45
|
{
|
|
34
46
|
"500" => InternalServerError,
|
|
@@ -43,7 +55,9 @@ module Monday
|
|
|
43
55
|
def response_error_exceptions_mapping(error_code)
|
|
44
56
|
{
|
|
45
57
|
"ComplexityException" => [ComplexityError, 429],
|
|
58
|
+
"COMPLEXITY_BUDGET_EXHAUSTED" => [RateLimitError, 429],
|
|
46
59
|
"UserUnauthorizedException" => [AuthorizationError, 403],
|
|
60
|
+
"USER_UNAUTHORIZED" => [AuthorizationError, 403],
|
|
47
61
|
"ResourceNotFoundException" => [ResourceNotFoundError, 404],
|
|
48
62
|
"InvalidUserIdException" => [InvalidRequestError, 400],
|
|
49
63
|
"InvalidVersionException" => [InvalidRequestError, 400],
|
|
@@ -58,7 +72,8 @@ module Monday
|
|
|
58
72
|
"ItemNameTooLongException" => [InvalidRequestError, 400],
|
|
59
73
|
"ColumnValueException" => [InvalidRequestError, 400],
|
|
60
74
|
"CorrectedValueException" => [InvalidRequestError, 400],
|
|
61
|
-
"InvalidWorkspaceIdException" => [InvalidRequestError, 400]
|
|
75
|
+
"InvalidWorkspaceIdException" => [InvalidRequestError, 400],
|
|
76
|
+
"INTERNAL_SERVER_ERROR" => [InternalServerError, 500]
|
|
62
77
|
}[error_code] || [Error, 400]
|
|
63
78
|
end
|
|
64
79
|
|
|
@@ -76,10 +91,27 @@ module Monday
|
|
|
76
91
|
end.join(" ")
|
|
77
92
|
end
|
|
78
93
|
|
|
94
|
+
def format_graphql_value(value)
|
|
95
|
+
case value
|
|
96
|
+
when Hash
|
|
97
|
+
format_graphql_object(value)
|
|
98
|
+
when Array
|
|
99
|
+
"[#{value.map { |v| format_graphql_value(v) }.join(", ")}]"
|
|
100
|
+
when Integer, TrueClass, FalseClass
|
|
101
|
+
value
|
|
102
|
+
when String
|
|
103
|
+
"\"#{value}\""
|
|
104
|
+
else
|
|
105
|
+
value.to_s
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
79
109
|
def formatted_args_value(value)
|
|
80
110
|
return value if value.is_a?(Symbol)
|
|
111
|
+
return value if value.to_s.include?("$") # GraphQL query variable
|
|
81
112
|
return value.to_json.to_json if value.is_a?(Hash)
|
|
82
113
|
return value if integer?(value)
|
|
114
|
+
return "[#{value.map { |v| formatted_args_value(v) }.join(", ")}]" if value.is_a?(Array)
|
|
83
115
|
|
|
84
116
|
"\"#{value}\""
|
|
85
117
|
end
|
data/lib/monday/version.rb
CHANGED