Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BGDIINF_SB-2461: Added author_version information and made author field required - #major #64

Merged
merged 6 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ good-names=i,
Run,
db,
assertKml,
assertKmlMetadata,
assertKmlFile,
assertKmlInDb,
assertKmlDbData,
_

# Good variable names regexes, separated by a comma. If names match any regex,
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ serve: clean_logs $(LOGS_DIR)

.PHONY: gunicornserve
gunicornserve: clean_logs $(LOGS_DIR)
mkdir -p /tmp/gunicorn_workers
SCRIPT_NAME=$(ROUTE_PREFIX) ENV_FILE=$(ENV_FILE) LOGS_DIR=$(LOGS_DIR) $(PYTHON) wsgi.py


Expand Down
381 changes: 202 additions & 179 deletions Pipfile.lock

Large diffs are not rendered by default.

69 changes: 38 additions & 31 deletions app/helpers/dynamodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,29 @@ def __init__(self, table_name, bucket_name, endpoint_url, table_region):
self.endpoint = endpoint_url

def save_item(
self, kml_id, kml_admin_id, file_key, file_length, timestamp, empty=False, author=''
self, kml_id, kml_admin_id, file_key, file_length, timestamp, author, author_version, empty
):
logger.debug('Saving dynamodb item with primary key %s', kml_id)
db_item = {
'kml_id': kml_id,
'admin_id': kml_admin_id,
'created': timestamp,
'updated': timestamp,
'bucket': self.bucket_name,
'file_key': file_key,
'empty': empty,
'length': file_length,
'encoding': KML_FILE_CONTENT_ENCODING,
'content_type': KML_FILE_CONTENT_TYPE,
'author': author,
'author_version': author_version
}
try:
self.table.put_item(
Item={
'kml_id': kml_id,
'admin_id': kml_admin_id,
'created': timestamp,
'updated': timestamp,
'bucket': self.bucket_name,
'file_key': file_key,
'empty': empty,
'length': file_length,
'encoding': KML_FILE_CONTENT_ENCODING,
'content_type': KML_FILE_CONTENT_TYPE,
'author': author
}
)
self.table.put_item(Item=db_item)
except EndpointConnectionError as error:
logger.exception('Failed to connect to DynamoDB: %s', error)
abort(502, 'Backend DB connection error, please consult logs')
return db_item

def get_item(self, kml_id):
logger.debug('Get dynamodb item with primary key %s', kml_id)
Expand Down Expand Up @@ -106,27 +107,33 @@ def get_item_by_admin_id(self, admin_id):

return items[0]

def update_item(self, kml_id, file_length, timestamp, empty):
def update_item(self, kml_id, db_item, file_length, timestamp, empty, author_version=None):
logger.debug('Updating dynamodb item with primary key %s', kml_id)
db_item['updated'] = timestamp
db_item['empty'] = empty
db_item['length'] = file_length
attribute_updates = {
'updated': {
'Value': timestamp, 'Action': 'PUT'
},
'empty': {
'Value': empty, 'Action': 'PUT'
},
'length': {
'Value': file_length, 'Action': 'PUT'
}
}
if author_version is not None:
attribute_updates['author_version'] = {'Value': author_version, 'Action': 'PUT'}
db_item['author_version'] = author_version
try:
self.table.update_item(
Key={'kml_id': kml_id},
AttributeUpdates={
'updated': {
'Value': timestamp, 'Action': 'PUT'
},
'empty': {
'Value': empty, 'Action': 'PUT'
},
'length': {
'Value': file_length, 'Action': 'PUT'
}
}
)
self.table.update_item(Key={'kml_id': kml_id}, AttributeUpdates=attribute_updates)
except EndpointConnectionError as error:
logger.exception('Failed to connect to DynamoDB: %s', error)
abort(502, 'Backend DB connection error, please consult logs')

return db_item

def delete_item(self, kml_id):
logger.debug('Deleting dynamodb item with primary key %s', kml_id)
try:
Expand Down
30 changes: 30 additions & 0 deletions app/helpers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from flask import jsonify
from flask import make_response
from flask import request
from flask.helpers import url_for

from app.settings import DEFAULT_AUTHOR_VERSION
from app.settings import KML_FILE_CONTENT_TYPE
from app.settings import KML_MAX_SIZE
from app.settings import KML_STORAGE_HOST_URL
Expand Down Expand Up @@ -179,6 +181,13 @@ def validate_kml_file():
return gzip_string(kml_string), empty


def validate_author():
author = request.form.get('author', None)
if author is None:
abort(400, "Missing author field")
return author


def get_kml_file_link(file_key):
if KML_STORAGE_HOST_URL:
return f'{KML_STORAGE_HOST_URL}/{file_key}'
Expand Down Expand Up @@ -228,3 +237,24 @@ def decompress_if_gzipped(file_content):
logger.error("Error when trying to decompress kml file: %s", error)
raise error
return ret


def get_json_metadata(db_item, with_admin_id=False):
'''Return a json metadata output of a DB entry'''
metadata = {
'id': db_item['kml_id'],
'success': True,
'created': db_item['created'],
'updated': db_item['updated'],
'empty': db_item['empty'],
'author': db_item['author'],
'author_version': db_item.get('author_version', DEFAULT_AUTHOR_VERSION),
'links':
{
'self': url_for('get_kml_metadata', kml_id=db_item['kml_id'], _external=True),
'kml': get_kml_file_link(db_item['file_key']),
}
}
if with_admin_id:
metadata['admin_id'] = db_item['admin_id']
return metadata
112 changes: 31 additions & 81 deletions app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
from flask import jsonify
from flask import make_response
from flask import request
from flask.helpers import url_for

