propel_api 0.3.0 → 0.3.1.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 +4 -4
- data/CHANGELOG.md +47 -0
- data/README.md +8 -8
- data/lib/generators/propel_api/controller/controller_generator.rb +26 -0
- data/lib/generators/propel_api/core/named_base.rb +5 -0
- data/lib/generators/propel_api/install/install_generator.rb +37 -0
- data/lib/generators/propel_api/templates/controllers/api_base_controller.rb +75 -0
- data/lib/generators/propel_api/templates/controllers/api_controller_graphiti.rb +1 -1
- data/lib/generators/propel_api/templates/controllers/api_controller_propel_facets.rb +2 -2
- data/lib/generators/propel_api/templates/errors/propel_api_csrf_error.rb +19 -0
- data/lib/generators/propel_api/templates/scaffold/facet_controller_template.rb.tt +18 -10
- data/lib/generators/propel_api/templates/scaffold/facet_model_template.rb.tt +26 -2
- data/lib/generators/propel_api/templates/seeds/seeds_template.rb.tt +3 -3
- data/lib/generators/propel_api/templates/tests/controller_test_template.rb.tt +12 -6
- data/lib/generators/propel_api/templates/tests/fixtures_template.yml.tt +3 -3
- data/lib/generators/propel_api/templates/tests/integration_test_template.rb.tt +48 -8
- data/lib/generators/propel_api/templates/tests/model_test_template.rb.tt +2 -2
- data/lib/propel_api.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0678229260f4bf549c0400eb94198b37474caf360b1abf8812fbd126b5b99ab2'
|
4
|
+
data.tar.gz: db237ccf7c112fe8ed510236e2eba216e3cc8cb3fa1529b1d549f9fe00e6200f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e4d691dfc7ddcb82c9bac1991aabf43d77a8d0b03f5604744e1c07fac90a122c2baf1667e0ea753bcf71dfde57e8aa45513b1bfc55aed34f9aca4b0694f6d44
|
7
|
+
data.tar.gz: 776098346e44a9b1aa68d28507d9cb0e4dbffdea4ebd08711c66d1906501d372020107ba877fbce8ab32469c745f7c540362b7b015fee249eec25db4388e8e78
|
data/CHANGELOG.md
CHANGED
@@ -10,6 +10,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
10
10
|
### Planned Features
|
11
11
|
- GraphQL adapter support
|
12
12
|
|
13
|
+
## [0.3.1.1] - 2025-09-11
|
14
|
+
|
15
|
+
### 🐛 Bug Fixes
|
16
|
+
- **PostgreSQL Migration Generation**: Fixed migration generation bug that caused issues with PostgreSQL database constraints
|
17
|
+
- Corrected polymorphic migration templates to use proper PostgreSQL-compatible syntax
|
18
|
+
- Enhanced migration generation to handle PostgreSQL foreign key constraints correctly
|
19
|
+
- Fixed issues with polymorphic associations causing migration failures on PostgreSQL
|
20
|
+
|
21
|
+
## [0.3.1] - 2025-09-11
|
22
|
+
|
23
|
+
### 🎉 New Features
|
24
|
+
- **Full Stack and API-Only Rails App Support**: Enhanced architecture for different Rails application types
|
25
|
+
- New dedicated `api_base_controller.rb` template for API-only applications
|
26
|
+
- Improved controller template organization with better separation of concerns
|
27
|
+
- Enhanced CSRF error handling with dedicated `PropelApiCsrfError` class
|
28
|
+
- Better support for both full-stack Rails apps and API-only applications
|
29
|
+
|
30
|
+
### 🔧 Enhanced Polymorphic Support
|
31
|
+
- **Improved Polymorphic Generators**: Enhanced automatic fixture and test generation with --parents flag
|
32
|
+
- Better polymorphic association handling in controller and model generators
|
33
|
+
- Automatic fixture template adoption for polymorphic relationships
|
34
|
+
- Enhanced agency-organization relationship support in polymorphic contexts
|
35
|
+
- **URL/Website/Domain Support**: Enhanced template handling for URL, website, and domain fields
|
36
|
+
- Improved model templates for URL validation and formatting
|
37
|
+
- Better seed data generation for URL-type fields
|
38
|
+
- Enhanced test coverage for URL/domain fields in fixtures and integration tests
|
39
|
+
|
40
|
+
### 🛠️ Generator Improvements
|
41
|
+
- **Enhanced Core Named Base**: Improved polymorphic association detection and fixture generation
|
42
|
+
- Better template processing for polymorphic relationships
|
43
|
+
- Enhanced fixture name resolution and parent type selection
|
44
|
+
- Improved error handling for polymorphic parent specifications
|
45
|
+
- **Template System Enhancements**: Upgraded all ERB templates for better polymorphic handling
|
46
|
+
- Consistent fixture template adoption across all generators
|
47
|
+
- Better integration with PropelAuthentication fixture patterns
|
48
|
+
- Enhanced test template generation with polymorphic parameter support
|
49
|
+
- **Controller Architecture Improvements**: Enhanced controller template system
|
50
|
+
- Updated Graphiti and PropelFacets controller templates for better inheritance
|
51
|
+
- Improved controller base class organization and method visibility
|
52
|
+
|
53
|
+
### 🐛 Bug Fixes
|
54
|
+
- **Gem Installation**: Fixed issues with gems not being included when commented out in Gemfile
|
55
|
+
- **Integration Test Templates**: Enhanced error handling and parameter validation in generated integration tests
|
56
|
+
|
57
|
+
### ⚠️ Breaking Changes
|
58
|
+
- **REQUIRED --parents for Polymorphic**: Polymorphic associations now require explicit `--parents Parent1,Parent2` declaration. Auto-discovery fallback has been removed. Generator will block with confirmation if --parents is missing.
|
59
|
+
|
13
60
|
## [0.3.0] - 2025-01-15
|
14
61
|
|
15
62
|
### 🎉 Major Features Added
|
data/README.md
CHANGED
@@ -54,11 +54,11 @@ rails generate propel_api:resource User name:string email:string role:string
|
|
54
54
|
# Resource with associations
|
55
55
|
rails generate propel_api:resource Article title:string content:text user:references category:references
|
56
56
|
|
57
|
-
# 🎉 NEW: Polymorphic associations (v0.3.0)
|
58
|
-
rails generate propel_api:resource Comment content:text commentable:references{polymorphic} --parents
|
57
|
+
# 🎉 NEW: Polymorphic associations (v0.3.0) - REQUIRES --parents flag
|
58
|
+
rails generate propel_api:resource Comment content:text commentable:references{polymorphic} --parents Post,Video,Product
|
59
59
|
|
60
60
|
# Polymorphic with tenancy
|
61
|
-
rails generate propel_api:resource Review rating:integer comment:text review_parent:polymorphic --parents
|
61
|
+
rails generate propel_api:resource Review rating:integer comment:text review_parent:polymorphic --parents Product,Agency
|
62
62
|
|
63
63
|
# With Graphiti adapter
|
64
64
|
rails generate propel_api Product name:string price:decimal --adapter=graphiti
|
@@ -81,10 +81,10 @@ PropelApi now provides comprehensive support for polymorphic associations with a
|
|
81
81
|
|
82
82
|
```bash
|
83
83
|
# Generate a Comment that can belong to Posts, Videos, or Products
|
84
|
-
rails generate propel_api:resource Comment body:text commentable:references{polymorphic} --parents
|
84
|
+
rails generate propel_api:resource Comment body:text commentable:references{polymorphic} --parents Post,Video,Product
|
85
85
|
|
86
86
|
# Generate a Review with full tenancy support
|
87
|
-
rails generate propel_api:resource Review rating:integer comment:text review_parent:references{polymorphic} --parents
|
87
|
+
rails generate propel_api:resource Review rating:integer comment:text review_parent:references{polymorphic} --parents Product,Agency
|
88
88
|
```
|
89
89
|
|
90
90
|
### Key Features
|
@@ -93,10 +93,10 @@ rails generate propel_api:resource Review rating:integer comment:text review_par
|
|
93
93
|
- Additional support `field_name:polymorphic` instead of Rails' verbose `field_name:references{polymorphic}`
|
94
94
|
- No need for complex quote escaping or nested syntax
|
95
95
|
|
96
|
-
#### 🎯
|
97
|
-
- `--parents
|
96
|
+
#### 🎯 Required Parent Specification
|
97
|
+
- `--parents Parent1,Parent2,Parent3` defines valid parent models (REQUIRED for polymorphic associations)
|
98
98
|
- Automatically generates varied test data using different parent types
|
99
|
-
-
|
99
|
+
- Generator will block with confirmation if --parents is not provided
|
100
100
|
|
101
101
|
#### 🧪 Complete Test Coverage
|
102
102
|
- **Model Tests**: Proper polymorphic association validation and parent type checking
|
@@ -48,6 +48,11 @@ module PropelApi
|
|
48
48
|
type: :boolean,
|
49
49
|
default: false,
|
50
50
|
desc: "Include usage comments in generated controller"
|
51
|
+
|
52
|
+
class_option :parents,
|
53
|
+
type: :hash,
|
54
|
+
default: {},
|
55
|
+
desc: "Specify parent types for polymorphic associations (e.g., --parents commentable:Post,Photo,Video)"
|
51
56
|
|
52
57
|
def validate_model_exists
|
53
58
|
# Ensure attributes are parsed before validation
|
@@ -64,6 +69,9 @@ module PropelApi
|
|
64
69
|
if !options[:all_attributes] && (@attributes.nil? || @attributes.empty?) && model_file_exists
|
65
70
|
show_auto_introspection_warning
|
66
71
|
end
|
72
|
+
# Process polymorphic parent information if provided
|
73
|
+
process_polymorphic_parents if options[:parents].present?
|
74
|
+
|
67
75
|
# Validate attributes if not using introspection
|
68
76
|
validate_attributes_exist unless should_auto_introspect?
|
69
77
|
end
|
@@ -110,6 +118,24 @@ module PropelApi
|
|
110
118
|
|
111
119
|
private
|
112
120
|
|
121
|
+
def process_polymorphic_parents
|
122
|
+
# Convert _type attributes to polymorphic associations for template processing
|
123
|
+
options[:parents].each do |field_name, parent_list|
|
124
|
+
# Check if we have the corresponding _id and _type attributes
|
125
|
+
type_attr = @attributes.find { |attr| attr.name == "#{field_name}_type" }
|
126
|
+
id_attr = @attributes.find { |attr| attr.name == "#{field_name}_id" }
|
127
|
+
|
128
|
+
if type_attr && id_attr
|
129
|
+
# Add a mock polymorphic attribute to @attributes for template processing
|
130
|
+
@attributes << OpenStruct.new(
|
131
|
+
name: field_name,
|
132
|
+
type: :references,
|
133
|
+
polymorphic?: true
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
113
139
|
def route_name
|
114
140
|
file_name.pluralize
|
115
141
|
end
|
@@ -527,6 +527,11 @@ module PropelApi
|
|
527
527
|
@attributes.any? { |attr| attr.name == 'agency' && attr.type == :references }
|
528
528
|
end
|
529
529
|
|
530
|
+
def has_user_reference?
|
531
|
+
return false unless defined?(@attributes) && @attributes
|
532
|
+
@attributes.any? { |attr| (attr.name == 'user' && attr.type == :references) || attr.name == 'user_id' }
|
533
|
+
end
|
534
|
+
|
530
535
|
# Polymorphic associations helper methods
|
531
536
|
def polymorphic_parent_types
|
532
537
|
@polymorphic_parent_types ||= parse_polymorphic_parent_types
|
@@ -31,6 +31,12 @@ module PropelApi
|
|
31
31
|
else
|
32
32
|
say "Installing API controller with #{@adapter} adapter", :green
|
33
33
|
say "Using namespace: #{namespace_display} version: #{version_display}", :blue
|
34
|
+
|
35
|
+
# Create Api::BaseController first
|
36
|
+
create_api_base_controller
|
37
|
+
|
38
|
+
# Optionally enhance CSRF error messages (non-breaking)
|
39
|
+
add_csrf_error_handling
|
34
40
|
end
|
35
41
|
|
36
42
|
case @adapter
|
@@ -107,6 +113,37 @@ module PropelApi
|
|
107
113
|
|
108
114
|
private
|
109
115
|
|
116
|
+
def create_api_base_controller
|
117
|
+
template "controllers/api_base_controller.rb", "app/controllers/api/base_controller.rb"
|
118
|
+
say "Created Api::BaseController for CSRF-free API endpoints", :green
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_csrf_error_handling
|
122
|
+
# Copy the error handler
|
123
|
+
template "errors/propel_api_csrf_error.rb", "app/errors/propel_api_csrf_error.rb"
|
124
|
+
|
125
|
+
# Inject rescue_from into ApplicationController
|
126
|
+
inject_into_class "app/controllers/application_controller.rb", ApplicationController, <<-RUBY
|
127
|
+
# Enhanced CSRF error handling for JSON requests - added by PropelAPI
|
128
|
+
rescue_from ActionController::InvalidAuthenticityToken, with: :handle_propel_csrf_error
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def handle_propel_csrf_error
|
133
|
+
if request.format.json? || request.content_type&.include?('application/json')
|
134
|
+
PropelApiCsrfError.handle_csrf_error_for_json(self)
|
135
|
+
else
|
136
|
+
raise ActionController::InvalidAuthenticityToken
|
137
|
+
end
|
138
|
+
end
|
139
|
+
RUBY
|
140
|
+
|
141
|
+
say "Enhanced ApplicationController with helpful API guidance", :green
|
142
|
+
rescue => e
|
143
|
+
say "Could not inject CSRF error handling into ApplicationController: #{e.message}", :yellow
|
144
|
+
say "You can manually add the rescue_from handler if desired", :blue
|
145
|
+
end
|
146
|
+
|
110
147
|
def copy_propel_facets_controller
|
111
148
|
template "controllers/api_controller_propel_facets.rb", api_controller_path
|
112
149
|
copy_example_controller
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# Base controller for API endpoints ONLY
|
5
|
+
#
|
6
|
+
# ⚠️ SECURITY WARNING: This controller disables CSRF protection!
|
7
|
+
# ⚠️ ONLY use this for stateless JSON API endpoints with JWT authentication
|
8
|
+
# ⚠️ DO NOT use this for web controllers that serve HTML or handle sessions
|
9
|
+
#
|
10
|
+
# For web controllers, inherit from ApplicationController instead.
|
11
|
+
#
|
12
|
+
class Api::BaseController < ActionController::Base
|
13
|
+
# Set up and then skip CSRF protection for API endpoints (they use JWT tokens instead)
|
14
|
+
protect_from_forgery with: :null_session
|
15
|
+
skip_before_action :verify_authenticity_token
|
16
|
+
|
17
|
+
# Disable session handling for API requests (stateless)
|
18
|
+
before_action :disable_session
|
19
|
+
before_action :ensure_json_request, if: -> { Rails.env.production? }
|
20
|
+
|
21
|
+
# Handle common API errors
|
22
|
+
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
|
23
|
+
rescue_from ActionController::ParameterMissing, with: :parameter_missing
|
24
|
+
rescue_from ActiveRecord::RecordInvalid, with: :record_invalid
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def disable_session
|
29
|
+
request.session_options[:skip] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def ensure_json_request
|
33
|
+
unless request.format.json? || request.content_type&.include?('application/json')
|
34
|
+
Rails.logger.warn "Non-JSON request to API controller: #{controller_name}##{action_name}"
|
35
|
+
render json: {
|
36
|
+
error: 'Invalid request format',
|
37
|
+
message: 'This endpoint only accepts JSON requests'
|
38
|
+
}, status: :not_acceptable
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def record_not_found(exception)
|
43
|
+
Rails.logger.error "API Record not found: #{exception.message}"
|
44
|
+
render json: {
|
45
|
+
error: 'Record not found',
|
46
|
+
message: 'The requested resource could not be found'
|
47
|
+
}, status: :not_found
|
48
|
+
end
|
49
|
+
|
50
|
+
def parameter_missing(exception)
|
51
|
+
Rails.logger.error "API Parameter missing: #{exception.message}"
|
52
|
+
render json: {
|
53
|
+
error: 'Missing required parameters',
|
54
|
+
message: 'Required parameters are missing from the request'
|
55
|
+
}, status: :bad_request
|
56
|
+
end
|
57
|
+
|
58
|
+
def record_invalid(exception)
|
59
|
+
Rails.logger.error "API Record invalid: #{exception.record.errors.full_messages}"
|
60
|
+
|
61
|
+
# In development, show full validation errors for debugging
|
62
|
+
# In production, show generic message to prevent information disclosure
|
63
|
+
errors = if Rails.env.development?
|
64
|
+
exception.record.errors.full_messages
|
65
|
+
else
|
66
|
+
['Validation failed - please check your input']
|
67
|
+
end
|
68
|
+
|
69
|
+
render json: {
|
70
|
+
error: 'Validation failed',
|
71
|
+
errors: errors
|
72
|
+
}, status: :unprocessable_content
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class <%= api_controller_class_name %> <
|
1
|
+
class <%= api_controller_class_name %> < Api::BaseController
|
2
2
|
include HasScope
|
3
3
|
include Pagy::Backend
|
4
4
|
include FacetRenderer
|
@@ -216,7 +216,7 @@ class <%= api_controller_class_name %> < ApplicationController
|
|
216
216
|
def agency_tenancy_enabled?
|
217
217
|
# Check PropelAuthentication configuration (owns tenancy models)
|
218
218
|
if defined?(PropelAuthentication) && PropelAuthentication.respond_to?(:configuration)
|
219
|
-
PropelAuthentication.configuration.
|
219
|
+
PropelAuthentication.configuration.agency_required?
|
220
220
|
else
|
221
221
|
true # Safe default - enables agency tenancy when configuration unavailable
|
222
222
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# PropelAPI CSRF Error Handler
|
5
|
+
# Provides helpful guidance when developers send JSON requests to web controllers
|
6
|
+
#
|
7
|
+
class PropelApiCsrfError < StandardError
|
8
|
+
def self.handle_csrf_error_for_json(controller)
|
9
|
+
# Log security event for monitoring
|
10
|
+
Rails.logger.warn "CSRF bypass attempted on #{controller.controller_name}##{controller.action_name} from #{controller.request.remote_ip}"
|
11
|
+
|
12
|
+
controller.render json: {
|
13
|
+
error: "CSRF token verification failed",
|
14
|
+
message: "This endpoint requires CSRF protection and is intended for web browser requests.",
|
15
|
+
hint: "For API requests, use the Propel-generated API controllers instead.",
|
16
|
+
note: "API controllers are CSRF-free and use JWT authentication."
|
17
|
+
}, status: :unprocessable_content
|
18
|
+
end
|
19
|
+
end
|
@@ -12,38 +12,46 @@ class <%= controller_class_name_with_namespace %>Controller < <%= api_controller
|
|
12
12
|
<% end -%>
|
13
13
|
permitted_params <%
|
14
14
|
# Generate permitted parameters using shared polymorphic support
|
15
|
-
|
15
|
+
regular_params = []
|
16
|
+
json_params = []
|
16
17
|
|
17
18
|
# Add attribute-based params using polymorphic support
|
18
19
|
attributes.each do |attr|
|
19
20
|
if attr.type == :json
|
20
|
-
|
21
|
+
json_params << "#{attr.name}: {}"
|
21
22
|
elsif attr.type == :references
|
22
23
|
# Use Rails' built-in polymorphic detection
|
23
24
|
if attr.respond_to?(:polymorphic?) && attr.polymorphic?
|
24
|
-
|
25
|
-
|
25
|
+
regular_params << ":#{attr.name}_id"
|
26
|
+
regular_params << ":#{attr.name}_type"
|
26
27
|
else
|
27
|
-
|
28
|
+
regular_params << ":#{attr.name}_id"
|
28
29
|
end
|
29
30
|
else
|
30
|
-
|
31
|
+
regular_params << ":#{attr.name}"
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
# Add multi-tenancy params unless skipped
|
35
36
|
if has_organization_reference?
|
36
|
-
|
37
|
+
regular_params += [":organization_id"]
|
37
38
|
end
|
38
39
|
|
39
40
|
if has_agency_reference?
|
40
|
-
|
41
|
+
regular_params += [":agency_id"]
|
41
42
|
end
|
42
43
|
|
43
44
|
# Remove organization/agency duplicates if they were user-specified
|
44
|
-
|
45
|
+
regular_params.uniq!
|
45
46
|
|
46
|
-
|
47
|
+
# Combine regular and JSON params properly
|
48
|
+
if json_params.any?
|
49
|
+
all_params = regular_params + json_params
|
50
|
+
else
|
51
|
+
all_params = regular_params
|
52
|
+
end
|
53
|
+
|
54
|
+
-%><%= all_params.join(', ') %>
|
47
55
|
|
48
56
|
<% if options[:with_comments] -%>
|
49
57
|
# Connect facets to actions - customize as needed for your <%= class_name.downcase %> model
|
@@ -24,7 +24,7 @@ class <%= class_name %> < ApplicationRecord
|
|
24
24
|
<% if attribute.name.to_s.match?(/\A(phone|phone_number)\z/i) -%>
|
25
25
|
validates :<%= attribute.name %>, format: { with: /\A\+?[\d\s-\(\)]+\z/ }, allow_nil: <%= !attribute.required? %>
|
26
26
|
<% end -%>
|
27
|
-
<% if attribute.name.to_s.match?(/\A(url|website|web_address)\z/i) -%>
|
27
|
+
<% if attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
28
28
|
validates :<%= attribute.name %>, format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]) }, allow_nil: <%= !attribute.required? %>
|
29
29
|
<% end -%>
|
30
30
|
<% if attribute.name.to_s.match?(/\Alatitude\z/i) -%>
|
@@ -69,12 +69,16 @@ class <%= class_name %> < ApplicationRecord
|
|
69
69
|
<% unless (has_organization_reference? && attribute.name == 'organization') || (has_agency_reference? && attribute.name == 'agency') -%>
|
70
70
|
<% if attribute.name == 'organization' -%>
|
71
71
|
belongs_to :organization
|
72
|
+
<% else -%>
|
73
|
+
<% if attribute.respond_to?(:polymorphic?) && attribute.polymorphic? -%>
|
74
|
+
belongs_to :<%= attribute.name %>, polymorphic: true
|
72
75
|
<% else -%>
|
73
76
|
belongs_to :<%= attribute.name %> # Add 'optional: true' if this should be optional
|
74
77
|
<% end -%>
|
75
78
|
<% end -%>
|
76
79
|
<% end -%>
|
77
80
|
<% end -%>
|
81
|
+
<% end -%>
|
78
82
|
# Add additional associations here
|
79
83
|
# has_many :comments, dependent: :destroy
|
80
84
|
|
@@ -109,7 +113,13 @@ json_facet :short, fields: [:id<%
|
|
109
113
|
attr.name.to_s !~ security_patterns &&
|
110
114
|
attr.name.to_s.length < 20)
|
111
115
|
end
|
112
|
-
|
116
|
+
|
117
|
+
# Add polymorphic type fields to short facet (important for frontend handling)
|
118
|
+
polymorphic_type_fields = attributes.select { |attr| attr.name.end_with?('_type') && attr.type == :string }
|
119
|
+
# Also include polymorphic associations (which generate _type fields)
|
120
|
+
polymorphic_associations = attributes.select { |attr| attr.type == :references && attr.respond_to?(:polymorphic?) && attr.polymorphic? }
|
121
|
+
|
122
|
+
short_attributes.each do |attribute| -%>, :<%= attribute.name %><% end %><% polymorphic_type_fields.each do |type_field| -%>, :<%= type_field.name %><% end %><% polymorphic_associations.each do |poly_attr| -%>, :<%= poly_attr.name %>_type<% end %>]
|
113
123
|
json_facet :details, fields: [:id<%
|
114
124
|
# Include most fields except timestamps, security-sensitive, and internal Rails fields for details facet
|
115
125
|
detail_attributes = attributes.reject do |attr|
|
@@ -128,6 +138,11 @@ json_facet :details, fields: [:id<%
|
|
128
138
|
# Collect all field names, avoiding duplicates
|
129
139
|
all_fields = Set.new
|
130
140
|
|
141
|
+
# Find polymorphic type fields
|
142
|
+
polymorphic_type_fields = attributes.select { |attr| attr.name.end_with?('_type') && attr.type == :string }
|
143
|
+
# Also find polymorphic associations (which generate _type fields)
|
144
|
+
polymorphic_associations = attributes.select { |attr| attr.type == :references && attr.respond_to?(:polymorphic?) && attr.polymorphic? }
|
145
|
+
|
131
146
|
# Add non-reference attributes
|
132
147
|
detail_attributes.reject { |attr| attr.type == :references }.each do |attribute|
|
133
148
|
all_fields << attribute.name.to_sym
|
@@ -146,6 +161,15 @@ json_facet :details, fields: [:id<%
|
|
146
161
|
all_fields << :agency_id
|
147
162
|
end
|
148
163
|
|
164
|
+
# Add polymorphic type fields to details facet (important for frontend handling)
|
165
|
+
polymorphic_type_fields.each do |type_field|
|
166
|
+
all_fields << type_field.name.to_sym
|
167
|
+
end
|
168
|
+
# Also add polymorphic association type fields
|
169
|
+
polymorphic_associations.each do |poly_attr|
|
170
|
+
all_fields << "#{poly_attr.name}_type".to_sym
|
171
|
+
end
|
172
|
+
|
149
173
|
# Output the unique fields
|
150
174
|
all_fields.to_a.sort.each do |field| -%>, :<%= field %><% end -%>]<%
|
151
175
|
|
@@ -126,7 +126,7 @@ if users.count < 6
|
|
126
126
|
if org_agencies.any?
|
127
127
|
# Assign to random agency within organization
|
128
128
|
agency = org_agencies.sample
|
129
|
-
Agent.create!(user: new_user, agency: agency, role: 'member')
|
129
|
+
Agent.create!(user: new_user, agency: agency, organization: org, role: 'member')
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
@@ -171,7 +171,7 @@ end
|
|
171
171
|
<% end -%>
|
172
172
|
<% if has_agency_reference? -%>
|
173
173
|
# Multi-tenancy: Assign agency
|
174
|
-
<%= singular_table_name %>_attributes[:agency] =
|
174
|
+
<%= singular_table_name %>_attributes[:agency] = agency # Always assign agency for valid records
|
175
175
|
<% end -%>
|
176
176
|
<% if has_organization_reference? || has_agency_reference? -%>
|
177
177
|
|
@@ -211,7 +211,7 @@ end
|
|
211
211
|
<%= singular_table_name %>_attributes[:<%= attribute.name %>] = Faker::Internet.unique.username
|
212
212
|
<% elsif attribute.name.to_s.match?(/\A(phone|phone_number)\z/i) -%>
|
213
213
|
<%= singular_table_name %>_attributes[:<%= attribute.name %>] = Faker::PhoneNumber.phone_number
|
214
|
-
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address)\z/i) -%>
|
214
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
215
215
|
<%= singular_table_name %>_attributes[:<%= attribute.name %>] = Faker::Internet.url
|
216
216
|
<% elsif attribute.name.to_s.match?(/\A(slug)\z/i) -%>
|
217
217
|
<%= singular_table_name %>_attributes[:<%= attribute.name %>] = Faker::Internet.slug
|
@@ -8,13 +8,15 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
|
|
8
8
|
@organization = organizations(:acme_org)
|
9
9
|
@user = users(:john_user)
|
10
10
|
@agency = agencies(:marketing_agency)
|
11
|
+
<% # Set up polymorphic associations using --parents specification -%>
|
11
12
|
<% polymorphic_associations.each do |assoc| -%>
|
12
|
-
<% assoc[:parent_types]
|
13
|
-
<%
|
14
|
-
|
15
|
-
@<%=
|
16
|
-
|
17
|
-
|
13
|
+
<% if assoc[:parent_types] && assoc[:parent_types].any? -%>
|
14
|
+
<% first_parent = assoc[:parent_types].first -%>
|
15
|
+
# Set up polymorphic association for <%= assoc[:field_name] %> using specified parents
|
16
|
+
@<%= first_parent.underscore %> = <%= first_parent.underscore.pluralize %>(<%= case first_parent.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
17
|
+
@<%= assoc[:field_name] %> = @<%= first_parent.underscore %> # Use first specified parent type
|
18
|
+
<% assoc[:parent_types][1..-1].each do |parent_type| -%>
|
19
|
+
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= case parent_type.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
18
20
|
<% end -%>
|
19
21
|
<% end -%>
|
20
22
|
<% end -%>
|
@@ -698,6 +700,10 @@ class <%= controller_class_name_with_namespace %>ControllerTest < ActionDispatch
|
|
698
700
|
<%= attribute.name %>: "test@example.com"<%= ',' if index < attributes.length - 1 %>
|
699
701
|
<% elsif attribute.name.to_s.match?(/password/) -%>
|
700
702
|
<%= attribute.name %>: "password123"<%= ',' if index < attributes.length - 1 %>
|
703
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
704
|
+
<%= attribute.name %>: "https://example.com"<%= ',' if index < attributes.length - 1 %>
|
705
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
706
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
701
707
|
<% else -%>
|
702
708
|
<%= attribute.name %>: "Test <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
703
709
|
<% end -%>
|
@@ -40,7 +40,7 @@ one:
|
|
40
40
|
<%= attribute.name %>: test_user_1
|
41
41
|
<% elsif attribute.name.to_s.match?(/\A(phone|phone_number)\z/i) -%>
|
42
42
|
<%= attribute.name %>: "+1-555-0001"
|
43
|
-
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address)\z/i) -%>
|
43
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
44
44
|
<%= attribute.name %>: "https://example1.com"
|
45
45
|
<% elsif attribute.name.to_s.match?(/\A(name|title|label)\z/i) -%>
|
46
46
|
<%= attribute.name %>: "Test <%= attribute.name.humanize %> One"
|
@@ -144,7 +144,7 @@ two:
|
|
144
144
|
<%= attribute.name %>: test_user_2
|
145
145
|
<% elsif attribute.name.to_s.match?(/\A(phone|phone_number)\z/i) -%>
|
146
146
|
<%= attribute.name %>: "+1-555-0002"
|
147
|
-
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address)\z/i) -%>
|
147
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
148
148
|
<%= attribute.name %>: "https://example2.com"
|
149
149
|
<% elsif attribute.name.to_s.match?(/\A(name|title|label)\z/i) -%>
|
150
150
|
<%= attribute.name %>: "Test <%= attribute.name.humanize %> Two"
|
@@ -248,7 +248,7 @@ three:
|
|
248
248
|
<%= attribute.name %>: test_user_3
|
249
249
|
<% elsif attribute.name.to_s.match?(/\A(phone|phone_number)\z/i) -%>
|
250
250
|
<%= attribute.name %>: "+1-555-0003"
|
251
|
-
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address)\z/i) -%>
|
251
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
252
252
|
<%= attribute.name %>: "https://example3.com"
|
253
253
|
<% elsif attribute.name.to_s.match?(/\A(name|title|label)\z/i) -%>
|
254
254
|
<%= attribute.name %>: "Test <%= attribute.name.humanize %> Three"
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
require "test_helper"
|
4
4
|
|
5
|
+
<%
|
6
|
+
# Check attributes directly instead of database columns (more reliable during generation)
|
7
|
+
has_agency_id = attributes.any? { |attr| attr.name == 'agency' && attr.type == :references }
|
8
|
+
has_user_id = attributes.any? { |attr| attr.name == 'user' && attr.type == :references }
|
9
|
+
-%>
|
10
|
+
|
5
11
|
class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
6
12
|
|
7
13
|
def setup
|
@@ -11,10 +17,10 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
11
17
|
<% polymorphic_associations.each do |assoc| -%>
|
12
18
|
<% assoc[:parent_types].each_with_index do |parent_type, index| -%>
|
13
19
|
<% if index == 0 -%>
|
14
|
-
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= parent_type.underscore
|
20
|
+
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= case parent_type.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
15
21
|
@<%= assoc[:field_name] %> = @<%= parent_type.underscore %> # Default polymorphic parent for tests
|
16
22
|
<% else -%>
|
17
|
-
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%=
|
23
|
+
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= case parent_type.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
18
24
|
<% end -%>
|
19
25
|
<% end -%>
|
20
26
|
<% end -%>
|
@@ -26,6 +32,21 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
26
32
|
@<%= singular_table_name %> = <%= table_name %>(:marketing_agency)
|
27
33
|
<% else -%>
|
28
34
|
@<%= singular_table_name %> = <%= table_name %>(:one)
|
35
|
+
<% end -%>
|
36
|
+
<% if has_user_reference? -%>
|
37
|
+
@user = users(:john_user)
|
38
|
+
<% end -%>
|
39
|
+
<% # Set up polymorphic associations using --parents specification -%>
|
40
|
+
<% polymorphic_associations.each do |assoc| -%>
|
41
|
+
<% if assoc[:parent_types] && assoc[:parent_types].any? -%>
|
42
|
+
<% first_parent = assoc[:parent_types].first -%>
|
43
|
+
# Set up polymorphic association for <%= assoc[:field_name] %> using specified parents
|
44
|
+
@<%= first_parent.underscore %> = <%= first_parent.underscore.pluralize %>(<%= case first_parent.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
45
|
+
@<%= assoc[:field_name] %> = @<%= first_parent.underscore %> # Use first specified parent type
|
46
|
+
<% assoc[:parent_types][1..-1].each do |parent_type| -%>
|
47
|
+
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= case parent_type.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
48
|
+
<% end -%>
|
49
|
+
<% end -%>
|
29
50
|
<% end -%>
|
30
51
|
@token = @user.generate_jwt_token
|
31
52
|
@auth_headers = { 'Authorization' => "Bearer #{@token}" }
|
@@ -66,6 +87,10 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
66
87
|
<%= attribute.name %>: "workflow@example.com"<%= ',' if index < attributes.length - 1 %>
|
67
88
|
<% elsif attribute.name.to_s.match?(/password/) -%>
|
68
89
|
<%= attribute.name %>: "password123"<%= ',' if index < attributes.length - 1 %>
|
90
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
91
|
+
<%= attribute.name %>: "https://workflow.example.com"<%= ',' if index < attributes.length - 1 %>
|
92
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
93
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
69
94
|
<% else -%>
|
70
95
|
<%= attribute.name %>: "Workflow Test <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
71
96
|
<% end -%>
|
@@ -125,7 +150,7 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
125
150
|
|
126
151
|
# Verify created <%= singular_table_name %> data
|
127
152
|
assert_equal @organization.id, created_<%= singular_table_name %>['organization']['id']
|
128
|
-
<%
|
153
|
+
<% if has_user_id && singular_table_name != 'user' -%>
|
129
154
|
assert_equal @user.id, created_<%= singular_table_name %>['user']['id']
|
130
155
|
<% end -%>
|
131
156
|
<% attributes.each do |attribute| -%>
|
@@ -304,6 +329,10 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
304
329
|
<%= attribute.name %>: "pagination#{i}@example.com"<%= ',' if index < attributes.length - 1 %>
|
305
330
|
<% elsif attribute.name.include?('email') -%>
|
306
331
|
<%= attribute.name %>: "pagination#{i}@example.com"<%= ',' if index < attributes.length - 1 %>
|
332
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
333
|
+
<%= attribute.name %>: "https://pagination#{i}.example.com"<%= ',' if index < attributes.length - 1 %>
|
334
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
335
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
307
336
|
<% else -%>
|
308
337
|
<%= attribute.name %>: "Pagination Test <%= attribute.name.humanize %> #{i}"<%= ',' if index < attributes.length - 1 %>
|
309
338
|
<% end -%>
|
@@ -389,11 +418,6 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
389
418
|
# Test 4: Tenancy context behavior based on configuration
|
390
419
|
require_org_id = PropelAuthentication.configuration.require_organization_id
|
391
420
|
require_user_id = PropelAuthentication.configuration.require_user_id
|
392
|
-
<%
|
393
|
-
# Check attributes directly instead of database columns (more reliable during generation)
|
394
|
-
has_agency_id = attributes.any? { |attr| attr.name == 'agency' && attr.type == :references }
|
395
|
-
has_user_id = attributes.any? { |attr| attr.name == 'user' && attr.type == :references }
|
396
|
-
-%>
|
397
421
|
|
398
422
|
<% if has_agency_id -%>
|
399
423
|
# Model with agency - provide agency_id but test missing organization_id
|
@@ -573,6 +597,10 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
573
597
|
<%= attribute.name %>: "org1_<%= attribute.name %>@example.com"<%= ',' if index < attributes.length - 1 %>
|
574
598
|
<% elsif attribute.name.include?('password') -%>
|
575
599
|
<%= attribute.name %>: "org1_secure_password"<%= ',' if index < attributes.length - 1 %>
|
600
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
601
|
+
<%= attribute.name %>: "https://org1.example.com"<%= ',' if index < attributes.length - 1 %>
|
602
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
603
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
576
604
|
<% else -%>
|
577
605
|
<%= attribute.name %>: "Org 1 <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
578
606
|
<% end -%>
|
@@ -619,6 +647,10 @@ class <%= class_name %>ApiTest < ActionDispatch::IntegrationTest
|
|
619
647
|
<%= attribute.name %>: "org2_<%= attribute.name %>@example.com"<%= ',' if index < attributes.length - 1 %>
|
620
648
|
<% elsif attribute.name.include?('password') -%>
|
621
649
|
<%= attribute.name %>: "org2_secure_password"<%= ',' if index < attributes.length - 1 %>
|
650
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
651
|
+
<%= attribute.name %>: "https://org2.example.com"<%= ',' if index < attributes.length - 1 %>
|
652
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
653
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
622
654
|
<% else -%>
|
623
655
|
<%= attribute.name %>: "Org 2 <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
624
656
|
<% end -%>
|
@@ -775,6 +807,10 @@ if attributes.any? { |attr| attr.type == :references && attr.name == 'user' } &&
|
|
775
807
|
<%= attribute.name %>: "concurrent@example.com"<%= ',' if index < attributes.length - 1 %>
|
776
808
|
<% elsif attribute.name.include?('email') -%>
|
777
809
|
<%= attribute.name %>: "concurrent@example.com"<%= ',' if index < attributes.length - 1 %>
|
810
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
811
|
+
<%= attribute.name %>: "https://concurrent.example.com"<%= ',' if index < attributes.length - 1 %>
|
812
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
813
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
778
814
|
<% else -%>
|
779
815
|
<%= attribute.name %>: "Concurrent Test <%= attribute.name.humanize %>"<%= ',' if index < attributes.length - 1 %>
|
780
816
|
<% end -%>
|
@@ -890,6 +926,10 @@ if attributes.any? { |attr| attr.type == :references && attr.name == 'user' } &&
|
|
890
926
|
<%= attribute.name %>: "bulk#{i}@example.com"<%= ',' if index < attributes.length - 1 %>
|
891
927
|
<% elsif attribute.name.include?('email') -%>
|
892
928
|
<%= attribute.name %>: "bulk#{i}@example.com"<%= ',' if index < attributes.length - 1 %>
|
929
|
+
<% elsif attribute.name.to_s.match?(/\A(url|website|web_address|domain|domain_name)\z/i) -%>
|
930
|
+
<%= attribute.name %>: "https://bulk#{i}.example.com"<%= ',' if index < attributes.length - 1 %>
|
931
|
+
<% elsif attribute.name.to_s.end_with?('_type') -%>
|
932
|
+
<%= attribute.name %>: @<%= attribute.name.gsub('_type', '') %>.class.name<%= ',' if index < attributes.length - 1 %>
|
893
933
|
<% else -%>
|
894
934
|
<%= attribute.name %>: "Bulk Test <%= attribute.name.humanize %> #{i}"<%= ',' if index < attributes.length - 1 %>
|
895
935
|
<% end -%>
|
@@ -11,10 +11,10 @@ class <%= class_name %>Test < ActiveSupport::TestCase
|
|
11
11
|
<% polymorphic_associations.each do |assoc| -%>
|
12
12
|
<% assoc[:parent_types].each_with_index do |parent_type, index| -%>
|
13
13
|
<% if index == 0 -%>
|
14
|
-
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= parent_type.underscore
|
14
|
+
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= case parent_type.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
15
15
|
@<%= assoc[:field_name] %> = @<%= parent_type.underscore %> # Default polymorphic parent for tests
|
16
16
|
<% else -%>
|
17
|
-
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%=
|
17
|
+
@<%= parent_type.underscore %> = <%= parent_type.underscore.pluralize %>(<%= case parent_type.underscore; when 'agency'; ':marketing_agency'; when 'user'; ':john_user'; when 'organization'; ':acme_org'; else; ':one'; end %>)
|
18
18
|
<% end -%>
|
19
19
|
<% end -%>
|
20
20
|
<% end -%>
|
data/lib/propel_api.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: propel_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Martin, Rafael Pivato, Chi Putera
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-09-
|
11
|
+
date: 2025-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -73,9 +73,11 @@ files:
|
|
73
73
|
- lib/generators/propel_api/install/install_generator.rb
|
74
74
|
- lib/generators/propel_api/resource/resource_generator.rb
|
75
75
|
- lib/generators/propel_api/templates/config/propel_api.rb.tt
|
76
|
+
- lib/generators/propel_api/templates/controllers/api_base_controller.rb
|
76
77
|
- lib/generators/propel_api/templates/controllers/api_controller_graphiti.rb
|
77
78
|
- lib/generators/propel_api/templates/controllers/api_controller_propel_facets.rb
|
78
79
|
- lib/generators/propel_api/templates/controllers/example_controller.rb.tt
|
80
|
+
- lib/generators/propel_api/templates/errors/propel_api_csrf_error.rb
|
79
81
|
- lib/generators/propel_api/templates/scaffold/facet_controller_template.rb.tt
|
80
82
|
- lib/generators/propel_api/templates/scaffold/facet_model_template.rb.tt
|
81
83
|
- lib/generators/propel_api/templates/scaffold/graphiti_controller_template.rb.tt
|