zero_ruby 0.1.0.alpha1 → 0.1.0.alpha2
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/README.md +43 -46
- data/lib/zero_ruby/errors.rb +15 -0
- data/lib/zero_ruby/mutation.rb +5 -3
- data/lib/zero_ruby/push_processor.rb +28 -5
- data/lib/zero_ruby/schema.rb +29 -5
- data/lib/zero_ruby/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d8baa0973deb010422a2e5d6679af4580b9c37666b2649f383c0bf6525a77c9
|
|
4
|
+
data.tar.gz: 27e0a5593d3344c6260d96838e94ce94a8af36225b861318091ed57532a92dda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83b6256cf6db475ad7f6c5de6d958f6ed72627dd2d2ae5211183d2ed2fe2c346338fbda6cb52ca79aa7aaf7c343e342e017b4015004cba3c97d351da80b679de
|
|
7
|
+
data.tar.gz: 694ac953e5df97e3a747054846612923928d24eb3a184d5f53c999895419f2ba3885fb128034c8bd54b934c976e832d8cf58fb5a7469c8ace7e75f91185266c4
|
data/README.md
CHANGED
|
@@ -22,41 +22,8 @@ gem 'zero_ruby'
|
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
|
-
### 1. Base classes (optional)
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
```ruby
|
|
30
|
-
# app/zero/types/base_input_object.rb
|
|
31
|
-
module Types
|
|
32
|
-
class BaseInputObject < ZeroRuby::InputObject
|
|
33
|
-
# Add shared behavior across all input objects here
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# app/zero/mutations/application_mutation.rb
|
|
38
|
-
class ApplicationMutation < ZeroRuby::Mutation
|
|
39
|
-
def current_user
|
|
40
|
-
ctx[:current_user]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 2. Define custom input types (optional)
|
|
46
|
-
|
|
47
|
-
```ruby
|
|
48
|
-
# app/zero/types/post_input.rb
|
|
49
|
-
module Types
|
|
50
|
-
class PostInput < Types::BaseInputObject
|
|
51
|
-
argument :title, String, required: true,
|
|
52
|
-
validates: { length: { minimum: 1, maximum: 200 } }
|
|
53
|
-
argument :body, String, required: false
|
|
54
|
-
argument :published, Boolean, required: false, default: false
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 3. Define mutations
|
|
26
|
+
### 1. Define mutations
|
|
60
27
|
|
|
61
28
|
```ruby
|
|
62
29
|
# app/zero/mutations/post_update.rb
|
|
@@ -73,7 +40,7 @@ module Mutations
|
|
|
73
40
|
end
|
|
74
41
|
```
|
|
75
42
|
|
|
76
|
-
###
|
|
43
|
+
### 2. Register mutations in schema
|
|
77
44
|
|
|
78
45
|
```ruby
|
|
79
46
|
# app/zero/app_schema.rb
|
|
@@ -85,7 +52,7 @@ class ZeroSchema < ZeroRuby::Schema
|
|
|
85
52
|
end
|
|
86
53
|
```
|
|
87
54
|
|
|
88
|
-
###
|
|
55
|
+
### 3. Add zero_controller and route
|
|
89
56
|
|
|
90
57
|
```ruby
|
|
91
58
|
# app/controllers/zero_controller.rb
|
|
@@ -112,23 +79,55 @@ class ZeroController < ApplicationController
|
|
|
112
79
|
end
|
|
113
80
|
rescue JSON::ParserError => e
|
|
114
81
|
render json: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
82
|
+
kind: "PushFailed",
|
|
83
|
+
origin: "server",
|
|
84
|
+
reason: "parse",
|
|
85
|
+
message: "Invalid JSON: #{e.message}",
|
|
86
|
+
mutationIDs: []
|
|
120
87
|
}, status: :bad_request
|
|
121
88
|
end
|
|
122
89
|
end
|
|
123
90
|
```
|
|
124
91
|
|
|
125
|
-
### 6. Add route
|
|
126
|
-
|
|
127
92
|
```ruby
|
|
128
93
|
# config/routes.rb
|
|
129
94
|
match '/zero/push', to: 'zero#push', via: [:get, :post]
|
|
130
95
|
```
|
|
131
96
|
|
|
97
|
+
## Base classes (optional)
|
|
98
|
+
|
|
99
|
+
Create base classes to share behavior across mutations and input types:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
# app/zero/types/base_input_object.rb
|
|
103
|
+
module Types
|
|
104
|
+
class BaseInputObject < ZeroRuby::InputObject
|
|
105
|
+
# Add shared behavior across all input objects here
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# app/zero/mutations/application_mutation.rb
|
|
110
|
+
class ApplicationMutation < ZeroRuby::Mutation
|
|
111
|
+
def current_user
|
|
112
|
+
ctx[:current_user]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Define custom input types (optional)
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
# app/zero/types/post_input.rb
|
|
121
|
+
module Types
|
|
122
|
+
class PostInput < Types::BaseInputObject
|
|
123
|
+
argument :title, String, required: true,
|
|
124
|
+
validates: { length: { minimum: 1, maximum: 200 } }
|
|
125
|
+
argument :body, String, required: false
|
|
126
|
+
argument :published, Boolean, required: false, default: false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
132
131
|
## Configuration
|
|
133
132
|
|
|
134
133
|
Create an initializer to customize settings (all options have sensible defaults):
|
|
@@ -179,7 +178,7 @@ export const mutators = defineMutators({
|
|
|
179
178
|
update: defineMutator(postsUpdateArgsSchema, async ({ tx, args }) => {
|
|
180
179
|
await tx.mutate.posts.update({
|
|
181
180
|
id: args.id,
|
|
182
|
-
|
|
181
|
+
title: args.postInput.title,
|
|
183
182
|
updatedAt: Date.now(),
|
|
184
183
|
})
|
|
185
184
|
}),
|
|
@@ -191,8 +190,6 @@ export type Mutators = typeof mutators
|
|
|
191
190
|
|
|
192
191
|
## Validation
|
|
193
192
|
|
|
194
|
-
Built-in validators:
|
|
195
|
-
|
|
196
193
|
```ruby
|
|
197
194
|
argument :name, String, required: true,
|
|
198
195
|
validates: {
|
data/lib/zero_ruby/errors.rb
CHANGED
|
@@ -87,4 +87,19 @@ module ZeroRuby
|
|
|
87
87
|
"ooo"
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
|
+
|
|
91
|
+
# Raised for database transaction errors.
|
|
92
|
+
# Triggers top-level PushFailed with reason: "database"
|
|
93
|
+
class DatabaseError < Error
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Raised when push data is malformed or missing required fields.
|
|
97
|
+
# Triggers top-level PushFailed with reason: "parse"
|
|
98
|
+
class ParseError < Error
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Raised for unexpected internal server errors.
|
|
102
|
+
# Wrapped as app error per Zero protocol (no "internal" error type at mutation level)
|
|
103
|
+
class InternalError < Error
|
|
104
|
+
end
|
|
90
105
|
end
|
data/lib/zero_ruby/mutation.rb
CHANGED
|
@@ -120,11 +120,13 @@ module ZeroRuby
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
# Execute the mutation
|
|
123
|
-
# @return [Hash] Empty hash on success
|
|
123
|
+
# @return [Hash] Empty hash on success, or {data: ...} if execute returns a Hash
|
|
124
124
|
# @raise [ZeroRuby::Error] On failure (formatted at boundary)
|
|
125
125
|
def call
|
|
126
|
-
execute(**@args)
|
|
127
|
-
{}
|
|
126
|
+
data = execute(**@args)
|
|
127
|
+
result = {}
|
|
128
|
+
result[:data] = data if data.is_a?(Hash) && !data.empty?
|
|
129
|
+
result
|
|
128
130
|
end
|
|
129
131
|
|
|
130
132
|
private
|
|
@@ -36,12 +36,29 @@ module ZeroRuby
|
|
|
36
36
|
mutations = push_data["mutations"] || []
|
|
37
37
|
results = []
|
|
38
38
|
|
|
39
|
-
mutations.
|
|
39
|
+
mutations.each_with_index do |mutation_data, index|
|
|
40
40
|
result = process_mutation_with_lmid(mutation_data, client_group_id, context)
|
|
41
41
|
results << result
|
|
42
|
-
|
|
43
|
-
#
|
|
44
|
-
|
|
42
|
+
rescue OutOfOrderMutationError => e
|
|
43
|
+
# Return top-level PushFailedBody with all unprocessed mutation IDs
|
|
44
|
+
unprocessed_ids = mutations[index..].map { |m| {id: m["id"], clientID: m["clientID"]} }
|
|
45
|
+
return {
|
|
46
|
+
kind: "PushFailed",
|
|
47
|
+
origin: "server",
|
|
48
|
+
reason: "oooMutation",
|
|
49
|
+
message: e.message,
|
|
50
|
+
mutationIDs: unprocessed_ids
|
|
51
|
+
}
|
|
52
|
+
rescue DatabaseError => e
|
|
53
|
+
# Database errors trigger top-level PushFailed per Zero protocol
|
|
54
|
+
unprocessed_ids = mutations[index..].map { |m| {id: m["id"], clientID: m["clientID"]} }
|
|
55
|
+
return {
|
|
56
|
+
kind: "PushFailed",
|
|
57
|
+
origin: "server",
|
|
58
|
+
reason: "database",
|
|
59
|
+
message: e.message,
|
|
60
|
+
mutationIDs: unprocessed_ids
|
|
61
|
+
}
|
|
45
62
|
end
|
|
46
63
|
|
|
47
64
|
{mutations: results}
|
|
@@ -69,6 +86,8 @@ module ZeroRuby
|
|
|
69
86
|
result = execute_with_retry(mutation_data, context)
|
|
70
87
|
{id: mutation_id_obj, result: result}
|
|
71
88
|
end
|
|
89
|
+
rescue OutOfOrderMutationError, DatabaseError
|
|
90
|
+
raise
|
|
72
91
|
rescue ZeroRuby::Error => e
|
|
73
92
|
{id: mutation_id_obj, result: format_error_response(e)}
|
|
74
93
|
end
|
|
@@ -111,12 +130,16 @@ module ZeroRuby
|
|
|
111
130
|
|
|
112
131
|
# Format an error into Zero protocol response
|
|
113
132
|
def format_error_response(error)
|
|
114
|
-
result = {error: error.error_type
|
|
133
|
+
result = {error: error.error_type}
|
|
115
134
|
|
|
116
135
|
case error
|
|
117
136
|
when ValidationError
|
|
137
|
+
result[:message] = error.message
|
|
118
138
|
result[:details] = {messages: error.errors}
|
|
139
|
+
when MutationAlreadyProcessedError
|
|
140
|
+
result[:details] = error.message
|
|
119
141
|
else
|
|
142
|
+
result[:message] = error.message
|
|
120
143
|
result[:details] = error.details if error.details
|
|
121
144
|
end
|
|
122
145
|
|
data/lib/zero_ruby/schema.rb
CHANGED
|
@@ -46,16 +46,21 @@ module ZeroRuby
|
|
|
46
46
|
# result = ZeroSchema.execute(body, context: {current_user: user})
|
|
47
47
|
# render json: result
|
|
48
48
|
def execute(push_data, context:, lmid_store: nil)
|
|
49
|
+
validate_push_structure!(push_data)
|
|
50
|
+
|
|
49
51
|
push_version = push_data["pushVersion"]
|
|
50
52
|
supported_version = ZeroRuby.configuration.supported_push_version
|
|
51
53
|
|
|
52
54
|
unless push_version == supported_version
|
|
55
|
+
mutations = push_data["mutations"] || []
|
|
56
|
+
mutation_ids = mutations.map { |m| {id: m["id"], clientID: m["clientID"]} }
|
|
57
|
+
|
|
53
58
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
kind: "PushFailed",
|
|
60
|
+
origin: "server",
|
|
61
|
+
reason: "unsupportedPushVersion",
|
|
62
|
+
message: "Unsupported push version: #{push_version}. Expected: #{supported_version}",
|
|
63
|
+
mutationIDs: mutation_ids
|
|
59
64
|
}
|
|
60
65
|
end
|
|
61
66
|
|
|
@@ -66,6 +71,14 @@ module ZeroRuby
|
|
|
66
71
|
max_retries: ZeroRuby.configuration.max_retry_attempts
|
|
67
72
|
)
|
|
68
73
|
processor.process(push_data, context)
|
|
74
|
+
rescue ParseError => e
|
|
75
|
+
{
|
|
76
|
+
kind: "PushFailed",
|
|
77
|
+
origin: "server",
|
|
78
|
+
reason: "parse",
|
|
79
|
+
message: e.message,
|
|
80
|
+
mutationIDs: []
|
|
81
|
+
}
|
|
69
82
|
end
|
|
70
83
|
|
|
71
84
|
# Execute a single mutation.
|
|
@@ -90,6 +103,17 @@ module ZeroRuby
|
|
|
90
103
|
|
|
91
104
|
private
|
|
92
105
|
|
|
106
|
+
# Validate push data structure
|
|
107
|
+
# @raise [ParseError] If push data is malformed
|
|
108
|
+
def validate_push_structure!(push_data)
|
|
109
|
+
unless push_data.is_a?(Hash)
|
|
110
|
+
raise ParseError.new("Push data must be a hash")
|
|
111
|
+
end
|
|
112
|
+
unless push_data.key?("clientGroupID")
|
|
113
|
+
raise ParseError.new("Missing required field: clientGroupID")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
93
117
|
# Normalize mutation name (convert | to . for Zero's format)
|
|
94
118
|
def normalize_mutation_name(name)
|
|
95
119
|
return "" if name.nil?
|
data/lib/zero_ruby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zero_ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.0.
|
|
4
|
+
version: 0.1.0.alpha2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Serban
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-12-
|
|
10
|
+
date: 2025-12-21 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: bundler
|
|
@@ -79,7 +79,7 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '1.51'
|
|
82
|
-
description: Handle Zero mutations
|
|
82
|
+
description: Handle Zero mutations
|
|
83
83
|
executables: []
|
|
84
84
|
extensions: []
|
|
85
85
|
extra_rdoc_files: []
|
|
@@ -141,5 +141,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
141
141
|
requirements: []
|
|
142
142
|
rubygems_version: 3.6.2
|
|
143
143
|
specification_version: 4
|
|
144
|
-
summary: Ruby gem for handling Zero mutations
|
|
144
|
+
summary: Ruby gem for handling Zero mutations
|
|
145
145
|
test_files: []
|