mirror of
https://github.com/we-promise/sure.git
synced 2026-04-20 12:34:12 +00:00
FIX OpenAPI auth specs (#722)
* FIX auth specs * FIX header params are not required with auth spec * Add missing endpoints
This commit is contained in:
@@ -11,10 +11,11 @@ servers:
|
|||||||
description: Local development
|
description: Local development
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
bearerAuth:
|
apiKeyAuth:
|
||||||
type: http
|
type: apiKey
|
||||||
scheme: bearer
|
name: X-Api-Key
|
||||||
bearerFormat: JWT
|
in: header
|
||||||
|
description: API key for authentication. Generate one from your account settings.
|
||||||
schemas:
|
schemas:
|
||||||
Pagination:
|
Pagination:
|
||||||
type: object
|
type: object
|
||||||
@@ -222,6 +223,41 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
account_type:
|
account_type:
|
||||||
type: string
|
type: string
|
||||||
|
AccountDetail:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- balance
|
||||||
|
- currency
|
||||||
|
- classification
|
||||||
|
- account_type
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
balance:
|
||||||
|
type: string
|
||||||
|
currency:
|
||||||
|
type: string
|
||||||
|
classification:
|
||||||
|
type: string
|
||||||
|
account_type:
|
||||||
|
type: string
|
||||||
|
AccountCollection:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- accounts
|
||||||
|
- pagination
|
||||||
|
properties:
|
||||||
|
accounts:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
"$ref": "#/components/schemas/AccountDetail"
|
||||||
|
pagination:
|
||||||
|
"$ref": "#/components/schemas/Pagination"
|
||||||
Category:
|
Category:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -328,6 +364,32 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
color:
|
color:
|
||||||
type: string
|
type: string
|
||||||
|
TagDetail:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- color
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
color:
|
||||||
|
type: string
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
TagCollection:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
"$ref": "#/components/schemas/TagDetail"
|
||||||
Transfer:
|
Transfer:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -596,20 +658,41 @@ components:
|
|||||||
data:
|
data:
|
||||||
"$ref": "#/components/schemas/ImportDetail"
|
"$ref": "#/components/schemas/ImportDetail"
|
||||||
paths:
|
paths:
|
||||||
|
"/api/v1/accounts":
|
||||||
|
get:
|
||||||
|
summary: List accounts
|
||||||
|
tags:
|
||||||
|
- Accounts
|
||||||
|
security:
|
||||||
|
- apiKeyAuth: []
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: 'Page number (default: 1)'
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: per_page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: 'Items per page (default: 25, max: 100)'
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: accounts paginated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/AccountCollection"
|
||||||
"/api/v1/categories":
|
"/api/v1/categories":
|
||||||
get:
|
get:
|
||||||
summary: List categories
|
summary: List categories
|
||||||
tags:
|
tags:
|
||||||
- Categories
|
- Categories
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
@@ -653,12 +736,6 @@ paths:
|
|||||||
"$ref": "#/components/schemas/CategoryCollection"
|
"$ref": "#/components/schemas/CategoryCollection"
|
||||||
"/api/v1/categories/{id}":
|
"/api/v1/categories/{id}":
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
- name: id
|
- name: id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
@@ -670,7 +747,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Categories
|
- Categories
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: subcategory retrieved with parent
|
description: subcategory retrieved with parent
|
||||||
@@ -690,14 +767,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chats
|
- Chats
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: chats listed
|
description: chats listed
|
||||||
@@ -716,14 +786,8 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chats
|
- Chats
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
parameters: []
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with write scope
|
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
description: chat created
|
description: chat created
|
||||||
@@ -757,12 +821,6 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
"/api/v1/chats/{id}":
|
"/api/v1/chats/{id}":
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
- name: id
|
- name: id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
@@ -774,7 +832,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chats
|
- Chats
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: chat retrieved
|
description: chat retrieved
|
||||||
@@ -793,7 +851,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chats
|
- Chats
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters: []
|
parameters: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -829,7 +887,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chats
|
- Chats
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: chat deleted
|
description: chat deleted
|
||||||
@@ -837,12 +895,6 @@ paths:
|
|||||||
description: chat not found
|
description: chat not found
|
||||||
"/api/v1/chats/{chat_id}/messages":
|
"/api/v1/chats/{chat_id}/messages":
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with write scope
|
|
||||||
- name: chat_id
|
- name: chat_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
@@ -854,7 +906,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chat Messages
|
- Chat Messages
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters: []
|
parameters: []
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
@@ -890,12 +942,6 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
"/api/v1/chats/{chat_id}/messages/retry":
|
"/api/v1/chats/{chat_id}/messages/retry":
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with write scope
|
|
||||||
- name: chat_id
|
- name: chat_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
@@ -907,7 +953,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Chat Messages
|
- Chat Messages
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'202':
|
'202':
|
||||||
description: retry started
|
description: retry started
|
||||||
@@ -934,14 +980,8 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Imports
|
- Imports
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
@@ -993,14 +1033,8 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Imports
|
- Imports
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
parameters: []
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with write scope
|
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
description: import created
|
description: import created
|
||||||
@@ -1085,12 +1119,6 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
"/api/v1/imports/{id}":
|
"/api/v1/imports/{id}":
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
- name: id
|
- name: id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
@@ -1104,7 +1132,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Imports
|
- Imports
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: import retrieved
|
description: import retrieved
|
||||||
@@ -1118,20 +1146,141 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
"$ref": "#/components/schemas/ErrorResponse"
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
"/api/v1/tags":
|
||||||
|
get:
|
||||||
|
summary: List tags
|
||||||
|
tags:
|
||||||
|
- Tags
|
||||||
|
security:
|
||||||
|
- apiKeyAuth: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: tags listed
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/TagCollection"
|
||||||
|
post:
|
||||||
|
summary: Create tag
|
||||||
|
tags:
|
||||||
|
- Tags
|
||||||
|
security:
|
||||||
|
- apiKeyAuth: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: tag created with auto-assigned color
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/TagDetail"
|
||||||
|
'422':
|
||||||
|
description: validation error - missing name
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tag:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: Tag name (required)
|
||||||
|
color:
|
||||||
|
type: string
|
||||||
|
description: Hex color code (optional, auto-assigned if not
|
||||||
|
provided)
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
required:
|
||||||
|
- tag
|
||||||
|
required: true
|
||||||
|
"/api/v1/tags/{id}":
|
||||||
|
parameters:
|
||||||
|
- name: id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: Tag ID
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
get:
|
||||||
|
summary: Retrieve a tag
|
||||||
|
tags:
|
||||||
|
- Tags
|
||||||
|
security:
|
||||||
|
- apiKeyAuth: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: tag retrieved
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/TagDetail"
|
||||||
|
'404':
|
||||||
|
description: tag not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
patch:
|
||||||
|
summary: Update a tag
|
||||||
|
tags:
|
||||||
|
- Tags
|
||||||
|
security:
|
||||||
|
- apiKeyAuth: []
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: tag updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/TagDetail"
|
||||||
|
'404':
|
||||||
|
description: tag not found
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tag:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
color:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
delete:
|
||||||
|
summary: Delete a tag
|
||||||
|
tags:
|
||||||
|
- Tags
|
||||||
|
security:
|
||||||
|
- apiKeyAuth: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: tag deleted
|
||||||
|
'404':
|
||||||
|
description: tag not found
|
||||||
"/api/v1/transactions":
|
"/api/v1/transactions":
|
||||||
get:
|
get:
|
||||||
summary: List transactions
|
summary: List transactions
|
||||||
tags:
|
tags:
|
||||||
- Transactions
|
- Transactions
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with read scope
|
|
||||||
- name: page
|
- name: page
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
@@ -1247,14 +1396,8 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Transactions
|
- Transactions
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters:
|
parameters: []
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token with write scope
|
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
description: transaction created
|
description: transaction created
|
||||||
@@ -1332,12 +1475,6 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
"/api/v1/transactions/{id}":
|
"/api/v1/transactions/{id}":
|
||||||
parameters:
|
parameters:
|
||||||
- name: Authorization
|
|
||||||
in: header
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: Bearer token
|
|
||||||
- name: id
|
- name: id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
@@ -1349,7 +1486,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Transactions
|
- Transactions
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: transaction retrieved
|
description: transaction retrieved
|
||||||
@@ -1368,7 +1505,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Transactions
|
- Transactions
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
parameters: []
|
parameters: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -1431,7 +1568,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Transactions
|
- Transactions
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- apiKeyAuth: []
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: transaction deleted
|
description: transaction deleted
|
||||||
|
|||||||
102
spec/requests/api/v1/accounts_spec.rb
Normal file
102
spec/requests/api/v1/accounts_spec.rb
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'swagger_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'API V1 Accounts', type: :request do
|
||||||
|
let(:family) do
|
||||||
|
Family.create!(
|
||||||
|
name: 'API Family',
|
||||||
|
currency: 'USD',
|
||||||
|
locale: 'en',
|
||||||
|
date_format: '%m-%d-%Y'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:user) do
|
||||||
|
family.users.create!(
|
||||||
|
email: 'api-user@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:api_key) do
|
||||||
|
key = ApiKey.generate_secure_key
|
||||||
|
ApiKey.create!(
|
||||||
|
user: user,
|
||||||
|
name: 'API Docs Key',
|
||||||
|
key: key,
|
||||||
|
scopes: %w[read_write],
|
||||||
|
source: 'web'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:'X-Api-Key') { api_key.plain_key }
|
||||||
|
|
||||||
|
let!(:checking_account) do
|
||||||
|
Account.create!(
|
||||||
|
family: family,
|
||||||
|
name: 'Checking Account',
|
||||||
|
balance: 1500.50,
|
||||||
|
currency: 'USD',
|
||||||
|
accountable: Depository.create!
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:savings_account) do
|
||||||
|
Account.create!(
|
||||||
|
family: family,
|
||||||
|
name: 'Savings Account',
|
||||||
|
balance: 10000.00,
|
||||||
|
currency: 'USD',
|
||||||
|
accountable: Depository.create!
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:credit_card) do
|
||||||
|
Account.create!(
|
||||||
|
family: family,
|
||||||
|
name: 'Credit Card',
|
||||||
|
balance: -500.00,
|
||||||
|
currency: 'USD',
|
||||||
|
accountable: CreditCard.create!
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
path '/api/v1/accounts' do
|
||||||
|
get 'List accounts' do
|
||||||
|
tags 'Accounts'
|
||||||
|
security [ { apiKeyAuth: [] } ]
|
||||||
|
produces 'application/json'
|
||||||
|
parameter name: :page, in: :query, type: :integer, required: false,
|
||||||
|
description: 'Page number (default: 1)'
|
||||||
|
parameter name: :per_page, in: :query, type: :integer, required: false,
|
||||||
|
description: 'Items per page (default: 25, max: 100)'
|
||||||
|
|
||||||
|
response '200', 'accounts listed' do
|
||||||
|
schema '$ref' => '#/components/schemas/AccountCollection'
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload.fetch('accounts')).to be_present
|
||||||
|
expect(payload.fetch('accounts').length).to eq(3)
|
||||||
|
expect(payload.fetch('pagination')).to include('page', 'per_page', 'total_count', 'total_pages')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response '200', 'accounts paginated' do
|
||||||
|
schema '$ref' => '#/components/schemas/AccountCollection'
|
||||||
|
|
||||||
|
let(:page) { 1 }
|
||||||
|
let(:per_page) { 2 }
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload.fetch('accounts').length).to eq(2)
|
||||||
|
expect(payload.dig('pagination', 'per_page')).to eq(2)
|
||||||
|
expect(payload.dig('pagination', 'total_count')).to eq(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -20,25 +20,18 @@ RSpec.describe 'API V1 Categories', type: :request do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:oauth_application) do
|
let(:api_key) do
|
||||||
Doorkeeper::Application.create!(
|
key = ApiKey.generate_secure_key
|
||||||
name: 'API Docs',
|
ApiKey.create!(
|
||||||
redirect_uri: 'https://example.com/callback',
|
user: user,
|
||||||
scopes: 'read read_write'
|
name: 'API Docs Key',
|
||||||
|
key: key,
|
||||||
|
scopes: %w[read_write],
|
||||||
|
source: 'web'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:access_token) do
|
let(:'X-Api-Key') { api_key.plain_key }
|
||||||
Doorkeeper::AccessToken.create!(
|
|
||||||
application: oauth_application,
|
|
||||||
resource_owner_id: user.id,
|
|
||||||
scopes: 'read_write',
|
|
||||||
expires_in: 2.hours,
|
|
||||||
token: SecureRandom.hex(32)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:Authorization) { "Bearer #{access_token.token}" }
|
|
||||||
|
|
||||||
let!(:parent_category) do
|
let!(:parent_category) do
|
||||||
family.categories.create!(
|
family.categories.create!(
|
||||||
@@ -71,10 +64,8 @@ RSpec.describe 'API V1 Categories', type: :request do
|
|||||||
path '/api/v1/categories' do
|
path '/api/v1/categories' do
|
||||||
get 'List categories' do
|
get 'List categories' do
|
||||||
tags 'Categories'
|
tags 'Categories'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
parameter name: :page, in: :query, type: :integer, required: false,
|
parameter name: :page, in: :query, type: :integer, required: false,
|
||||||
description: 'Page number (default: 1)'
|
description: 'Page number (default: 1)'
|
||||||
parameter name: :per_page, in: :query, type: :integer, required: false,
|
parameter name: :per_page, in: :query, type: :integer, required: false,
|
||||||
@@ -141,13 +132,11 @@ RSpec.describe 'API V1 Categories', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/api/v1/categories/{id}' do
|
path '/api/v1/categories/{id}' do
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
parameter name: :id, in: :path, type: :string, required: true, description: 'Category ID'
|
parameter name: :id, in: :path, type: :string, required: true, description: 'Category ID'
|
||||||
|
|
||||||
get 'Retrieve a category' do
|
get 'Retrieve a category' do
|
||||||
tags 'Categories'
|
tags 'Categories'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:id) { parent_category.id }
|
let(:id) { parent_category.id }
|
||||||
|
|||||||
@@ -21,25 +21,18 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:oauth_application) do
|
let(:api_key) do
|
||||||
Doorkeeper::Application.create!(
|
key = ApiKey.generate_secure_key
|
||||||
name: 'API Docs',
|
ApiKey.create!(
|
||||||
redirect_uri: 'https://example.com/callback',
|
user: user,
|
||||||
scopes: 'read read_write'
|
name: 'API Docs Key',
|
||||||
|
key: key,
|
||||||
|
scopes: %w[read_write],
|
||||||
|
source: 'web'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:access_token) do
|
let(:'X-Api-Key') { api_key.plain_key }
|
||||||
Doorkeeper::AccessToken.create!(
|
|
||||||
application: oauth_application,
|
|
||||||
resource_owner_id: user.id,
|
|
||||||
scopes: 'read_write',
|
|
||||||
expires_in: 2.hours,
|
|
||||||
token: SecureRandom.hex(32)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:Authorization) { "Bearer #{access_token.token}" }
|
|
||||||
|
|
||||||
let!(:chat) do
|
let!(:chat) do
|
||||||
user.chats.create!(title: 'Budget planning').tap do |record|
|
user.chats.create!(title: 'Budget planning').tap do |record|
|
||||||
@@ -84,10 +77,8 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
path '/api/v1/chats' do
|
path '/api/v1/chats' do
|
||||||
get 'List chats' do
|
get 'List chats' do
|
||||||
tags 'Chats'
|
tags 'Chats'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
|
|
||||||
response '200', 'chats listed' do
|
response '200', 'chats listed' do
|
||||||
schema '$ref' => '#/components/schemas/ChatCollection'
|
schema '$ref' => '#/components/schemas/ChatCollection'
|
||||||
@@ -117,11 +108,9 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
|
|
||||||
post 'Create chat' do
|
post 'Create chat' do
|
||||||
tags 'Chats'
|
tags 'Chats'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with write scope'
|
|
||||||
parameter name: :chat_params, in: :body, required: true, schema: {
|
parameter name: :chat_params, in: :body, required: true, schema: {
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: {
|
properties: {
|
||||||
@@ -161,13 +150,11 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/api/v1/chats/{id}' do
|
path '/api/v1/chats/{id}' do
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
parameter name: :id, in: :path, type: :string, required: true, description: 'Chat ID'
|
parameter name: :id, in: :path, type: :string, required: true, description: 'Chat ID'
|
||||||
|
|
||||||
get 'Retrieve a chat' do
|
get 'Retrieve a chat' do
|
||||||
tags 'Chats'
|
tags 'Chats'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:id) { chat.id }
|
let(:id) { chat.id }
|
||||||
@@ -192,7 +179,7 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
|
|
||||||
patch 'Update a chat' do
|
patch 'Update a chat' do
|
||||||
tags 'Chats'
|
tags 'Chats'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
@@ -235,7 +222,7 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
|
|
||||||
delete 'Delete a chat' do
|
delete 'Delete a chat' do
|
||||||
tags 'Chats'
|
tags 'Chats'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:id) { another_chat.id }
|
let(:id) { another_chat.id }
|
||||||
@@ -253,13 +240,11 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/api/v1/chats/{chat_id}/messages' do
|
path '/api/v1/chats/{chat_id}/messages' do
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with write scope'
|
|
||||||
parameter name: :chat_id, in: :path, type: :string, required: true, description: 'Chat ID'
|
parameter name: :chat_id, in: :path, type: :string, required: true, description: 'Chat ID'
|
||||||
|
|
||||||
post 'Create a message' do
|
post 'Create a message' do
|
||||||
tags 'Chat Messages'
|
tags 'Chat Messages'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
@@ -309,13 +294,11 @@ RSpec.describe 'API V1 Chats', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/api/v1/chats/{chat_id}/messages/retry' do
|
path '/api/v1/chats/{chat_id}/messages/retry' do
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with write scope'
|
|
||||||
parameter name: :chat_id, in: :path, type: :string, required: true, description: 'Chat ID'
|
parameter name: :chat_id, in: :path, type: :string, required: true, description: 'Chat ID'
|
||||||
|
|
||||||
post 'Retry the last assistant response' do
|
post 'Retry the last assistant response' do
|
||||||
tags 'Chat Messages'
|
tags 'Chat Messages'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:chat_id) { chat.id }
|
let(:chat_id) { chat.id }
|
||||||
|
|||||||
@@ -20,25 +20,18 @@ RSpec.describe 'API V1 Imports', type: :request do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:oauth_application) do
|
let(:api_key) do
|
||||||
Doorkeeper::Application.create!(
|
key = ApiKey.generate_secure_key
|
||||||
name: 'API Docs',
|
ApiKey.create!(
|
||||||
redirect_uri: 'https://example.com/callback',
|
user: user,
|
||||||
scopes: 'read read_write'
|
name: 'API Docs Key',
|
||||||
|
key: key,
|
||||||
|
scopes: %w[read_write],
|
||||||
|
source: 'web'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:access_token) do
|
let(:'X-Api-Key') { api_key.plain_key }
|
||||||
Doorkeeper::AccessToken.create!(
|
|
||||||
application: oauth_application,
|
|
||||||
resource_owner_id: user.id,
|
|
||||||
scopes: 'read_write',
|
|
||||||
expires_in: 2.hours,
|
|
||||||
token: SecureRandom.hex(32)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:Authorization) { "Bearer #{access_token.token}" }
|
|
||||||
|
|
||||||
let(:account) do
|
let(:account) do
|
||||||
Account.create!(
|
Account.create!(
|
||||||
@@ -72,10 +65,8 @@ RSpec.describe 'API V1 Imports', type: :request do
|
|||||||
get 'List imports' do
|
get 'List imports' do
|
||||||
description 'List all imports for the user\'s family with pagination and filtering.'
|
description 'List all imports for the user\'s family with pagination and filtering.'
|
||||||
tags 'Imports'
|
tags 'Imports'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
parameter name: :page, in: :query, type: :integer, required: false,
|
parameter name: :page, in: :query, type: :integer, required: false,
|
||||||
description: 'Page number (default: 1)'
|
description: 'Page number (default: 1)'
|
||||||
parameter name: :per_page, in: :query, type: :integer, required: false,
|
parameter name: :per_page, in: :query, type: :integer, required: false,
|
||||||
@@ -127,11 +118,9 @@ RSpec.describe 'API V1 Imports', type: :request do
|
|||||||
post 'Create import' do
|
post 'Create import' do
|
||||||
description 'Create a new import from raw CSV content.'
|
description 'Create a new import from raw CSV content.'
|
||||||
tags 'Imports'
|
tags 'Imports'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with write scope'
|
|
||||||
|
|
||||||
parameter name: :body, in: :body, required: true, schema: {
|
parameter name: :body, in: :body, required: true, schema: {
|
||||||
type: :object,
|
type: :object,
|
||||||
@@ -238,14 +227,12 @@ RSpec.describe 'API V1 Imports', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/api/v1/imports/{id}' do
|
path '/api/v1/imports/{id}' do
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
parameter name: :id, in: :path, type: :string, required: true, description: 'Import ID'
|
parameter name: :id, in: :path, type: :string, required: true, description: 'Import ID'
|
||||||
|
|
||||||
get 'Retrieve an import' do
|
get 'Retrieve an import' do
|
||||||
description 'Retrieve detailed information about a specific import, including configuration and row statistics.'
|
description 'Retrieve detailed information about a specific import, including configuration and row statistics.'
|
||||||
tags 'Imports'
|
tags 'Imports'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:id) { pending_import.id }
|
let(:id) { pending_import.id }
|
||||||
|
|||||||
235
spec/requests/api/v1/tags_spec.rb
Normal file
235
spec/requests/api/v1/tags_spec.rb
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'swagger_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'API V1 Tags', type: :request do
|
||||||
|
let(:family) do
|
||||||
|
Family.create!(
|
||||||
|
name: 'API Family',
|
||||||
|
currency: 'USD',
|
||||||
|
locale: 'en',
|
||||||
|
date_format: '%m-%d-%Y'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:user) do
|
||||||
|
family.users.create!(
|
||||||
|
email: 'api-user@example.com',
|
||||||
|
password: 'password123',
|
||||||
|
password_confirmation: 'password123'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:api_key) do
|
||||||
|
key = ApiKey.generate_secure_key
|
||||||
|
ApiKey.create!(
|
||||||
|
user: user,
|
||||||
|
name: 'API Docs Key',
|
||||||
|
key: key,
|
||||||
|
scopes: %w[read_write],
|
||||||
|
source: 'web'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:'X-Api-Key') { api_key.plain_key }
|
||||||
|
|
||||||
|
let!(:essential_tag) do
|
||||||
|
family.tags.create!(name: 'Essential', color: '#22c55e')
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:discretionary_tag) do
|
||||||
|
family.tags.create!(name: 'Discretionary', color: '#f97316')
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:recurring_tag) do
|
||||||
|
family.tags.create!(name: 'Recurring', color: '#3b82f6')
|
||||||
|
end
|
||||||
|
|
||||||
|
path '/api/v1/tags' do
|
||||||
|
get 'List tags' do
|
||||||
|
tags 'Tags'
|
||||||
|
security [ { apiKeyAuth: [] } ]
|
||||||
|
produces 'application/json'
|
||||||
|
|
||||||
|
response '200', 'tags listed' do
|
||||||
|
schema '$ref' => '#/components/schemas/TagCollection'
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload).to be_an(Array)
|
||||||
|
expect(payload.length).to eq(3)
|
||||||
|
expect(payload.first).to include('id', 'name', 'color', 'created_at', 'updated_at')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post 'Create tag' do
|
||||||
|
tags 'Tags'
|
||||||
|
security [ { apiKeyAuth: [] } ]
|
||||||
|
consumes 'application/json'
|
||||||
|
produces 'application/json'
|
||||||
|
parameter name: :body, in: :body, required: true, schema: {
|
||||||
|
type: :object,
|
||||||
|
properties: {
|
||||||
|
tag: {
|
||||||
|
type: :object,
|
||||||
|
properties: {
|
||||||
|
name: { type: :string, description: 'Tag name (required)' },
|
||||||
|
color: { type: :string, description: 'Hex color code (optional, auto-assigned if not provided)' }
|
||||||
|
},
|
||||||
|
required: %w[name]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: %w[tag]
|
||||||
|
}
|
||||||
|
|
||||||
|
response '201', 'tag created' do
|
||||||
|
schema '$ref' => '#/components/schemas/TagDetail'
|
||||||
|
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
tag: {
|
||||||
|
name: 'Business',
|
||||||
|
color: '#8b5cf6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload.fetch('name')).to eq('Business')
|
||||||
|
expect(payload.fetch('color')).to eq('#8b5cf6')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response '201', 'tag created with auto-assigned color' do
|
||||||
|
schema '$ref' => '#/components/schemas/TagDetail'
|
||||||
|
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
tag: {
|
||||||
|
name: 'Travel'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload.fetch('name')).to eq('Travel')
|
||||||
|
expect(payload.fetch('color')).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response '422', 'validation error - missing name' do
|
||||||
|
schema '$ref' => '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
tag: {
|
||||||
|
color: '#8b5cf6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path '/api/v1/tags/{id}' do
|
||||||
|
parameter name: :id, in: :path, type: :string, required: true, description: 'Tag ID'
|
||||||
|
|
||||||
|
get 'Retrieve a tag' do
|
||||||
|
tags 'Tags'
|
||||||
|
security [ { apiKeyAuth: [] } ]
|
||||||
|
produces 'application/json'
|
||||||
|
|
||||||
|
let(:id) { essential_tag.id }
|
||||||
|
|
||||||
|
response '200', 'tag retrieved' do
|
||||||
|
schema '$ref' => '#/components/schemas/TagDetail'
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload.fetch('id')).to eq(essential_tag.id)
|
||||||
|
expect(payload.fetch('name')).to eq('Essential')
|
||||||
|
expect(payload.fetch('color')).to eq('#22c55e')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response '404', 'tag not found' do
|
||||||
|
schema '$ref' => '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
|
let(:id) { SecureRandom.uuid }
|
||||||
|
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
patch 'Update a tag' do
|
||||||
|
tags 'Tags'
|
||||||
|
security [ { apiKeyAuth: [] } ]
|
||||||
|
consumes 'application/json'
|
||||||
|
produces 'application/json'
|
||||||
|
|
||||||
|
let(:id) { essential_tag.id }
|
||||||
|
|
||||||
|
parameter name: :body, in: :body, required: true, schema: {
|
||||||
|
type: :object,
|
||||||
|
properties: {
|
||||||
|
tag: {
|
||||||
|
type: :object,
|
||||||
|
properties: {
|
||||||
|
name: { type: :string },
|
||||||
|
color: { type: :string }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
tag: {
|
||||||
|
name: 'Must Have',
|
||||||
|
color: '#10b981'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
response '200', 'tag updated' do
|
||||||
|
schema '$ref' => '#/components/schemas/TagDetail'
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
expect(payload.fetch('name')).to eq('Must Have')
|
||||||
|
expect(payload.fetch('color')).to eq('#10b981')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response '404', 'tag not found' do
|
||||||
|
schema '$ref' => '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
|
let(:id) { SecureRandom.uuid }
|
||||||
|
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
delete 'Delete a tag' do
|
||||||
|
tags 'Tags'
|
||||||
|
security [ { apiKeyAuth: [] } ]
|
||||||
|
|
||||||
|
let(:id) { recurring_tag.id }
|
||||||
|
|
||||||
|
response '204', 'tag deleted' do
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
|
||||||
|
response '404', 'tag not found' do
|
||||||
|
let(:id) { SecureRandom.uuid }
|
||||||
|
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -20,25 +20,18 @@ RSpec.describe 'API V1 Transactions', type: :request do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:oauth_application) do
|
let(:api_key) do
|
||||||
Doorkeeper::Application.create!(
|
key = ApiKey.generate_secure_key
|
||||||
name: 'API Docs',
|
ApiKey.create!(
|
||||||
redirect_uri: 'https://example.com/callback',
|
user: user,
|
||||||
scopes: 'read read_write'
|
name: 'API Docs Key',
|
||||||
|
key: key,
|
||||||
|
scopes: %w[read_write],
|
||||||
|
source: 'web'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:access_token) do
|
let(:'X-Api-Key') { api_key.plain_key }
|
||||||
Doorkeeper::AccessToken.create!(
|
|
||||||
application: oauth_application,
|
|
||||||
resource_owner_id: user.id,
|
|
||||||
scopes: 'read_write',
|
|
||||||
expires_in: 2.hours,
|
|
||||||
token: SecureRandom.hex(32)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:Authorization) { "Bearer #{access_token.token}" }
|
|
||||||
|
|
||||||
let(:account) do
|
let(:account) do
|
||||||
Account.create!(
|
Account.create!(
|
||||||
@@ -96,10 +89,8 @@ RSpec.describe 'API V1 Transactions', type: :request do
|
|||||||
path '/api/v1/transactions' do
|
path '/api/v1/transactions' do
|
||||||
get 'List transactions' do
|
get 'List transactions' do
|
||||||
tags 'Transactions'
|
tags 'Transactions'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with read scope'
|
|
||||||
parameter name: :page, in: :query, type: :integer, required: false,
|
parameter name: :page, in: :query, type: :integer, required: false,
|
||||||
description: 'Page number (default: 1)'
|
description: 'Page number (default: 1)'
|
||||||
parameter name: :per_page, in: :query, type: :integer, required: false,
|
parameter name: :per_page, in: :query, type: :integer, required: false,
|
||||||
@@ -174,11 +165,9 @@ RSpec.describe 'API V1 Transactions', type: :request do
|
|||||||
|
|
||||||
post 'Create transaction' do
|
post 'Create transaction' do
|
||||||
tags 'Transactions'
|
tags 'Transactions'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token with write scope'
|
|
||||||
parameter name: :body, in: :body, required: true, schema: {
|
parameter name: :body, in: :body, required: true, schema: {
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: {
|
properties: {
|
||||||
@@ -260,13 +249,11 @@ RSpec.describe 'API V1 Transactions', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
path '/api/v1/transactions/{id}' do
|
path '/api/v1/transactions/{id}' do
|
||||||
parameter name: :Authorization, in: :header, required: true, schema: { type: :string },
|
|
||||||
description: 'Bearer token'
|
|
||||||
parameter name: :id, in: :path, type: :string, required: true, description: 'Transaction ID'
|
parameter name: :id, in: :path, type: :string, required: true, description: 'Transaction ID'
|
||||||
|
|
||||||
get 'Retrieve a transaction' do
|
get 'Retrieve a transaction' do
|
||||||
tags 'Transactions'
|
tags 'Transactions'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:id) { transaction.id }
|
let(:id) { transaction.id }
|
||||||
@@ -295,7 +282,7 @@ RSpec.describe 'API V1 Transactions', type: :request do
|
|||||||
|
|
||||||
patch 'Update a transaction' do
|
patch 'Update a transaction' do
|
||||||
tags 'Transactions'
|
tags 'Transactions'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
@@ -352,7 +339,7 @@ RSpec.describe 'API V1 Transactions', type: :request do
|
|||||||
|
|
||||||
delete 'Delete a transaction' do
|
delete 'Delete a transaction' do
|
||||||
tags 'Transactions'
|
tags 'Transactions'
|
||||||
security [ { bearerAuth: [] } ]
|
security [ { apiKeyAuth: [] } ]
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
let(:id) { another_transaction.id }
|
let(:id) { another_transaction.id }
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ RSpec.configure do |config|
|
|||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
securitySchemes: {
|
securitySchemes: {
|
||||||
bearerAuth: {
|
apiKeyAuth: {
|
||||||
type: :http,
|
type: :apiKey,
|
||||||
scheme: :bearer,
|
name: 'X-Api-Key',
|
||||||
bearerFormat: :JWT
|
in: :header,
|
||||||
|
description: 'API key for authentication. Generate one from your account settings.'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
schemas: {
|
schemas: {
|
||||||
@@ -171,6 +172,29 @@ RSpec.configure do |config|
|
|||||||
account_type: { type: :string }
|
account_type: { type: :string }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
AccountDetail: {
|
||||||
|
type: :object,
|
||||||
|
required: %w[id name balance currency classification account_type],
|
||||||
|
properties: {
|
||||||
|
id: { type: :string, format: :uuid },
|
||||||
|
name: { type: :string },
|
||||||
|
balance: { type: :string },
|
||||||
|
currency: { type: :string },
|
||||||
|
classification: { type: :string },
|
||||||
|
account_type: { type: :string }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AccountCollection: {
|
||||||
|
type: :object,
|
||||||
|
required: %w[accounts pagination],
|
||||||
|
properties: {
|
||||||
|
accounts: {
|
||||||
|
type: :array,
|
||||||
|
items: { '$ref' => '#/components/schemas/AccountDetail' }
|
||||||
|
},
|
||||||
|
pagination: { '$ref' => '#/components/schemas/Pagination' }
|
||||||
|
}
|
||||||
|
},
|
||||||
Category: {
|
Category: {
|
||||||
type: :object,
|
type: :object,
|
||||||
required: %w[id name classification color icon],
|
required: %w[id name classification color icon],
|
||||||
@@ -233,6 +257,21 @@ RSpec.configure do |config|
|
|||||||
color: { type: :string }
|
color: { type: :string }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
TagDetail: {
|
||||||
|
type: :object,
|
||||||
|
required: %w[id name color created_at updated_at],
|
||||||
|
properties: {
|
||||||
|
id: { type: :string, format: :uuid },
|
||||||
|
name: { type: :string },
|
||||||
|
color: { type: :string },
|
||||||
|
created_at: { type: :string, format: :'date-time' },
|
||||||
|
updated_at: { type: :string, format: :'date-time' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TagCollection: {
|
||||||
|
type: :array,
|
||||||
|
items: { '$ref' => '#/components/schemas/TagDetail' }
|
||||||
|
},
|
||||||
Transfer: {
|
Transfer: {
|
||||||
type: :object,
|
type: :object,
|
||||||
required: %w[id amount currency],
|
required: %w[id amount currency],
|
||||||
|
|||||||
Reference in New Issue
Block a user