mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-28 01:53:25 +00:00
Add specs and JSON schemas
Include test helpers
This commit is contained in:
committed by
Maikel Linke
parent
39f4feed4a
commit
76f14a03c6
@@ -27,11 +27,16 @@ Metrics/BlockLength:
|
||||
"class_eval",
|
||||
"collection",
|
||||
"context",
|
||||
"delete",
|
||||
"describe",
|
||||
"feature",
|
||||
"get",
|
||||
"it",
|
||||
"member",
|
||||
"namespace",
|
||||
"path",
|
||||
"post",
|
||||
"put",
|
||||
"resource",
|
||||
"resources",
|
||||
"scenario",
|
||||
|
||||
@@ -466,6 +466,7 @@ Metrics/BlockLength:
|
||||
- 'spec/lib/open_food_network/group_buy_report_spec.rb'
|
||||
- 'spec/requests/api/orders_spec.rb'
|
||||
- 'spec/spec_helper.rb'
|
||||
- 'spec/swagger_helper.rb'
|
||||
- 'spec/support/cancan_helper.rb'
|
||||
- 'spec/support/matchers/select2_matchers.rb'
|
||||
- 'spec/support/matchers/table_matchers.rb'
|
||||
|
||||
22
app/json_schemas/customer_schema.rb
Normal file
22
app/json_schemas/customer_schema.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CustomerSchema < JsonApiSchema
|
||||
def self.object_name
|
||||
"customer"
|
||||
end
|
||||
|
||||
def self.attributes
|
||||
{
|
||||
id: { type: :integer, example: 1 },
|
||||
enterprise_id: { type: :integer, example: 2 },
|
||||
first_name: { type: :string, nullable: true, example: "Alice" },
|
||||
last_name: { type: :string, nullable: true, example: "Springs" },
|
||||
code: { type: :string, nullable: true, example: "BUYER1" },
|
||||
email: { type: :string, example: "alice@example.com" }
|
||||
}
|
||||
end
|
||||
|
||||
def self.required_attributes
|
||||
[:enterprise_id, :email]
|
||||
end
|
||||
end
|
||||
24
app/json_schemas/errors_schema.rb
Normal file
24
app/json_schemas/errors_schema.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ErrorsSchema
|
||||
def self.schema
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
errors: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: {
|
||||
title: { type: :string },
|
||||
detail: { type: :string },
|
||||
source: { type: :object }
|
||||
},
|
||||
required: [:detail]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: [:errors]
|
||||
}
|
||||
end
|
||||
end
|
||||
65
app/json_schemas/json_api_schema.rb
Normal file
65
app/json_schemas/json_api_schema.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class JsonApiSchema
|
||||
class << self
|
||||
def attributes
|
||||
{}
|
||||
end
|
||||
|
||||
def required_attributes
|
||||
[]
|
||||
end
|
||||
|
||||
def all_attributes
|
||||
attributes.keys
|
||||
end
|
||||
|
||||
def schema(options = {})
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :object,
|
||||
properties: data_properties(**options)
|
||||
},
|
||||
meta: { type: :object }
|
||||
},
|
||||
required: [:data]
|
||||
}
|
||||
end
|
||||
|
||||
def collection(options)
|
||||
{
|
||||
type: :object,
|
||||
properties: {
|
||||
data: {
|
||||
type: :array,
|
||||
items: {
|
||||
type: :object,
|
||||
properties: data_properties(**options)
|
||||
}
|
||||
},
|
||||
meta: { type: :object }
|
||||
},
|
||||
required: [:data]
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def data_properties(require_all: false)
|
||||
required = require_all ? all_attributes : required_attributes
|
||||
|
||||
{
|
||||
id: { type: :string, example: "1" },
|
||||
type: { type: :string, example: object_name },
|
||||
attributes: {
|
||||
type: :object,
|
||||
properties: attributes,
|
||||
required: required
|
||||
},
|
||||
relationships: { type: :object }
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ module Api
|
||||
class CustomerSerializer
|
||||
include JSONAPI::Serializer
|
||||
|
||||
attributes :id, :enterprise_id, :name, :code, :email
|
||||
attributes :id, :enterprise_id, :first_name, :last_name, :code, :email
|
||||
|
||||
belongs_to :enterprise, record_type: :enterprise, serializer: :id
|
||||
end
|
||||
|
||||
166
spec/requests/api/v1/customers_spec.rb
Normal file
166
spec/requests/api/v1/customers_spec.rb
Normal file
@@ -0,0 +1,166 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "swagger_helper"
|
||||
|
||||
describe "Customers", type: :request do
|
||||
let!(:enterprise1) { create(:enterprise) }
|
||||
let!(:enterprise2) { create(:enterprise) }
|
||||
let!(:customer1) { create(:customer, enterprise: enterprise1) }
|
||||
let!(:customer2) { create(:customer, enterprise: enterprise1) }
|
||||
let!(:customer3) { create(:customer, enterprise: enterprise2) }
|
||||
|
||||
before { login_as enterprise1.owner }
|
||||
|
||||
path "/api/v1/customers" do
|
||||
get "List customers" do
|
||||
tags "Customers"
|
||||
parameter name: :enterprise_id, in: :query, type: :string
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customers list" do
|
||||
param(:enterprise_id) { enterprise1.id }
|
||||
schema CustomerSchema.collection(require_all: true)
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
|
||||
describe "returning results based on permissions" do
|
||||
context "as an enterprise owner" do
|
||||
before { login_as enterprise1.owner }
|
||||
|
||||
it "returns customers of enterprises the user manages" do
|
||||
get "/api/v1/customers"
|
||||
expect(json_response_ids).to eq [customer1.id.to_s, customer2.id.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
context "as another enterprise owner" do
|
||||
before { login_as enterprise2.owner }
|
||||
|
||||
it "returns customers of enterprises the user manages" do
|
||||
get "/api/v1/customers"
|
||||
expect(json_response_ids).to eq [customer3.id.to_s]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "Create customer" do
|
||||
tags "Customers"
|
||||
consumes "application/json"
|
||||
produces "application/json"
|
||||
|
||||
parameter name: :customer, in: :body, schema: {
|
||||
type: :object,
|
||||
properties: CustomerSchema.attributes.except(:id),
|
||||
required: CustomerSchema.required_attributes
|
||||
}
|
||||
|
||||
response "201", "Customer created" do
|
||||
param(:customer) do
|
||||
{
|
||||
email: "test@example.com",
|
||||
enterprise_id: enterprise1.id.to_s
|
||||
}
|
||||
end
|
||||
schema CustomerSchema.schema(require_all: true)
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "422", "Unprocessable entity" do
|
||||
param(:customer) { {} }
|
||||
schema ErrorsSchema.schema
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
path "/api/v1/customers/{id}" do
|
||||
get "Show customer" do
|
||||
tags "Customers"
|
||||
parameter name: :id, in: :path, type: :string
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customer" do
|
||||
param(:id) { customer1.id }
|
||||
schema CustomerSchema.schema(require_all: true)
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "404", "Not found" do
|
||||
param(:id) { 0 }
|
||||
schema ErrorsSchema.schema
|
||||
|
||||
run_test! do
|
||||
expect(json_error_detail).to eq "The resource you were looking for could not be found."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
put "Update customer" do
|
||||
tags "Customers"
|
||||
parameter name: :id, in: :path, type: :string
|
||||
consumes "application/json"
|
||||
produces "application/json"
|
||||
|
||||
parameter name: :customer, in: :body, schema: {
|
||||
type: :object,
|
||||
properties: CustomerSchema.attributes,
|
||||
required: CustomerSchema.required_attributes
|
||||
}
|
||||
|
||||
response "200", "Customer updated" do
|
||||
param(:id) { customer1.id }
|
||||
param(:customer) do
|
||||
{
|
||||
id: customer1.id.to_s,
|
||||
email: "test@example.com",
|
||||
enterprise_id: enterprise1.id.to_s
|
||||
}
|
||||
end
|
||||
schema CustomerSchema.schema(require_all: true)
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "422", "Unprocessable entity" do
|
||||
param(:id) { customer1.id }
|
||||
param(:customer) { {} }
|
||||
schema ErrorsSchema.schema
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
|
||||
delete "Delete customer" do
|
||||
tags "Customers"
|
||||
parameter name: :id, in: :path, type: :string
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customer deleted" do
|
||||
param(:id) { customer1.id }
|
||||
schema CustomerSchema.schema(require_all: true)
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
path "/api/v1/enterprises/{enterprise_id}/customers" do
|
||||
get "List customers of an enterprise" do
|
||||
tags "Customers", "Enterprises"
|
||||
parameter name: :enterprise_id, in: :path, type: :string, required: true
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customers list" do
|
||||
param(:enterprise_id) { enterprise1.id }
|
||||
schema CustomerSchema.collection(require_all: true)
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -14,6 +14,14 @@ module OpenFoodNetwork
|
||||
end
|
||||
end
|
||||
|
||||
def json_response_ids
|
||||
json_response[:data].map{ |item| item["id"] }
|
||||
end
|
||||
|
||||
def json_error_detail
|
||||
json_response[:errors][0][:detail]
|
||||
end
|
||||
|
||||
def assert_unauthorized!
|
||||
expect(json_response).to eq("error" => "You are not authorized to perform that action.")
|
||||
expect(response.status).to eq 401
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include Devise::Test::IntegrationHelpers, type: :request
|
||||
config.include OpenFoodNetwork::ApiHelper, type: :request
|
||||
|
||||
# Specify a root folder where Swagger JSON files are generated
|
||||
# NOTE: If you're using the rswag-api to serve API descriptions, you'll need
|
||||
# to ensure that it's configured to serve Swagger from the same folder
|
||||
@@ -21,6 +24,12 @@ RSpec.configure do |config|
|
||||
title: 'API V1',
|
||||
version: 'v1'
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
error_response: ErrorsSchema.schema,
|
||||
customer: CustomerSchema.schema
|
||||
}
|
||||
},
|
||||
paths: {},
|
||||
servers: [
|
||||
{ url: "/" }
|
||||
@@ -41,3 +50,10 @@ RSpec.configure do |config|
|
||||
# Defaults to json. Accepts ':json' and ':yaml'.
|
||||
config.swagger_format = :yaml
|
||||
end
|
||||
|
||||
module RswagExtension
|
||||
def param(args, &block)
|
||||
public_send(:let, args) { instance_eval(&block) }
|
||||
end
|
||||
end
|
||||
Rswag::Specs::ExampleGroupHelpers.prepend RswagExtension
|
||||
|
||||
Reference in New Issue
Block a user