Add error handling for 404 and 500 results from PostgreSQL

select-best-date
Mike Benowitz 2019-08-05 15:20:27 -04:00
parent 0a8fb1cde4
commit 73834ff63f
3 changed files with 354 additions and 295 deletions

View File

@ -5,405 +5,440 @@ class SwaggerDoc():
def getDocs(self):
return {
"swagger": "2.0",
"info": {
"title": "CCE Search",
"description": "API for searching Copyright Registrations and Renewals",
"contact": {
"responsibleOrganization": "NYPL",
"responsibleDeveloper": "Michael Benowitz",
"email": "michaelbenowitz@nypl.org",
"url": "www.nypl.org",
'swagger': '2.0',
'info': {
'title': 'CCE Search',
'description': 'API for searching Copyright Registrations and Renewals',
'contact': {
'responsibleOrganization': 'NYPL',
'responsibleDeveloper': 'Michael Benowitz',
'email': 'michaelbenowitz@nypl.org',
'url': 'www.nypl.org',
},
"version": "v0.1"
'version': 'v0.1'
},
"basePath": "/", # base bash for blueprint registration
"schemes": [
"http",
"https"
'basePath': '/', # base bash for blueprint registration
'schemes': [
'http',
'https'
],
"paths": {
"/search/fulltext": {
"get": {
"tags": ["Search"],
"summary": "Returns a set of registration and renewal objects",
"description": "Accepts a search_query string with full boolean logic to fuzzy search across both registration and renewal records",
"parameters": [
'paths': {
'/search/fulltext': {
'get': {
'tags': ['Search'],
'summary': 'Returns a set of registration and renewal objects',
'description': 'Accepts a search_query string with full boolean logic to fuzzy search across both registration and renewal records',
'parameters': [
{
"name": "query",
"in": "query",
"type": "string",
"required": True,
"default": "*"
'name': 'query',
'in': 'query',
'type': 'string',
'required': True,
'default': '*'
},{
"name": "source",
"in": "query",
"type": "boolean",
"required": False,
"default": False,
"description": "Return source XML/CSV data"
'name': 'source',
'in': 'query',
'type': 'boolean',
'required': False,
'default': False,
'description': 'Return source XML/CSV data'
},{
"name": "page",
"in": "query",
"type": "number",
"required": False,
"default": 0
'name': 'page',
'in': 'query',
'type': 'number',
'required': False,
'default': 0
},{
"name": "per_page",
"in": "query",
"type": "number",
"required": False,
"default": 10
'name': 'per_page',
'in': 'query',
'type': 'number',
'required': False,
'default': 10
}
],
"responses": {
'responses': {
200: {
"description": "A list of copyright registrations and renewals",
"schema": {
"$ref": "#/definitions/MultiResponse"
'description': 'A list of copyright registrations and renewals',
'schema': {
'$ref': '#/definitions/MultiResponse'
}
}
}
}
},
"/search/registration/{regnum}": {
"get": {
"tags": ["Search"],
"summary": "Returns a set of registration and renewal objects",
"description": "Accepts a copyright registration number and returns all matching records",
"parameters": [
'/search/registration/{regnum}': {
'get': {
'tags': ['Search'],
'summary': 'Returns a set of registration and renewal objects',
'description': 'Accepts a copyright registration number and returns all matching records',
'parameters': [
{
"name": "regnum",
"in": "path",
"required": True,
"schema": {
"type": "string"
'name': 'regnum',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
},
"description": "Standard copyright registration number"
'description': 'Standard copyright registration number'
},{
"name": "source",
"in": "query",
"type": "boolean",
"required": False,
"default": False,
"description": "Return source XML/CSV data"
'name': 'source',
'in': 'query',
'type': 'boolean',
'required': False,
'default': False,
'description': 'Return source XML/CSV data'
},{
"name": "page",
"in": "query",
"type": "number",
"required": False,
"default": 0
'name': 'page',
'in': 'query',
'type': 'number',
'required': False,
'default': 0
},{
"name": "per_page",
"in": "query",
"type": "number",
"required": False,
"default": 10
'name': 'per_page',
'in': 'query',
'type': 'number',
'required': False,
'default': 10
}
],
"responses": {
'responses': {
200: {
"description": "A list of copyright registrations and renewals",
"schema": {
"$ref": "#/definitions/MultiResponse"
'description': 'A list of copyright registrations and renewals',
'schema': {
'$ref': '#/definitions/MultiResponse'
}
}
}
}
},
"/search/renewal/{rennum}": {
"get": {
"tags": ["Search"],
"summary": "Returns a set of registration and renewal objects",
"description": "Accepts a copyright renewal number and returns all matching records",
"parameters": [
'/search/renewal/{rennum}': {
'get': {
'tags': ['Search'],
'summary': 'Returns a set of registration and renewal objects',
'description': 'Accepts a copyright renewal number and returns all matching records',
'parameters': [
{
"name": "rennum",
"in": "path",
"required": True,
"schema": {
"type": "string"
'name': 'rennum',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
},
"description": "Standard copyright renewal number"
'description': 'Standard copyright renewal number'
},{
"name": "source",
"in": "query",
"type": "boolean",
"required": False,
"default": False,
"description": "Return source XML/CSV data"
'name': 'source',
'in': 'query',
'type': 'boolean',
'required': False,
'default': False,
'description': 'Return source XML/CSV data'
},{
"name": "page",
"in": "query",
"type": "number",
"required": False,
"default": 0
'name': 'page',
'in': 'query',
'type': 'number',
'required': False,
'default': 0
},{
"name": "per_page",
"in": "query",
"type": "number",
"required": False,
"default": 10
'name': 'per_page',
'in': 'query',
'type': 'number',
'required': False,
'default': 10
}
],
"responses": {
'responses': {
200: {
"description": "A list of copyright registrations and renewals",
"schema": {
"$ref": "#/definitions/MultiResponse"
'description': 'A list of copyright registrations and renewals',
'schema': {
'$ref': '#/definitions/MultiResponse'
}
}
}
}
},
"/registration/{uuid}": {
"get": {
"tags": ["Lookup"],
"summary": "Return a specific Registration record by UUID",
"description": "Accepts a UUID and returns a registration record",
"parameters": [{
"name": "uuid",
"in": "path",
"required": True,
"schema": {
"type": "string"
'/registration/{uuid}': {
'get': {
'tags': ['Lookup'],
'summary': 'Return a specific Registration record by UUID',
'description': 'Accepts a UUID and returns a registration record',
'parameters': [{
'name': 'uuid',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
},
"description": "Standard UUID"
'description': 'Standard UUID'
}],
"responses": {
'responses': {
200: {
"description": "A single Registration record",
"schema": {
"$ref": "#/definitions/SingleResponse"
'description': 'A single Registration record',
'schema': {
'$ref': '#/definitions/SingleResponse'
}
},
404: {
'description': 'A message noting that the UUID could not be found',
'schema': {
'$ref': '#/definitions/ErrorResponse'
}
},
500: {
'description': 'Generic internal error message',
'schema': {
'$ref': '#/definitions/ErrorResponse'
}
}
}
}
},
"/renewal/{uuid}": {
"get": {
"tags": ["Lookup"],
"summary": "Return a specific Renewal record by UUID",
"description": "Accepts a UUID and returns either an orphan renewal record or the parent registration with associated renewals",
"parameters": [{
"name": "uuid",
"in": "path",
"required": True,
"schema": {
"type": "string"
'/renewal/{uuid}': {
'get': {
'tags': ['Lookup'],
'summary': 'Return a specific Renewal record by UUID',
'description': 'Accepts a UUID and returns either an orphan renewal record or the parent registration with associated renewals',
'parameters': [{
'name': 'uuid',
'in': 'path',
'required': True,
'schema': {
'type': 'string'
},
"description": "Standard UUID"
'description': 'Standard UUID'
}],
"responses": {
'responses': {
200: {
"description": "A single Renewal or Registration record",
"schema": {
"$ref": "#/definitions/SingleResponse"
'description': 'A single Renewal or Registration record',
'schema': {
'$ref': '#/definitions/SingleResponse'
}
},
404: {
'description': 'A message noting that the UUID could not be found',
'schema': {
'$ref': '#/definitions/ErrorResponse'
}
},
500: {
'description': 'Generic internal error message',
'schema': {
'$ref': '#/definitions/ErrorResponse'
}
}
}
}
}
},
"definitions": {
"SingleResponse": {
"type": "object",
"properties": {
"status": {
"type": "integer"
'definitions': {
'ErrorResponse': {
'type': 'object',
'properties': {
'status': {
'type': 'integer'
},
"data": {
"type": "object",
"anyOf": [
{"$ref": "#/definitions/Registration"},
{"$ref": "#/definitions/Renewal"}
'message': {
'type': 'string'
}
}
},
'SingleResponse': {
'type': 'object',
'properties': {
'status': {
'type': 'integer'
},
'data': {
'type': 'object',
'anyOf': [
{'$ref': '#/definitions/Registration'},
{'$ref': '#/definitions/Renewal'}
]
}
}
},
"MultiResponse": {
"type": "object",
"properties": {
"total": {
"type": "integer",
'MultiResponse': {
'type': 'object',
'properties': {
'total': {
'type': 'integer',
},
"query": {
"type": "object",
"$ref": "#/definitions/Query"
'query': {
'type': 'object',
'$ref': '#/definitions/Query'
},
"paging": {
"type": "object",
"$ref": "#/definitions/Paging"
'paging': {
'type': 'object',
'$ref': '#/definitions/Paging'
},
"results": {
"type": "array",
"items": {
"anyOf": [
{"$ref": "#/definitions/Registration"},
{"$ref": "#/definitions/Renewal"}
'results': {
'type': 'array',
'items': {
'anyOf': [
{'$ref': '#/definitions/Registration'},
{'$ref': '#/definitions/Renewal'}
]
}
}
}
},
"Query": {
"type": "object",
"properties": {
"endpoint": {
"type": "string"
'Query': {
'type': 'object',
'properties': {
'endpoint': {
'type': 'string'
},
"term": {
"type": "string"
'term': {
'type': 'string'
}
}
},
"Paging": {
"type": "object",
"properties": {
"first": {
"type": "string"
'Paging': {
'type': 'object',
'properties': {
'first': {
'type': 'string'
},
"previous": {
"type": "string"
'previous': {
'type': 'string'
},
"next": {
"type": "string"
'next': {
'type': 'string'
},
"last": {
"type": "string"
'last': {
'type': 'string'
}
}
},
"Registration": {
"type": "object",
"properties": {
"title": {
"type": "string"
'Registration': {
'type': 'object',
'properties': {
'title': {
'type': 'string'
},
"copies": {
"type": "string"
'copies': {
'type': 'string'
},
"copy_date": {
"type": "string"
'copy_date': {
'type': 'string'
},
"description": {
"type": "string"
'description': {
'type': 'string'
},
"authors": {
"type": "array",
"items": {
"$ref": "#/definitions/Agent"
'authors': {
'type': 'array',
'items': {
'$ref': '#/definitions/Agent'
}
},
"publishers": {
"type": "array",
"items": {
"$ref": "#/definitions/Agent"
'publishers': {
'type': 'array',
'items': {
'$ref': '#/definitions/Agent'
}
},
"registrations": {
"type": "array",
"items": {
"$ref": "#/definitions/RegRegistration"
'registrations': {
'type': 'array',
'items': {
'$ref': '#/definitions/RegRegistration'
}
},
"renewals":{
"type": "array",
"items": {
"$ref": "#/definitions/Renewal"
'renewals':{
'type': 'array',
'items': {
'$ref': '#/definitions/Renewal'
}
},
"source": {
"type": "object",
"properties": {
"page": {
"type": "integer"
'source': {
'type': 'object',
'properties': {
'page': {
'type': 'integer'
},
"page_position": {
"type": "integer"
'page_position': {
'type': 'integer'
},
"part": {
"type": "string"
'part': {
'type': 'string'
},
"series": {
"type": "string"
'series': {
'type': 'string'
},
"url": {
"type": "string"
'url': {
'type': 'string'
},
"year": {
"type": "integer"
'year': {
'type': 'integer'
}
}
}
}
},
"Agent": {
"type": "string"
'Agent': {
'type': 'string'
},
"RegRegistration": {
"type": "object",
"properties": {
"number": {
"type": "string"
'RegRegistration': {
'type': 'object',
'properties': {
'number': {
'type': 'string'
},
"date": {
"type": "string"
'date': {
'type': 'string'
}
}
},
"Renewal": {
"type": "object",
"properties": {
"type": {
"type": "string"
'Renewal': {
'type': 'object',
'properties': {
'type': {
'type': 'string'
},
"title": {
"type": "string"
'title': {
'type': 'string'
},
"author": {
"type": "string"
'author': {
'type': 'string'
},
"new_matter": {
"type": "string"
'new_matter': {
'type': 'string'
},
"renewal_num": {
"type": "string"
'renewal_num': {
'type': 'string'
},
"renewal_date": {
"type": "string"
'renewal_date': {
'type': 'string'
},
"notes": {
"type": "string"
'notes': {
'type': 'string'
},
"volume": {
"type": "string"
'volume': {
'type': 'string'
},
"part": {
"type": "string"
'part': {
'type': 'string'
},
"number": {
"type": "string"
'number': {
'type': 'string'
},
"page": {
"type": "string"
'page': {
'type': 'string'
},
"claimants": {
"type": "array",
"items": {
"$ref": "#/definitions/Claimant"
'claimants': {
'type': 'array',
'items': {
'$ref': '#/definitions/Claimant'
}
}
}
},
"Claimant": {
"type": "object",
"properties": {
"name": {
"type": "string"
'Claimant': {
'type': 'object',
'properties': {
'name': {
'type': 'string'
},
"type": {
"type": "string"
'type': {
'type': 'string'
}
}
}

View File

@ -1,40 +1,57 @@
from flask import (
Blueprint, request, session, url_for, redirect, current_app, jsonify
)
from flask import Blueprint, request, jsonify
from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound
from api.db import db
from api.elastic import elastic
from api.response import SingleResponse
from model.cce import CCE
from model.registration import Registration
from model.renewal import Renewal, RENEWAL_REG
from model.volume import Volume
from helpers.errors import LookupError
uuid = Blueprint('uuid', __name__, url_prefix='/')
@uuid.route('/registration/<uuid>', methods=['GET'])
def regQuery(uuid):
err = None
regRecord = SingleResponse('uuid', request.base_url)
try:
dbEntry = db.session.query(CCE)\
.outerjoin(Registration, RENEWAL_REG, Renewal)\
.filter(CCE.uuid == uuid).one()
regRecord = SingleResponse('uuid', request.base_url)
regRecord.result = SingleResponse.parseEntry(dbEntry, xml=True)
regRecord.createDataBlock()
return jsonify(regRecord.createResponse(200))
status = 200
except NoResultFound:
status = 404
err = LookupError('Unable to locate UUID {} in database'.format(uuid))
except DataError:
status = 500
err = LookupError('Malformed UUID {} received'.format(uuid))
return jsonify(regRecord.createResponse(status, err=err))
@uuid.route('/renewal/<uuid>', methods=['GET'])
def renQuery(uuid):
err = None
renRecord = SingleResponse('uuid', request.base_url)
try:
dbRenewal = db.session.query(Renewal)\
.outerjoin(RENEWAL_REG, Registration, CCE)\
.filter(Renewal.uuid == uuid).one()
renRecord = SingleResponse('uuid', request.base_url)
renRecord.result = parseRetRenewal(dbRenewal)
renRecord.createDataBlock()
return jsonify(renRecord.createResponse(200))
status = 200
except NoResultFound:
status = 404
err = LookupError('Unable to locate UUID {} in database'.format(uuid))
except DataError:
status = 500
err = LookupError('Malformed UUID {} received'.format(uuid))
return jsonify(renRecord.createResponse(status, err=err))
def parseRetRenewal(dbRenewal):

View File

@ -5,3 +5,10 @@ class DataError(Exception):
for key, value in kwargs.items():
setattr(self, key, value)
class LookupError(Exception):
def __init__(self, message, **kwargs):
self.message = message
for key, value in kwargs.items():
setattr(self, key, value)