Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/npm_and_yarn/npm_and_yarn-022c405edc
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuyuutsu authored Oct 18, 2024
2 parents 2fd7b11 + 2bba332 commit b223dd5
Show file tree
Hide file tree
Showing 15 changed files with 707 additions and 670 deletions.
9 changes: 2 additions & 7 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ limit-inference-results=100

# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=pylint.extensions.mccabe,pylint_quotes
load-plugins=pylint.extensions.mccabe

# Pickle collected data for later comparisons.
persistent=yes

# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.11
py-version=3.12

# Discover python modules and packages in the file system subtree.
recursive=no
Expand Down Expand Up @@ -343,11 +343,6 @@ single-line-class-stmt=no
# else.
single-line-if-stmt=no

string-quote=double-avoid-escape
triple-quote=double
docstring-quote=double


[IMPORTS]

# List of modules that can be imported at any level, not just the top level
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11.4
3.12.6
17 changes: 10 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
FROM python:3.11-slim
FROM python:3.12-slim

RUN apt-get update && apt-get install --no-install-recommends -y git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

RUN pip install "poetry==1.3.2"
RUN pip install \
setuptools \
"poetry==1.8.3"
RUN poetry config virtualenvs.create false

RUN mkdir -p /usr/src/
Expand All @@ -13,11 +15,12 @@ WORKDIR /usr/src/
COPY app /usr/src/app
COPY api.py poetry.lock pyproject.toml /usr/src/

RUN poetry install --no-dev
RUN poetry install --only main

EXPOSE 5000

ENV FLASK_APP=api.py

ENTRYPOINT flask run --host 0.0.0.0

CMD ["gunicorn", "api:app", \
"--bind", "0.0.0.0:5000", \
"--workers", "20", \
"--worker-class", "uvicorn.workers.UvicornWorker", \
"--timeout", "0"]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ start-ajv:
npm run start

run: start-ajv
poetry run ./scripts/run_app.sh
poetry run python api.py

lint: lint-python
npm run lint
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ To run the app:
```
make run
```
If you want to run the app locally using multiple server workers set reload to "False" in the Uvicorn settings in api.py:
```python
uvicorn.run("api:app", workers=20, port=5001, reload=False)
```
The validator can be called directly in the browser using the "/validate" endpoint and the "url" parameter for the address where the schema is located (eg. GitHub Gist raw json):
```
http://localhost:5001/validate?url=...
```

## Testing

Expand Down
95 changes: 88 additions & 7 deletions api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,92 @@
from flask import Flask
import json
import os
from json import JSONDecodeError
from urllib import error, request

from app.views.status import status_blueprint
from app.views.validate import validate_blueprint
import requests
import uvicorn
from fastapi import Body, FastAPI, Response
from requests import RequestException
from structlog import get_logger

from app.validators.questionnaire_validator import QuestionnaireValidator

app = FastAPI()


@app.get("/status")
async def status():
return Response(status_code=200)


logger = get_logger()

AJV_HOST = os.getenv("AJV_HOST", "localhost")

AJV_VALIDATOR_URL = f"http://{AJV_HOST}:5002/validate"


@app.post("/validate")
async def validate_schema_request_body(payload=Body(None)):
logger.info("Validating schema")
return await validate_schema(payload)


@app.get("/validate")
async def validate_schema_from_url(url=None):
if url:
logger.info("Validating schema from URL", url=url)
try:
with request.urlopen(url) as opened_url:
return await validate_schema(data=opened_url.read().decode())
except error.URLError:
return Response(
status_code=404, content=f"Could not load schema at URL [{url}]"
)


async def validate_schema(data):
json_to_validate = None
if data:
if isinstance(data, str):
try:
json_to_validate = json.loads(data)
except JSONDecodeError:
logger.info("Could not parse JSON", status=400)
return Response(status_code=400, content="Could not parse JSON")
elif isinstance(data, dict):
json_to_validate = data

response = {}
try:
ajv_response = requests.post(
AJV_VALIDATOR_URL, json=json_to_validate, timeout=10
)
if ajv_response_dict := ajv_response.json():
response["errors"] = ajv_response_dict["errors"]
logger.info("Schema validator returned errors", status=400)
return response, 400

except RequestException:
logger.info("AJV Schema validator service unavailable")
return json.dumps(obj={}, error="AJV Schema validator service unavailable")

validator = QuestionnaireValidator(json_to_validate)
validator.validate()

if validator.errors:
response["errors"] = validator.errors
logger.info("Questionnaire validator returned errors", status=400)
response = Response(content=json.dumps(response), status_code=400)

return response

logger.info("Schema validation passed", status=200)

response = Response(content=json.dumps(response), status_code=200)

return response

application = Flask(__name__)
application.register_blueprint(validate_blueprint)
application.register_blueprint(status_blueprint)

if __name__ == "__main__":
application.run(port=5001)
uvicorn.run("api:app", workers=20, port=5001, reload=True)
1 change: 0 additions & 1 deletion app/validators/questionnaire_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ def validate_referred_numeric_answer(self, answer, answer_ranges):
def validate_smart_quotes(self):
schema_object = SurveySchema(self.schema_element)

# pylint: disable=invalid-string-quote
quote_regex = re.compile(r"['|\"]+(?![^{]*})+(?![^<]*>)")

for translatable_item in schema_object.translatable_items:
Expand Down
2 changes: 1 addition & 1 deletion app/validators/value_source_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ValueSourceValidator(Validator):
}
RESPONSE_METADATA_IDENTIFIERS = ["started_at"]

def __init__(
def __init__( # pylint: disable=too-many-positional-arguments
self,
value_source,
json_path,
Expand Down
Empty file removed app/views/__init__.py
Empty file.
8 changes: 0 additions & 8 deletions app/views/status.py

This file was deleted.

78 changes: 0 additions & 78 deletions app/views/validate.py

This file was deleted.

Loading

0 comments on commit b223dd5

Please sign in to comment.