Repository: fossasia/open-event-server

Issue:
Implement the Settings API with all it's attributes and proper permissions



Discussion:

Resolving patch:
diff --git a/app/api/__init__.py b/app/api/__init__.py
--- a/app/api/__init__.py
+++ b/app/api/__init__.py
@@ -16,14 +16,16 @@
 from app.api.session_types import SessionTypeList, SessionTypeDetail, SessionTypeRelationship
 from app.api.event_copyright import EventCopyrightList, EventCopyrightDetail, EventCopyrightRelationship
 from app.api.pages import PageList, PageDetail
-from app.api.tax import TaxList, TaxDetail,TaxRelationship
+from app.api.tax import TaxList, TaxDetail, TaxRelationship
+from app.api.settings import SettingDetail
 
 api_v1 = Blueprint('v1', __name__, url_prefix='/v1')
 api = Api(app, api_v1)
 
 # users
 api.route(UserList, 'user_list', '/users')
-api.route(UserDetail, 'user_detail', '/users/<int:id>', '/notifications/<int:notification_id>/user', '/event-invoices/<int:event_invoice_id>/user')
+api.route(UserDetail, 'user_detail', '/users/<int:id>',
+          '/notifications/<int:notification_id>/user', '/event-invoices/<int:event_invoice_id>/user')
 api.route(UserRelationship, 'user_notification', '/users/<int:id>/relationships/notifications')
 api.route(UserRelationship, 'user_event_invoices', '/users/<int:id>/relationships/event-invoices')
 
@@ -37,6 +39,9 @@
 api.route(ImageSizeList, 'image_size_list', '/image-sizes')
 api.route(ImageSizeDetail, 'image_size_detail', '/image-sizes/<int:id>')
 
+# settings
+api.route(SettingDetail, 'setting_detail', '/settings')
+
 # pages
 api.route(PageList, 'page_list', '/pages')
 api.route(PageDetail, 'page_detail', '/pages/<int:id>')
@@ -59,7 +64,8 @@
 api.route(EventRelationship, 'event_social_link', '/events/<int:id>/relationships/social-links')
 api.route(EventRelationship, 'event_sponsor', '/events/<int:id>/relationships/sponsors')
 api.route(EventRelationship, 'event_tracks', '/events/<int:id>/relationships/tracks')
-api.route(EventRelationship, 'event_call_for_paper', '/events/<int:id>/relationships/call-for-papers')
+api.route(EventRelationship, 'event_call_for_paper',
+          '/events/<int:id>/relationships/call-for-paper')
 api.route(EventRelationship, 'event_session_types', '/events/<int:id>/relationships/session-types')
 api.route(EventRelationship, 'event_copyright', '/events/<int:id>/relationships/event-copyright')
 api.route(EventRelationship, 'event_tax', '/events/<int:id>/relationships/tax')
@@ -78,11 +84,13 @@
 # sessions
 api.route(SessionList, 'session_list', '/events/<int:id>/sessions', '/tracks/<int:track_id>/sessions',
           '/session-types/<int:session_type_id>/sessions', '/microlocations/<int:microlocation_id>/sessions')
-api.route(SessionDetail, 'session_detail', '/sessions/<int:id>', '/events/<int:event_id>/microlocations')
+api.route(SessionDetail, 'session_detail', '/sessions/<int:id>',
+          '/events/<int:event_id>/microlocations')
 api.route(SessionRelationship, 'session_microlocation',
           '/sessions/<int:id>/relationships/microlocation')
 api.route(SessionRelationship, 'session_track', '/sessions/<int:id>/relationships/track')
-api.route(SessionRelationship, 'session_session_type', '/sessions/<int:id>/relationships/session-type')
+api.route(SessionRelationship, 'session_session_type',
+          '/sessions/<int:id>/relationships/session-type')
 
 # social_links
 api.route(SocialLinkList, 'social_link_list', '/events/<int:id>/social-links')
