Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# grape-swagger-entity

Adapter for grape-swagger that parses Grape::Entity classes into OpenAPI schema definitions.

## Quick Reference

```bash
bundle install # Install dependencies
bundle exec rspec # Run tests
bundle exec rubocop # Run linter
bundle exec rubocop -a # Auto-fix linter issues
```

## Key Constraints

- Ruby >= 3.0
- All files must start with `# frozen_string_literal: true`
- CHANGELOG.md entry required for every PR (danger enforces this)

## Documentation

- [Testing Patterns](docs/testing.md)
- [Contributing Guidelines](docs/contributing.md)
- [Architecture Overview](docs/architecture.md)
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### Features

* Your contribution here.
* [#91](https://github.com/ruby-grape/grape-swagger-entity/pull/91): Improve README and add documentation structure - [@numbata](https://github.com/numbata).

#### Fixes

Expand Down
109 changes: 102 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,64 @@
## Table of Contents

- [What is grape-swagger-entity?](#what-is-grape-swagger-entity)
- [What it does](#what-it-does)
- [Example Output](#example-output)
- [Related Projects](#related-projects)
- [Compatibility](#compatibility)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Entity](#basic-entity)
- [Custom Model Description](#custom-model-description)
- [Entity References](#entity-references)
- [Documentation Options](#documentation-options)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)


## What is grape-swagger-entity?

A simple grape-swagger adapter to allow parse representers as response model
This gem provides an adapter for [grape-swagger](https://github.com/ruby-grape/grape-swagger) that allows parsing [grape-entity](https://github.com/ruby-grape/grape-entity) classes to generate OpenAPI/Swagger model definitions automatically.

### What it does

- Generates `definitions` in your Swagger JSON from Grape::Entity exposures
- Maps entity properties to OpenAPI schema properties with types and descriptions
- Handles nested entities and entity references via `$ref`
- Supports arrays, required fields, and documentation options

### Example Output

```json
{
"definitions": {
"User": {
"type": "object",
"description": "User model",
"properties": {
"id": { "type": "integer", "description": "User ID" },
"name": { "type": "string", "description": "Full name" }
},
"required": ["id", "name"]
}
}
}
```

## Related Projects

- [Grape](https://github.com/ruby-grape/grape)
- [Grape Entity](https://github.com/ruby-grape/grape-entity)
- [Grape Swagger](https://github.com/ruby-grape/grape-swagger)
- [Grape Swagger Representable](https://github.com/ruby-grape/grape-swagger-representable)

## Compatibility

This gem is tested with the following versions:

| grape-swagger-entity | grape-swagger | grape-entity | grape |
|---------------------|---------------|--------------|---------|
| 0.7.x | >= 2.0.0 | >= 1.0.0 | >= 1.3 |
| 0.6.x | >= 1.2.0 | >= 0.6.0 | >= 1.3 |

## Installation

Expand All @@ -32,17 +81,63 @@ Or install it yourself as:

$ gem install grape-swagger-entity

## Development
## Usage

### Basic Entity

Define your entities with `documentation` options to generate OpenAPI schema properties:

```ruby
class UserEntity < Grape::Entity
expose :id, documentation: { type: Integer, desc: 'User ID' }
expose :name, documentation: { type: String, desc: 'Full name' }
expose :email, documentation: { type: String, desc: 'Email address' }
end
```

### Custom Model Description

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/pry` for an interactive prompt that will allow you to experiment.
Override the default "{ModelName} model" description by defining a `self.documentation` method. This feature is handled by [grape-swagger](https://github.com/ruby-grape/grape-swagger) (requires grape-swagger >= 2.2.0):

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
```ruby
class UserEntity < Grape::Entity
def self.documentation
{ desc: 'Represents a user account with profile information' }
end

expose :id, documentation: { type: Integer, desc: 'User ID' }
expose :name, documentation: { type: String, desc: 'Full name' }
end
```

### Entity References

Use `using:` to reference other entities and `is_array:` for collections:

```ruby
class OrderEntity < Grape::Entity
expose :id, documentation: { type: Integer, desc: 'Order ID' }
expose :user, using: UserEntity,
documentation: { desc: 'The customer who placed this order' }
expose :items, using: ItemEntity,
documentation: { desc: 'Line items', is_array: true }
end
```

### Documentation Options

Common options: `type`, `desc`, `required`, `is_array`, `values`, `example`.

See [full documentation options](docs/documentation-options.md) for all available options including validation constraints.

## Development

See [testing documentation](docs/testing.md) for development setup and running tests.

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby-grape/grape-swagger-entity.
See [contributing guidelines](docs/contributing.md).

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).

71 changes: 71 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Architecture Overview

## Purpose

This gem registers a model parser with grape-swagger that converts `Grape::Entity` classes into OpenAPI schema definitions.

## Core Components

```
lib/grape-swagger/entity/
├── parser.rb # Main parser - converts Entity to OpenAPI schema
├── attribute_parser.rb # Parses individual exposure attributes
├── helper.rb # Utility methods for model naming, discriminators
└── version.rb # Gem version
```

### Parser (`parser.rb`)

Entry point for entity parsing. Responsibilities:
- Extract exposures from Grape::Entity
- Handle nested entities and `using:` references
- Process `merge: true` exposures
- Handle inheritance with `allOf` and discriminators
- Determine required fields

### AttributeParser (`attribute_parser.rb`)

Converts individual exposure options to OpenAPI property schema:
- Maps Ruby types to OpenAPI types
- Handles arrays (`is_array: true`)
- Processes enums (`values:`)
- Adds constraints (min/max, minLength/maxLength)

### Helper (`helper.rb`)

Utilities for:
- Model name resolution (strips Entity/Entities suffix)
- Discriminator detection for inheritance
- Root exposure extraction

## Data Flow

```
Grape::Entity class
Parser.call
├── extract_params (get exposures)
├── parse_grape_entity_params
│ │
│ ├── AttributeParser (per exposure)
│ │
│ └── parse_nested (for nested blocks)
└── handle_discriminator (inheritance)
[properties_hash, required_array]
```

## Integration Point

Registered with grape-swagger in `lib/grape-swagger/entity.rb`:

```ruby
GrapeSwagger.model_parsers.register(GrapeSwagger::Entity::Parser, Grape::Entity)
```

grape-swagger calls `Parser.new(model, endpoint).call` when it encounters a `Grape::Entity` subclass.
57 changes: 57 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Contributing Guidelines

## PR Requirements

Every PR must include:

1. **CHANGELOG.md entry** - Add under `### X.X.X (Next)` in the appropriate section:
```markdown
#### Fixes
* [#123](https://github.com/ruby-grape/grape-swagger-entity/pull/123): Brief description - [@username](https://github.com/username).
```

2. **Passing CI** - RuboCop + RSpec must pass

3. **Tests** - New functionality needs specs; bug fixes need regression tests

## CHANGELOG Format

```markdown
### 0.7.1 (Next)

#### Features

* Your contribution here.

#### Fixes

* [#PR](URL): Description - [@author](URL).
```

- Use `Features` for new functionality
- Use `Fixes` for bug fixes and maintenance

## Commit Messages

Follow conventional style:
- `Fix #123: description` for bug fixes
- `Add feature description` for features
- `Update dependency/docs` for maintenance

## Code Style

RuboCop enforces style. Key rules:
- `frozen_string_literal: true` pragma required
- Consistent hash indentation
- No trailing whitespace

```bash
bundle exec rubocop -a # Auto-fix most issues
```

## Danger Checks

CI runs danger which enforces:
- CHANGELOG entry present
- Table of Contents in README is current
- PR has description
89 changes: 89 additions & 0 deletions docs/documentation-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Documentation Options

The `documentation` hash in entity exposures supports the following options:

## Type Options

| Option | Description | Example |
|--------|-------------|---------|
| `type` | OpenAPI data type | `String`, `Integer`, `Boolean`, `Float`, `Date`, `DateTime` |
| `is_array` | Marks field as array type | `true` / `false` |

## Description Options

| Option | Description | Example |
|--------|-------------|---------|
| `desc` | Property description | `'User email address'` |
| `example` | Example value for documentation | `'user@example.com'` |
| `default` | Default value | `'pending'` |

## Validation Options

| Option | Description | Example |
|--------|-------------|---------|
| `required` | Whether field is required | `true` / `false` |
| `values` | Enum values (array or proc) | `%w[pending active]` or `-> { Status.all }` |
| `minimum` | Minimum value for numeric types | `0` |
| `maximum` | Maximum value for numeric types | `100` |
| `min_length` | Minimum length for strings | `1` |
| `max_length` | Maximum length for strings | `255` |
| `min_items` | Minimum items for arrays | `1` |
| `max_items` | Maximum items for arrays | `10` |
| `unique_items` | Array items must be unique | `true` |

## Display Options

| Option | Description | Example |
|--------|-------------|---------|
| `read_only` | Marks field as read-only | `true` |
| `hidden` | Hide field from documentation | `true` |

## Examples

### Basic types

```ruby
expose :id, documentation: { type: Integer, desc: 'Unique identifier' }
expose :name, documentation: { type: String, desc: 'Full name', required: true }
expose :active, documentation: { type: Boolean, default: true }
```

### Enums

```ruby
expose :status, documentation: {
type: String,
desc: 'Current status',
values: %w[pending active suspended]
}
```

### Numeric constraints

```ruby
expose :age, documentation: {
type: Integer,
minimum: 0,
maximum: 150
}
```

### String constraints

```ruby
expose :username, documentation: {
type: String,
min_length: 3,
max_length: 20
}
```

### Arrays

```ruby
expose :tags, documentation: {
type: String,
is_array: true,
desc: 'Associated tags'
}
```
Loading