rails_audit_log-graphql 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 35b1c07883c1e3b4036690804d2cf1600e484771b49a58f3954278d39fb136ae
4
+ data.tar.gz: 15b543bf26d1d19ff3e8fbc29488871f325d492cef51b215c60dff8d1f22906b
5
+ SHA512:
6
+ metadata.gz: 9409ded3977d2fa0f31f5a60be9bf841cabe5e324aed231e8119fc59c433988affe7ab7f873aa31e1de7a2538f054e281c1af46f4946632fa2cff46d39ad35f5
7
+ data.tar.gz: f52a3c3d184d1ff8f201011ca014338e4a13b3d1a3d055f1372ea5fa72f4a2898765cde924777d38e5843f21b3319ee13a309cb6e4b37b23130f4986a559daf1
data/.standard.yml ADDED
@@ -0,0 +1,6 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.2
4
+
5
+ ignore:
6
+ - "benchmarks/**/*"
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-06-03
4
+
5
+ ### Added
6
+
7
+ - `AuditLogEntryType` GraphQL object type exposing all 13 `RailsAuditLog::AuditLogEntry` fields
8
+ - `BaseObject` base class for all gem GraphQL types
9
+ - `AuditLogEntriesQueryMixin` — include into host app's `QueryType` to add `auditLogEntry(id:)` and `auditLogEntries(event:, itemType:, itemId:, actorId:, page:, perPage:)` queries
10
+ - Authentication support — `RailsAuditLog.authenticate` is respected; block receives the GraphQL context and raises `GraphQL::ExecutionError` when it returns falsy
11
+ - `rails g rails_audit_log:graphql:install` generator — injects `AuditLogEntriesQueryMixin` into `app/graphql/types/query_type.rb`
12
+
data/CLAUDE.md ADDED
@@ -0,0 +1,89 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this is
6
+
7
+ `solid_queue_web` is a mountable Rails engine (published as a gem) that provides a web dashboard for [Solid Queue](https://github.com/rails/solid_queue). It has no host application — development and testing both use the dummy Rails app under `spec/dummy/`.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Run the full suite (rubocop + rspec) — this is what CI runs
13
+ bundle exec rake
14
+
15
+ # Run only tests
16
+ bundle exec rspec
17
+
18
+ # Run a single spec file
19
+ bundle exec rspec spec/requests/solid_queue_web/jobs_spec.rb
20
+
21
+ # Run a single example by line number
22
+ bundle exec rspec spec/requests/solid_queue_web/jobs_spec.rb:42
23
+
24
+ # Lint
25
+ bin/rubocop
26
+
27
+ # Set up and seed the development database (dummy app)
28
+ bundle exec rake dev:setup # creates and migrates spec/dummy/db/development.sqlite3
29
+ bundle exec rake dev:seed # populates with realistic fake jobs/processes
30
+
31
+ # Reset dev database (setup + seed)
32
+ bundle exec rake dev:reset
33
+
34
+ # Start the dummy app for manual testing
35
+ cd spec/dummy && bin/rails server
36
+ # Dashboard is at http://localhost:3000/jobs
37
+ ```
38
+
39
+ ## Architecture
40
+
41
+ ### Engine isolation
42
+
43
+ The engine uses `isolate_namespace SolidQueueWeb`, so all routes, controllers, helpers, and models live under that namespace. The engine requires no database migrations of its own — it reads directly from the host app's Solid Queue tables via `SolidQueue::*` models (a declared gem dependency).
44
+
45
+ ### Execution model pattern
46
+
47
+ Solid Queue represents job state with separate execution tables. Each status maps to a distinct ActiveRecord model:
48
+
49
+ | Status | Model |
50
+ |-------------|------------------------------------|
51
+ | `ready` | `SolidQueue::ReadyExecution` |
52
+ | `scheduled` | `SolidQueue::ScheduledExecution` |
53
+ | `claimed` | `SolidQueue::ClaimedExecution` |
54
+ | `blocked` | `SolidQueue::BlockedExecution` |
55
+ | `failed` | `SolidQueue::FailedExecution` |
56
+
57
+ `JobsController` maps the `?status=` param to these models via `EXECUTION_MODELS`. Only `ready`, `scheduled`, and `blocked` jobs can be discarded from the jobs list (`DISCARDABLE`); failed jobs have their own `FailedJobsController` with retry/discard.
58
+
59
+ ### No asset pipeline dependency
60
+
61
+ CSS is delivered entirely via the `inline_styles` helper, which reads `application.css` at request time and injects it as a `<style>` tag. This prevents conflicts when mounted in any host app. There is no JavaScript — queue pause/resume and job discard use standard form POSTs or Turbo Stream responses.
62
+
63
+ ### Turbo Stream responses
64
+
65
+ `JobsController#destroy` responds to both HTML and `turbo_stream` format. When a job is discarded:
66
+ - If more jobs remain in the filtered scope → removes that row from the DOM.
67
+ - If it was the last job → replaces the table with an empty-state element (`sqd-empty`).
68
+
69
+ ### Authentication
70
+
71
+ `SolidQueueWeb.authenticate` stores a single block (set via an initializer in the host app). `ApplicationController#authenticate!` runs that block in controller context via `instance_exec`. If the block returns falsy, it falls back to HTTP Basic auth. No auth is enforced by default.
72
+
73
+ ### Pagination
74
+
75
+ Pagy is included via `Pagy::Method` (not `Pagy::Backend`) and configured globally in the engine initializer with a limit of 25.
76
+
77
+ ### Test setup
78
+
79
+ `spec/rails_helper.rb` loads `spec/dummy/db/schema.rb` directly on every test run (no migrations). Tests are all request specs that hit the dummy app's mounted engine at `/jobs`. Factories are not used — records are created directly with `SolidQueue::*` models.
80
+
81
+ ### Releasing
82
+
83
+ `bin/release <version>` — bumps the version file, updates `Gemfile.lock` and `CHANGELOG.md`, commits, tags, and pushes. CI picks up the tag and publishes to RubyGems via Trusted Publishing. Must be run from `main` with a clean working tree.
84
+
85
+ ### CHANGELOG conventions
86
+
87
+ - New entries go under `## [Unreleased]` on the feature branch, before opening a PR.
88
+ - Sections within each version must appear in this order: `### Added`, `### Changed`, `### Fixed`. Omit sections that have no entries — never add an empty section header.
89
+ - Branch workflow: always commit on a `feat/*` or `chore/*` branch; never commit directly to `main`.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,51 @@
1
+ # Contributing
2
+
3
+ Bug reports and pull requests are welcome on GitHub at https://github.com/eclectic-coding/rails_audit_log-graphql.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/eclectic-coding/rails_audit_log-graphql.git
9
+ cd rails_audit_log-graphql
10
+ bin/setup
11
+ ```
12
+
13
+ ## Running the Test Suite
14
+
15
+ ```bash
16
+ bundle exec rake # lint (StandardRB) + tests — this is what CI runs
17
+ bundle exec rspec # tests only
18
+ bundle exec standardrb # lint only
19
+ ```
20
+
21
+ All pull requests must pass the full suite before merging.
22
+
23
+ ## Branch Workflow
24
+
25
+ - Branch from `main` using a `feat/*` or `chore/*` prefix
26
+ - One logical change per branch
27
+ - Open a pull request against `main`; CI must be green before merge
28
+
29
+ ## Changelog
30
+
31
+ Add an entry under `## [Unreleased]` in `CHANGELOG.md` with every PR. Use the appropriate section:
32
+
33
+ - `### Added` — new functionality
34
+ - `### Changed` — changes to existing behaviour
35
+ - `### Fixed` — bug fixes
36
+
37
+ ## Roadmap
38
+
39
+ When a feature listed in `ROADMAP.md` is completed, remove its bullet in the same PR that implements it.
40
+
41
+ ## Code Style
42
+
43
+ This project uses [Standard Ruby](https://standardrb.com) (`standard` gem). Run `bundle exec rake standard:fix` to auto-correct violations before committing.
44
+
45
+ ## RBS Signatures
46
+
47
+ Update `sig/rails_audit_log/graphql.rbs` when adding or changing public classes and modules.
48
+
49
+ ## Releasing
50
+
51
+ Releases are managed by the maintainer. Do not bump `version.rb` in feature PRs.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Chuck Smith
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # RailsAuditLog::Graphql
2
+
3
+ [![CI](https://github.com/eclectic-coding/rails_audit_log-graphql/actions/workflows/main.yml/badge.svg)](https://github.com/eclectic-coding/rails_audit_log-graphql/actions/workflows/main.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/rails_audit_log-graphql)](https://rubygems.org/gems/rails_audit_log-graphql)
5
+ [![Total Downloads](https://img.shields.io/gem/dt/rails_audit_log-graphql)](https://rubygems.org/gems/rails_audit_log-graphql)
6
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.3-CC342D)](https://www.ruby-lang.org)
7
+ [![codecov](https://codecov.io/gh/eclectic-coding/rails_audit_log-graphql/graph/badge.svg)](https://codecov.io/gh/eclectic-coding/rails_audit_log-graphql)
8
+
9
+ A [graphql-ruby](https://graphql-ruby.org) API layer for the [`rails_audit_log`](https://github.com/eclectic-coding/rails_audit_log) gem. Provides ready-made GraphQL types, queries, and subscriptions for querying audit log entries — without coupling `graphql-ruby` to the base gem.
10
+
11
+ ## Table of Contents
12
+
13
+ - [Installation](#installation)
14
+ - [Usage](#usage)
15
+ - [AuditLogEntryType](#auditlogentrytype)
16
+ - [AuditLogEntriesQueryMixin](#auditlogentriesquerymixin)
17
+ - [auditLogEntry](#auditlogentryid-id-auditlogentry)
18
+ - [auditLogEntries](#auditlogentries-auditlogentry)
19
+ - [Authentication](#authentication)
20
+ - [Development](#development)
21
+ - [Contributing](#contributing)
22
+ - [License](#license)
23
+
24
+ ## Installation
25
+
26
+ Add to your application's Gemfile:
27
+
28
+ ```ruby
29
+ gem "rails_audit_log"
30
+ gem "rails_audit_log-graphql"
31
+ ```
32
+
33
+ Then run the install generator to wire up your schema:
34
+
35
+ ```bash
36
+ rails g rails_audit_log:graphql:install
37
+ ```
38
+
39
+ This injects `include RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin` into `app/graphql/types/query_type.rb`. If that file doesn't exist, the generator prints the line for you to add manually.
40
+
41
+ [↑ Back to top](#table-of-contents)
42
+
43
+ ## Usage
44
+
45
+ ### AuditLogEntryType
46
+
47
+ `RailsAuditLog::Graphql::Types::AuditLogEntryType` is a graphql-ruby object type that maps directly to `RailsAuditLog::AuditLogEntry`.
48
+
49
+ | GraphQL field | Type | Nullable |
50
+ |---|---|---|
51
+ | `id` | `ID` | no |
52
+ | `event` | `String` | no |
53
+ | `itemType` | `String` | no |
54
+ | `itemId` | `ID` | no |
55
+ | `createdAt` | `ISO8601DateTime` | no |
56
+ | `objectChanges` | `JSON` | yes |
57
+ | `object` | `JSON` | yes |
58
+ | `metadata` | `JSON` | yes |
59
+ | `reason` | `String` | yes |
60
+ | `whodunnitSnapshot` | `String` | yes |
61
+ | `actorType` | `String` | yes |
62
+ | `actorId` | `ID` | yes |
63
+ | `tenantId` | `String` | yes |
64
+
65
+ [↑ Back to top](#table-of-contents)
66
+
67
+ ### AuditLogEntriesQueryMixin
68
+
69
+ Include `RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin` into your app's `QueryType` to add two fields:
70
+
71
+ ```ruby
72
+ # app/graphql/types/query_type.rb
73
+ class Types::QueryType < Types::BaseObject
74
+ include RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin
75
+ end
76
+ ```
77
+
78
+ #### `auditLogEntry(id: ID!): AuditLogEntry`
79
+
80
+ Fetch a single entry by ID. Returns `nil` if not found.
81
+
82
+ #### `auditLogEntries(...): [AuditLogEntry!]!`
83
+
84
+ List entries with optional filters and offset pagination.
85
+
86
+ | Argument | Type | Default | Description |
87
+ |---|---|---|---|
88
+ | `event` | `String` | — | Filter by event type (`create`, `update`, `destroy`) |
89
+ | `itemType` | `String` | — | Filter by audited model class name |
90
+ | `itemId` | `ID` | — | Filter by audited record ID |
91
+ | `actorId` | `ID` | — | Filter by actor ID |
92
+ | `page` | `Int` | `1` | Page number (1-based) |
93
+ | `perPage` | `Int` | `25` | Results per page |
94
+
95
+ Results are ordered by `created_at DESC`.
96
+
97
+ [↑ Back to top](#table-of-contents)
98
+
99
+ ### Authentication
100
+
101
+ If `RailsAuditLog.authenticate` is configured, the block is called with the GraphQL context before every query. Return a truthy value to allow access; return falsy to raise `GraphQL::ExecutionError` with `"Unauthorized"`.
102
+
103
+ ```ruby
104
+ RailsAuditLog.configure do |config|
105
+ config.authenticate { |ctx| ctx[:current_user]&.admin? }
106
+ end
107
+ ```
108
+
109
+ If no authenticate block is set, all queries are permitted.
110
+
111
+ [↑ Back to top](#table-of-contents)
112
+
113
+ ## Development
114
+
115
+ ```bash
116
+ bin/setup # install dependencies
117
+ bundle exec rake # lint + tests
118
+ ```
119
+
120
+ [↑ Back to top](#table-of-contents)
121
+
122
+ ## Contributing
123
+
124
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
125
+
126
+ [↑ Back to top](#table-of-contents)
127
+
128
+ ## License
129
+
130
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/ROADMAP.md ADDED
@@ -0,0 +1,57 @@
1
+ # Roadmap
2
+
3
+ This gem adds a GraphQL API layer on top of [`rails_audit_log`](https://github.com/eclectic-coding/rails_audit_log). It is intentionally a separate gem so that `graphql-ruby` remains an optional dependency for host applications.
4
+
5
+ ---
6
+
7
+ ## 0.2.0 — Filtering & Connections
8
+
9
+ - **Time-range filters** — `since:` and `until:` arguments on `auditLogEntries`
10
+ - **`touching:` filter** — narrow results to entries that changed a specific attribute
11
+ - **Relay-style connection** — replace offset pagination with cursor-based `AuditLogEntryConnection` for forward/backward pagination
12
+ - **Sorting** — `orderBy: { field: CREATED_AT, direction: DESC }`
13
+
14
+ ---
15
+
16
+ ## 0.3.0 — Actor & Resource Resolver Types
17
+
18
+ - **`ActorType`** — resolve the polymorphic `actor` to the concrete type in the host app's schema (requires a configurable type resolver proc)
19
+ - **`AuditedResourceType`** — resolve `item_type`/`item_id` to the concrete audited model type
20
+ - **`diffType`** — structured `{ from, to }` diff type instead of raw `objectChanges` JSON
21
+
22
+ ---
23
+
24
+ ## 0.4.0 — Subscriptions
25
+
26
+ Requires Action Cable in the host application.
27
+
28
+ - **`auditLogEntryCreated(itemType:, itemId:)`** — subscribe to new entries for a specific record
29
+ - **`auditLogEntryCreated(actorId:)`** — subscribe to all entries by a specific actor
30
+ - Hooks into `RailsAuditLog::Streaming::NotificationsAdapter` to trigger broadcasts
31
+
32
+ ---
33
+
34
+ ## 0.5.0 — Multi-tenancy & Advanced Filtering
35
+
36
+ - **Tenant scoping** — automatically scope queries via `RailsAuditLog.current_tenant` when configured
37
+ - **`forTenant:` argument** — explicit tenant filter on `auditLogEntries`
38
+ - **Aggregations** — `auditLogEntriesCount(event:, itemType:, since:)` for dashboard metrics
39
+
40
+ ---
41
+
42
+ ## 0.6.0 — Performance & Safety
43
+
44
+ - **Dataloader batch loading** — batch-resolve polymorphic `actor` and `item` associations using graphql-ruby's `dataloader` to eliminate N+1 queries on list responses
45
+ - **`auditLogReify` query** — `auditLogReify(itemType:, itemId:, at:)` returns the reconstructed object state as JSON at a given point in time, backed by `RailsAuditLog.version_at`
46
+ - **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
47
+ - **`AuditLogJsonScalar`** — proper JSON scalar type for `objectChanges` and `metadata` fields, replacing opaque String serialization and making the schema self-documenting
48
+
49
+ ---
50
+
51
+ ## 1.0.0 — Stable API
52
+
53
+ - Full YARD documentation
54
+ - **RSpec matchers** — `expect(response).to have_graphql_audit_entry(:update).touching(:title)`
55
+ - **Minitest assertions** — `assert_graphql_audit_entry`
56
+ - API stability guarantee — no breaking changes without a major version bump
57
+ - Complete README with setup guide, examples, and schema reference
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "standard/rake"
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: [:standard, :spec]
data/codecov.yml ADDED
@@ -0,0 +1,17 @@
1
+ # Configure Pull Request Bot Comments
2
+ comment:
3
+ layout: "reach, diff, flags, files"
4
+ behavior: default
5
+ # Only post or update the comment if the coverage drops
6
+ require_changes: "coverage_drop"
7
+
8
+ # Configure Commit Status Checks (the green checkmark/red X list)
9
+ coverage:
10
+ status:
11
+ project:
12
+ default:
13
+ # Prevent "Informational" mode so a drop causes an actual red failure check
14
+ informational: false
15
+ patch:
16
+ default:
17
+ informational: false
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module RailsAuditLog
6
+ module Generators
7
+ module Graphql
8
+ class InstallGenerator < Rails::Generators::Base
9
+ source_root File.expand_path("templates", __dir__)
10
+ desc "Injects AuditLogEntriesQueryMixin into your GraphQL QueryType."
11
+
12
+ QUERY_TYPE_PATH = "app/graphql/types/query_type.rb"
13
+ MIXIN = "RailsAuditLog::Graphql::Queries::AuditLogEntriesQueryMixin"
14
+
15
+ def inject_mixin
16
+ if File.exist?(File.join(destination_root, QUERY_TYPE_PATH))
17
+ inject_into_file QUERY_TYPE_PATH,
18
+ " include #{MIXIN}\n",
19
+ after: /class\s+\S+\s*<\s*\S+\s*\n/
20
+ else
21
+ say ""
22
+ say "#{QUERY_TYPE_PATH} not found. Add this line manually to your QueryType:", :yellow
23
+ say " include #{MIXIN}", :green
24
+ end
25
+ end
26
+
27
+ def print_next_steps
28
+ say ""
29
+ say "Done! Your GraphQL API now has:", :green
30
+ say " auditLogEntry(id: ID!): AuditLogEntry"
31
+ say " auditLogEntries(...): [AuditLogEntry!]!"
32
+ say ""
33
+ say "See the README for full documentation."
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ module Queries
6
+ module AuditLogEntriesQueryMixin
7
+ def self.included(base)
8
+ base.field(
9
+ :audit_log_entry,
10
+ RailsAuditLog::Graphql::Types::AuditLogEntryType,
11
+ null: true,
12
+ description: "Fetch a single audit log entry by ID. Returns nil if not found.",
13
+ resolver_method: :resolve_audit_log_entry
14
+ ) do
15
+ argument :id, GraphQL::Types::ID, required: true,
16
+ description: "ID of the audit log entry."
17
+ end
18
+
19
+ base.field(
20
+ :audit_log_entries,
21
+ [RailsAuditLog::Graphql::Types::AuditLogEntryType, null: false],
22
+ null: false,
23
+ description: "List audit log entries with optional filters. Offset-paginated.",
24
+ resolver_method: :resolve_audit_log_entries
25
+ ) do
26
+ argument :event, String, required: false, description: "Filter by event type (create, update, destroy)."
27
+ argument :item_type, String, required: false, description: "Filter by audited model class name."
28
+ argument :item_id, GraphQL::Types::ID, required: false, description: "Filter by audited record ID."
29
+ argument :actor_id, GraphQL::Types::ID, required: false, description: "Filter by actor ID."
30
+ argument :page, GraphQL::Types::Int, required: false, default_value: 1, description: "Page number (1-based)."
31
+ argument :per_page, GraphQL::Types::Int, required: false, default_value: 25, description: "Number of results per page."
32
+ end
33
+ end
34
+
35
+ def resolve_audit_log_entry(id:)
36
+ check_authentication!
37
+ RailsAuditLog::AuditLogEntry.find_by(id: id)
38
+ end
39
+
40
+ def resolve_audit_log_entries(event: nil, item_type: nil, item_id: nil, actor_id: nil, page: 1, per_page: 25)
41
+ check_authentication!
42
+ scope = RailsAuditLog::AuditLogEntry.order(created_at: :desc)
43
+ scope = scope.where(event: event) if event
44
+ scope = scope.where(item_type: item_type) if item_type
45
+ scope = scope.where(item_id: item_id) if item_id
46
+ scope = scope.where(actor_id: actor_id) if actor_id
47
+ scope.limit(per_page).offset((page - 1) * per_page)
48
+ end
49
+
50
+ private
51
+
52
+ def check_authentication!
53
+ auth = RailsAuditLog.authenticate
54
+ return unless auth
55
+ raise GraphQL::ExecutionError, "Unauthorized" unless auth.call(context)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ module RailsAuditLog
6
+ module Graphql
7
+ # @api private
8
+ module ReleaseTooling
9
+ module_function
10
+
11
+ VERSION_PATTERN = /VERSION = "[^"]+"/
12
+ SEMVER_PATTERN = /\A\d+\.\d+\.\d+(?:[-.][0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?\z/
13
+ UNRELEASED_HEADING = "## [Unreleased]"
14
+
15
+ def normalize_version(version)
16
+ normalized = version.to_s.sub(/\Av/, "")
17
+ raise ArgumentError, "version must look like x.y.z" unless normalized.match?(SEMVER_PATTERN)
18
+
19
+ normalized
20
+ end
21
+
22
+ def update_version_file(contents, version)
23
+ normalized = normalize_version(version)
24
+ raise ArgumentError, "version file does not define VERSION" unless contents.match?(VERSION_PATTERN)
25
+
26
+ contents.sub(VERSION_PATTERN, %(VERSION = "#{normalized}"))
27
+ end
28
+
29
+ def finalize_changelog(contents, version, date = Date.today)
30
+ normalized = normalize_version(version)
31
+ release_heading = "## [#{normalized}] - #{date.iso8601}"
32
+
33
+ raise ArgumentError, "CHANGELOG.md must contain an Unreleased heading" unless contents.include?(UNRELEASED_HEADING)
34
+
35
+ if contents.match?(/^## \[#{Regexp.escape(normalized)}\](?: - .+)?$/)
36
+ raise ArgumentError, "CHANGELOG.md already contains #{normalized}"
37
+ end
38
+
39
+ contents.sub(UNRELEASED_HEADING, "#{UNRELEASED_HEADING}\n\n#{release_heading}")
40
+ end
41
+
42
+ # Removes milestone sections from ROADMAP.md where all feature bullets
43
+ # have been implemented (i.e. no remaining "- **" bullet points).
44
+ def prune_roadmap(contents)
45
+ separator = "\n\n---\n\n"
46
+ sections = contents.split(separator)
47
+
48
+ pruned = sections.reject do |section|
49
+ next false unless section.lstrip.match?(/\A## \d+\.\d+\.\d+/)
50
+
51
+ !section.include?("- **")
52
+ end
53
+
54
+ pruned.join(separator)
55
+ end
56
+
57
+ def extract_release_notes(contents, version)
58
+ normalized = normalize_version(version)
59
+ lines = contents.lines
60
+ release_heading = /^## \[#{Regexp.escape(normalized)}\](?: - .+)?$/
61
+ start_index = lines.index { |line| line.match?(release_heading) }
62
+
63
+ raise ArgumentError, "CHANGELOG.md is missing release notes for #{normalized}" unless start_index
64
+
65
+ body = lines[(start_index + 1)..].take_while { |line| !line.start_with?("## [") }.join.strip
66
+ body = "- No changes listed." if body.empty?
67
+
68
+ <<~MARKDOWN
69
+ ## RailsAuditLog::Graphql #{normalized}
70
+
71
+ #{body}
72
+ MARKDOWN
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ module Types
6
+ class AuditLogEntryType < BaseObject
7
+ graphql_name "AuditLogEntry"
8
+ description "A single audited event on an ActiveRecord model."
9
+
10
+ field :id, GraphQL::Types::ID, null: false
11
+ field :event, String, null: false
12
+ field :item_type, String, null: false
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
17
+ field :reason, String, null: true
18
+ field :whodunnit_snapshot, String, null: true
19
+ field :actor_type, String, null: true
20
+ field :actor_id, GraphQL::Types::ID, null: true
21
+ field :tenant_id, String, null: true
22
+ field :created_at, GraphQL::Types::ISO8601DateTime, null: false
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ module Types
6
+ class BaseObject < GraphQL::Schema::Object
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAuditLog
4
+ module Graphql
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql"
4
+ require_relative "graphql/version"
5
+ require_relative "graphql/types/base_object"
6
+ require_relative "graphql/types/audit_log_entry_type"
7
+ require_relative "graphql/queries/audit_log_entries_query_mixin"
8
+
9
+ module RailsAuditLog
10
+ module Graphql
11
+ class Error < StandardError; end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ module RailsAuditLog
2
+ module Graphql
3
+ VERSION: String
4
+
5
+ module Types
6
+ class BaseObject < GraphQL::Schema::Object
7
+ end
8
+
9
+ class AuditLogEntryType < BaseObject
10
+ end
11
+ end
12
+
13
+ module Generators
14
+ module Graphql
15
+ class InstallGenerator < ::Rails::Generators::Base
16
+ def inject_mixin: () -> void
17
+ def print_next_steps: () -> void
18
+ end
19
+ end
20
+ end
21
+
22
+ module Queries
23
+ module AuditLogEntriesQueryMixin
24
+ def self.included: (untyped base) -> void
25
+ def resolve_audit_log_entry: (id: String) -> untyped
26
+ def resolve_audit_log_entries: (?event: String?, ?item_type: String?, ?item_id: String?, ?actor_id: String?, ?page: Integer, ?per_page: Integer) -> untyped
27
+ private
28
+ def check_authentication!: () -> void
29
+ end
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_audit_log-graphql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chuck Smith
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: graphql
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rails_audit_log
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.4'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.4'
40
+ - !ruby/object:Gem::Dependency
41
+ name: simplecov
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.22'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.22'
54
+ - !ruby/object:Gem::Dependency
55
+ name: simplecov_json_formatter
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.1'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.1'
68
+ description: Provides audit logging for GraphQL mutations and queries in Rails applications.
69
+ email:
70
+ - chuck@eclecticcoding.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".standard.yml"
76
+ - CHANGELOG.md
77
+ - CLAUDE.md
78
+ - CONTRIBUTING.md
79
+ - LICENSE.txt
80
+ - README.md
81
+ - ROADMAP.md
82
+ - Rakefile
83
+ - codecov.yml
84
+ - lib/generators/rails_audit_log/graphql/install/install_generator.rb
85
+ - lib/rails_audit_log/graphql.rb
86
+ - lib/rails_audit_log/graphql/queries/audit_log_entries_query_mixin.rb
87
+ - lib/rails_audit_log/graphql/release_tooling.rb
88
+ - lib/rails_audit_log/graphql/types/audit_log_entry_type.rb
89
+ - lib/rails_audit_log/graphql/types/base_object.rb
90
+ - lib/rails_audit_log/graphql/version.rb
91
+ - sig/rails_audit_log/graphql.rbs
92
+ homepage: https://github.com/eclectic-coding/rails_audit_log-graphql
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ allowed_push_host: https://rubygems.org
97
+ homepage_uri: https://github.com/eclectic-coding/rails_audit_log-graphql
98
+ source_code_uri: https://github.com/eclectic-coding/rails_audit_log-graphql
99
+ changelog_uri: https://github.com/eclectic-coding/rails_audit_log-graphql/blob/main/CHANGELOG.md
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 3.2.0
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.6.9
115
+ specification_version: 4
116
+ summary: GraphQL audit logging for Rails applications.
117
+ test_files: []