databasus_ruby 0.0.1 → 0.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.
@@ -3,65 +3,90 @@
3
3
  module DatabasusRuby
4
4
  # Logical backups.
5
5
  #
6
- # backups = client.backups.list(database_id: id, status: %w[COMPLETED FAILED])
7
- # backups.total
8
- # backups.first.fileName
6
+ # page = client.backups.list(database_id: id, status: "COMPLETED", limit: 20)
7
+ # page.extras.total_usage_mb # storage used by all of the database's backups
8
+ # page.first.raw_database_size_mb # size of the database when it was dumped
9
9
  #
10
- # Downloads are a two-step flow: mint a short-lived token, then stream the
11
- # file with it. #download does both.
10
+ # File.open("dump", "wb") { |io| client.backups.download_to(backup.id, io) }
11
+ #
12
+ # A download is a single-use token (POST /download-tokens, valid five
13
+ # minutes) spent on GET /logical-backups/{id}/file. #download_to streams the
14
+ # file; #download reads it into memory and is only fit for small dumps.
12
15
  class Backups < Object
13
16
  include Endpoint
14
17
 
15
18
  STATUSES = %w[IN_PROGRESS COMPLETED FAILED CANCELED].freeze
16
- ENCRYPTIONS = %w[NONE ENCRYPTED].freeze
17
19
  VERIFICATION_STATUSES = %w[NOT_VERIFIED VERIFIED_SUCCESSFUL VERIFICATION_FAILED].freeze
18
20
 
