diff --git a/appointment-frontend/public/config/configuration.json b/appointment-frontend/public/config/configuration.json index 6d68a718a..78214055e 100644 --- a/appointment-frontend/public/config/configuration.json +++ b/appointment-frontend/public/config/configuration.json @@ -3,5 +3,6 @@ "VUE_APP_ROOT_API": "http://localhost:5000/api/v1", "hideBCServicesCard": false, "BCEIDRegistrationUrl": "https://www.test.bceid.ca/os/?7521&SkipTo=Basic", - "VUE_APP_GOOGLE_STATIC_MAP_API_KEY": "AIzaSyDdUTtNy6pLWILtlqHtcaXsFSprFQ9UroI" + "VUE_APP_GOOGLE_STATIC_MAP_API_KEY": "AIzaSyDdUTtNy6pLWILtlqHtcaXsFSprFQ9UroI", + "VUE_APP_FEEDBACK_API": "http://localhost:5001/api/v1" } diff --git a/appointment-frontend/src/App.vue b/appointment-frontend/src/App.vue index 265c27bcb..43c50a5a1 100644 --- a/appointment-frontend/src/App.vue +++ b/appointment-frontend/src/App.vue @@ -2,6 +2,7 @@
+
+ + + + + + + + diff --git a/appointment-frontend/src/components/feedback/index.ts b/appointment-frontend/src/components/feedback/index.ts new file mode 100644 index 000000000..fbb73c45c --- /dev/null +++ b/appointment-frontend/src/components/feedback/index.ts @@ -0,0 +1,5 @@ +import Feedback from './Feedback.vue' + +export { + Feedback +} diff --git a/appointment-frontend/src/models/feedback.ts b/appointment-frontend/src/models/feedback.ts new file mode 100644 index 000000000..7704d66f3 --- /dev/null +++ b/appointment-frontend/src/models/feedback.ts @@ -0,0 +1,36 @@ +export interface FeedbackModel{ + feedbackType: string, + feedbackHeader:string, + feedbackMessage:string, + name: string, + email: string, + phone: string, + responseRequired: boolean + } + +export interface FeedbackRequestObject{ + variables: FeedbackRequestItem +} + +export interface FeedbackRequestItem { + engagement: FeedbackRequestKV, + citizen_comments:FeedbackRequestKV, + service_channel:FeedbackRequestKV, + response: FeedbackRequestKV, + citizen_name: FeedbackRequestKV, + citizen_contact: FeedbackRequestKV, + entity_key: FeedbackRequestKV, + service_date:FeedbackRequestKV, + submit_date_time:FeedbackRequestKV +} + +export interface FeedbackResponseObject{ + response_code: number +} + +export interface FeedbackRequestKV{ + value?: string, + type?:string +} + + \ No newline at end of file diff --git a/appointment-frontend/src/services/feedback.service.ts b/appointment-frontend/src/services/feedback.service.ts new file mode 100644 index 000000000..bac81172b --- /dev/null +++ b/appointment-frontend/src/services/feedback.service.ts @@ -0,0 +1,11 @@ +import Axios, { AxiosResponse } from 'axios' +import { FeedbackRequestObject, FeedbackResponseObject } from '@/models/feedback' +import ConfigHelper from '@/utils/config-helper' +import { addAxiosInterceptors } from '@/utils/interceptors' +const axios = addAxiosInterceptors(Axios.create()) + +export default class FeedbackService { + public static async submitFeedback (feedbackBody: FeedbackRequestObject): Promise> { + return axios.post(`${ConfigHelper.getFeedbackURL()}/feedback`, feedbackBody) + } +} diff --git a/appointment-frontend/src/store/modules/appointment.ts b/appointment-frontend/src/store/modules/appointment.ts index c9fdae83e..fd5d7c247 100644 --- a/appointment-frontend/src/store/modules/appointment.ts +++ b/appointment-frontend/src/store/modules/appointment.ts @@ -1,7 +1,9 @@ import { Action, Module, Mutation, VuexModule } from 'vuex-module-decorators' +import { FeedbackRequestObject, FeedbackResponseObject } from '@/models/feedback' import { Appointment } from '@/models/appointment' import AppointmentService from '@/services/appointment.services' import CommonUtils from '@/utils/common-util' +import FeedbackService from '@/services/feedback.service' import OfficeService from '@/services/office.services' @Module({ @@ -57,4 +59,10 @@ export default class AppointmentModule extends VuexModule { const response = await AppointmentService.deleteAppointment(appointmentId) return response || {} } + + @Action({ rawError: true }) + public async submitFeedback (feedbackRequest: FeedbackRequestObject) { + const response = await FeedbackService.submitFeedback(feedbackRequest) + return response || {} + } } diff --git a/appointment-frontend/src/utils/config-helper.ts b/appointment-frontend/src/utils/config-helper.ts index ea1fe4fb3..3c77f6769 100644 --- a/appointment-frontend/src/utils/config-helper.ts +++ b/appointment-frontend/src/utils/config-helper.ts @@ -44,6 +44,9 @@ export default class ConfigHelper { return ConfigHelper.getValue('VUE_APP_ROOT_API') // return process.env.VUE_APP_ROOT_API } + static getFeedbackURL () { + return ConfigHelper.getValue('VUE_APP_FEEDBACK_API') + } static getValue (key: String) { // @ts-ignore diff --git a/feedback-api/.dockerignore b/feedback-api/.dockerignore new file mode 100644 index 000000000..e69de29bb diff --git a/feedback-api/.s2i/environment b/feedback-api/.s2i/environment new file mode 100644 index 000000000..55c62ce9c --- /dev/null +++ b/feedback-api/.s2i/environment @@ -0,0 +1 @@ +APP_CONFIG=gunicorn_config.py diff --git a/feedback-api/Dockerfile b/feedback-api/Dockerfile new file mode 100644 index 000000000..8d6f0abdc --- /dev/null +++ b/feedback-api/Dockerfile @@ -0,0 +1,45 @@ +FROM python:3.8.5-buster +#FROM python:.6-stretch + +#RUN echo "deb http://ftp.debian.org/debian stretch main contrib" > /etc/apt/sources.list + +# todo: Revert this entire pull request when libcairo2 >= 1.14.2 is available from the debian +# jessie repo. This is a temporary fix for https://github.com/Kozea/WeasyPrint/issues/233 + +# reconfigure Debian to allow installs from both stretch (testing) repo and jessie (stable) repo +# install all the dependencies except libcairo2 from jessie, then install libcairo2 from stretch + +#RUN apt-get -y update \ +# && apt-get install -y \ +# fonts-font-awesome \ +# libffi-dev \ +# libgdk-pixbuf2.0-0 \ +# python-dev \ +# python-lxml \ +# shared-mime-info \ +# && apt-get install -y ttf-mscorefonts-installer \ +# libpango1.0-0 \ +# libcairo2 \ +# libpangocairo-1.0-0 \ +# && apt-get -y clean + + + + +RUN apt-get -y update && apt-get install -y build-essential python3-dev python3-pip python3-setuptools python3-wheel python3-cffi libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info + + +COPY . /app +WORKDIR /app + +RUN python3 -m pip install -r requirements.txt +RUN python3 setup.py install + + +EXPOSE 5001 + +ENV NUM_WORKERS=3 +ENV TIMEOUT=120 + +#CMD ["gunicorn", "--bind", "0.0.0.0:5001", "--timeout", "$TIMEOUT", "--workers", "$NUM_WORKERS", "wsgi:app"] +CMD gunicorn --bind 0.0.0.0:5001 --timeout $TIMEOUT --workers $NUM_WORKERS wsgi:application diff --git a/feedback-api/LICENSE b/feedback-api/LICENSE new file mode 100644 index 000000000..18b5abc34 --- /dev/null +++ b/feedback-api/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2018 Province of British Columbia + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/feedback-api/MANIFEST.in b/feedback-api/MANIFEST.in new file mode 100644 index 000000000..d8402a687 --- /dev/null +++ b/feedback-api/MANIFEST.in @@ -0,0 +1,7 @@ +include requirements/prod.txt +include config.py +include logging.conf +include LICENSE +include README.md +include src/api/static/images/*.png +include src/api/static/images/*.jpg \ No newline at end of file diff --git a/feedback-api/Makefile b/feedback-api/Makefile new file mode 100644 index 000000000..c1ed9ac03 --- /dev/null +++ b/feedback-api/Makefile @@ -0,0 +1,90 @@ +.PHONY: license +.PHONY: setup +.PHONY: ci cd +.PHONY: run + +MKFILE_PATH:=$(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_ABS_DIR:=$(patsubst %/,%,$(dir $(MKFILE_PATH))) + +PROJECT_NAME:=pay_api + +################################################################################# +# COMMANDS -- Setup # +################################################################################# +setup: install install-dev ## Setup the project + +clean: clean-build clean-pyc clean-test ## Clean the project + rm -rf venv/ + +clean-build: ## Clean build files + rm -fr build/ + rm -fr dist/ + rm -fr .eggs/ + find . -name '*.egg-info' -exec rm -fr {} + + find . -name '*.egg' -exec rm -fr {} + + +clean-pyc: ## Clean cache files + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + +clean-test: ## clean test files + find . -name '.pytest_cache' -exec rm -fr {} + + rm -fr .tox/ + rm -f .coverage + rm -fr htmlcov/ + +install: clean ## Install python virtrual environment + test -f venv/bin/activate || python3 -m venv $(CURRENT_ABS_DIR)/venv ;\ + . venv/bin/activate ;\ + pip install --upgrade pip ;\ + pip install-Ur requirements.txt ;\ + touch venv/bin/activate # update so it's as new as requirements/prod.txt + +install-dev: ## Install local application + . venv/bin/activate && pip install -e . + +################################################################################# +# COMMANDS - CI # +################################################################################# +ci: lint flake8 test ## CI flow + +lint: ## Linting with pylint + . venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME) + +flake8: ## Linting with flake8 + . venv/bin/activate && flake8 src/$(PROJECT_NAME) tests + +test: ## Unit testing + . venv/bin/activate && pytest + +mac-cov: test ## Run the coverage report and display in a browser window (mac) + @open -a "Google Chrome" htmlcov/index.html + +################################################################################# +# COMMANDS - CD # +################################################################################# +cd: build ## CD flow + +build: ## Build the docker container + docker build -t $(PROJECT_NAME) . + +build-nc: ## Build the docker container without caching + docker build --no-cache -t $(PROJECT_NAME) . + +################################################################################# +# COMMANDS - Local # +################################################################################# +run: ## Run the project in local + . venv/bin/activate && python -m flask run -p 5000 + +################################################################################# +# Self Documenting Commands # +################################################################################# +.PHONY: help + +.DEFAULT_GOAL := help + +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/feedback-api/README.md b/feedback-api/README.md new file mode 100644 index 000000000..958401b10 --- /dev/null +++ b/feedback-api/README.md @@ -0,0 +1,5 @@ + +# Feedback API + +Service BC Feedback API. + diff --git a/feedback-api/config.py b/feedback-api/config.py new file mode 100644 index 000000000..f68e6bf93 --- /dev/null +++ b/feedback-api/config.py @@ -0,0 +1,78 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""All of the configuration for the service is captured here. All items are loaded, or have Constants defined here that are loaded into the Flask configuration. All modules and lookups get their configuration from the Flask config, rather than reading environment variables directly or by accessing this configuration directly. +""" + +import os +import sys + +from dotenv import find_dotenv, load_dotenv + +# this will load all the envars from a .env file located in the project root (api) +load_dotenv(find_dotenv()) + +CONFIGURATION = { + 'development': 'config.DevConfig', + 'testing': 'config.TestConfig', + 'production': 'config.ProdConfig', + 'default': 'config.ProdConfig' +} + + +def get_named_config(config_name: str = 'production'): + """Return the configuration object based on the name + + :raise: KeyError: if an unknown configuration is requested + """ + if config_name in ['production', 'staging', 'default']: + config = ProdConfig() + elif config_name == 'testing': + config = TestConfig() + elif config_name == 'development': + config = DevConfig() + else: + raise KeyError(f"Unknown configuration '{config_name}'") + return config + + +class _Config(object): # pylint: disable=too-few-public-methods + """Base class configuration that should set reasonable defaults for all the other configurations. """ + PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) + + FEEDBACK_CAMUNDA_URL = os.getenv('FEEDBACK_CAMUNDA_URL') + FEEDBACK_AUTH_URL = os.getenv('FEEDBACK_AUTH_URL') + FEEDBACK_AUTH_CLIENT_ID = os.getenv('FEEDBACK_AUTH_CLIENT_ID') + FEEDBACK_AUTH_CLIENT_SECRET = os.getenv('FEEDBACK_AUTH_CLIENT_SECRET') + + TESTING = False + DEBUG = True + + +class DevConfig(_Config): # pylint: disable=too-few-public-methods + TESTING = False + DEBUG = True + + +class TestConfig(_Config): # pylint: disable=too-few-public-methods + """In support of testing only used by the py.test suite.""" + + DEBUG = True + TESTING = True + + +class ProdConfig(_Config): # pylint: disable=too-few-public-methods + """Production environment configuration.""" + + TESTING = False + DEBUG = False diff --git a/feedback-api/entrypoint.sh b/feedback-api/entrypoint.sh new file mode 100644 index 000000000..1c88d960e --- /dev/null +++ b/feedback-api/entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "Waiting for postgres..." + +gunicorn -b 0.0.0.0:5000 wsgi:application diff --git a/feedback-api/gunicorn_config.py b/feedback-api/gunicorn_config.py new file mode 100644 index 000000000..9cb75dd26 --- /dev/null +++ b/feedback-api/gunicorn_config.py @@ -0,0 +1,25 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The configuration for gunicorn, which picks up the + runtime options from environment variables +""" + +import os + + +workers = int(os.environ.get('GUNICORN_PROCESSES', '1')) # pylint: disable=invalid-name +threads = int(os.environ.get('GUNICORN_THREADS', '1')) # pylint: disable=invalid-name + +forwarded_allow_ips = '*' # pylint: disable=invalid-name +secure_scheme_headers = {'X-Forwarded-Proto': 'https'} # pylint: disable=invalid-name diff --git a/feedback-api/logging.conf b/feedback-api/logging.conf new file mode 100644 index 000000000..ffc1a01e3 --- /dev/null +++ b/feedback-api/logging.conf @@ -0,0 +1,28 @@ +[loggers] +keys=root,api + +[handlers] +keys=console + +[formatters] +keys=simple + +[logger_root] +level=DEBUG +handlers=console + +[logger_api] +level=DEBUG +handlers=console +qualname=api +propagate=0 + +[handler_console] +class=StreamHandler +level=DEBUG +formatter=simple +args=(sys.stdout,) + +[formatter_simple] +format=%(asctime)s - %(name)s - %(levelname)s in %(module)s:%(filename)s:%(lineno)d - %(funcName)s: %(message)s +datefmt= \ No newline at end of file diff --git a/feedback-api/manage.py b/feedback-api/manage.py new file mode 100644 index 000000000..f92e6e21f --- /dev/null +++ b/feedback-api/manage.py @@ -0,0 +1,25 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Manage the database and some other items required to run the API +""" +import logging + +from api import create_app + + +APP = create_app() + +if __name__ == '__main__': + logging.log(logging.INFO, 'Running the Manager') diff --git a/feedback-api/pytest.xml b/feedback-api/pytest.xml new file mode 100644 index 000000000..42c1bdf97 --- /dev/null +++ b/feedback-api/pytest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/feedback-api/requirements.txt b/feedback-api/requirements.txt new file mode 100644 index 000000000..ff649e305 --- /dev/null +++ b/feedback-api/requirements.txt @@ -0,0 +1,13 @@ +Flask +requests +python-dotenv +Jinja2 +pytz +Flask-Caching +Werkzeug +aniso8601 +attrs +flask-cors +flask-restx +gunicorn +flask-jwt-oidc==0.2.0 diff --git a/feedback-api/setup.cfg b/feedback-api/setup.cfg new file mode 100644 index 000000000..5fafda28e --- /dev/null +++ b/feedback-api/setup.cfg @@ -0,0 +1,82 @@ +[metadata] +name = feedback_api +url = https://github.com/bcgov/queue-management/ +author = Service BC +author_email = +classifiers = + Development Status :: Beta + Intended Audience :: Developers / QA + Topic :: Report + License :: OSI Approved :: Apache Software License + Natural Language :: English + Programming Language :: Python :: 3.7 +license = Apache Software License Version 2.0 +description = A short description of the project +long_description = file: README.md +keywords = + +[options] +zip_safe = True +python_requires = >=3.6 +include_package_data = True +packages = find: + +[options.package_data] +report_api = + +[wheel] +universal = 1 + +[bdist_wheel] +universal = 1 + +[aliases] +test = pytest + +[flake8] +ignore = I001, I003, I004, E126, W504 +exclude = .git,*migrations* +max-line-length = 120 +docstring-min-length=10 +per-file-ignores = + */__init__.py:F401 + +[pycodestyle] +max_line_length = 120 +ignore = E501 +docstring-min-length=10 +notes=FIXME,XXX # TODO is ignored +match_dir = src/api +ignored-modules=flask_sqlalchemy + sqlalchemy +per-file-ignores = + */__init__.py:F401 +good-names= + b, + d, + i, + e, + f, + u, + rv, + logger, + +[pylint] +ignore=migrations,test +max_line_length=120 +notes=FIXME,XXX,TODO +ignored-modules=flask_sqlalchemy,sqlalchemy,SQLAlchemy,alembic,scoped_session +ignored-classes=scoped_session +disable=C0301,W0511 + +[isort] +line_length = 120 +indent = 4 +multi_line_output = 4 +lines_after_imports = 2 + +[tool:pytest] +addopts = --cov=src --cov-report html:htmlcov --cov-report xml:coverage.xml +testpaths = tests/unit +filterwarnings = + ignore::UserWarning diff --git a/feedback-api/setup.py b/feedback-api/setup.py new file mode 100644 index 000000000..ed353033f --- /dev/null +++ b/feedback-api/setup.py @@ -0,0 +1,59 @@ +# Copyright © 2019 Province of British Columbia. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Installer and setup for this module +""" +from glob import glob +from os.path import basename, splitext + +from setuptools import find_packages, setup + + +def read_requirements(filename): + """ + Get application requirements from + the requirements.txt file. + :return: Python requirements + :rtype: list + """ + with open(filename, 'r') as req: + requirements = req.readlines() + install_requires = [r.strip() for r in requirements if r.find('git+') != 0] + return install_requires + + +def read(filepath): + """ + Read the contents from a file. + :param str filepath: path to the file to be read + :return: file contents + :rtype: str + """ + with open(filepath, 'r') as file_handle: + content = file_handle.read() + return content + + +REQUIREMENTS = read_requirements('requirements.txt') + +setup( + name="api", + packages=find_packages('src'), + package_dir={'': 'src'}, + py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], + include_package_data=True, + zip_safe=False, + install_requires=REQUIREMENTS, + setup_requires=["pytest-runner"], + tests_require=["pytest"], +) diff --git a/feedback-api/src/api/__init__.py b/feedback-api/src/api/__init__.py new file mode 100644 index 000000000..27560ac5a --- /dev/null +++ b/feedback-api/src/api/__init__.py @@ -0,0 +1,49 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The report Microservice.This module is the API for the Legal Entity system.""" + +import os +from flask import Flask + +import config # pylint: disable=import-error +from api.resources import API +from flask_cors import CORS + + +def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): + """Return a configured Flask App using the Factory method.""" + app = Flask(__name__) + app.config.from_object(config.CONFIGURATION[run_mode]) # pylint: disable=no-member + CORS(app) + API.init_app(app) + + @app.after_request + def add_version(response): # pylint: disable=unused-variable + version = os.getenv('OPENSHIFT_BUILD_COMMIT', '') + response.headers['API'] = f'feedback_api/{version}' + return response + + register_shellcontext(app) + + return app + + +def register_shellcontext(app): + """Register shell context objects.""" + + def shell_context(): + """Shell context objects.""" + return {'app': app} # pragma: no cover + + app.shell_context_processor(shell_context) diff --git a/feedback-api/src/api/resources/__init__.py b/feedback-api/src/api/resources/__init__.py new file mode 100644 index 000000000..ac122b70e --- /dev/null +++ b/feedback-api/src/api/resources/__init__.py @@ -0,0 +1,42 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Exposes all of the resource endpoints mounted in Flask-Blueprint style. + +Uses restplus namespaces to mount individual api endpoints into the service. + +All services have 2 defaults sets of endpoints: + - ops + - metaTEMPLATE_FOLDER_PATH = None +That are used to expose operational health information about the service, and meta information. +""" + +from flask_restx import Api +# from .trace import API as TRACE_API +from .meta import api as META_API +from .ops import api as OPS_API +from .feedback import api as FEEDBACK_API + + +# This will add the Authorize button to the swagger docs +# TODO oauth2 & openid may not yet be supported by restplus <- check on this + +API = Api( + title='Feedback API', + version='1.0', + description='Feedback API for Service BC', + prefix='/api/v1') + +API.add_namespace(OPS_API, path='') +API.add_namespace(META_API, path='') +API.add_namespace(FEEDBACK_API, path='') diff --git a/feedback-api/src/api/resources/feedback.py b/feedback-api/src/api/resources/feedback.py new file mode 100644 index 000000000..d01fc731a --- /dev/null +++ b/feedback-api/src/api/resources/feedback.py @@ -0,0 +1,32 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Endpoints to submit citizen feedback.""" + +from flask import request, jsonify +from flask_restx import Namespace +from flask_restx import Resource, cors +from api.services.feedback import get_feedback_service + +api = Namespace('', description='API for submitting feedback') + +@api.route('/feedback') +class Feedback(Resource): + """Feedback resource.""" + + def post(self): + """Submit Feedback.""" + payload = request.get_json(force=True) + response_code = get_feedback_service().submit(payload) + print(response_code) + return jsonify({"response_code":response_code}) diff --git a/feedback-api/src/api/resources/meta.py b/feedback-api/src/api/resources/meta.py new file mode 100644 index 000000000..a5834addd --- /dev/null +++ b/feedback-api/src/api/resources/meta.py @@ -0,0 +1,35 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Meta information about the service. + +Currently this only provides API versioning information +""" +from flask import jsonify +from flask_restx import cors +from flask_restx import Resource +from flask_restx import Namespace +import os + +api = Namespace('', description='API for Submitting feedback') + + +@api.route('/info') +class Info(Resource): + """Meta information about the overall service.""" + + @cors.crossdomain(origin='*') + def get(self): + """Return a JSON object with meta information about the Service.""" + version = os.getenv('OPENSHIFT_BUILD_COMMIT', '') + return jsonify(API=f'feedback_api/{version}') diff --git a/feedback-api/src/api/resources/ops.py b/feedback-api/src/api/resources/ops.py new file mode 100644 index 000000000..2f948aabe --- /dev/null +++ b/feedback-api/src/api/resources/ops.py @@ -0,0 +1,44 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Endpoints to check and manage the health of the service.""" + +from flask_restx import Namespace +from flask_restx import Resource +from flask_restx import cors + +api = Namespace('', description='API for submitting feedback') + + +@api.route('/healthz') +class Health(Resource): + """Determines if the service and required dependencies are still working. + + This could be thought of as a heartbeat for the service. + """ + + @cors.crossdomain(origin='*') + def get(self): + """Made it here..so its all fine.""" + return {'message': 'api is healthy'}, 200 + + +@api.route('/readyz') +class Ready(Resource): + """Determines if the service is ready to respond.""" + + @cors.crossdomain(origin='*') + def get(self): + """Return a JSON object that identifies if the service is setupAnd ready to work.""" + # TODO: add a poll to the DB when called + return {'message': 'api is ready'}, 200 diff --git a/feedback-api/src/api/services/__init__.py b/feedback-api/src/api/services/__init__.py new file mode 100644 index 000000000..73f1345ab --- /dev/null +++ b/feedback-api/src/api/services/__init__.py @@ -0,0 +1,14 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Exposes all of the Services used in the API.""" diff --git a/feedback-api/src/api/services/feedback/__init__.py b/feedback-api/src/api/services/feedback/__init__.py new file mode 100644 index 000000000..a37a4942a --- /dev/null +++ b/feedback-api/src/api/services/feedback/__init__.py @@ -0,0 +1,22 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .feedback_base_service import FeedbackBaseService +import os + + +def get_feedback_service(): + from .feedback_camunda_service import FeedbackCamundaService + """Return Feedback Service implementation.""" + _instance: FeedbackBaseService = FeedbackCamundaService() + return _instance diff --git a/feedback-api/src/api/services/feedback/feedback_base_service.py b/feedback-api/src/api/services/feedback/feedback_base_service.py new file mode 100644 index 000000000..248fdf103 --- /dev/null +++ b/feedback-api/src/api/services/feedback/feedback_base_service.py @@ -0,0 +1,29 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Base class for Feedback Service.""" + +from abc import ABC, abstractmethod +from typing import Dict + + +class FeedbackBaseService(ABC): + """Base class for Feedback Service.""" + + def __init__(self): + """Initialize.""" + super(FeedbackBaseService, self).__init__() + + @abstractmethod + def submit(self, payload: Dict): + """Submit Feedback.""" diff --git a/feedback-api/src/api/services/feedback/feedback_camunda_service.py b/feedback-api/src/api/services/feedback/feedback_camunda_service.py new file mode 100644 index 000000000..5ccfff2ec --- /dev/null +++ b/feedback-api/src/api/services/feedback/feedback_camunda_service.py @@ -0,0 +1,45 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Submit Citizen feedback. + +This module consists of API that calls Camunda BPM to save citizen feedback comments. +""" +import os, requests, json +from typing import Dict +from .feedback_base_service import FeedbackBaseService +from flask import jsonify + + +class FeedbackCamundaService(FeedbackBaseService): + """Implementation from FeedbackService.""" + + def submit(self, payload): + """Submit feedback to Camunda API""" + camunda_service_endpoint = os.getenv('FEEDBACK_CAMUNDA_URL') + keycloak_endpoint = os.getenv('FEEDBACK_AUTH_URL') + keycloak_client_id = os.getenv('FEEDBACK_AUTH_CLIENT_ID') + keycloak_client_secret = os.getenv('FEEDBACK_AUTH_CLIENT_SECRET') + + auth_payload = {"grant_type":"client_credentials", + "client_id":keycloak_client_id, + "client_secret":keycloak_client_secret} + auth_response = requests.post(keycloak_endpoint,data=auth_payload) + access_token = auth_response.json()['access_token'] + headers = {'Content-Type': 'application/json', 'Authorization': f'Bearer {access_token}'} + feedback_response = requests.post(camunda_service_endpoint, + headers=headers, + data=json.dumps(payload)) + print(feedback_response.json()) + return feedback_response.status_code + diff --git a/feedback-api/wsgi.py b/feedback-api/wsgi.py new file mode 100644 index 000000000..251afc015 --- /dev/null +++ b/feedback-api/wsgi.py @@ -0,0 +1,21 @@ +# Copyright © 2019 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Provides the WSGI entry point for running the application +""" +from api import create_app + +application = create_app() + +if __name__ == "__main__": + application.run()