rails_audit_log-graphql 0.5.0 → 0.6.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: f64cce54638f8999c6489e0881a1fedfcb447b0b5b8f52270a0d8247719c8f19
4
- data.tar.gz: 4b1225952a5a6adce3a5a03b804360765b0c70b9ad9a9688db96ba34139430c5
3
+ metadata.gz: a1df5ae47e9bae5cc635eb04372e8a84f3c484350f50284d98ab4602d9f2175c
4
+ data.tar.gz: 5bd2df5463caab8646bfab17973cd85343819b94fb189d6c3d52bbe65cbf11ce
5
5
  SHA512:
6
- metadata.gz: 0b54b242a022b49d1917dd3c0370f96bde5ec210dad8959a34bc424132c7efd97aa0e1f781cebd0eaf21056b3b29c2c0c9632c08e94a4b0e2da851afe57044e5
7
- data.tar.gz: fb448e67961e641eb6ce89081e832082cfa886856ccbd5f4d5de767d1bd21835d38034e6dd55a4d00b1f633f58da6a094e5d8e4e4886c24a6aece095386023f2
6
+ metadata.gz: 3899c333a5af6c7f9c1a05cfe1bc882f8464ad9da9d21ed677c75ce7ec6546a6181893d8a27f563822d46ed4bfb7d6f91e8349285d1bc83e3cc6aaae2a8d5147
7
+ data.tar.gz: cdc13b1d159a794f67fa1c46a3410b3196529dba59e234eba32147338d15e85a2478bcb676821738256792e4358100a4c07bc96cfb855c00d68db828807a1d48
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.1] - 2026-06-04
4
+
5
+ ### Added
6
+
7
+ - `actorType:` filter argument on `auditLogEntries`, `auditLogEntriesConnection`, and `auditLogEntriesCount` — filter by actor model class name (e.g. `"User"`)
8
+ - `forTenant:` argument on `auditLogReify` and `auditLogEntriesCount` — consistent tenant scoping across all query fields; both also respect `RailsAuditLog.current_tenant` auto-tenant
9
+
10
+ ### Changed
11
+
12
+ - `auditLogReify` return type changed from generic `JSON` to `AuditLogJson` for consistency with other entry fields
13
+ - `rails g rails_audit_log:graphql:install` now also injects `SchemaPlugin` into the host schema file (detected via `app/graphql/**/*schema*.rb` glob); `print_next_steps` updated to mention all available queries and the complexity config override
14
+
15
+ ## [0.6.0] - 2026-06-04
16
+
17
+ ### Added
18
+
19
+ - `AuditLogJsonScalar` (`AuditLogJson`) — custom scalar type for `objectChanges`, `object`, and `metadata` fields; replaces the generic `JSON` scalar with a self-documenting type
20
+ - `RecordByIdSource` — `GraphQL::Dataloader::Source` that batch-loads AR records by class name and ID; eliminates N+1 queries when resolving `actor.record` and `auditedResource.record` on list responses
21
+ - `record` field on `AuditLogActor` and `AuditedResource` — returns the database record as JSON, batch-loaded via dataloader
22
+ - `auditLogReify(itemType:, itemId:, at:)` — reconstructs the attribute state of a record at a given point in time; returns `JSON` or `nil` when the record was destroyed or no entry exists
23
+ - `SchemaPlugin` — include into host schema to apply `max_complexity` (200), `max_depth` (10), `default_max_page_size` (25), and `use GraphQL::Dataloader`; all limits configurable via `RailsAuditLog::Graphql.max_complexity=` etc.
24
+
3
25
  ## [0.5.0] - 2026-06-04
4
26
 
5
27
  ### Added