from app import app
from app.helpers.dynamodb import get_db
from app.helpers.s3 import get_storage
from app.helpers.utils import get_kml_file_link
from app.helpers.utils import get_json_metadata
from app.helpers.utils import validate_author
from app.helpers.utils import validate_content_length
from app.helpers.utils import validate_content_type
from app.helpers.utils import validate_kml_file
from app.helpers.utils import validate_permissions
from app.settings import DEFAULT_AUTHOR_VERSION
from app.settings import SCRIPT_NAME
from app.version import APP_VERSION

Expand All @@ -39,7 +40,9 @@ def create_kml():
# Get the kml file data
kml_string_gzip, empty = validate_kml_file()
# Get the author
author = request.form.get('author', 'unknown')
author = validate_author()
# Get the client version
author_version = request.form.get('author_version', DEFAULT_AUTHOR_VERSION)

kml_admin_id = urlsafe_b64encode(uuid4().bytes).decode('utf8').replace('=', '')
kml_id = urlsafe_b64encode(uuid4().bytes).decode('utf8').replace('=', '')
Expand All @@ -50,75 +53,34 @@ def create_kml():
storage.upload_object_to_bucket(file_key, kml_string_gzip)

db = get_db()
db.save_item(kml_id, kml_admin_id, file_key, len(kml_string_gzip), timestamp, empty, author)

return make_response(
jsonify(
{
'id': kml_id,
'admin_id': kml_admin_id,
'success': True,
'created': timestamp,
'updated': timestamp,
'empty': empty,
'links':
{
'self': url_for('get_kml_metadata', kml_id=kml_id, _external=True),
'kml': get_kml_file_link(file_key),
}
}
),
201
db_item = db.save_item(
kml_id,
kml_admin_id,
file_key,
len(kml_string_gzip),
timestamp,
author,
author_version,
empty
)

return make_response(jsonify(get_json_metadata(db_item, with_admin_id=True)), 201)


@app.route('/admin', methods=['GET'])
def get_kml_metadata_by_admin_id():
admin_id = request.args.get('admin_id')
if not admin_id:
logger.error("Query parameter admin_id is required: query=%s", request.args)
abort(400, "Query parameter admin_id is required")
item = get_db().get_item_by_admin_id(admin_id)
return make_response(
jsonify(
{
'id': item['kml_id'],
'admin_id': admin_id,
'success': True,
'created': item['created'],
'updated': item['updated'],
'empty': item['empty'],
'links':
{
'self': url_for('get_kml_metadata', kml_id=item['kml_id'], _external=True),
'kml': get_kml_file_link(item['file_key']),
}
}
),
200
)
db_item = get_db().get_item_by_admin_id(admin_id)
return make_response(jsonify(get_json_metadata(db_item, with_admin_id=True)), 200)


@app.route('/admin/<kml_id>', methods=['GET'])
def get_kml_metadata(kml_id):
item = get_db().get_item(kml_id)
return make_response(
jsonify(
{
'id': kml_id,
'success': True,
'created': item['created'],
'updated': item['updated'],
'empty': item['empty'],
'links':
{
'self': url_for('get_kml_metadata', kml_id=kml_id, _external=True),
'kml': get_kml_file_link(item['file_key']),
}
}
),
200
)
db_item = get_db().get_item(kml_id)
return make_response(jsonify(get_json_metadata(db_item, with_admin_id=False)), 200)


@app.route('/admin/<kml_id>', methods=['PUT'])
Expand All @@ -127,37 +89,25 @@ def get_kml_metadata(kml_id):
def update_kml(kml_id):
db = get_db()

item = db.get_item(kml_id)
admin_id = validate_permissions(item)
db_item = db.get_item(kml_id)
admin_id = validate_permissions(db_item)

# Get the client version
author_version = request.form.get('author_version', None)
ltshb marked this conversation as resolved.
Show resolved Hide resolved

# Get the kml file data
kml_string_gzip, empty = validate_kml_file()

storage = get_storage()
storage.upload_object_to_bucket(item['file_key'], kml_string_gzip)
storage.upload_object_to_bucket(db_item['file_key'], kml_string_gzip)

timestamp = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat(timespec='milliseconds')
db.update_item(kml_id, len(kml_string_gzip), timestamp, empty)

return make_response(
jsonify(
{
'id': kml_id,
'admin_id': admin_id,
'success': True,
'created': item['created'],
'updated': timestamp,
'empty': empty,
'links':
{
'self': url_for('get_kml_metadata', kml_id=kml_id, _external=True),
'kml': get_kml_file_link(item['file_key']),
}
}
),
200
db_item = db.update_item(
kml_id, db_item, len(kml_string_gzip), timestamp, empty, author_version
)

return make_response(jsonify(get_json_metadata(db_item, with_admin_id=True)), 200)


@app.route('/admin/<kml_id>', methods=['DELETE'])
@validate_content_type("multipart/form-data")
Expand Down
2 changes: 2 additions & 0 deletions app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@

CACHE_CONTROL = os.getenv('CACHE_CONTROL', 'no-cache, no-store, must-revalidate')
CACHE_CONTROL_4XX = os.getenv('CACHE_CONTROL_4XX', 'public, max-age=3600')

DEFAULT_AUTHOR_VERSION = '0.0.0'
Loading