Fix relationships between site and the menu models.
Make strings consistent across API.
This commit is contained in:
parent
6520e57696
commit
b229400557
2
Rotefile
2
Rotefile
|
@ -121,5 +121,5 @@ desc("Bootstrap DB")
|
||||||
task("bootstrap", function()
|
task("bootstrap", function()
|
||||||
exec("docker-compose", "up", "-d", "db")
|
exec("docker-compose", "up", "-d", "db")
|
||||||
local db_connection_flag = string.format("--db-connection=%s", db_connection)
|
local db_connection_flag = string.format("--db-connection=%s", db_connection)
|
||||||
exec("docker-compose", "run", "api", "inv", "db.bootstrap", db_connection_flag)
|
exec("docker-compose", "run", "api", "inv", "db.bootstrap", db_connection_flag, '--reset-db')
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -41,7 +41,7 @@ def make_json_app(import_name, **kwargs):
|
||||||
error_code = http.HTTPStatus.INTERNAL_SERVER_ERROR
|
error_code = http.HTTPStatus.INTERNAL_SERVER_ERROR
|
||||||
if isinstance(error, exceptions.HTTPException):
|
if isinstance(error, exceptions.HTTPException):
|
||||||
error_code = error.code
|
error_code = error.code
|
||||||
error_json = {"error": {"code": error_code, "message": error_message}}
|
error_json = {'error': {'code': error_code, 'message': error_message}}
|
||||||
|
|
||||||
response = flask.jsonify(error_json)
|
response = flask.jsonify(error_json)
|
||||||
response.status_code = error_code
|
response.status_code = error_code
|
||||||
|
|
|
@ -18,6 +18,7 @@ def register_views():
|
||||||
rookeries_views = [
|
rookeries_views = [
|
||||||
'rookeries.views',
|
'rookeries.views',
|
||||||
'rookeries.pages.views',
|
'rookeries.pages.views',
|
||||||
|
'rookeries.sites.views',
|
||||||
'rookeries.security',
|
'rookeries.security',
|
||||||
]
|
]
|
||||||
for views_modules in rookeries_views:
|
for views_modules in rookeries_views:
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Page(database.ModelBase):
|
||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<JournalEntry %r>' % self.slug
|
return '<JournalEntry {!r}>'.format(self.slug)
|
||||||
|
|
||||||
def to_json(self) -> dict:
|
def to_json(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -20,7 +20,7 @@ from rookeries.main import rookeries_app
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@rookeries_app.route("/api/pages", methods=['GET'])
|
@rookeries_app.route('/api/pages', methods=['GET'])
|
||||||
def serve_landing_page():
|
def serve_landing_page():
|
||||||
"""
|
"""
|
||||||
Serving up a landing page with Markdown content.
|
Serving up a landing page with Markdown content.
|
||||||
|
@ -28,10 +28,10 @@ def serve_landing_page():
|
||||||
:request page: The name of the page to display.
|
:request page: The name of the page to display.
|
||||||
"""
|
"""
|
||||||
# TODO: Remove hardcoding of landing url
|
# TODO: Remove hardcoding of landing url
|
||||||
return serve_page("about")
|
return serve_page('about')
|
||||||
|
|
||||||
|
|
||||||
@rookeries_app.route("/api/pages/<slug>", methods=['GET'])
|
@rookeries_app.route('/api/pages/<slug>', methods=['GET'])
|
||||||
def serve_page(slug):
|
def serve_page(slug):
|
||||||
"""
|
"""
|
||||||
Serving up a page with Markdown content.
|
Serving up a page with Markdown content.
|
||||||
|
@ -45,18 +45,18 @@ def serve_page(slug):
|
||||||
try:
|
try:
|
||||||
journal_entry = db_session.query(models.Page).filter_by(slug=slug).first()
|
journal_entry = db_session.query(models.Page).filter_by(slug=slug).first()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error("Error retrieving '%s' page because of: %s", slug, err)
|
logger.error('Error retrieving "%s" page because of: %s', slug, err)
|
||||||
flask.abort(http.HTTPStatus.INTERNAL_SERVER_ERROR)
|
flask.abort(http.HTTPStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
if journal_entry is None:
|
if journal_entry is None:
|
||||||
logger.warn("Unable to find retrieve page: %s", slug)
|
logger.warn('Unable to find retrieve page: %s', slug)
|
||||||
flask.abort(http.HTTPStatus.NOT_FOUND)
|
flask.abort(http.HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
return flask.jsonify(journal_entry.to_json())
|
return flask.jsonify(journal_entry.to_json())
|
||||||
|
|
||||||
|
|
||||||
@flask_jwt.jwt_required
|
@flask_jwt.jwt_required
|
||||||
@rookeries_app.route("/api/pages/<slug>", methods=['POST', 'PUT'])
|
@rookeries_app.route('/api/pages/<slug>', methods=['POST', 'PUT'])
|
||||||
def save_page(slug):
|
def save_page(slug):
|
||||||
updated_journal_entry = flask.request.json
|
updated_journal_entry = flask.request.json
|
||||||
db_session = database.SQLAlchemy.session
|
db_session = database.SQLAlchemy.session
|
||||||
|
@ -70,7 +70,7 @@ def save_page(slug):
|
||||||
content=updated_journal_entry['content'])
|
content=updated_journal_entry['content'])
|
||||||
db_session.add(journal_entry)
|
db_session.add(journal_entry)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error("Error retrieving '%s' page because of: %s", slug, err)
|
logger.error('Error retrieving "%s" page because of: %s', slug, err)
|
||||||
flask.abort(http.HTTPStatus.INTERNAL_SERVER_ERROR)
|
flask.abort(http.HTTPStatus.INTERNAL_SERVER_ERROR)
|
||||||
else:
|
else:
|
||||||
journal_entry.slug = slug
|
journal_entry.slug = slug
|
||||||
|
|
|
@ -22,7 +22,6 @@ class Site(database.ModelBase):
|
||||||
|
|
||||||
# TODO: Add in tag line, copyright notice, favicon and logo image, other attributes in here.
|
# TODO: Add in tag line, copyright notice, favicon and logo image, other attributes in here.
|
||||||
config = sqlalchemy.Column(sqlalchemy.JSON)
|
config = sqlalchemy.Column(sqlalchemy.JSON)
|
||||||
menu = orm.relationship("SiteMap", order_by="SiteMenu.orderinal", back_populates="site")
|
|
||||||
|
|
||||||
def __init__(self, name, url, config=None):
|
def __init__(self, name, url, config=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -30,7 +29,7 @@ class Site(database.ModelBase):
|
||||||
self.config = config or {}
|
self.config = config or {}
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<Site %r - %r>' % self.name, self.url
|
return '<Site {!r} - {!r}>'.format(self.name, self.url)
|
||||||
|
|
||||||
def to_json(self) -> dict:
|
def to_json(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
@ -47,8 +46,12 @@ class SiteMenu(database.ModelBase):
|
||||||
id = sqlalchemy.Column(sqlalchemy.Integer(), primary_key=True)
|
id = sqlalchemy.Column(sqlalchemy.Integer(), primary_key=True)
|
||||||
orderinal = sqlalchemy.Column(sqlalchemy.Integer(), unique=True)
|
orderinal = sqlalchemy.Column(sqlalchemy.Integer(), unique=True)
|
||||||
title = sqlalchemy.Column(sqlalchemy.String())
|
title = sqlalchemy.Column(sqlalchemy.String())
|
||||||
site = orm.relationship("Site", back_populates="menu")
|
|
||||||
page = orm.relationship("Page")
|
site_id = sqlalchemy.Column(sqlalchemy.Integer(), sqlalchemy.ForeignKey('site.id'))
|
||||||
|
site = orm.relationship('Site', back_populates='menu')
|
||||||
|
|
||||||
|
page_id = sqlalchemy.Column(sqlalchemy.Integer(), sqlalchemy.ForeignKey('page.id'))
|
||||||
|
page = orm.relationship('Page')
|
||||||
|
|
||||||
def __init__(self, title, site, page):
|
def __init__(self, title, site, page):
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -56,10 +59,14 @@ class SiteMenu(database.ModelBase):
|
||||||
self.page = page
|
self.page = page
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<SiteMenu %r>' % self.slug
|
return '<SiteMenu {!r} - {!r}>'.format(self.orderinal, self.title)
|
||||||
|
|
||||||
def to_json(self) -> dict:
|
def to_json(self) -> dict:
|
||||||
return {
|
return {
|
||||||
'title': self.title,
|
'title': self.title,
|
||||||
|
'orderinal': self.orderinal,
|
||||||
'page': self.page.slug,
|
'page': self.page.slug,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Site.menu = orm.relationship(SiteMenu, order_by=SiteMenu.orderinal, back_populates="site")
|
||||||
|
|
|
@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
|
||||||
@rookeries_app.route('/api/config')
|
@rookeries_app.route('/api/config')
|
||||||
def app_config():
|
def app_config():
|
||||||
# TODO Make testable and configurable
|
# TODO Make testable and configurable
|
||||||
logger.warning("Use of hardcoded data in '%s'", flask.request.path)
|
logger.warning('Use of hardcoded data in "%s"', flask.request.path)
|
||||||
webapp_config = {
|
webapp_config = {
|
||||||
'siteName': 'Rookeries',
|
'siteName': 'Rookeries',
|
||||||
'siteTagline': 'Simple Site Scaffolding!',
|
'siteTagline': 'Simple Site Scaffolding!',
|
||||||
|
@ -32,7 +32,7 @@ def app_config():
|
||||||
@rookeries_app.route('/api/menu')
|
@rookeries_app.route('/api/menu')
|
||||||
def menu_config():
|
def menu_config():
|
||||||
# TODO Make testable and configurable
|
# TODO Make testable and configurable
|
||||||
logger.warning("Use of hardcoded data in '%s'", flask.request.path)
|
logger.warning('Use of hardcoded data in "%s"', flask.request.path)
|
||||||
nav_menu = {'menu': generate_menu()}
|
nav_menu = {'menu': generate_menu()}
|
||||||
return flask.jsonify(nav_menu)
|
return flask.jsonify(nav_menu)
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,12 @@ from tasks.utils import environment
|
||||||
from tests.fixtures import sample_site
|
from tests.fixtures import sample_site
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_DB_CONNECTION = "postgresql+psycopg2://admin:password@localhost:5432/rookeries"
|
DEFAULT_DB_CONNECTION = 'postgresql+psycopg2://admin:password@localhost:5432/rookeries'
|
||||||
|
|
||||||
|
|
||||||
def get_db_connection_uri(db_connection, db_name):
|
def get_db_connection_uri(db_connection, db_name):
|
||||||
db_connection = os.environ.get(rookeries_db.EXTERNAL_DB_URI_PARAM, db_connection)
|
db_connection = os.environ.get(rookeries_db.EXTERNAL_DB_URI_PARAM, db_connection)
|
||||||
return "/".join((db_connection.rsplit("/", maxsplit=1)[0], db_name))
|
return '/'.join((db_connection.rsplit('/', maxsplit=1)[0], db_name))
|
||||||
|
|
||||||
|
|
||||||
@inv.task
|
@inv.task
|
||||||
|
@ -67,7 +67,7 @@ def test_database(db_connection=DEFAULT_DB_CONNECTION, keep_test_db=False,
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db_connection = os.environ.get(rookeries_db.EXTERNAL_DB_URI_PARAM, db_connection)
|
db_connection = os.environ.get(rookeries_db.EXTERNAL_DB_URI_PARAM, db_connection)
|
||||||
db_connection = "/".join((db_connection.rsplit("/", maxsplit=1)[0], test_db_name))
|
db_connection = '/'.join((db_connection.rsplit('/', maxsplit=1)[0], test_db_name))
|
||||||
|
|
||||||
if not sqlalchemy_utils.database_exists(db_connection):
|
if not sqlalchemy_utils.database_exists(db_connection):
|
||||||
sqlalchemy_utils.create_database(db_connection)
|
sqlalchemy_utils.create_database(db_connection)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import pathlib
|
||||||
|
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
from rookeries.sites import models as site_models
|
||||||
from rookeries.pages import models as journal_models
|
from rookeries.pages import models as journal_models
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +29,11 @@ def sample_page_collection():
|
||||||
|
|
||||||
|
|
||||||
def populate_database(db_engine):
|
def populate_database(db_engine):
|
||||||
|
site = site_models.Site(name='Test Site', url='https://test.rookeries.org/', config={'header': 'Test Site!'})
|
||||||
docs = [journal_models.Page(**doc) for doc in sample_page_collection()]
|
docs = [journal_models.Page(**doc) for doc in sample_page_collection()]
|
||||||
|
|
||||||
session = orm.sessionmaker(bind=db_engine)()
|
session = orm.sessionmaker(bind=db_engine)()
|
||||||
session.add_all(docs)
|
session.add_all(docs)
|
||||||
|
|
||||||
|
session.add(site)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
Loading…
Reference in New Issue