@@ -106,14 +114,17 @@
 api.route(CallForPaperList, 'call_for_paper_list', '/events/<int:event_id>/call-for-papers')
 api.route(CallForPaperDetail, 'call_for_paper_detail', '/call-for-papers/<int:id>',
           '/events/<int:event_id>/call-for-papers')
-api.route(CallForPaperRelationship, 'call_for_paper_event', '/call-for-papers/<int:id>/relationships/event')
+api.route(CallForPaperRelationship, 'call_for_paper_event',
+          '/call-for-papers/<int:id>/relationships/event')
 
 # session_types
 api.route(SessionTypeList, 'session_type_list', '/events/<int:event_id>/session-types')
 api.route(SessionTypeDetail, 'session_type_detail', '/session-types/<int:id>',
           '/sessions/<int:session_id>/session-type')
-api.route(SessionTypeRelationship, 'session_type_sessions', '/session-types/<int:id>/relationships/sessions')
-api.route(SessionTypeRelationship, 'session_type_event', '/session-types/<int:id>/relationships/event')
+api.route(SessionTypeRelationship, 'session_type_sessions',
+          '/session-types/<int:id>/relationships/sessions')
+api.route(SessionTypeRelationship, 'session_type_event',
+          '/session-types/<int:id>/relationships/event')
 
 # event_copyright
 api.route(EventCopyrightList, 'event_copyright_list', '/events/<int:id>/event-copyright')
@@ -125,11 +136,13 @@
 # tax
 api.route(TaxList, 'tax_list', '/events/<int:id>/tax')
 api.route(TaxDetail, 'tax_detail', '/tax/<int:id>', '/events/<int:event_id>/tax')
-api.route(TaxRelationship, 'tax_event','/tax/<int:id>/relationships/event')
+api.route(TaxRelationship, 'tax_event', '/tax/<int:id>/relationships/event')
 
 # event invoices
 api.route(EventInvoiceList, 'event_invoice_list', '/events/<int:event_id>/event-invoices',
           '/users/<int:user_id>/event-invoices')
 api.route(EventInvoiceDetail, 'event_invoice_detail', '/event-invoices/<int:id>')