19
- # GET /backups — `status` accepts a single value or an array.
20
- def list(database_id:, limit: nil, offset: nil, status: nil, before_date: nil, pg_wal_backup_type: nil)
21
- response = http_get("backups", {
22
- database_id: database_id,
23
- limit: limit,
24
- offset: offset,
21
+ # GET /logical-backups — newest first. `status` takes one value or an array.
22
+ def list(database_id:, status: nil, created_before: nil, limit: nil, after: nil)
23
+ response = http_get("logical-backups", {
24
+ databaseId: require_id(database_id),
25
25
  status: status,
26
- beforeDate: before_date,
27
- pgWalBackupType: pg_wal_backup_type
26
+ createdBefore: created_before,
27
+ limit: limit,
28
+ after: after
28
29
  })
29
- collection(response, key: "backups")
30
+ collection(response)
30
31
  end
31
32
  alias all list
32
33
 
33
- # POST /backups — queues a backup run for the database.
34
+ # GET /logical-backups/{id}
35
+ def get(id)
36
+ object(http_get("logical-backups/#{escape(id)}"))
37
+ end
38
+ alias find get
39
+
40
+ # POST /logical-backups — queues a backup run for the database.
34
41
  def create(database_id:)
35
- object(http_post("backups", { database_id: database_id }))
42
+ object(http_post("logical-backups", { databaseId: require_id(database_id) }))
36
43
  end
37
44
  alias trigger create
38
45
 
39
- # DELETE /backups/{id}
46
+ # DELETE /logical-backups/{id}
40
47
  def delete(id)
41
- acknowledged(http_delete("backups/#{escape(id)}"))
48
+ acknowledged(http_delete("logical-backups/#{escape(id)}"))
42
49
  end
43
50
 
44
- # POST /backups/{id}/cancel
51
+ # POST /logical-backups/{id}/cancel
45
52
  def cancel(id)
46
- acknowledged(http_post("backups/#{escape(id)}/cancel"))
53
+ object(http_post("logical-backups/#{escape(id)}/cancel"))
47
54
  end
48
55
 
49
- # POST /backups/{id}/download-token -> { token, filename, backupId }
56
+ # POST /download-tokens -> { token, fileName, expiresAt }
50
57
  def download_token(id)
51
- object(http_post("backups/#{escape(id)}/download-token"))
58
+ object(http_post("download-tokens", { backupId: require_id(id) }))
52
59
  end
53
60
 
54
- # GET /backups/{id}/file — returns the raw body; pass a token from
55
- # #download_token.
61
+ # GET /logical-backups/{id}/file — the raw body, in memory.
56
62
  def file(id, token:)
57
- http_get("backups/#{escape(id)}/file", { token: token }).body
63
+ http_get("logical-backups/#{escape(id)}/file", { token: token }).body
64
+ end
65
+
66
+ # Mints a token and streams the file into `io`.
67
+ #
68
+ # @return [DatabasusRuby::Object] the token grant (`file_name`, `expires_at`)
69
+ # plus `bytes`, the number of bytes written
70
+ def download_to(id, io)
71
+ grant = download_token(id)
72
+ bytes = file_to(id, io, token: grant.token)
73
+ Object.new(grant.to_h.except("token").merge("bytes" => bytes))
74
+ end
75
+
76
+ # Streams the file into `io` with a token from #download_token. Mint the
77
+ # token first when the caller must fail before it starts answering (an
78
+ # HTTP response whose headers are already sent cannot turn into an error).
79
+ #
80
+ # @return [Integer] bytes written
81
+ def file_to(id, io, token:)
82
+ client.stream("logical-backups/#{escape(id)}/file", io: io, params: { token: token })
58
83
  end
59
84
 
60
- # Mints a token and fetches the file in one call. Returns
61
- # [filename, contents].
85
+ # Mints a token and fetches the file in one call, in memory. Returns
86
+ # [file_name, contents]. Prefer #download_to for real dumps.
62
87
  def download(id)
63
88
  grant = download_token(id)
64
- [grant.filename, file(id, token: grant.token)]
89
+ [grant.file_name, file(id, token: grant.token)]
65
90
  end
66
91
  end
67
92
  end
@@ -3,39 +3,59 @@
3
3
  module DatabasusRuby
4
4
  # Databases under backup.
5
5
  #
6
- # client.databases.list(workspace_id: id)
7
- # client.databases.create(name: "primary", type: "POSTGRES_LOGICAL",
8
- # workspaceId: id, postgresqlLogical: { ... })
6
+ # client.databases.list(workspace_id: id).each { |database| database.name }
7
+ # client.databases.create(name: "primary", workspaceId: id, notifierIds: [],
8
+ # settings: { type: "POSTGRES_LOGICAL", ... })
9
9
  #
10
- # The create/update bodies are the full databases.Database payload, whose
11
- # engine-specific half lives under one of `postgresqlLogical`,
12
- # `postgresqlPhysical`, `mysql`, `mariadb` or `mongodb`.
10
+ # `settings` is the engine-specific half, discriminated by its `type`
11
+ # (POSTGRES_LOGICAL, POSTGRES_PHYSICAL, MYSQL, MARIADB, MONGODB).
13
12
  class Databases < Object
14
13
  include Endpoint
15
14
 
16
15
  TYPES = %w[POSTGRES_LOGICAL POSTGRES_PHYSICAL MYSQL MARIADB MONGODB].freeze
17
- HEALTH_STATUSES = %w[AVAILABLE UNAVAILABLE].freeze
18
16
 
19
17
  # GET /databases
20
- def list(workspace_id: nil)
21
- collection(http_get("databases", { workspace_id: workspace_id || client.workspace_id! }), key: "databases")
18
+ def list(workspace_id: nil, notifier_id: nil, limit: nil, after: nil)
19
+ response = http_get("databases", {
20
+ workspaceId: workspace_id || client.workspace_id!,
21
+ notifierId: notifier_id,
22
+ limit: limit,
23
+ after: after
24
+ })
25
+ collection(response, as: Database)
22
26
  end
23
27
  alias all list
24
28
 
29
+ # Every database of the workspace, following the cursor across pages.
30
+ #
31
+ # @return [Array<DatabasusRuby::Database>]
32
+ def list_all(workspace_id: nil, limit: 100)
33
+ databases = []
34
+ after = nil
35
+ loop do
36
+ page = list(workspace_id: workspace_id, limit: limit, after: after)
37
+ databases.concat(page.data)
38
+ break if page.last_page?
39
+
40
+ after = page.next_cursor
41
+ end
42
+ databases
43
+ end
44
+
25
45
  # GET /databases/{id}
26
46
  def get(id)
27
- object(http_get("databases/#{escape(id)}"))
47
+ object(http_get("databases/#{escape(id)}"), as: Database)
28
48
  end
29
49
  alias find get
30
50
 
31
- # POST /databases/create
51
+ # POST /databases
32
52
  def create(**attributes)
33
- object(http_post("databases/create", with_default_workspace(attributes)))
53
+ object(http_post("databases", with_default_workspace(attributes)), as: Database)
34
54
  end
35
55
 
36
- # POST /databases/update — the id travels in the body, not the path.
37
- def update(**attributes)
38
- object(http_post("databases/update", attributes))
56
+ # PUT /databases/{id} — the body is the full DatabaseUpdate payload.
57
+ def update(id, **attributes)
58
+ object(http_put("databases/#{escape(id)}", attributes), as: Database)
39
59
  end
40
60
 
41
61
  # DELETE /databases/{id}
@@ -45,47 +65,20 @@ module DatabasusRuby
45
65
 
46
66
  # POST /databases/{id}/copy
47
67
  def copy(id)
48
- object(http_post("databases/#{escape(id)}/copy"))
49
- end
50
-
51
- # POST /databases/{id}/test-connection — true when the database answers.
52
- def test_connection(id)
53
- acknowledged(http_post("databases/#{escape(id)}/test-connection"))
54
- end
55
-
56
- # POST /databases/test-connection-direct — tests a payload that has not
57
- # been saved yet.
58
- def test_connection_direct(**attributes)
59
- acknowledged(http_post("databases/test-connection-direct", attributes))
60
- end
61
-
62
- # POST /databases/create-readonly-user -> { username, password }
63
- def create_readonly_user(**attributes)
64
- object(http_post("databases/create-readonly-user", attributes))
65
- end
66
-
67
- # POST /databases/create-replication-only-user (PostgreSQL physical only)
68
- def create_replication_only_user(**attributes)
69
- object(http_post("databases/create-replication-only-user", attributes))
68
+ object(http_post("databases/#{escape(id)}/copy"), as: Database)
70
69
  end
71
70
 
72
- # POST /databases/is-readonly -> { isReadOnly, privileges }
73
- def readonly(**attributes)
74
- object(http_post("databases/is-readonly", attributes))
71
+ # POST /databases/{id}/test -> { isConnected, reason, error }
72
+ def test(id)
73
+ object(http_post("databases/#{escape(id)}/test"))
75
74
  end
76
75
 
77
- def readonly?(**attributes)
78
- readonly(**attributes).isReadOnly == true
76
+ def connected?(id)
77
+ test(id).is_connected == true
79
78
  end
80
79
 
81
- # The backups of one database, so a caller holding a database does not have
82
- # to reach back for the other endpoint. Both delegate to Backups, which
83
- # owns the requests.
84
- #
85
- # client.databases.backups(database.id, status: "COMPLETED")
86
- # client.databases.backup(database.id) # queue a run
87
- #
88
- # #backups takes the same filters as Backups#list.
80
+ # The backups of one database by id. A database returned by this endpoint
81
+ # answers the same two methods without the id — see Database.
89
82
  def backups(id, **filters)
90
83
  client.backups.list(database_id: require_id(id), **filters)
91
84
  end
@@ -93,15 +86,5 @@ module DatabasusRuby
93
86
  def backup(id)
94
87
  client.backups.create(database_id: require_id(id))
95
88
  end
96
-
97
- # GET /databases/notifier/{id}/databases-count
98
- def notifier_databases_count(notifier_id)
99
- single_value(http_get("databases/notifier/#{escape(notifier_id)}/databases-count"))
100
- end
101
-
102
- # GET /databases/notifier/{id}/is-using
103
- def notifier_using?(notifier_id)
104
- single_value(http_get("databases/notifier/#{escape(notifier_id)}/is-using")) == true
105
- end
106
89
  end
107
90
  end
@@ -3,23 +3,17 @@
3
3
  module DatabasusRuby
4
4
  # Notification channels.
5
5
  #
6
- # client.notifiers.save(name: "ops", notifierType: "SLACK",
7
- # workspaceId: id, slackNotifier: { ... })
8
- #
9
- # POST /notifiers both creates and updates: include `id` in the payload to
10
- # update an existing notifier.
6
+ # client.notifiers.create(workspaceId: id, name: "ops", settings: { type: "SLACK", ... })
7
+ # client.notifiers.update(notifier.id, name: "ops", settings: { type: "SLACK", ... })
11
8
  class Notifiers < Object
12
9
  include Endpoint
13
10
 
14
11
  TYPES = %w[EMAIL TELEGRAM WEBHOOK SLACK DISCORD TEAMS].freeze
15
- NOTIFICATION_TYPES = %w[
16
- ALL BACKUP_SUCCESS BACKUP_FAILED HEALTHCHECK_SUCCESS HEALTHCHECK_FAILED
17
- VERIFICATION_SUCCESS VERIFICATION_FAILED
18
- ].freeze
19
12
 
20
13
  # GET /notifiers
21
- def list(workspace_id: nil)
22
- collection(http_get("notifiers", { workspace_id: workspace_id || client.workspace_id! }), key: "notifiers")
14
+ def list(workspace_id: nil, limit: nil, after: nil)
15
+ collection(http_get("notifiers",
16
+ { workspaceId: workspace_id || client.workspace_id!, limit: limit, after: after }))
23
17
  end
24
18
  alias all list
25
19
 
@@ -29,12 +23,15 @@ module DatabasusRuby
29
23
  end
30
24
  alias find get
31
25
 
32
- # POST /notifiers
33
- def save(**attributes)
26
+ # POST /notifiers — `workspaceId`, `name` and the type-specific `settings`.
27
+ def create(**attributes)
34
28
  object(http_post("notifiers", with_default_workspace(attributes)))
35
29
  end
36
- alias create save
37
- alias update save
30
+
31
+ # PUT /notifiers/{id} — `name` and `settings`.
32
+ def update(id, **attributes)
33
+ object(http_put("notifiers/#{escape(id)}", attributes))
34
+ end
38
35
 
39
36
  # DELETE /notifiers/{id}
40
37
  def delete(id)
@@ -43,17 +40,12 @@ module DatabasusRuby
43
40
 
44
41
  # POST /notifiers/{id}/test — sends a test notification.
45
42
  def test(id)
46
- acknowledged(http_post("notifiers/#{escape(id)}/test"))
47
- end
48
-
49
- # POST /notifiers/direct-test — tests a payload that has not been saved yet.
50
- def direct_test(**attributes)
51
- acknowledged(http_post("notifiers/direct-test", attributes))
43
+ object(http_post("notifiers/#{escape(id)}/test"))
52
44
  end
53
45
 
54
46
  # POST /notifiers/{id}/transfer
55
47
  def transfer(id, target_workspace_id:)
56
- acknowledged(http_post("notifiers/#{escape(id)}/transfer", { targetWorkspaceId: target_workspace_id }))
48
+ object(http_post("notifiers/#{escape(id)}/transfer", { targetWorkspaceId: target_workspace_id }))
57
49
  end
58
50
  end
59
51
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabasusRuby
4
+ # Restores of a logical backup into a target database.
5
+ #
6
+ # target = DatabasusRuby::Restores.postgresql_target(
7
+ # host: "db.internal", port: 5432, username: "app", password: "secret",
8
+ # database: "copy_of_prod"
9
+ # )
10
+ # restore = client.restores.start(backup_id: backup.id, target: target)
11
+ # client.restores.get(restore.id).status # IN_PROGRESS -> COMPLETED | FAILED | CANCELED
12
+ #
13
+ # The target's secrets are used only by the running restore; Databasus does
14
+ # not store them.
15
+ class Restores < Object
16
+ include Endpoint
17
+
18
+ STATUSES = %w[IN_PROGRESS COMPLETED FAILED CANCELED].freeze
19
+ SSL_MODES = %w[disable require verify-ca verify-full].freeze
20
+
21
+ # Builds a PostgreSQL logical restore target.
22
+ #
23
+ # Ownership and privileges are off by default: restoring them needs the
24
+ # source cluster's roles on the target, and their absence is the usual
25
+ # cause of a restore that loads every row and still fails.
26
+ #
27
+ # @param cpu_count [Integer] parallel pg_restore jobs (Databasus caps it at 8)
28
+ # @param exclude_extensions [Boolean] skip CREATE EXTENSION entries
29
+ # @return [Hash] the `target` body of #start
30
+ def self.postgresql_target(host:, port:, username:, password:, database:, ssl_mode: "disable", cpu_count: 1,
31
+ exclude_extensions: false, restore_ownership: false, restore_privileges: false,
32
+ ssh_tunnel: nil)
33
+ {
34
+ type: "POSTGRES_LOGICAL",
35
+ host: host,
36
+ port: port,
37
+ username: username,
38
+ password: password,
39
+ database: database,
40
+ sslMode: ssl_mode,
41
+ sshTunnel: ssh_tunnel || { isEnabled: false },
42
+ cpuCount: cpu_count,
43
+ isExcludeExtensions: exclude_extensions,
44
+ isRestoreOwnership: restore_ownership,
45
+ isRestorePrivileges: restore_privileges
46
+ }
47
+ end
48
+
49
+ # GET /restores — the restores of one backup, newest first.
50
+ def list(backup_id:, limit: nil, after: nil)
51
+ collection(http_get("restores", { backupId: require_id(backup_id), limit: limit, after: after }))
52
+ end
53
+ alias all list
54
+
55
+ # POST /restores — answers at once with the restore IN_PROGRESS.
56
+ def start(backup_id:, target:)
57
+ object(http_post("restores", { backupId: require_id(backup_id), target: target }))
58
+ end
59
+ alias create start
60
+
61
+ # GET /restores/{id}
62
+ def get(id)
63
+ object(http_get("restores/#{escape(id)}"))
64
+ end
65
+ alias find get
66
+
67
+ # POST /restores/{id}/cancel
68
+ def cancel(id)
69
+ object(http_post("restores/#{escape(id)}/cancel"))
70
+ end
71
+ end
72
+ end
@@ -3,19 +3,17 @@
3
3
  module DatabasusRuby
4
4
  # Backup destinations.
5
5
  #
6
- # client.storages.save(name: "offsite", type: "S3",
7
- # workspaceId: id, s3Storage: { ... })
8
- #
9
- # POST /storages both creates and updates: include `id` in the payload to
10
- # update an existing storage.
6
+ # client.storages.create(workspaceId: id, name: "offsite", settings: { type: "S3", ... })
7
+ # client.storages.backup_usage(storage.id)
11
8
  class Storages < Object
12
9
  include Endpoint
13
10
 
14
11
  TYPES = %w[LOCAL S3 GOOGLE_DRIVE NAS AZURE_BLOB FTP SFTP RCLONE].freeze
15
12
 
16
13
  # GET /storages
17
- def list(workspace_id: nil)
18
- collection(http_get("storages", { workspace_id: workspace_id || client.workspace_id! }), key: "storages")
14
+ def list(workspace_id: nil, limit: nil, after: nil)
15
+ collection(http_get("storages",
16
+ { workspaceId: workspace_id || client.workspace_id!, limit: limit, after: after }))
19
17
  end
20
18
  alias all list
21
19
 
@@ -25,31 +23,34 @@ module DatabasusRuby
25
23
  end
26
24
  alias find get
27
25
 
28
- # POST /storages
29
- def save(**attributes)
26
+ # POST /storages — `workspaceId`, `name` and the type-specific `settings`.
27
+ def create(**attributes)
30
28
  object(http_post("storages", with_default_workspace(attributes)))
31
29
  end
32
- alias create save
33
- alias update save
30
+
31
+ # PUT /storages/{id} — `name` and `settings`.
32
+ def update(id, **attributes)
33
+ object(http_put("storages/#{escape(id)}", attributes))
34
+ end
34
35
 
35
36
  # DELETE /storages/{id}
36
37
  def delete(id)
37
38
  acknowledged(http_delete("storages/#{escape(id)}"))
38
39
  end
39
40
 
40
- # POST /storages/{id}/test
41
- def test(id)
42
- acknowledged(http_post("storages/#{escape(id)}/test"))
41
+ # GET /storages/{id}/backup-usage — the catalog-recorded usage of the storage.
42
+ def backup_usage(id)
43
+ object(http_get("storages/#{escape(id)}/backup-usage"))
43
44
  end
44
45
 
45
- # POST /storages/direct-test — tests a payload that has not been saved yet.
46
- def direct_test(**attributes)
47
- acknowledged(http_post("storages/direct-test", attributes))
46
+ # POST /storages/{id}/test
47
+ def test(id)
48
+ object(http_post("storages/#{escape(id)}/test"))
48
49
  end
49
50
 
50
51
  # POST /storages/{id}/transfer
51
52
  def transfer(id, target_workspace_id:)
52
- acknowledged(http_post("storages/#{escape(id)}/transfer", { targetWorkspaceId: target_workspace_id }))
53
+ object(http_post("storages/#{escape(id)}/transfer", { targetWorkspaceId: target_workspace_id }))
53
54
  end
54
55
  end
55
56
  end
@@ -1,79 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabasusRuby
4
- # The signed-in user, instance-wide user administration and instance
5
- # settings.
4
+ # Instance-wide user administration and instance settings.
6
5
  #
7
- # client.users.me.email
8
6
  # client.users.list(query: "ops", limit: 25).each { |user| ... }
9
7
  #
10
- # The #list / #get / #activate / #deactivate / #change_role and settings
11
- # methods are ADMIN-only and answer 403 for a MEMBER token.
8
+ # Everything here except #update and #change_password on your own user is
9
+ # ADMIN-only and answers 403 for a MEMBER token.
12
10
  class Users < Object
13
11
  include Endpoint
14
12
 
15
13
  ROLES = %w[ADMIN MEMBER].freeze
16
14
 
17
- # GET /users/me
18
- def me
19
- object(http_get("users/me"))
20
- end
21
-
22
- # PUT /users/me
23
- def update_me(name: nil, email: nil)
24
- object(http_put("users/me", { name: name, email: email }))
25
- end
26
-
27
- # PUT /users/change-password
28
- def change_password(new_password:)
29
- object(http_put("users/change-password", { newPassword: new_password }))
30
- end
31
-
32
- # POST /users/invite
33
- def invite(email:, intended_workspace_id: nil, intended_workspace_role: nil)
34
- object(http_post("users/invite", {
35
- email: email,
36
- intendedWorkspaceId: intended_workspace_id,
37
- intendedWorkspaceRole: intended_workspace_role
38
- }))
39
- end
40
-
41
- # GET /users (ADMIN)
42
- def list(limit: nil, offset: nil, before_date: nil, query: nil)
43
- response = http_get("users", { limit: limit, offset: offset, beforeDate: before_date, query: query })
44
- collection(response, key: "users")
15
+ # GET /users
16
+ def list(query: nil, limit: nil, after: nil)
17
+ collection(http_get("users", { query: query, limit: limit, after: after }))
45
18
  end
46
19
  alias all list
47
20
 
48
- # GET /users/{id} (ADMIN)
21
+ # GET /users/{id}
49
22
  def get(id)
50
23
  object(http_get("users/#{escape(id)}"))
51
24
  end
52
25
  alias find get
53
26
 
54
- # POST /users/{id}/activate (ADMIN)
27
+ # PUT /users/{id}
28
+ def update(id, name:, email:)
29
+ object(http_put("users/#{escape(id)}", { name: name, email: email }))
30
+ end
31
+
32
+ # PUT /users/{id}/password
33
+ def change_password(id, new_password:)
34
+ acknowledged(http_put("users/#{escape(id)}/password", { newPassword: new_password }))
35
+ end
36
+
37
+ # POST /users/{id}/activate
55
38
  def activate(id)
56
39
  acknowledged(http_post("users/#{escape(id)}/activate"))
57
40
  end
58
41
 
59
- # POST /users/{id}/deactivate (ADMIN)
42
+ # POST /users/{id}/deactivate
60
43
  def deactivate(id)
61
44
  acknowledged(http_post("users/#{escape(id)}/deactivate"))
62
45
  end
63
46
 
64
- # PUT /users/{id}/role (ADMIN) — role is one of ROLES.
47
+ # PUT /users/{id}/role — role is one of ROLES.
65
48
  def change_role(id, role:)
66
49
  acknowledged(http_put("users/#{escape(id)}/role", { role: role }))
67
50
  end
68
51
 
69
- # GET /users/settings (ADMIN) — instance-wide registration settings.
52
+ # GET /instance-settings
70
53
  def settings
71
- object(http_get("users/settings"))
54
+ object(http_get("instance-settings"))
72
55
  end
73
56
 
74
- # PUT /users/settings (ADMIN)
57
+ # PUT /instance-settings
75
58
  def update_settings(**attributes)
76
- object(http_put("users/settings", attributes))
59
+ object(http_put("instance-settings", attributes))
77
60
  end
78
61
  end
79
62
  end