graphiti-activegraph 1.3.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64aa5789f68595122f2d8113a497fa4f822f262bed07cac85da0474cb1ed7d51
4
- data.tar.gz: f95a5b41c60d54f43e245c0b0279577b69325fb777e41c526f376e7fed40f1a3
3
+ metadata.gz: 99c8c00fe58ed3146c7feae681bac89ec82989b09f88dfa97aa6a54ef7e9930b
4
+ data.tar.gz: c21e632e4fa1a4f6b24315c01c76d89f91158ae9996a73cdf6da83e97805e2ce
5
5
  SHA512:
6
- metadata.gz: c2d65e132c567d4fcb814389e199a3b4c4c13d53f83f9604d57ddc98b7dd05573b8527eecc67e78294de9cc9e178ef7ab287ad571626f5854efcc8db30de32a2
7
- data.tar.gz: 6fdd6b5a5ec1be222a0905bf1877761c1b81342e0d48f19d662e5ae014c3819938e5a72aaf159e65c86b7a58d173fd2e3d65f66071734e7cc90d9e4d83ea08ed
6
+ metadata.gz: 8942c7a92552d7554cdc78e5183b970bab9c6d9cee23e5d21e23244c84c45491bbd5a3085671cde7919ca3f7e531cd2e7c6388f0ad367cecc430b77597c0db9c
7
+ data.tar.gz: e31a9631f62d743899851ea815c2158c801f550a5d7ec86eafab260045441fbbd84813f0c5511f9cadc3ad2a7fe8b750d315effc65cbc46866235d31f136731a
@@ -10,11 +10,14 @@ on:
10
10
  workflow_dispatch:
11
11
 
12
12
  permissions:
13
+ actions: write
13
14
  contents: read
14
15
 
15
16
  jobs:
16
17
  test:
17
18
  runs-on: ubuntu-latest
19
+ permissions:
20
+ id-token: write
18
21
  strategy:
19
22
  fail-fast: false
20
23
  matrix:
@@ -49,3 +52,8 @@ jobs:
49
52
 
50
53
  - name: Run tests
51
54
  run: bundle exec rspec
55
+
56
+ - uses: qltysh/qlty-action/coverage@v1
57
+ with:
58
+ oidc: true
59
+ files: coverage/coverage.json
data/CHANGELOG.md CHANGED
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.1] - 2025-11-5
11
+ ### Added
12
+ - **Deserializer: relationship id(s) helpers**: introduced `relationship_id` and `relationship_ids` methods in `Deserializer` to fetch relationship id(s) by association name from request payload. (PR #49)
13
+
10
14
  ## [1.3.0] - 2025-10-13
11
15
  ### Added
12
16
  - **Extra fields preloading in main query**: Support preloading associations for `extra_fields` for main and sideloaded records. Adds `preload` option to `extra_attribute` in resource classes. Example: `extra_attribute :full_post_title, :string, preload: :author`. (PR #45)
data/README.md CHANGED
@@ -45,6 +45,11 @@ graphiti-activegraph allows assigning and unassigning relationships via sidepost
45
45
  Graphiti stores context using `Thread.current[]`, which does not persist across different fibers within the same thread. In graphiti-activegraph, when running on MRI (non-JRuby environments), the gem uses `thread_variable_get` and `thread_variable_set`. Ensuring the context remains consistent across different fibers in the same thread.
46
46
 
47
47
  ### New Features in graphiti-activegraph
48
+ For detailed API documentation and helper method behavior, see the [docs](./docs) directory.
49
+ - [Deserializer](./docs/deserializer.md)
50
+ - [Adapter](./docs/adapter.md) *(planned)*
51
+ - [Resource](./docs/resource.md) *(planned)*
52
+
48
53
  #### Rendering Preloaded Objects Without Extra Queries
49
54
  graphiti-activegraph introduces two new methods on the Graphiti resource class:
50
55
  `with_preloaded_obj(record, params)` – Renders a single preloaded ActiveGraph object without querying the database.
@@ -107,6 +112,8 @@ Currently, this feature does not support preloading for deep sideloads such as `
107
112
 
108
113
  Check [spec/support/factory_bot_setup.rb](https://github.com/mrhardikjoshi/graphiti-activegraph/blob/master/spec/support/factory_bot_setup.rb) and [spec/active_graph/sideload_resolve_spec.rb](https://github.com/mrhardikjoshi/graphiti-activegraph/blob/master/spec/active_graph/sideload_resolve_spec.rb) for examples of usage.
109
114
 
115
+ ## [CHANGELOG](CHANGELOG.md)
116
+
110
117
  ## Contributing
111
118
  Bug reports and pull requests are welcome on GitHub at https://github.com/mrhardikjoshi/graphiti-activegraph. This project is intended to be a safe, welcoming space for collaboration.
112
119
 
@@ -0,0 +1,40 @@
1
+ # Deserializer
2
+
3
+ ### Overview
4
+ Handles incoming JSON:API payloads for Graphiti. Deserializes the request body and provides helper methods to extract and reconcile data, relationships and meta.
5
+
6
+ ### Methods
7
+
8
+ #### `relationship_id(name)`
9
+ Returns the single related id for the given association name.
10
+ Used when the relationship is `has_one`.
11
+
12
+ #### `relationship_ids(name)`
13
+ Returns an array of related ids for the given association name.
14
+ Used when the relationship is `has_many`.
15
+
16
+ #### `add_path_id_to_relationships!(params)`
17
+ Ensures that relationship ids passed in the request path are reflected in `params[:data][:relationships]` when missing in the request body.
18
+ If the request body already contains an id for the same relationship, it compares the path and body ids using `detect_conflict` to prevent mismatched identifiers.
19
+ The method is idempotent per request—subsequent calls are ignored once processed.
20
+
21
+ **Behavior summary:**
22
+ - Adds missing relationship ids to the request body.
23
+ - Detects and surfaces id mismatches between path and body.
24
+ - Updates the internal `relationships` hash for consistency.
25
+ - Marks the deserializer as updated to avoid repeated execution.
26
+
27
+ **Parameters:**
28
+ - `params` — Hash (usually the request parameters).
29
+
30
+ **Returns:**
31
+ - Modified `params` Hash with relationship ids added where necessary.
32
+
33
+ **Example:**
34
+ ```ruby
35
+ # Given a path like /authors/42/books
36
+ path_map = { author: "42" }
37
+ params = { data: { type: "books", attributes: { title: "Graph Databases" } } }
38
+
39
+ deserializer.add_path_id_to_relationships!(params)
40
+ # => params[:data][:relationships][:author][:data][:id] == "42"
@@ -26,6 +26,8 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'standard'
27
27
  spec.add_development_dependency 'pry'
28
28
  spec.add_development_dependency 'ffaker'
29
+ spec.add_development_dependency 'simplecov'
30
+ spec.add_development_dependency 'simplecov_json_formatter'
29
31
  spec.add_development_dependency 'factory_bot_rails'
30
32
  spec.add_development_dependency 'rake', '>= 10.0'
31
33
  spec.add_development_dependency 'rspec', '>= 3.9.0'
@@ -0,0 +1,15 @@
1
+ module Graphiti::ActiveGraph::Concerns
2
+ module Relationships
3
+ def relationship?(name)
4
+ relationships[name.to_sym].present?
5
+ end
6
+
7
+ def relationship_id(name)
8
+ relationships[name]&.dig(:attributes, :id)
9
+ end
10
+
11
+ def relationship_ids(name)
12
+ Array.wrap(relationships[name]).pluck(:attributes).pluck(:id)
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,7 @@
1
1
  module Graphiti::ActiveGraph
2
2
  class Deserializer < Graphiti::Deserializer
3
3
  include Concerns::PathRelationships
4
+ include Concerns::Relationships
4
5
 
5
6
  class Conflict < StandardError
6
7
  attr_reader :key, :path_value, :body_value
@@ -55,10 +56,6 @@ module Graphiti::ActiveGraph
55
56
  end
56
57
  end
57
58
 
58
- def relationship?(name)
59
- relationships[name.to_sym].present?
60
- end
61
-
62
59
  # change empty relationship as `disassociate` hash so they will be removed
63
60
  def process_nil_relationship(name)
64
61
  attributes = {}
@@ -0,0 +1,38 @@
1
+ module Graphiti::ActiveGraph::Extensions::QueryDsl
2
+ module Performer
3
+ attr_accessor :with_vars, :skip_arrow_cypher_rels
4
+
5
+ def apply_query_dsl
6
+ query_param = resource.context&.params&.[](:query)
7
+ query_param.present? ? apply_query_param(query_param) : scope
8
+ end
9
+
10
+ def apply_query_param(query_param)
11
+ @scope = query_generator.new(query_param, **query_generator_config).tap do |qg|
12
+ qg.generate_functions_optional_match
13
+ qg.generate_with_clause_partition_query
14
+ qg.generate_match_query
15
+ qg.generate_with_clause_query
16
+ end.query
17
+ end
18
+
19
+ private
20
+
21
+ def query_generator
22
+ Graphiti::ActiveGraph::Extensions::QueryDsl::QueryGenerator
23
+ end
24
+
25
+ def query_generator_config
26
+ {
27
+ query: scope,
28
+ with_vars_to_carry:,
29
+ skip_arrow_cypher_rels: skip_arrow_cypher_rels || [],
30
+ resource: resource.class
31
+ }
32
+ end
33
+
34
+ def with_vars_to_carry
35
+ (query ? with_vars_for_sort : []).push(*with_vars).push(*Graphiti.context[:with_vars]).compact
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ module Graphiti::ActiveGraph::Extensions::QueryDsl
2
+ class QueryGenerator
3
+ def initialize(query_param, query:, **config)
4
+ @query_param = query_param
5
+ @query = query
6
+ end
7
+
8
+ def generate_functions_optional_match
9
+ end
10
+
11
+ def generate_with_clause_partition_query
12
+ end
13
+
14
+ def generate_match_query
15
+ end
16
+
17
+ def generate_with_clause_query
18
+ end
19
+ end
20
+ end
@@ -2,6 +2,15 @@ module Graphiti::ActiveGraph
2
2
  module Scoping
3
3
  module Filter
4
4
  include Filterable
5
+ include Internal::SortingAliases
6
+ include Extensions::QueryDsl::Performer
7
+
8
+ attr_reader :scope
9
+
10
+ def apply
11
+ super
12
+ apply_query_dsl
13
+ end
5
14
 
6
15
  def each_filter
7
16
  filter_param.each_pair do |param_name, param_value|
@@ -1,5 +1,5 @@
1
1
  module Graphiti
2
2
  module ActiveGraph
3
- VERSION = '1.3.0'
3
+ VERSION = '1.3.1'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti-activegraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hardik Joshi
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-10-13 00:00:00.000000000 Z
10
+ date: 2025-11-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: graphiti
@@ -127,6 +127,34 @@ dependencies:
127
127
  - - ">="
128
128
  - !ruby/object:Gem::Version
129
129
  version: '0'
130
+ - !ruby/object:Gem::Dependency
131
+ name: simplecov
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ - !ruby/object:Gem::Dependency
145
+ name: simplecov_json_formatter
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
130
158
  - !ruby/object:Gem::Dependency
131
159
  name: factory_bot_rails
132
160
  requirement: !ruby/object:Gem::Requirement
@@ -183,6 +211,7 @@ files:
183
211
  - Gemfile
184
212
  - LICENSE.txt
185
213
  - README.md
214
+ - docs/deserializer.md
186
215
  - graphiti-activegraph.gemspec
187
216
  - lib/graphiti-activegraph.rb
188
217
  - lib/graphiti/active_graph/adapters/active_graph.rb
@@ -192,9 +221,12 @@ files:
192
221
  - lib/graphiti/active_graph/adapters/active_graph/polymorphic_belongs_to.rb
193
222
  - lib/graphiti/active_graph/adapters/active_graph/sideload.rb
194
223
  - lib/graphiti/active_graph/concerns/path_relationships.rb
224
+ - lib/graphiti/active_graph/concerns/relationships.rb
195
225
  - lib/graphiti/active_graph/deserializer.rb
196
226
  - lib/graphiti/active_graph/extensions/context.rb
197
227
  - lib/graphiti/active_graph/extensions/grouping/params.rb
228
+ - lib/graphiti/active_graph/extensions/query_dsl/performer.rb
229
+ - lib/graphiti/active_graph/extensions/query_dsl/query_generator.rb
198
230
  - lib/graphiti/active_graph/extensions/query_params.rb
199
231
  - lib/graphiti/active_graph/extensions/resources/authorizationable.rb
200
232
  - lib/graphiti/active_graph/extensions/resources/payload_combinable.rb