data/README.md CHANGED
@@ -23,6 +23,8 @@ A [graphql-ruby](https://graphql-ruby.org) API layer for the [`rails_audit_log`]
23
23
  - [auditLogEntriesCount](#auditlogentriescount-int)
24
24
  - [Tenant scoping](#tenant-scoping)
25
25
  - [Authentication](#authentication)
26
+ - [SchemaPlugin](#schemaplugin)
27
+ - [auditLogReify](#auditlogreify)
26
28
  - [Subscriptions](#subscriptions)
27
29
  - [AuditLogSubscriptionsMixin](#auditlogsubscriptionsmixin)
28
30
  - [auditLogEntryCreated](#auditlogentrycreated)
@@ -150,6 +152,7 @@ List entries with optional filters and offset pagination.
150
152
  | `itemType` | `String` | — | Filter by audited model class name |
151
153
  | `itemId` | `ID` | — | Filter by audited record ID |
152
154
  | `actorId` | `ID` | — | Filter by actor ID |
155
+ | `actorType` | `String` | — | Filter by actor model class name (e.g. `"User"`) |
153
156
  | `since` | `ISO8601DateTime` | — | Return entries created at or after this time |
154
157
  | `until` | `ISO8601DateTime` | — | Return entries created at or before this time |
155
158
  | `touching` | `String` | — | Filter to entries that changed a specific attribute |
@@ -170,6 +173,7 @@ Same filters as `auditLogEntries`, but returns a [Relay-style connection](https:
170
173
  | `itemType` | `String` | Filter by audited model class name |
171
174
  | `itemId` | `ID` | Filter by audited record ID |
172
175
  | `actorId` | `ID` | Filter by actor ID |
176
+ | `actorType` | `String` | Filter by actor model class name (e.g. `"User"`) |
173
177
  | `forTenant` | `String` | Scope to a specific tenant ID; overrides auto-tenant |
174
178
  | `first` | `Int` | Return the first N edges after `after` |
175
179
  | `after` | `String` | Cursor to paginate forward from |
@@ -219,7 +223,9 @@ Returns the count of matching audit log entries. Respects auto-tenant when `Rail
219
223
  |---|---|---|
220
224
  | `event` | `String` | Filter by event type (`create`, `update`, `destroy`) |
221
225
  | `itemType` | `String` | Filter by audited model class name |
226
+ | `actorType` | `String` | Filter by actor model class name (e.g. `"User"`) |
222
227
  | `since` | `ISO8601DateTime` | Count entries created at or after this time |
228
+ | `forTenant` | `String` | Scope to a specific tenant ID; overrides auto-tenant |
223
229
 
224
230
  ```graphql
225
231
  { auditLogEntriesCount(event: "update", itemType: "Post") }
@@ -259,6 +265,51 @@ If no authenticate block is set, all queries are permitted.
259
265
 
260
266
  [↑ Back to top](#table-of-contents)
261
267
 
268
+ ### SchemaPlugin
269
+
270
+ Include `RailsAuditLog::Graphql::SchemaPlugin` into your schema to enable query protection and dataloader batching in one step:
271
+
272
+ ```ruby
273
+ class MySchema < GraphQL::Schema
274
+ include RailsAuditLog::Graphql::SchemaPlugin
275
+ query Types::QueryType
276
+ end
277
+ ```
278
+
279
+ This applies the following defaults (all overridable via `RailsAuditLog::Graphql.*=`):
280
+
281
+ | Setting | Default | Description |
282
+ |---|---|---|
283
+ | `max_complexity` | `200` | Reject queries whose field-complexity sum exceeds this |
284
+ | `max_depth` | `10` | Reject queries nested deeper than this |
285
+ | `default_max_page_size` | `25` | Assumed page size for connection complexity calculation |
286
+
287
+ Override in an initializer:
288
+
289
+ ```ruby
290
+ RailsAuditLog::Graphql.max_complexity = 500
291
+ RailsAuditLog::Graphql.max_depth = 15
292
+ ```
293
+
294
+ The plugin also adds `AuditLogActor.record` and `AuditedResource.record` fields — nullable JSON fields that load the actual database record via `RecordByIdSource`, a `GraphQL::Dataloader::Source` that batches loads by class name to eliminate N+1 queries on list responses.
295
+
296
+ [↑ Back to top](#table-of-contents)
297
+
298
+ ### `auditLogReify(itemType:, itemId:, at:): AuditLogJson`
299
+
300
+ Reconstructs the attribute state of a record at a given point in time. Returns the attributes as `AuditLogJson`, or `nil` when no entry exists at or before `at` or the record was destroyed at that time. Accepts `forTenant:` and respects auto-tenant.
301
+
302
+ ```graphql
303
+ {
304
+ auditLogReify(itemType: "Post", itemId: "42", at: "2026-01-15T12:00:00Z") {
305
+ title
306
+ publishedAt
307
+ }
308
+ }
309
+ ```
310
+
311
+ [↑ Back to top](#table-of-contents)
312
+
262
313
  ### Subscriptions
263
314
 
264
315
  Requires Action Cable in the host application.
data/ROADMAP.md CHANGED
@@ -4,15 +4,6 @@ This gem adds a GraphQL API layer on top of [`rails_audit_log`](https://github.c
4
4
 
5
5
  ---
6
6
 
7
- ## 0.6.0 — Performance & Safety
8
-
9
- - **Dataloader batch loading** — batch-resolve polymorphic `actor` and `item` associations using graphql-ruby's `dataloader` to eliminate N+1 queries on list responses
10
- - **`auditLogReify` query** — `auditLogReify(itemType:, itemId:, at:)` returns the reconstructed object state as JSON at a given point in time, backed by `RailsAuditLog.version_at`
11
- - **Query complexity & depth limits** — built-in defaults (`max_complexity`, `max_depth`) with a config override (`RailsAuditLogGraphql.max_complexity = 200`) to protect against expensive queries before the API is declared stable
12
- - **`AuditLogJsonScalar`** — proper JSON scalar type for `objectChanges` and `metadata` fields, replacing opaque String serialization and making the schema self-documenting
13
-
14
- ---
15
-
16
7
  ## 1.0.0 — Stable API
17
8
 
18
9
  - Full YARD documentation
@@ -7,10 +7,11 @@ module RailsAuditLog
7
7
  module Graphql
8
8
  class InstallGenerator < Rails::Generators::Base
9
9
  source_root File.expand_path("templates", __dir__)
10
- desc "Injects AuditLogEntriesQueryMixin into your GraphQL QueryType."
10
+ desc "Injects AuditLogEntriesQueryMixin into your GraphQL QueryType and SchemaPlugin into your schema."
11
11
 
12
12
  QUERY_TYPE_PATH = "app/graphql/types/query_type.rb"
13
13
  MIXIN = "RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin"
14
+ SCHEMA_PLUGIN = "RailsAuditLog::Graphql::SchemaPlugin"
14
15
 
15
16
  def inject_mixin
16
17
  if File.exist?(File.join(destination_root, QUERY_TYPE_PATH))
@@ -24,11 +25,31 @@ module RailsAuditLog
24
25
  end
25
26
  end
26
27
 
28
+ def inject_schema_plugin
29
+ schema_files = Dir.glob(File.join(destination_root, "app/graphql/**/*schema*.rb"))
30
+ if schema_files.any?
31
+ schema_path = schema_files.first.delete_prefix("#{destination_root}/")
32
+ inject_into_file schema_path,
33
+ " include #{SCHEMA_PLUGIN}\n",
34
+ after: /class\s+\S+\s*<\s*GraphQL::Schema\s*\n/
35
+ else
36
+ say ""
37
+ say "No schema file found. Add this line manually to your GraphQL::Schema subclass:", :yellow
38
+ say " include #{SCHEMA_PLUGIN}", :green
39
+ end
40
+ end
41
+
27
42
  def print_next_steps
28
43
  say ""
29
44
  say "Done! Your GraphQL API now has:", :green
30
45
  say " auditLogEntry(id: ID!): AuditLogEntry"
31
46
  say " auditLogEntries(...): [AuditLogEntry!]!"
47
+ say " auditLogReify(itemType:, itemId:, at:): AuditLogJson"
48
+ say " auditLogEntriesCount(...): Int!"
49
+ say ""
50
+ say "SchemaPlugin applies complexity/depth limits and enables dataloader."
51
+ say "Override defaults in an initializer:"
52
+ say " RailsAuditLog::Graphql.max_complexity = 500"
32
53
  say ""
33
54
  say "See the README for full documentation."
34
55
  end
@@ -29,6 +29,7 @@ module RailsAuditLog
29
29
  argument :item_type, String, required: false, description: "Filter by audited model class name."
30
30
  argument :item_id, GraphQL::Types::ID, required: false, description: "Filter by audited record ID."
31
31
  argument :actor_id, GraphQL::Types::ID, required: false, description: "Filter by actor ID."
32
+ argument :actor_type, String, required: false, description: "Filter by actor model class name (e.g. \"User\")."
32
33
  argument :since, GraphQL::Types::ISO8601DateTime, required: false, description: "Return entries created at or after this time."
33
34
  argument :until, GraphQL::Types::ISO8601DateTime, required: false, as: :until_time, description: "Return entries created at or before this time."
34
35
  argument :touching, String, required: false, description: "Filter to entries that changed a specific attribute (matches object_changes keys)."
@@ -50,6 +51,7 @@ module RailsAuditLog
50
51
  argument :item_type, String, required: false, description: "Filter by audited model class name."
51
52
  argument :item_id, GraphQL::Types::ID, required: false, description: "Filter by audited record ID."
52
53
  argument :actor_id, GraphQL::Types::ID, required: false, description: "Filter by actor ID."
54
+ argument :actor_type, String, required: false, description: "Filter by actor model class name (e.g. \"User\")."
53
55
  argument :since, GraphQL::Types::ISO8601DateTime, required: false, description: "Return entries created at or after this time."
54
56
  argument :until, GraphQL::Types::ISO8601DateTime, required: false, as: :until_time, description: "Return entries created at or before this time."
55
57
  argument :touching, String, required: false, description: "Filter to entries that changed a specific attribute (matches object_changes keys)."
@@ -58,6 +60,20 @@ module RailsAuditLog
58
60
  description: "Scope to a specific tenant ID. Overrides auto-tenant when RailsAuditLog.current_tenant is configured."
59
61
  end
60
62
 
63
+ base.field(
64
+ :audit_log_reify,
65
+ Types::AuditLogJsonScalar,
66
+ null: true,
67
+ description: "Reconstruct the attribute state of a record at a given point in time. Returns nil when no entry exists at or before `at`, or when the record was destroyed.",
68
+ resolver_method: :resolve_audit_log_reify
69
+ ) do
70
+ argument :item_type, String, required: true, description: "The audited model class name."
71
+ argument :item_id, GraphQL::Types::ID, required: true, description: "The audited record ID."
72
+ argument :at, GraphQL::Types::ISO8601DateTime, required: true, description: "Reconstruct state as of this time."
73
+ argument :for_tenant, String, required: false,
74
+ description: "Scope to a specific tenant ID. Overrides auto-tenant when RailsAuditLog.current_tenant is configured."
75
+ end
76
+
61
77
  base.field(
62
78
  :audit_log_entries_count,
63
79
  GraphQL::Types::Int,
@@ -67,7 +83,10 @@ module RailsAuditLog
67
83
  ) do
68
84
  argument :event, String, required: false, description: "Filter by event type (create, update, destroy)."
69
85
  argument :item_type, String, required: false, description: "Filter by audited model class name."
86
+ argument :actor_type, String, required: false, description: "Filter by actor model class name (e.g. \"User\")."
70
87
  argument :since, GraphQL::Types::ISO8601DateTime, required: false, description: "Count entries created at or after this time."
88
+ argument :for_tenant, String, required: false,
89
+ description: "Scope to a specific tenant ID. Overrides auto-tenant when RailsAuditLog.current_tenant is configured."
71
90
  end
72
91
  end
73
92
 
@@ -78,31 +97,45 @@ module RailsAuditLog
78
97
  base.find_by(id: id)
79
98
  end
80
99
 
81
- def resolve_audit_log_entries(event: nil, item_type: nil, item_id: nil, actor_id: nil, since: nil, until_time: nil, touching: nil, order_by: nil, for_tenant: nil, page: 1, per_page: 25)
100
+ def resolve_audit_log_entries(event: nil, item_type: nil, item_id: nil, actor_id: nil, actor_type: nil, since: nil, until_time: nil, touching: nil, order_by: nil, for_tenant: nil, page: 1, per_page: 25)
82
101
  check_authentication!
83
- scope = build_scope(event: event, item_type: item_type, item_id: item_id, actor_id: actor_id, since: since, until_time: until_time, touching: touching, order_by: order_by, for_tenant: for_tenant)
102
+ scope = build_scope(event: event, item_type: item_type, item_id: item_id, actor_id: actor_id, actor_type: actor_type, since: since, until_time: until_time, touching: touching, order_by: order_by, for_tenant: for_tenant)
84
103
  scope.limit(per_page).offset((page - 1) * per_page)
85
104
  end
86
105
 
87
- def resolve_audit_log_entries_connection(event: nil, item_type: nil, item_id: nil, actor_id: nil, since: nil, until_time: nil, touching: nil, order_by: nil, for_tenant: nil)
106
+ def resolve_audit_log_entries_connection(event: nil, item_type: nil, item_id: nil, actor_id: nil, actor_type: nil, since: nil, until_time: nil, touching: nil, order_by: nil, for_tenant: nil)
88
107
  check_authentication!
89
- build_scope(event: event, item_type: item_type, item_id: item_id, actor_id: actor_id, since: since, until_time: until_time, touching: touching, order_by: order_by, for_tenant: for_tenant)
108
+ build_scope(event: event, item_type: item_type, item_id: item_id, actor_id: actor_id, actor_type: actor_type, since: since, until_time: until_time, touching: touching, order_by: order_by, for_tenant: for_tenant)
90
109
  end
91
110
 
92
- def resolve_audit_log_entries_count(event: nil, item_type: nil, since: nil)
111
+ def resolve_audit_log_reify(item_type:, item_id:, at:, for_tenant: nil)
112
+ check_authentication!
113
+ tenant_id = for_tenant || RailsAuditLog.current_tenant&.call
114
+ scope = RailsAuditLog::AuditLogEntry
115
+ .where(item_type: item_type, item_id: item_id)
116
+ .where("created_at <= ?", at)
117
+ scope = scope.for_tenant(tenant_id) if tenant_id
118
+ entry = scope.order(created_at: :desc, id: :desc).first
119
+ return nil if entry.nil? || entry.event == "destroy"
120
+ to_attrs = (entry.object_changes || {}).transform_values { |v| v[1] }
121
+ entry.object.present? ? entry.object.merge(to_attrs) : to_attrs
122
+ end
123
+
124
+ def resolve_audit_log_entries_count(event: nil, item_type: nil, actor_type: nil, since: nil, for_tenant: nil)
93
125
  check_authentication!
94
126
  scope = RailsAuditLog::AuditLogEntry.all
95
127
  scope = scope.where(event: event) if event
96
128
  scope = scope.where(item_type: item_type) if item_type
129
+ scope = scope.where(actor_type: actor_type) if actor_type
97
130
  scope = scope.where("created_at >= ?", since) if since
98
- tenant_id = RailsAuditLog.current_tenant&.call
131
+ tenant_id = for_tenant || RailsAuditLog.current_tenant&.call
99
132
  scope = scope.for_tenant(tenant_id) if tenant_id
100
133
  scope.count
101
134
  end
102
135
 
103
136
  private
104
137
 
105
- def build_scope(event: nil, item_type: nil, item_id: nil, actor_id: nil, since: nil, until_time: nil, touching: nil, order_by: nil, for_tenant: nil)
138
+ def build_scope(event: nil, item_type: nil, item_id: nil, actor_id: nil, actor_type: nil, since: nil, until_time: nil, touching: nil, order_by: nil, for_tenant: nil)
106
139
  sort_field = order_by&.field || :created_at
107
140
  sort_direction = order_by&.direction || :desc
108
141
  scope = RailsAuditLog::AuditLogEntry.order(sort_field => sort_direction)
@@ -110,6 +143,7 @@ module RailsAuditLog
110
143
  scope = scope.where(item_type: item_type) if item_type
111
144
  scope = scope.where(item_id: item_id) if item_id
112
145
  scope = scope.where(actor_id: actor_id) if actor_id
146
+ scope = scope.where(actor_type: actor_type) if actor_type
113
147
  scope = scope.where("created_at >= ?", since) if since
114
148
  scope = scope.where("created_at <= ?", until_time) if until_time
115
149
  if touching
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ module SchemaPlugin
6
+ def self.included(base)
7
+ base.max_complexity(RailsAuditLog::Graphql.max_complexity)
8
+ base.max_depth(RailsAuditLog::Graphql.max_depth)
9
+ base.default_max_page_size(RailsAuditLog::Graphql.default_max_page_size)
10
+ base.use(GraphQL::Dataloader)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ module Sources
6
+ class RecordByIdSource < GraphQL::Dataloader::Source
7
+ def initialize(class_name)
8
+ @class_name = class_name
9
+ end
10
+
11
+ def fetch(ids)
12
+ klass = @class_name.safe_constantize
13
+ return ids.map { nil } unless klass
14
+
15
+ records = klass.where(id: ids).index_by { |r| r.id.to_s }
16
+ ids.map { |id| records[id]&.attributes }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -9,6 +9,8 @@ module RailsAuditLog
9
9
 
10
10
  field :id, GraphQL::Types::ID, null: false, description: "The actor's ID."
11
11
  field :type_name, String, null: false, description: "The actor's model class name (e.g. \"User\")."
12
+ field :record, GraphQL::Types::JSON, null: true,
13
+ description: "The actor record loaded from the database, serialized as JSON. Batch-loaded via dataloader."
12
14
 
13
15
  def id
14
16
  object[:id]
@@ -17,6 +19,11 @@ module RailsAuditLog
17
19
  def type_name
18
20
  object[:type_name]
19
21
  end
22
+
23
+ def record
24
+ return nil unless object[:id] && object[:type_name]
25
+ dataloader.with(Sources::RecordByIdSource, object[:type_name]).load(object[:id].to_s)
26
+ end
20
27
  end
21
28
  end
22
29
  end
@@ -11,9 +11,9 @@ module RailsAuditLog
11
11
  field :event, String, null: false
12
12
  field :item_type, String, null: false
13
13
  field :item_id, GraphQL::Types::ID, null: false
14
- field :object_changes, GraphQL::Types::JSON, null: true
15
- field :object, GraphQL::Types::JSON, null: true, method_conflict_warning: false
16
- field :metadata, GraphQL::Types::JSON, null: true
14
+ field :object_changes, Types::AuditLogJsonScalar, null: true
15
+ field :object, Types::AuditLogJsonScalar, null: true, method_conflict_warning: false
16
+ field :metadata, Types::AuditLogJsonScalar, null: true
17
17
  field :reason, String, null: true
18
18
  field :whodunnit_snapshot, String, null: true
19
19
  field :actor_type, String, null: true
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ module Types
6
+ class AuditLogJsonScalar < GraphQL::Schema::Scalar
7
+ graphql_name "AuditLogJson"
8
+ description "A JSON blob stored on an audit log entry (objectChanges, object, or metadata)."
9
+
10
+ def self.coerce_input(value, _ctx)
11
+ value.is_a?(Hash) ? value : JSON.parse(value)
12
+ rescue JSON::ParserError
13
+ raise GraphQL::CoercionError, "#{value.inspect} is not valid JSON"
14
+ end
15
+
16
+ def self.coerce_result(value, _ctx)
17
+ value
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -9,6 +9,8 @@ module RailsAuditLog
9
9
 
10
10
  field :id, GraphQL::Types::ID, null: false, description: "The audited record's ID."
11
11
  field :type_name, String, null: false, description: "The audited model class name (e.g. \"Post\")."
12
+ field :record, GraphQL::Types::JSON, null: true,
13
+ description: "The audited record loaded from the database, serialized as JSON. Batch-loaded via dataloader."
12
14
 
13
15
  def id
14
16
  object[:id]
@@ -17,6 +19,10 @@ module RailsAuditLog
17
19
  def type_name
18
20
  object[:type_name]
19
21
  end
22
+
23
+ def record
24
+ dataloader.with(Sources::RecordByIdSource, object[:type_name]).load(object[:id].to_s)
25
+ end
20
26
  end
21
27
  end
22
28
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RailsAuditLog
4
4
  module Graphql
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.1"
6
6
  end
7
7
  end
@@ -4,6 +4,7 @@ require "graphql"
4
4
  require_relative "graphql/version"
5
5
  require_relative "graphql/types/base_object"
6
6
  require_relative "graphql/types/base_subscription"
7
+ require_relative "graphql/types/audit_log_json_scalar"
7
8
  require_relative "graphql/types/diff_type"
8
9
  require_relative "graphql/types/actor_type"
9
10
  require_relative "graphql/types/audited_resource_type"
@@ -11,13 +12,23 @@ require_relative "graphql/types/audit_log_entry_type"
11
12
  require_relative "graphql/types/sort_direction_enum"
12
13
  require_relative "graphql/types/audit_log_entry_sort_field_enum"
13
14
  require_relative "graphql/input_objects/audit_log_entry_sort_input"
15
+ require_relative "graphql/sources/record_by_id_source"
14
16
  require_relative "graphql/queries/audit_log_entries_query_mixin"
15
17
  require_relative "graphql/subscriptions/audit_log_entry_created"
16
18
  require_relative "graphql/subscriptions/audit_log_subscriptions_mixin"
17
19
  require_relative "graphql/subscriptions/broadcaster"
20
+ require_relative "graphql/schema_plugin"
18
21
 
19
22
  module RailsAuditLog
20
23
  module Graphql
21
24
  class Error < StandardError; end
25
+
26
+ @max_complexity = 200
27
+ @max_depth = 10
28
+ @default_max_page_size = 25
29
+
30
+ class << self
31
+ attr_accessor :max_complexity, :max_depth, :default_max_page_size
32
+ end
22
33
  end
23
34
  end
@@ -2,6 +2,17 @@ module RailsAuditLog
2
2
  module Graphql
3
3
  VERSION: String
4
4
 
5
+ @max_complexity: Integer
6
+ @max_depth: Integer
7
+ @default_max_page_size: Integer
8
+
9
+ def self.max_complexity: () -> Integer
10
+ def self.max_complexity=: (Integer) -> Integer
11
+ def self.max_depth: () -> Integer
12
+ def self.max_depth=: (Integer) -> Integer
13
+ def self.default_max_page_size: () -> Integer
14
+ def self.default_max_page_size=: (Integer) -> Integer
15
+
5
16
  module Types
6
17
  class BaseObject < GraphQL::Schema::Object
7
18
  end
@@ -9,6 +20,11 @@ module RailsAuditLog
9
20
  class BaseSubscription < GraphQL::Schema::Subscription
10
21
  end
11
22
 
23
+ class AuditLogJsonScalar < GraphQL::Schema::Scalar
24
+ def self.coerce_input: (untyped value, untyped ctx) -> Hash[String, untyped]
25
+ def self.coerce_result: (untyped value, untyped ctx) -> untyped
26
+ end
27
+
12
28
  class DiffType < BaseObject
13
29
  def attribute: () -> String
14
30
  def from: () -> untyped
@@ -18,11 +34,13 @@ module RailsAuditLog
18
34
  class ActorType < BaseObject
19
35
  def id: () -> String
20
36
  def type_name: () -> String
37
+ def record: () -> Hash[String, untyped]?
21
38
  end
22
39
 
23
40
  class AuditedResourceType < BaseObject
24
41
  def id: () -> String
25
42
  def type_name: () -> String
43
+ def record: () -> Hash[String, untyped]?
26
44
  end
27
45
 
28
46
  class AuditLogEntryType < BaseObject
@@ -54,6 +72,17 @@ module RailsAuditLog
54
72
  end
55
73
  end
56
74
 
75
+ module Sources
76
+ class RecordByIdSource < GraphQL::Dataloader::Source
77
+ def initialize: (String class_name) -> void
78
+ def fetch: (Array[String] ids) -> Array[Hash[String, untyped]?]
79
+ end
80
+ end
81
+
82
+ module SchemaPlugin
83
+ def self.included: (untyped base) -> void
84
+ end
85
+
57
86
  module Subscriptions
58
87
  class AuditLogEntryCreated < Types::BaseSubscription
59
88
  def subscribe: (?item_type: String?, ?item_id: String?, ?actor_id: String?) -> :no_response
@@ -83,6 +112,7 @@ module RailsAuditLog
83
112
  ?item_type: String?,
84
113
  ?item_id: String?,
85
114
  ?actor_id: String?,
115
+ ?actor_type: String?,
86
116
  ?since: Time?,
87
117
  ?until_time: Time?,
88
118
  ?touching: String?,
@@ -97,6 +127,7 @@ module RailsAuditLog
97
127
  ?item_type: String?,
98
128
  ?item_id: String?,
99
129
  ?actor_id: String?,
130
+ ?actor_type: String?,
100
131
  ?since: Time?,
101
132
  ?until_time: Time?,
102
133
  ?touching: String?,
@@ -104,10 +135,19 @@ module RailsAuditLog
104
135
  ?for_tenant: String?
105
136
  ) -> untyped
106
137
 
138
+ def resolve_audit_log_reify: (
139
+ item_type: String,
140
+ item_id: String,
141
+ at: Time,
142
+ ?for_tenant: String?
143
+ ) -> Hash[String, untyped]?
144
+
107
145
  def resolve_audit_log_entries_count: (
108
146
  ?event: String?,
109
147
  ?item_type: String?,
110
- ?since: Time?
148
+ ?actor_type: String?,
149
+ ?since: Time?,
150
+ ?for_tenant: String?
111
151
  ) -> Integer
112
152
 
113
153
  private
@@ -117,6 +157,7 @@ module RailsAuditLog
117
157
  ?item_type: String?,
118
158
  ?item_id: String?,
119
159
  ?actor_id: String?,
160
+ ?actor_type: String?,
120
161
  ?since: Time?,
121
162
  ?until_time: Time?,
122
163
  ?touching: String?,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_audit_log-graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -86,12 +86,15 @@ files:
86
86
  - lib/rails_audit_log/graphql/input_objects/audit_log_entry_sort_input.rb
87
87
  - lib/rails_audit_log/graphql/queries/audit_log_entries_query_mixin.rb
88
88
  - lib/rails_audit_log/graphql/release_tooling.rb
89
+ - lib/rails_audit_log/graphql/schema_plugin.rb
90
+ - lib/rails_audit_log/graphql/sources/record_by_id_source.rb
89
91
  - lib/rails_audit_log/graphql/subscriptions/audit_log_entry_created.rb
90
92
  - lib/rails_audit_log/graphql/subscriptions/audit_log_subscriptions_mixin.rb
91
93
  - lib/rails_audit_log/graphql/subscriptions/broadcaster.rb
92
94
  - lib/rails_audit_log/graphql/types/actor_type.rb
93
95
  - lib/rails_audit_log/graphql/types/audit_log_entry_sort_field_enum.rb
94
96
  - lib/rails_audit_log/graphql/types/audit_log_entry_type.rb
97
+ - lib/rails_audit_log/graphql/types/audit_log_json_scalar.rb
95
98
  - lib/rails_audit_log/graphql/types/audited_resource_type.rb
96
99
  - lib/rails_audit_log/graphql/types/base_object.rb
97
100
  - lib/rails_audit_log/graphql/types/base_subscription.rb