From c429f8bd2f8d5c137d19c9dd2588eeb75131d74c Mon Sep 17 00:00:00 2001 From: Martin Aceto Date: Sun, 12 Nov 2023 21:50:59 -0500 Subject: [PATCH] new endpoint page-weigth --- README.md | 36 ++++++++++++++++++++++++ functions/page-weight/libs/__init__.py | 0 functions/page-weight/libs/queries.py | 32 +++++++++++++++++++++ functions/page-weight/libs/result.py | 20 +++++++++++++ functions/page-weight/libs/utils.py | 17 ++++++++++++ functions/page-weight/libs/validator.py | 27 ++++++++++++++++++ functions/page-weight/main.py | 37 +++++++++++++++++++++++++ functions/page-weight/requirements.txt | 3 ++ 8 files changed, 172 insertions(+) create mode 100644 functions/page-weight/libs/__init__.py create mode 100644 functions/page-weight/libs/queries.py create mode 100644 functions/page-weight/libs/result.py create mode 100644 functions/page-weight/libs/utils.py create mode 100644 functions/page-weight/libs/validator.py create mode 100644 functions/page-weight/main.py create mode 100644 functions/page-weight/requirements.txt diff --git a/README.md b/README.md index 8adf2b6..5935c50 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,42 @@ Returns a JSON object with the following schema: ] ``` +### `GET /page-weight` + +#### Parameters + +The following parameters can be used to filter the data: + +- `geo` (`required`): A string representing the geographic location. +- `technology` (`required`): A comma-separated string representing the technology name(s). +- `rank` (`required`): An string representing the rank. +- `start` (optional): A string representing the start date in the format `YYYY-MM-DD`. +- `end` (optional): A string representing the end date in the format `YYYY-MM-DD`. + +#### Response + +```bash +curl --request GET \ + --url 'https://{{HOST}}/v1/page-weight?geo=ALL&technology=WordPress&rank=ALL' +``` + +Returns a JSON object with the following schema: + +```json +[ + { + "client": "desktop", + "date": "2023-07-01", + "geo": "ALL", + "median_bytes_image": "1048110", + "technology": "WordPress", + "median_bytes_total": "2600099", + "median_bytes_js": "652651", + "rank": "ALL" + } + ... +] +``` ### `GET /technologies` diff --git a/functions/page-weight/libs/__init__.py b/functions/page-weight/libs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/functions/page-weight/libs/queries.py b/functions/page-weight/libs/queries.py new file mode 100644 index 0000000..6818514 --- /dev/null +++ b/functions/page-weight/libs/queries.py @@ -0,0 +1,32 @@ +import os +import json +from google.cloud import firestore +from .result import Result +from .utils import convert_to_array + +DB = firestore.Client(project=os.environ.get('PROJECT')) + +def list_data(params): + ref = DB.collection(u'page_weight') + + query = ref + print("params", params) + if 'start' in params: + query = query.where('date', '>=', params['start']) + if 'end' in params: + query = query.where('date', '<=', params['end']) + if 'geo' in params: + query = query.where('geo', '==', params['geo']) + if 'technology' in params: + params_array = convert_to_array(params['technology']) + query = query.where('technology', 'in', params_array) + if 'rank' in params: + query = query.where('rank', '==', params['rank']) + + documents = query.stream() + + data = [] + for doc in documents: + data.append(doc.to_dict()) + + return Result(result=data) diff --git a/functions/page-weight/libs/result.py b/functions/page-weight/libs/result.py new file mode 100644 index 0000000..63034b6 --- /dev/null +++ b/functions/page-weight/libs/result.py @@ -0,0 +1,20 @@ + +class Result(): + def __init__(self, status=None, result=None, errors=[]): + self._status = status + self.result = result + self.errors = errors + + def success(self) -> bool: + return not self.failure() + + def failure(self) -> bool: + return len(self.errors) > 0 + + @property + def status(self): + if self._status != None: + return self._status + + return "ok" if self.success else "error" + \ No newline at end of file diff --git a/functions/page-weight/libs/utils.py b/functions/page-weight/libs/utils.py new file mode 100644 index 0000000..3a5fe39 --- /dev/null +++ b/functions/page-weight/libs/utils.py @@ -0,0 +1,17 @@ +import json + +def output(result, headers={}): + status = 200 if result.success() else 400 + payload = result.result if result.success() else convert_to_hashes(result.errors) + return (json.dumps(payload), status, headers) + +def convert_to_hashes(arr): + hashes_arr = [] + for inner_arr in arr: + hash_dict = {inner_arr[0]: inner_arr[1]} + hashes_arr.append(hash_dict) + return hashes_arr + +def convert_to_array(data_string): + list = data_string.split(',') + return list diff --git a/functions/page-weight/libs/validator.py b/functions/page-weight/libs/validator.py new file mode 100644 index 0000000..4c202a4 --- /dev/null +++ b/functions/page-weight/libs/validator.py @@ -0,0 +1,27 @@ +from .result import Result + +class Validator(): + def __init__(self, params): + self.params = params + self.errors = [] + self.normalizer_params = self.normalize(params) + + def validate(self): + result = Result(status="ok", result="()") + + if 'geo' not in self.params: + self.add_error("geo", "missing geo parameter") + + if 'technology' not in self.params: + self.add_error("technology", "missing technology parameter") + + if 'rank' not in self.params: + self.add_error("rank", "missing rank parameter") + + return Result(errors=self.errors, result=self.params) + + def add_error(self, key, error): + self.errors.append([key, error]) + + def normalize(self, params): + return "" diff --git a/functions/page-weight/main.py b/functions/page-weight/main.py new file mode 100644 index 0000000..b7e43b0 --- /dev/null +++ b/functions/page-weight/main.py @@ -0,0 +1,37 @@ +import functions_framework +from .libs.validator import Validator +from .libs.utils import output +from .libs.queries import list_data + +@functions_framework.http +def dispatcher(request): + # For more information about CORS and CORS preflight requests, see: + # https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request + + # Set CORS headers for the preflight request + if request.method == "OPTIONS": + # Allows GET requests from any origin with the Content-Type + # header and caches preflight response for an 3600s + headers = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + "Access-Control-Allow-Headers": "Content-Type", + "Access-Control-Max-Age": "3600", + } + + return ("", 204, headers) + + # Set CORS headers for the main request + headers = {"Access-Control-Allow-Origin": "*"} + args = request.args.to_dict() + + validator = Validator(params=args) + result = validator.validate() + + if result.failure(): + print("error", result.errors) + return output(result) + + response = list_data(result.result) + + return output(response, headers) \ No newline at end of file diff --git a/functions/page-weight/requirements.txt b/functions/page-weight/requirements.txt new file mode 100644 index 0000000..f336d0d --- /dev/null +++ b/functions/page-weight/requirements.txt @@ -0,0 +1,3 @@ +functions-framework +google-cloud-firestore +pytest \ No newline at end of file