-api.route(EventInvoiceRelationship, 'event_invoice_user', '/event-invoices/<int:id>/relationships/user')
-api.route(EventInvoiceRelationship, 'event_invoice_event', '/event-invoices/<int:id>/relationships/event')
+api.route(EventInvoiceRelationship, 'event_invoice_user',
+          '/event-invoices/<int:id>/relationships/user')
+api.route(EventInvoiceRelationship, 'event_invoice_event',
+          '/event-invoices/<int:id>/relationships/event')
diff --git a/app/api/settings.py b/app/api/settings.py
new file mode 100644
--- /dev/null
+++ b/app/api/settings.py
@@ -0,0 +1,176 @@
+from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
+from marshmallow_jsonapi.flask import Schema, Relationship
+from marshmallow_jsonapi import fields
+from app.models import db
+from app.api.helpers.permissions import jwt_required, is_admin, is_user_itself
+from flask_jwt import current_identity
+from sqlalchemy.orm.exc import NoResultFound
+from flask_rest_jsonapi.exceptions import ObjectNotFound
+from app.models.event import Event
+from app.models.setting import Setting
+
+
+class Environment:
+
+    def __init__(self):
+        pass
+
+    DEVELOPMENT = 'development'
+    STAGING = 'staging'
+    PRODUCTION = 'production'
+    TESTING = 'testing'
+
+
+class SettingSchemaAdmin(Schema):
+    """
+    Admin Api schema for settings Model
+    """
+    class Meta:
+        """
+        Meta class for setting Api Schema
+        """
+        type_ = 'setting'
+        self_view = 'v1.setting_detail'
+        self_view_kwargs = {'id': '<id>'}
+
+    id = fields.Str(dump_only=True)
+    #
+    # General
+    #
+
+    app_environment = fields.Str(default=Environment.PRODUCTION)
+    # Name of the application. (Eg. Event Yay!, Open Event)
+    app_name = fields.Str()
+    # Tagline for the application. (Eg. Event Management and Ticketing, Home)
+    tagline = fields.Str()
+    # App secret
+    secret = fields.Str()
+
+    #
+    #  STORAGE
+    #
+
+    # storage place, local, s3, .. can be more in future
+    storage_place = fields.Str()
+    # S3
+    aws_key = fields.Str()
+    aws_secret = fields.Str()
+    aws_bucket_name = fields.Str()
+    aws_region = fields.Str()
+    # Google Storage
+    gs_key = fields.Str()
+    gs_secret = fields.Str()
+    gs_bucket_name = fields.Str()
+
+    #
+    # Social Login
+    #
+
+    # Google Auth
+    google_client_id = fields.Str()
+    google_client_secret = fields.Str()
+    # FB
+    fb_client_id = fields.Str()
+    fb_client_secret = fields.Str()
+    # Twitter
+    tw_consumer_key = fields.Str()
+    tw_consumer_secret = fields.Str()
+    # Instagram
+    in_client_id = fields.Str()
+    in_client_secret = fields.Str()
+
+    #
+    # Payment Gateway
+    #
+
+    # Stripe Keys
+    stripe_client_id = fields.Str()
+    stripe_secret_key = fields.Str()
+    stripe_publishable_key = fields.Str()
+    # PayPal Credentials
+    paypal_mode = fields.Str()
+    paypal_sandbox_username = fields.Str()
+    paypal_sandbox_password = fields.Str()
+    paypal_sandbox_signature = fields.Str()
+    paypal_live_username = fields.Str()
+    paypal_live_password = fields.Str()
+    paypal_live_signature = fields.Str()
+
+    #
+    # EMAIL
+    #
+
+    # Email service. (sendgrid,smtp)
+    email_service = fields.Str()
+    email_from = fields.Str()
+    email_from_name = fields.Str()
+    # Sendgrid
+    sendgrid_key = fields.Str()
+    # SMTP
+    smtp_host = fields.Str()
+    smtp_username = fields.Str()
+    smtp_password = fields.Str()
+    smtp_port = fields.Integer()
+    smtp_encryption = fields.Str()  # Can be tls, ssl, none
+    # Google Analytics
+    analytics_key = fields.Str()
+
+    #
+    # Social links
+    #
+    google_url = fields.Str()
+    github_url = fields.Str()
+    twitter_url = fields.Str()
+    support_url = fields.Str()
+    facebook_url = fields.Str()
+    youtube_url = fields.Str()
+
+    #
+    # Generators
+    #
+    android_app_url = fields.Str()
+    web_app_url = fields.Str()
+
+
+class SettingSchemaNonAdmin(Schema):
+    """
+    Non Admin Api schema for settings Model
+    """
+    class Meta:
+        """
+        Meta class for setting Api Schema
+        """
+        type_ = 'setting'
+        self_view = 'v1.setting_detail'
+        self_view_kwargs = {'id': '<id>'}
+
+    id = fields.Str(dump_only=True)
+
+    # Name of the application. (Eg. Event Yay!, Open Event)
+    app_name = fields.Str()
+    # Tagline for the application. (Eg. Event Management and Ticketing, Home)
+    tagline = fields.Str()
+    # Google Analytics
+    analytics_key = fields.Str()
+
+
+class SettingDetail(ResourceDetail):
+    """
+    setting detail by id
+    """
+
+    def before_get(self, args, kwargs):
+        kwargs['id'] = 1
+        if current_identity.is_admin or current_identity.is_super_admin:
+            self.schema = SettingSchemaAdmin
+        else:
+            self.schema = SettingSchemaNonAdmin
+
+    def before_path(self, args, kwargs):
+        kwargs['id'] = 1
+
+    decorators = (jwt_required, )
+    patch = is_admin(ResourceDetail.patch.__func__)
+    schema = SettingSchemaAdmin
+    data_layer = {'session': db.session,
+                  'model': Setting}


Test patch: