diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 05ff9bb..869c2f1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,14 +1,14 @@ repos: - repo: https://github.com/ambv/black - rev: stable + rev: 22.3.0 hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.7.9 + rev: 3.9.2 hooks: - id: flake8 - repo: https://github.com/pycqa/isort - rev: 5.7.0 + rev: 5.10.1 hooks: - id: isort args: ["--profile", "black", "--filter-files"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 8752ea4..ef0aee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [v1.4.0](https://github.com/SebRut/pygrocy/tree/v1.4.0) (2022-07-24) + +[Full Changelog](https://github.com/SebRut/pygrocy/compare/v1.3.0...v1.4.0) + +**Merged pull requests:** + +- Add system endpoints [\#247](https://github.com/SebRut/pygrocy/pull/247) ([marcelvriend](https://github.com/marcelvriend)) +- Add aggregated amounts to stock info [\#246](https://github.com/SebRut/pygrocy/pull/246) ([marcelvriend](https://github.com/marcelvriend)) +- Return empty list instead of None [\#245](https://github.com/SebRut/pygrocy/pull/245) ([marcelvriend](https://github.com/marcelvriend)) +- Get details for all batteries [\#244](https://github.com/SebRut/pygrocy/pull/244) ([marcelvriend](https://github.com/marcelvriend)) +- Add open\_product API support [\#243](https://github.com/SebRut/pygrocy/pull/243) ([grablair](https://github.com/grablair)) +- Add support for filter conditions [\#242](https://github.com/SebRut/pygrocy/pull/242) ([marcelvriend](https://github.com/marcelvriend)) +- Add consume recipe endpoint [\#241](https://github.com/SebRut/pygrocy/pull/241) ([marcelvriend](https://github.com/marcelvriend)) +- Add support for skipping chores [\#240](https://github.com/SebRut/pygrocy/pull/240) ([marcelvriend](https://github.com/marcelvriend)) + ## [v1.3.0](https://github.com/SebRut/pygrocy/tree/v1.3.0) (2022-06-05) [Full Changelog](https://github.com/SebRut/pygrocy/compare/v1.2.1...v1.3.0) diff --git a/pygrocy/data_models/battery.py b/pygrocy/data_models/battery.py index ff2d0c5..56bf470 100644 --- a/pygrocy/data_models/battery.py +++ b/pygrocy/data_models/battery.py @@ -1,7 +1,11 @@ from datetime import datetime from pygrocy.base import DataModel -from pygrocy.grocy_api_client import BatteryDetailsResponse, CurrentBatteryResponse +from pygrocy.grocy_api_client import ( + BatteryDetailsResponse, + CurrentBatteryResponse, + GrocyApiClient, +) class Battery(DataModel): @@ -22,6 +26,7 @@ def _init_from_CurrentBatteryResponse(self, response: CurrentBatteryResponse): def _init_from_BatteryDetailsResponse(self, response: BatteryDetailsResponse): self._charge_cycles_count = response.charge_cycles_count self._last_charged = response.last_charged + self._last_tracked_time = response.last_charged # For compatibility self._id = response.battery.id self._name = response.battery.name self._description = response.battery.description @@ -41,6 +46,10 @@ def _init_empty(self): self._created_timestamp = None self._userfields = None + def get_details(self, api_client: GrocyApiClient): + details = api_client.get_battery(self._id) + self._init_from_BatteryDetailsResponse(details) + @property def id(self) -> int: return self._id diff --git a/pygrocy/data_models/product.py b/pygrocy/data_models/product.py index 661155c..aaf1e10 100644 --- a/pygrocy/data_models/product.py +++ b/pygrocy/data_models/product.py @@ -70,6 +70,10 @@ def _init_empty(self): self._is_partly_in_stock = None self._available_amount = None + self._amount_aggregated = None + self._amount_opened = None + self._amount_opened_aggregated = None + self._is_aggregated_amount = None self._best_before_date = None self._default_quantity_unit_purchase = None @@ -81,6 +85,10 @@ def _init_empty(self): def _init_from_CurrentStockResponse(self, response: CurrentStockResponse): self._id = response.product_id self._available_amount = response.amount + self._amount_aggregated = response.amount_aggregated + self._amount_opened = response.amount_opened + self._amount_opened_aggregated = response.amount_opened_aggregated + self._is_aggregated_amount = response.is_aggregated_amount self._best_before_date = response.best_before_date if response.product: @@ -136,6 +144,22 @@ def product_group_id(self) -> int: def available_amount(self) -> float: return self._available_amount + @property + def amount_aggregated(self) -> float: + return self._amount_aggregated + + @property + def amount_opened(self) -> float: + return self._amount_opened + + @property + def amount_opened_aggregated(self) -> float: + return self._amount_opened_aggregated + + @property + def is_aggregated_amount(self) -> bool: + return self._is_aggregated_amount + @property def best_before_date(self) -> datetime.date: return self._best_before_date diff --git a/pygrocy/data_models/system.py b/pygrocy/data_models/system.py new file mode 100644 index 0000000..1471b1b --- /dev/null +++ b/pygrocy/data_models/system.py @@ -0,0 +1,116 @@ +from datetime import date, datetime +from typing import List + +from pygrocy.base import DataModel +from pygrocy.grocy_api_client import SystemConfigDto, SystemInfoDto, SystemTimeDto + + +class SystemInfo(DataModel): + def __init__(self, system_info_dto: SystemInfoDto): + self._grocy_version = system_info_dto.grocy_version_info.version + self._grocy_release_date = system_info_dto.grocy_version_info.release_date + self._php_version = system_info_dto.php_version + self._sqlite_version = system_info_dto.sqlite_version + self._os = system_info_dto.os + self._client = system_info_dto.client + + @property + def grocy_version(self) -> str: + return self._grocy_version + + @property + def grocy_release_date(self) -> date: + return self._grocy_release_date + + @property + def php_version(self) -> str: + return self._php_version + + @property + def sqlite_version(self) -> str: + return self._sqlite_version + + @property + def os(self) -> str: + return self._os + + @property + def client(self) -> str: + return self._client + + +class SystemTime(DataModel): + def __init__(self, system_time_dto: SystemTimeDto): + self._timezone = system_time_dto.timezone + self._time_local = system_time_dto.time_local + self._time_local_sqlite3 = system_time_dto.time_local_sqlite3 + self._time_utc = system_time_dto.time_utc + self._timestamp = system_time_dto.timestamp + + @property + def timezone(self) -> str: + return self._timezone + + @property + def time_local(self) -> datetime: + return self._time_local + + @property + def time_local_sqlite3(self) -> datetime: + return self._time_local_sqlite3 + + @property + def time_utc(self) -> datetime: + return self._time_utc + + @property + def timestamp(self) -> int: + return self._timestamp + + +class SystemConfig(DataModel): + def __init__(self, system_config_dto: SystemConfigDto): + self._username = system_config_dto.username + self._base_path = system_config_dto.base_path + self._base_url = system_config_dto.base_url + self._mode = system_config_dto.mode + self._default_locale = system_config_dto.default_locale + self._locale = system_config_dto.locale + self._currency = system_config_dto.currency + + self._enabled_features = [] + for feature, value in system_config_dto.feature_flags.items(): + if bool(value): + self._enabled_features.append(feature) + + @property + def username(self) -> str: + return self._username + + @property + def base_path(self) -> str: + return self._base_path + + @property + def base_url(self) -> str: + return self._base_url + + @property + def mode(self) -> str: + return self._mode + + @property + def default_locale(self) -> str: + return self._default_locale + + @property + def locale(self) -> str: + return self._locale + + @property + def currency(self) -> str: + return self._currency + + @property + def enabled_features(self) -> List[str]: + return self._enabled_features diff --git a/pygrocy/grocy.py b/pygrocy/grocy.py index 58caf5f..b305b51 100644 --- a/pygrocy/grocy.py +++ b/pygrocy/grocy.py @@ -10,6 +10,7 @@ from .data_models.generic import EntityType from .data_models.meal_items import MealPlanItem, MealPlanSection, RecipeItem from .data_models.product import Group, Product, ShoppingListProduct +from .data_models.system import SystemConfig, SystemInfo, SystemTime from .data_models.task import Task from .data_models.user import User # noqa: F401 from .errors import GrocyError # noqa: F401 @@ -109,8 +110,10 @@ def all_products(self) -> List[Product]: product_datas = [ProductData(**product) for product in raw_products] return [Product(product) for product in product_datas] - def chores(self, get_details: bool = False) -> List[Chore]: - raw_chores = self._api_client.get_chores() + def chores( + self, get_details: bool = False, query_filters: List[str] = None + ) -> List[Chore]: + raw_chores = self._api_client.get_chores(query_filters) chores = [Chore(chore) for chore in raw_chores] if get_details: @@ -123,8 +126,9 @@ def execute_chore( chore_id: int, done_by: int = None, tracked_time: datetime = datetime.now(), + skipped: bool = False, ): - return self._api_client.execute_chore(chore_id, done_by, tracked_time) + return self._api_client.execute_chore(chore_id, done_by, tracked_time, skipped) def chore(self, chore_id: int) -> Chore: resp = self._api_client.get_chore(chore_id) @@ -154,6 +158,22 @@ def consume_product( product_id, amount, spoiled, transaction_type, allow_subproduct_substitution ) + def consume_recipe( + self, + recipe_id: int, + ): + return self._api_client.consume_recipe(recipe_id) + + def open_product( + self, + product_id: int, + amount: float = 1, + allow_subproduct_substitution: bool = False, + ): + return self._api_client.open_product( + product_id, amount, allow_subproduct_substitution + ) + def inventory_product( self, product_id: int, @@ -231,8 +251,10 @@ def inventory_product_by_barcode( product.get_details(self._api_client) return product - def shopping_list(self, get_details: bool = False) -> List[ShoppingListProduct]: - raw_shoppinglist = self._api_client.get_shopping_list() + def shopping_list( + self, get_details: bool = False, query_filters: List[str] = None + ) -> List[ShoppingListProduct]: + raw_shoppinglist = self._api_client.get_shopping_list(query_filters) shopping_list = [ShoppingListProduct(resp) for resp in raw_shoppinglist] if get_details: @@ -264,8 +286,8 @@ def remove_product_in_shopping_list( product_id, shopping_list_id, amount ) - def product_groups(self) -> List[Group]: - raw_groups = self._api_client.get_product_groups() + def product_groups(self, query_filters: List[str] = None) -> List[Group]: + raw_groups = self._api_client.get_product_groups(query_filters) return [Group(resp) for resp in raw_groups] def add_product_pic(self, product_id: int, pic_path: str): @@ -281,8 +303,23 @@ def set_userfields(self, entity: str, object_id: int, key: str, value): def get_last_db_changed(self): return self._api_client.get_last_db_changed() - def tasks(self) -> List[Task]: - raw_tasks = self._api_client.get_tasks() + def get_system_info(self) -> SystemInfo: + raw_system_info = self._api_client.get_system_info() + if raw_system_info: + return SystemInfo(raw_system_info) + + def get_system_time(self) -> SystemTime: + raw_system_time = self._api_client.get_system_time() + if raw_system_time: + return SystemTime(raw_system_time) + + def get_system_config(self) -> SystemConfig: + raw_system_config = self._api_client.get_system_config() + if raw_system_config: + return SystemConfig(raw_system_config) + + def tasks(self, query_filters: List[str] = None) -> List[Task]: + raw_tasks = self._api_client.get_tasks(query_filters) return [Task(task) for task in raw_tasks] def task(self, task_id: int) -> Task: @@ -292,8 +329,10 @@ def task(self, task_id: int) -> Task: def complete_task(self, task_id, done_time: datetime = datetime.now()): return self._api_client.complete_task(task_id, done_time) - def meal_plan(self, get_details: bool = False) -> List[MealPlanItem]: - raw_meal_plan = self._api_client.get_meal_plan() + def meal_plan( + self, get_details: bool = False, query_filters: List[str] = None + ) -> List[MealPlanItem]: + raw_meal_plan = self._api_client.get_meal_plan(query_filters) meal_plan = [MealPlanItem(data) for data in raw_meal_plan] if get_details: @@ -306,9 +345,16 @@ def recipe(self, recipe_id: int) -> RecipeItem: if recipe: return RecipeItem(recipe) - def batteries(self) -> List[Battery]: - raw_batteries = self._api_client.get_batteries() - return [Battery(bat) for bat in raw_batteries] + def batteries( + self, query_filters: List[str] = None, get_details: bool = False + ) -> List[Battery]: + raw_batteries = self._api_client.get_batteries(query_filters) + batteries = [Battery(bat) for bat in raw_batteries] + + if get_details: + for item in batteries: + item.get_details(self._api_client) + return batteries def battery(self, battery_id: int) -> Battery: battery = self._api_client.get_battery(battery_id) @@ -329,11 +375,17 @@ def update_generic(self, entity_type: EntityType, object_id: int, updated_data): def delete_generic(self, entity_type: EntityType, object_id: int): return self._api_client.delete_generic(entity_type, object_id) - def get_generic_objects_for_type(self, entity_type: EntityType): - return self._api_client.get_generic_objects_for_type(entity_type.value) + def get_generic_objects_for_type( + self, entity_type: EntityType, query_filters: List[str] = None + ): + return self._api_client.get_generic_objects_for_type( + entity_type.value, query_filters + ) - def meal_plan_sections(self) -> List[MealPlanSection]: - raw_sections = self._api_client.get_meal_plan_sections() + def meal_plan_sections( + self, query_filters: List[str] = None + ) -> List[MealPlanSection]: + raw_sections = self._api_client.get_meal_plan_sections(query_filters) return [MealPlanSection(section) for section in raw_sections] def meal_plan_section(self, meal_plan_section_id: int) -> MealPlanSection: diff --git a/pygrocy/grocy_api_client.py b/pygrocy/grocy_api_client.py index da2cd9a..6f140b3 100644 --- a/pygrocy/grocy_api_client.py +++ b/pygrocy/grocy_api_client.py @@ -3,11 +3,11 @@ import logging from datetime import datetime from enum import Enum -from typing import Dict, List, Optional, Any +from typing import Any, Dict, List, Optional from urllib.parse import urljoin import requests -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Extra, Field, root_validator, validator from pydantic.schema import date from pygrocy import EntityType @@ -140,6 +140,9 @@ class CurrentStockResponse(BaseModel): amount: float best_before_date: date amount_opened: float + amount_aggregated: float + amount_opened_aggregated: float + is_aggregated_amount: bool product: ProductData @@ -236,6 +239,7 @@ class BatteryDetailsResponse(BaseModel): battery: BatteryData charge_cycles_count: int last_charged: Optional[datetime] = None + last_tracked_time: Optional[datetime] = None next_estimated_charge_time: Optional[datetime] = None @@ -261,6 +265,53 @@ class StockLogResponse(BaseModel): transaction_type: TransactionType +class GrocyVersionDto(BaseModel): + version: str = Field(alias="Version") + release_date: date = Field(alias="ReleaseDate") + + +class SystemInfoDto(BaseModel): + grocy_version_info: GrocyVersionDto = Field(alias="grocy_version") + php_version: str + sqlite_version: str + os: str + client: str + + +class SystemTimeDto(BaseModel): + timezone: str + time_local: datetime + time_local_sqlite3: datetime + time_utc: datetime + timestamp: int + + +class SystemConfigDto(BaseModel, extra=Extra.allow): + username: str = Field(alias="USER_USERNAME") + base_path: str = Field(alias="BASE_PATH") + base_url: str = Field(alias="BASE_URL") + mode: str = Field(alias="MODE") + default_locale: str = Field(alias="DEFAULT_LOCALE") + locale: str = Field(alias="LOCALE") + currency: str = Field(alias="CURRENCY") + feature_flags: Dict[str, Any] + + @root_validator(pre=True) + def feature_flags_root_validator(cls, fields: Dict[str, Any]) -> Dict[str, Any]: + """Pydantic root validator to add all "FEATURE_FLAG_" settings to Dict.""" + features: Dict[str, Any] = {} + + class_defined_fields = cls.__fields__.values() + + for field, value in fields.items(): + if field.startswith("FEATURE_FLAG_") and field not in class_defined_fields: + features[field] = value + + fields["feature_flags"] = features + + return fields + + def _enable_debug_mode(): _LOGGER.setLevel(logging.DEBUG) @@ -291,9 +342,14 @@ def __init__( else: self._headers = {"accept": "application/json", "GROCY-API-KEY": api_key} - def _do_get_request(self, end_url: str): + def _do_get_request(self, end_url: str, query_filters: List[str] = None): req_url = urljoin(self._base_url, end_url) - resp = requests.get(req_url, verify=self._verify_ssl, headers=self._headers) + params = None + if query_filters: + params = {"query[]": query_filters} + resp = requests.get( + req_url, verify=self._verify_ssl, headers=self._headers, params=params + ) _LOGGER.debug("-->\tGET /%s", end_url) _LOGGER.debug("<--\t%d for /%s", resp.status_code, end_url) @@ -361,7 +417,9 @@ def _do_delete_request(self, end_url: str): def get_stock(self) -> List[CurrentStockResponse]: parsed_json = self._do_get_request("stock") - return [CurrentStockResponse(**response) for response in parsed_json] + if parsed_json: + return [CurrentStockResponse(**response) for response in parsed_json] + return [] def get_volatile_stock(self) -> CurrentVolatilStockResponse: parsed_json = self._do_get_request("stock/volatile") @@ -379,9 +437,11 @@ def get_product_by_barcode(self, barcode) -> ProductDetailsResponse: if parsed_json: return ProductDetailsResponse(**parsed_json) - def get_chores(self) -> List[CurrentChoreResponse]: - parsed_json = self._do_get_request("chores") - return [CurrentChoreResponse(**chore) for chore in parsed_json] + def get_chores(self, query_filters: List[str] = None) -> List[CurrentChoreResponse]: + parsed_json = self._do_get_request("chores", query_filters) + if parsed_json: + return [CurrentChoreResponse(**chore) for chore in parsed_json] + return [] def get_chore(self, chore_id: int) -> ChoreDetailsResponse: url = f"chores/{chore_id}" @@ -394,10 +454,14 @@ def execute_chore( chore_id: int, done_by: int = None, tracked_time: datetime = datetime.now(), + skipped: bool = False, ): localized_tracked_time = localize_datetime(tracked_time) - data = {"tracked_time": localized_tracked_time.isoformat()} + data = { + "tracked_time": localized_tracked_time.isoformat(), + "skipped": skipped, + } if done_by is not None: data["done_by"] = done_by @@ -440,6 +504,25 @@ def consume_product( self._do_post_request(f"stock/products/{product_id}/consume", data) + def open_product( + self, + product_id: int, + amount: float = 1, + allow_subproduct_substitution: bool = False, + ): + data = { + "amount": amount, + "allow_subproduct_substitution": allow_subproduct_substitution, + } + + self._do_post_request(f"stock/products/{product_id}/open", data) + + def consume_recipe( + self, + recipe_id: int, + ): + self._do_post_request(f"recipes/{recipe_id}/consume", None) + def inventory_product( self, product_id: int, @@ -548,9 +631,13 @@ def inventory_product_by_barcode( stockLog = [StockLogResponse(**response) for response in parsed_json] return stockLog[0] - def get_shopping_list(self) -> List[ShoppingListItem]: - parsed_json = self._do_get_request("objects/shopping_list") - return [ShoppingListItem(**response) for response in parsed_json] + def get_shopping_list( + self, query_filters: List[str] = None + ) -> List[ShoppingListItem]: + parsed_json = self._do_get_request("objects/shopping_list", query_filters) + if parsed_json: + return [ShoppingListItem(**response) for response in parsed_json] + return [] def add_missing_product_to_shopping_list(self, shopping_list_id: int = None): data = None @@ -590,9 +677,11 @@ def remove_product_in_shopping_list( } self._do_post_request("stock/shoppinglist/remove-product", data) - def get_product_groups(self) -> List[LocationData]: - parsed_json = self._do_get_request("objects/product_groups") - return [LocationData(**response) for response in parsed_json] + def get_product_groups(self, query_filters: List[str] = None) -> List[LocationData]: + parsed_json = self._do_get_request("objects/product_groups", query_filters) + if parsed_json: + return [LocationData(**response) for response in parsed_json] + return [] def upload_product_picture(self, product_id: int, pic_path: str): b64fn = base64.b64encode("{}.jpg".format(product_id).encode("ascii")) @@ -618,9 +707,26 @@ def get_last_db_changed(self): last_change_timestamp = parse_date(resp.get("changed_time")) return last_change_timestamp - def get_tasks(self) -> List[TaskResponse]: - parsed_json = self._do_get_request("tasks") - return [TaskResponse(**data) for data in parsed_json] + def get_system_info(self) -> SystemInfoDto: + parsed_json = self._do_get_request("system/info") + if parsed_json: + return SystemInfoDto(**parsed_json) + + def get_system_time(self) -> SystemTimeDto: + parsed_json = self._do_get_request("system/time") + if parsed_json: + return SystemTimeDto(**parsed_json) + + def get_system_config(self) -> SystemConfigDto: + parsed_json = self._do_get_request("system/config") + if parsed_json: + return SystemConfigDto(**parsed_json) + + def get_tasks(self, query_filters: List[str] = None) -> List[TaskResponse]: + parsed_json = self._do_get_request("tasks", query_filters) + if parsed_json: + return [TaskResponse(**data) for data in parsed_json] + return [] def get_task(self, task_id: int) -> TaskResponse: url = f"objects/tasks/{task_id}" @@ -635,19 +741,24 @@ def complete_task(self, task_id: int, done_time: datetime = datetime.now()): data = {"done_time": localized_done_time.isoformat()} self._do_post_request(url, data) - def get_meal_plan(self) -> List[MealPlanResponse]: - parsed_json = self._do_get_request("objects/meal_plan") - return [MealPlanResponse(**data) for data in parsed_json] + def get_meal_plan(self, query_filters: List[str] = None) -> List[MealPlanResponse]: + parsed_json = self._do_get_request("objects/meal_plan", query_filters) + if parsed_json: + return [MealPlanResponse(**data) for data in parsed_json] + return [] def get_recipe(self, object_id: int) -> RecipeDetailsResponse: parsed_json = self._do_get_request(f"objects/recipes/{object_id}") if parsed_json: return RecipeDetailsResponse(**parsed_json) - def get_batteries(self) -> List[CurrentBatteryResponse]: - parsed_json = self._do_get_request("batteries") + def get_batteries( + self, query_filters: List[str] = None + ) -> List[CurrentBatteryResponse]: + parsed_json = self._do_get_request("batteries", query_filters) if parsed_json: return [CurrentBatteryResponse(**data) for data in parsed_json] + return [] def get_battery(self, battery_id: int) -> BatteryDetailsResponse: parsed_json = self._do_get_request(f"batteries/{battery_id}") @@ -669,13 +780,20 @@ def update_generic(self, entity_type: str, object_id: int, data): def delete_generic(self, entity_type: str, object_id: int): return self._do_delete_request(f"objects/{entity_type}/{object_id}") - def get_generic_objects_for_type(self, entity_type: str): - return self._do_get_request(f"objects/{entity_type}") + def get_generic_objects_for_type( + self, entity_type: str, query_filters: List[str] = None + ): + return self._do_get_request(f"objects/{entity_type}", query_filters) - def get_meal_plan_sections(self) -> List[MealPlanSectionResponse]: - parsed_json = self.get_generic_objects_for_type(EntityType.MEAL_PLAN_SECTIONS) + def get_meal_plan_sections( + self, query_filters: List[str] = None + ) -> List[MealPlanSectionResponse]: + parsed_json = self.get_generic_objects_for_type( + EntityType.MEAL_PLAN_SECTIONS, query_filters + ) if parsed_json: return [MealPlanSectionResponse(**resp) for resp in parsed_json] + return [] def get_meal_plan_section(self, meal_plan_section_id) -> MealPlanSectionResponse: parsed_json = self._do_get_request( @@ -688,6 +806,7 @@ def get_users(self) -> List[UserDto]: parsed_json = self._do_get_request("users") if parsed_json: return [UserDto(**user) for user in parsed_json] + return [] def get_user(self, user_id: int) -> UserDto: query_params = [] diff --git a/setup.py b/setup.py index fe292cc..c48e183 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="pygrocy", - version="1.3.0", + version="1.4.0", author="Sebastian Rutofski", author_email="kontakt@sebastian-rutofski.de", description="", diff --git a/test/cassettes/test_battery/TestBattery.test_get_batteries_filters_invalid.yaml b/test/cassettes/test_battery/TestBattery.test_get_batteries_filters_invalid.yaml new file mode 100644 index 0000000..7b6c6de --- /dev/null +++ b/test/cassettes/test_battery/TestBattery.test_get_batteries_filters_invalid.yaml @@ -0,0 +1,64 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/BaseApiController.php(42): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/controllers\/BatteriesApiController.php(25): Grocy\\Controllers\\BaseApiController->FilteredApiResponse()\n#3 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\BatteriesApiController->Current()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#5 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#6 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#10 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#11 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#16 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#17 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#21 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#22 \/var\/www\/app.php(106): Slim\\App->run()\n#23 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#24 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:57 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_battery/TestBattery.test_get_batteries_filters_valid.yaml b/test/cassettes/test_battery/TestBattery.test_get_batteries_filters_valid.yaml new file mode 100644 index 0000000..ba67fb5 --- /dev/null +++ b/test/cassettes/test_battery/TestBattery.test_get_batteries_filters_valid.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries?query%5B%5D=next_estimated_charge_time%3C2022-06-20 + response: + body: + string: '[{"id":"1","battery_id":"1","last_tracked_time":"2021-12-19 23:59:59","next_estimated_charge_time":"2022-06-17 + 23:59:59"},{"id":"3","battery_id":"3","last_tracked_time":"2022-04-13 19:33:45","next_estimated_charge_time":"2022-06-12 + 19:33:45"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:57 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '243' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_battery/TestBattery.test_get_batteries_with_details_valid.yaml b/test/cassettes/test_battery/TestBattery.test_get_batteries_with_details_valid.yaml new file mode 100644 index 0000000..63a076f --- /dev/null +++ b/test/cassettes/test_battery/TestBattery.test_get_batteries_with_details_valid.yaml @@ -0,0 +1,218 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries + response: + body: + string: '[{"id":"1","battery_id":"1","last_tracked_time":"2022-07-17 21:19:14","next_estimated_charge_time":"2023-01-13 + 21:19:14"},{"id":"2","battery_id":"2","last_tracked_time":"2022-04-28 19:33:45","next_estimated_charge_time":"2999-12-31 + 23:59:59"},{"id":"3","battery_id":"3","last_tracked_time":"2022-04-13 19:33:45","next_estimated_charge_time":"2022-06-12 + 19:33:45"},{"id":"4","battery_id":"4","last_tracked_time":"2022-04-22 19:33:45","next_estimated_charge_time":"2022-06-21 + 19:33:45"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 17 Jul 2022 20:34:26 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '485' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries/1 + response: + body: + string: '{"battery":{"id":"1","name":"Battery1","description":"Warranty ends + 2023","used_in":"TV remote control","charge_interval_days":"180","row_created_timestamp":"2022-06-17 + 19:33:37","active":"1"},"last_charged":"2022-07-17 21:19:14","charge_cycles_count":7,"next_estimated_charge_time":"2023-01-13 + 21:19:14"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 17 Jul 2022 20:34:26 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '305' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries/2 + response: + body: + string: '{"battery":{"id":"2","name":"Battery2","description":"Warranty ends + 2022","used_in":"Alarm clock","charge_interval_days":"0","row_created_timestamp":"2022-06-17 + 19:33:37","active":"1"},"last_charged":"2022-04-28 19:33:45","charge_cycles_count":4,"next_estimated_charge_time":"2999-12-31 + 23:59:59"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 17 Jul 2022 20:34:26 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '297' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries/3 + response: + body: + string: '{"battery":{"id":"3","name":"Battery3","description":"Warranty ends + 2022","used_in":"Heat remote control","charge_interval_days":"60","row_created_timestamp":"2022-06-17 + 19:33:37","active":"1"},"last_charged":"2022-04-13 19:33:45","charge_cycles_count":1,"next_estimated_charge_time":"2022-06-12 + 19:33:45"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 17 Jul 2022 20:34:26 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '306' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/batteries/4 + response: + body: + string: '{"battery":{"id":"4","name":"Battery4","description":"Warranty ends + 2028","used_in":"Heat remote control","charge_interval_days":"60","row_created_timestamp":"2022-06-17 + 19:33:37","active":"1"},"last_charged":"2022-04-22 19:33:45","charge_cycles_count":1,"next_estimated_charge_time":"2022-06-21 + 19:33:45"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 17 Jul 2022 20:34:26 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '306' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_chores/TestChores.test_get_chores_filters_invalid.yaml b/test/cassettes/test_chores/TestChores.test_get_chores_filters_invalid.yaml new file mode 100644 index 0000000..ac2a764 --- /dev/null +++ b/test/cassettes/test_chores/TestChores.test_get_chores_filters_invalid.yaml @@ -0,0 +1,64 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/chores?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/BaseApiController.php(42): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/controllers\/ChoresApiController.php(59): Grocy\\Controllers\\BaseApiController->FilteredApiResponse()\n#3 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\ChoresApiController->Current()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#5 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#6 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#10 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#11 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#16 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#17 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#21 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#22 \/var\/www\/app.php(106): Slim\\App->run()\n#23 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#24 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:58 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_chores/TestChores.test_get_chores_filters_valid.yaml b/test/cassettes/test_chores/TestChores.test_get_chores_filters_valid.yaml new file mode 100644 index 0000000..b5f7086 --- /dev/null +++ b/test/cassettes/test_chores/TestChores.test_get_chores_filters_valid.yaml @@ -0,0 +1,89 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/chores?query%5B%5D=next_execution_assigned_to_user_id%3D1 + response: + body: + string: '[{"id":"1","chore_id":"1","chore_name":"Change towels in the bathroom","last_tracked_time":"2022-06-17 + 00:00:00","next_estimated_execution_time":"2022-06-20 23:59:59","track_date_only":"1","next_execution_assigned_to_user_id":"1","is_rescheduled":"0","is_reassigned":"0"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:58 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '272' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/chores/1 + response: + body: + string: '{"chore":{"id":"1","name":"Change towels in the bathroom","description":null,"period_type":"hourly","period_days":null,"row_created_timestamp":"2022-06-17 + 19:33:37","period_config":null,"track_date_only":"1","rollover":"0","assignment_type":"random","assignment_config":"1,2,3,4","next_execution_assigned_to_user_id":"1","consume_product_on_execution":"0","product_id":null,"product_amount":null,"period_interval":"72","active":"1","start_date":"2021-01-01","rescheduled_date":null,"rescheduled_next_execution_assigned_to_user_id":null},"last_tracked":"2022-06-17 + 00:00:00","tracked_count":20,"last_done_by":{"id":"4","username":"Demo User + 4","first_name":null,"last_name":null,"row_created_timestamp":"2022-06-17 + 19:33:35","display_name":"Demo User 4","picture_file_name":null},"next_estimated_execution_time":"2022-06-20 + 23:59:59","next_execution_assigned_user":{"id":"1","username":"Demo User","first_name":null,"last_name":null,"row_created_timestamp":"2022-06-17 + 19:33:31","display_name":"Demo User","picture_file_name":null},"average_execution_frequency_hours":"81.8181818181818"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:58 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '1086' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_generic/TestGeneric.test_get_generic_objects_for_type_filters_invalid.yaml b/test/cassettes/test_generic/TestGeneric.test_get_generic_objects_for_type_filters_invalid.yaml new file mode 100644 index 0000000..f495600 --- /dev/null +++ b/test/cassettes/test_generic/TestGeneric.test_get_generic_objects_for_type_filters_invalid.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/shopping_locations?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/GenericEntityApiController.php(152): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\GenericEntityApiController->GetObjects()\n#3 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#5 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#6 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#10 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#11 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#16 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#17 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#21 \/var\/www\/app.php(106): Slim\\App->run()\n#22 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#23 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:58 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_generic/TestGeneric.test_get_generic_objects_for_type_filters_valid.yaml b/test/cassettes/test_generic/TestGeneric.test_get_generic_objects_for_type_filters_valid.yaml new file mode 100644 index 0000000..f13e79a --- /dev/null +++ b/test/cassettes/test_generic/TestGeneric.test_get_generic_objects_for_type_filters_valid.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/shopping_locations?query%5B%5D=name%3DWalmart + response: + body: + string: '[{"id":"1","name":"Walmart","description":null,"row_created_timestamp":"2022-06-17 + 19:33:35"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:58 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '94' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_grocy/TestGrocy.test_consume_recipe_error.yaml b/test/cassettes/test_grocy/TestGrocy.test_consume_recipe_error.yaml new file mode 100644 index 0000000..2d86d17 --- /dev/null +++ b/test/cassettes/test_grocy/TestGrocy.test_consume_recipe_error.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - python-requests/2.27.1 + accept: + - application/json + method: POST + uri: https://localhost/api/recipes/4464/consume + response: + body: + string: '{"error_message":"Recipe does not exist"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sat, 18 Jun 2022 14:16:54 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 400 + message: Bad Request +version: 1 diff --git a/test/cassettes/test_grocy/TestGrocy.test_consume_recipe_valid.yaml b/test/cassettes/test_grocy/TestGrocy.test_consume_recipe_valid.yaml new file mode 100644 index 0000000..8eaf3ea --- /dev/null +++ b/test/cassettes/test_grocy/TestGrocy.test_consume_recipe_valid.yaml @@ -0,0 +1,40 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - python-requests/2.27.1 + accept: + - application/json + method: POST + uri: https://localhost/api/recipes/5/consume + response: + body: + string: '' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sat, 18 Jun 2022 14:16:54 GMT + Server: + - nginx/1.22.0 + X-Powered-By: + - PHP/8.0.20 + status: + code: 204 + message: No Content +version: 1 diff --git a/test/cassettes/test_meal_plan/TestMealPlan.test_get_meal_plan_filters_invalid.yaml b/test/cassettes/test_meal_plan/TestMealPlan.test_get_meal_plan_filters_invalid.yaml new file mode 100644 index 0000000..73f59a4 --- /dev/null +++ b/test/cassettes/test_meal_plan/TestMealPlan.test_get_meal_plan_filters_invalid.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/meal_plan?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/GenericEntityApiController.php(152): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\GenericEntityApiController->GetObjects()\n#3 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#5 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#6 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#10 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#11 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#16 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#17 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#21 \/var\/www\/app.php(106): Slim\\App->run()\n#22 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#23 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_meal_plan/TestMealPlan.test_get_meal_plan_filters_valid.yaml b/test/cassettes/test_meal_plan/TestMealPlan.test_get_meal_plan_filters_valid.yaml new file mode 100644 index 0000000..061e837 --- /dev/null +++ b/test/cassettes/test_meal_plan/TestMealPlan.test_get_meal_plan_filters_valid.yaml @@ -0,0 +1,84 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/meal_plan?query%5B%5D=day%3E%3D2022-06-15&query%5B%5D=product_amount%3E0 + response: + body: + string: '[{"id":"11","day":"2022-06-15","type":"product","recipe_id":null,"recipe_servings":"1","note":null,"product_id":"25","product_amount":"1.0","product_qu_id":null,"row_created_timestamp":"2022-06-17 + 19:33:36","done":"0","section_id":"1"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '236' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/meal_plan_sections?query%5B%5D=id%3D1 + response: + body: + string: '[{"id":"1","name":"Breakfast","sort_number":"10","row_created_timestamp":"2022-06-17 + 19:33:36","time_info":null}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '113' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_meal_plan_sections/TestMealPlanSections.test_get_sections_filters_invalid.yaml b/test/cassettes/test_meal_plan_sections/TestMealPlanSections.test_get_sections_filters_invalid.yaml new file mode 100644 index 0000000..e39074b --- /dev/null +++ b/test/cassettes/test_meal_plan_sections/TestMealPlanSections.test_get_sections_filters_invalid.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/meal_plan_sections?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/GenericEntityApiController.php(152): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\GenericEntityApiController->GetObjects()\n#3 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#5 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#6 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#10 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#11 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#16 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#17 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#21 \/var\/www\/app.php(106): Slim\\App->run()\n#22 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#23 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_meal_plan_sections/TestMealPlanSections.test_get_sections_filters_valid.yaml b/test/cassettes/test_meal_plan_sections/TestMealPlanSections.test_get_sections_filters_valid.yaml new file mode 100644 index 0000000..054ce9b --- /dev/null +++ b/test/cassettes/test_meal_plan_sections/TestMealPlanSections.test_get_sections_filters_valid.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/meal_plan_sections?query%5B%5D=name%3DBreakfast + response: + body: + string: '[{"id":"1","name":"Breakfast","sort_number":"10","row_created_timestamp":"2022-06-17 + 19:33:36","time_info":null}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '113' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_product_groups/TestProductGroups.test_get_product_groups_filters_invalid.yaml b/test/cassettes/test_product_groups/TestProductGroups.test_get_product_groups_filters_invalid.yaml new file mode 100644 index 0000000..98e3593 --- /dev/null +++ b/test/cassettes/test_product_groups/TestProductGroups.test_get_product_groups_filters_invalid.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/product_groups?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/GenericEntityApiController.php(152): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\GenericEntityApiController->GetObjects()\n#3 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#5 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#6 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#10 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#11 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#16 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#17 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#21 \/var\/www\/app.php(106): Slim\\App->run()\n#22 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#23 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_product_groups/TestProductGroups.test_get_product_groups_filters_valid.yaml b/test/cassettes/test_product_groups/TestProductGroups.test_get_product_groups_filters_valid.yaml new file mode 100644 index 0000000..b290586 --- /dev/null +++ b/test/cassettes/test_product_groups/TestProductGroups.test_get_product_groups_filters_valid.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/product_groups?query%5B%5D=id%3D6 + response: + body: + string: '[{"id":"6","name":"06 Refrigerated products","description":null,"row_created_timestamp":"2022-06-17 + 19:33:35"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '111' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_shoppinglist/TestShoppingList.test_get_shopping_list_filters_invalid.yaml b/test/cassettes/test_shoppinglist/TestShoppingList.test_get_shopping_list_filters_invalid.yaml new file mode 100644 index 0000000..871aa2f --- /dev/null +++ b/test/cassettes/test_shoppinglist/TestShoppingList.test_get_shopping_list_filters_invalid.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/shopping_list?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/GenericEntityApiController.php(152): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\GenericEntityApiController->GetObjects()\n#3 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#5 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#6 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#10 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#11 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#16 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#17 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#21 \/var\/www\/app.php(106): Slim\\App->run()\n#22 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#23 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_shoppinglist/TestShoppingList.test_get_shopping_list_filters_valid.yaml b/test/cassettes/test_shoppinglist/TestShoppingList.test_get_shopping_list_filters_valid.yaml new file mode 100644 index 0000000..7757ca9 --- /dev/null +++ b/test/cassettes/test_shoppinglist/TestShoppingList.test_get_shopping_list_filters_valid.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/objects/shopping_list?query%5B%5D=note~snacks + response: + body: + string: '[{"id":"1","product_id":null,"note":"Some good snacks","amount":"1","row_created_timestamp":"2022-06-17 + 19:33:36","shopping_list_id":"1","done":"0","qu_id":null}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '162' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_stock/TestStock.test_open_product_error.yaml b/test/cassettes/test_stock/TestStock.test_open_product_error.yaml new file mode 100644 index 0000000..7e90aa8 --- /dev/null +++ b/test/cassettes/test_stock/TestStock.test_open_product_error.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: '{"amount": 0}' + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '62' + Content-Type: + - application/json + User-Agent: + - python-requests/2.27.1 + accept: + - application/json + method: POST + uri: https://localhost/api/stock/products/13/open + response: + body: + string: '{"error_message":"No transaction was found by the given transaction id"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sat, 9 Jul 2022 15:40:16 GMT + Server: + - nginx/1.20.2 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.13 + status: + code: 400 + message: Bad Request +version: 1 diff --git a/test/cassettes/test_stock/TestStock.test_open_product_valid.yaml b/test/cassettes/test_stock/TestStock.test_open_product_valid.yaml new file mode 100644 index 0000000..ce3da0b --- /dev/null +++ b/test/cassettes/test_stock/TestStock.test_open_product_valid.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: '{"amount": 1}' + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '62' + Content-Type: + - application/json + User-Agent: + - python-requests/2.27.1 + accept: + - application/json + method: POST + uri: https://localhost/api/stock/products/13/open + response: + body: + string: '[{"id":"187","product_id":"13","amount":"1","best_before_date":"2022-09-27","purchased_date":"2022-07-09","used_date":null,"spoiled":"0","stock_id":"62c99c66aa8d8","transaction_type":"product-opened","price":null,"undone":"0","undone_timestamp":null,"opened_date":"2022-07-09","location_id":"10","recipe_id":null,"correlation_id":null,"transaction_id":"62c9a03984e51","stock_row_id":null,"shopping_location_id":null,"user_id":"2","row_created_timestamp":"2022-07-09 08:35:21","note":null},{"id":"188","product_id":"13","amount":"-1","best_before_date":"2022-07-23","purchased_date":"2022-07-09","used_date":null,"spoiled":"0","stock_id":"62c99c66aa8d8","transaction_type":"transfer_from","price":null,"undone":"0","undone_timestamp":null,"opened_date":"2022-07-09","location_id":"10","recipe_id":null,"correlation_id":"62c9a039a118f","transaction_id":"62c9a03984e51","stock_row_id":null,"shopping_location_id":null,"user_id":"2","row_created_timestamp":"2022-07-09 08:35:21","note":null},{"id":"189","product_id":"13","amount":"1","best_before_date":"2022-07-23","purchased_date":"2022-07-09","used_date":null,"spoiled":"0","stock_id":"62c99c66aa8d8","transaction_type":"transfer_to","price":null,"undone":"0","undone_timestamp":null,"opened_date":"2022-07-09","location_id":"2","recipe_id":null,"correlation_id":"62c9a039a118f","transaction_id":"62c9a03984e51","stock_row_id":null,"shopping_location_id":null,"user_id":"2","row_created_timestamp":"2022-07-09 08:35:21","note":null}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sat, 9 Jul 2022 15:40:16 GMT + Server: + - nginx/1.20.2 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.13 + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_system/TestSystem.test_get_system_config_valid.yaml b/test/cassettes/test_system/TestSystem.test_get_system_config_valid.yaml new file mode 100644 index 0000000..0645a21 --- /dev/null +++ b/test/cassettes/test_system/TestSystem.test_get_system_config_valid.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/system/config + response: + body: + string: '{"MODE":"demo","DEFAULT_LOCALE":"en","CALENDAR_FIRST_DAY_OF_WEEK":"","CALENDAR_SHOW_WEEK_OF_YEAR":true,"MEAL_PLAN_FIRST_DAY_OF_WEEK":"","CURRENCY":"USD","BASE_PATH":"","BASE_URL":"\/","STOCK_BARCODE_LOOKUP_PLUGIN":"DemoBarcodeLookupPlugin","DISABLE_URL_REWRITING":false,"ENTRY_PAGE":"stock","DISABLE_AUTH":false,"AUTH_CLASS":"Grocy\\Middleware\\DefaultAuthMiddleware","REVERSE_PROXY_AUTH_HEADER":"REMOTE_USER","REVERSE_PROXY_AUTH_USE_ENV":false,"LDAP_ADDRESS":"","LDAP_BASE_DN":"","LDAP_BIND_DN":"","LDAP_BIND_PW":"","LDAP_USER_FILTER":"","LDAP_UID_ATTR":"","DEFAULT_PERMISSIONS":["ADMIN"],"GROCYCODE_TYPE":"1D","LABEL_PRINTER_WEBHOOK":"","LABEL_PRINTER_RUN_SERVER":true,"LABEL_PRINTER_PARAMS":{"font_family":"Source + Sans Pro (Regular)"},"LABEL_PRINTER_HOOK_JSON":false,"TPRINTER_IS_NETWORK_PRINTER":false,"TPRINTER_PRINT_QUANTITY_NAME":true,"TPRINTER_PRINT_NOTES":true,"TPRINTER_IP":"127.0.0.1","TPRINTER_PORT":9100,"TPRINTER_CONNECTOR":"\/dev\/usb\/lp0","FEATURE_FLAG_STOCK":true,"FEATURE_FLAG_SHOPPINGLIST":true,"FEATURE_FLAG_RECIPES":true,"FEATURE_FLAG_CHORES":true,"FEATURE_FLAG_TASKS":true,"FEATURE_FLAG_BATTERIES":true,"FEATURE_FLAG_EQUIPMENT":true,"FEATURE_FLAG_CALENDAR":true,"FEATURE_FLAG_LABEL_PRINTER":false,"FEATURE_FLAG_STOCK_PRICE_TRACKING":true,"FEATURE_FLAG_STOCK_LOCATION_TRACKING":true,"FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING":true,"FEATURE_FLAG_STOCK_PRODUCT_OPENED_TRACKING":true,"FEATURE_FLAG_STOCK_PRODUCT_FREEZING":true,"FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_FIELD_NUMBER_PAD":true,"FEATURE_FLAG_SHOPPINGLIST_MULTIPLE_LISTS":true,"FEATURE_FLAG_CHORES_ASSIGNMENTS":true,"FEATURE_FLAG_THERMAL_PRINTER":false,"FEATURE_FLAG_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING":false,"FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA":true,"LOCALE":"en","USER_USERNAME":"Demo + User","USER_PICTURE_FILE_NAME":null}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 24 Jul 2022 16:18:25 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '1813' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_system/TestSystem.test_get_system_info_valid.yaml b/test/cassettes/test_system/TestSystem.test_get_system_info_valid.yaml new file mode 100644 index 0000000..5a9d44c --- /dev/null +++ b/test/cassettes/test_system/TestSystem.test_get_system_info_valid.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/system/info + response: + body: + string: '{"grocy_version":{"Version":"3.3.1","ReleaseDate":"2022-06-10"},"php_version":"8.0.20","sqlite_version":"3.38.5","os":"Linux + 5.10.0-15-amd64 #1 SMP Debian 5.10.120-1 (2022-06-09) x86_64","client":"python-requests\/2.28.0"}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 24 Jul 2022 16:18:25 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '222' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_system/TestSystem.test_get_system_time_valid.yaml b/test/cassettes/test_system/TestSystem.test_get_system_time_valid.yaml new file mode 100644 index 0000000..989bd97 --- /dev/null +++ b/test/cassettes/test_system/TestSystem.test_get_system_time_valid.yaml @@ -0,0 +1,43 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/system/time + response: + body: + string: '{"timezone":"UTC","time_local":"2022-07-24 16:18:25","time_local_sqlite3":"2022-07-24 + 16:18:25","time_utc":"2022-07-24 16:18:25","timestamp":1658679505,"offset":0}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Sun, 24 Jul 2022 16:18:25 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '163' + status: + code: 200 + message: OK +version: 1 diff --git a/test/cassettes/test_tasks/TestTasks.test_get_tasks_filters_invalid.yaml b/test/cassettes/test_tasks/TestTasks.test_get_tasks_filters_invalid.yaml new file mode 100644 index 0000000..aaa38f4 --- /dev/null +++ b/test/cassettes/test_tasks/TestTasks.test_get_tasks_filters_invalid.yaml @@ -0,0 +1,64 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/tasks?query%5B%5D=invalid + response: + body: + string: '{"error_message":"Invalid query","error_details":{"stack_trace":"#0 + \/var\/www\/controllers\/BaseApiController.php(50): Grocy\\Controllers\\BaseApiController->filter()\n#1 + \/var\/www\/controllers\/BaseApiController.php(42): Grocy\\Controllers\\BaseApiController->queryData()\n#2 + \/var\/www\/controllers\/TasksApiController.php(11): Grocy\\Controllers\\BaseApiController->FilteredApiResponse()\n#3 + \/var\/www\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(43): + Grocy\\Controllers\\TasksApiController->Current()\n#4 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(384): + Slim\\Handlers\\Strategies\\RequestResponse->__invoke()\n#5 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Slim\\Routing\\Route->handle()\n#6 \/var\/www\/middleware\/JsonMiddleware.php(13): + Slim\\MiddlewareDispatcher->handle()\n#7 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(209): + Grocy\\Middleware\\JsonMiddleware->__invoke()\n#8 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#9 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/Route.php(341): + Slim\\MiddlewareDispatcher->handle()\n#10 \/var\/www\/vendor\/slim\/slim\/Slim\/Routing\/RouteRunner.php(84): + Slim\\Routing\\Route->run()\n#11 \/var\/www\/middleware\/AuthMiddleware.php(48): + Slim\\Routing\\RouteRunner->handle()\n#12 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\AuthMiddleware->__invoke()\n#13 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/RoutingMiddleware.php(59): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#14 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\RoutingMiddleware->process()\n#15 \/var\/www\/vendor\/slim\/slim\/Slim\/Middleware\/ErrorMiddleware.php(107): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#16 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(147): + Slim\\Middleware\\ErrorMiddleware->process()\n#17 \/var\/www\/middleware\/CorsMiddleware.php(30): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#18 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(313): + Grocy\\Middleware\\CorsMiddleware->__invoke()\n#19 \/var\/www\/vendor\/slim\/slim\/Slim\/MiddlewareDispatcher.php(81): + Psr\\Http\\Server\\RequestHandlerInterface@anonymous->handle()\n#20 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(215): + Slim\\MiddlewareDispatcher->handle()\n#21 \/var\/www\/vendor\/slim\/slim\/Slim\/App.php(199): + Slim\\App->handle()\n#22 \/var\/www\/app.php(106): Slim\\App->run()\n#23 \/var\/www\/public\/index.php(45): + require_once(''...'')\n#24 {main}","file":"\/var\/www\/controllers\/BaseApiController.php","line":95}}' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + status: + code: 500 + message: Internal Server Error +version: 1 diff --git a/test/cassettes/test_tasks/TestTasks.test_get_tasks_filters_valid.yaml b/test/cassettes/test_tasks/TestTasks.test_get_tasks_filters_valid.yaml new file mode 100644 index 0000000..769393a --- /dev/null +++ b/test/cassettes/test_tasks/TestTasks.test_get_tasks_filters_valid.yaml @@ -0,0 +1,44 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.28.0 + accept: + - application/json + method: GET + uri: https://localhost/api/tasks?query%5B%5D=category_id%3D1 + response: + body: + string: '[{"id":"2","name":"Task2","description":null,"due_date":"2022-06-16","done":"0","done_timestamp":null,"category_id":"1","assigned_to_user_id":"1","row_created_timestamp":"2022-06-17 + 19:33:37"},{"id":"3","name":"Task3","description":null,"due_date":"2022-06-17","done":"0","done_timestamp":null,"category_id":"1","assigned_to_user_id":"1","row_created_timestamp":"2022-06-17 + 19:33:37"}]' + headers: + Access-Control-Allow-Headers: + - '*' + Access-Control-Allow-Methods: + - GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Wed, 06 Jul 2022 18:52:59 GMT + Server: + - nginx/1.22.0 + Transfer-Encoding: + - chunked + X-Powered-By: + - PHP/8.0.20 + content-length: + - '385' + status: + code: 200 + message: OK +version: 1 diff --git a/test/conftest.py b/test/conftest.py index be22a9f..6cfcb07 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,4 +1,5 @@ from test.test_const import CONST_BASE_URL, CONST_PORT, CONST_SSL +from typing import List import pytest @@ -18,4 +19,9 @@ def grocy_api_client(grocy): @pytest.fixture def vcr_config(): - return {"record_mode": "once"} + yield {"record_mode": "once", "decode_compressed_response": True} + + +@pytest.fixture +def invalid_query_filter() -> List[str]: + yield ["invalid"] diff --git a/test/test_battery.py b/test/test_battery.py index c4fd365..d32b534 100644 --- a/test/test_battery.py +++ b/test/test_battery.py @@ -2,14 +2,26 @@ import pytest +from pygrocy.errors import GrocyError + class TestBattery: @pytest.mark.vcr def test_get_batteries_valid(self, grocy): - batteries = grocy.batteries() + batteries = grocy.batteries(get_details=False) + + assert len(batteries) == 4 + assert isinstance(batteries[0].last_tracked_time, datetime) + + @pytest.mark.vcr + def test_get_batteries_with_details_valid(self, grocy): + batteries = grocy.batteries(get_details=True) assert len(batteries) == 4 assert isinstance(batteries[0].last_tracked_time, datetime) + assert batteries[0].last_charged == batteries[0].last_tracked_time + assert batteries[0].id == 1 + assert batteries[0].name == "Battery1" @pytest.mark.vcr def test_get_battery_details_valid(self, grocy): @@ -29,3 +41,19 @@ def test_get_battery_details_valid(self, grocy): @pytest.mark.vcr def test_charge_battery(self, grocy): assert grocy.charge_battery(1) + + @pytest.mark.vcr + def test_get_batteries_filters_valid(self, grocy): + query_filter = ["next_estimated_charge_time<2022-06-20"] + batteries = grocy.batteries(query_filters=query_filter) + + for item in batteries: + assert item.next_estimated_charge_time < datetime(2022, 6, 20) + + @pytest.mark.vcr + def test_get_batteries_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.batteries(query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_chores.py b/test/test_chores.py index 030fda7..27be7ad 100644 --- a/test/test_chores.py +++ b/test/test_chores.py @@ -4,7 +4,7 @@ from pygrocy.data_models.chore import AssignmentType, Chore, PeriodType from pygrocy.data_models.user import User -from pygrocy.errors.grocy_error import GrocyError +from pygrocy.errors import GrocyError class TestChores: @@ -51,7 +51,9 @@ def test_execute_chore_valid(self, grocy): @pytest.mark.vcr def test_execute_chore_valid_with_data(self, grocy): - result = grocy.execute_chore(1, done_by=1, tracked_time=datetime.now()) + result = grocy.execute_chore( + 1, done_by=1, tracked_time=datetime.now(), skipped=False + ) assert not isinstance(result, GrocyError) @pytest.mark.vcr @@ -61,3 +63,19 @@ def test_execute_chore_invalid(self, grocy): error = exc_info.value assert error.status_code == 400 + + @pytest.mark.vcr + def test_get_chores_filters_valid(self, grocy): + query_filter = ["next_execution_assigned_to_user_id=1"] + chores = grocy.chores(get_details=True, query_filters=query_filter) + + for item in chores: + assert item.next_execution_assigned_to_user_id == 1 + + @pytest.mark.vcr + def test_get_chores_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.chores(get_details=True, query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_generic.py b/test/test_generic.py index 7c47b24..2243c9e 100644 --- a/test/test_generic.py +++ b/test/test_generic.py @@ -1,7 +1,7 @@ import pytest from pygrocy.data_models.generic import EntityType -from pygrocy.errors.grocy_error import GrocyError +from pygrocy.errors import GrocyError class TestGeneric: @@ -58,3 +58,24 @@ def test_delete_generic_error(self, grocy): error = exc_info.value assert error.status_code == 404 + + @pytest.mark.vcr + def test_get_generic_objects_for_type_filters_valid(self, grocy): + query_filter = ["name=Walmart"] + shopping_locations = grocy.get_generic_objects_for_type( + EntityType.SHOPPING_LOCATIONS, query_filters=query_filter + ) + + assert len(shopping_locations) == 1 + + @pytest.mark.vcr + def test_get_generic_objects_for_type_filters_invalid( + self, grocy, invalid_query_filter + ): + with pytest.raises(GrocyError) as exc_info: + grocy.get_generic_objects_for_type( + EntityType.SHOPPING_LOCATIONS, query_filters=invalid_query_filter + ) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_grocy.py b/test/test_grocy.py index c758942..2f87d48 100644 --- a/test/test_grocy.py +++ b/test/test_grocy.py @@ -218,6 +218,18 @@ def test_consume_product_error(self): ) self.assertRaises(GrocyError, self.grocy.consume_product, 1, 1.3) + @pytest.mark.vcr + def test_consume_recipe_valid(self): + self.grocy.consume_recipe(5) + + @pytest.mark.vcr + def test_consume_recipe_error(self): + with pytest.raises(GrocyError) as exc_info: + self.grocy.consume_recipe(4464) + + error = exc_info.value + assert error.status_code == 400 + @pytest.mark.vcr def test_inventory_product_valid(self): current_inventory = int(self.grocy.product(4).available_amount) @@ -301,12 +313,14 @@ def test_inventory_product_by_barcode_error(self): @responses.activate def test_execute_chore_valid(self): responses.add(responses.POST, f"{self.base_url}/chores/1/execute", status=200) - self.assertIsNone(self.grocy.execute_chore(1, 1, self.date_test)) + self.assertIsNone(self.grocy.execute_chore(1, 1, self.date_test, False)) @responses.activate def test_execute_chore_error(self): responses.add(responses.POST, f"{self.base_url}/chores/1/execute", status=400) - self.assertRaises(GrocyError, self.grocy.execute_chore, 1, 1, self.date_test) + self.assertRaises( + GrocyError, self.grocy.execute_chore, 1, 1, self.date_test, False + ) @responses.activate def test_get_meal_plan(self): diff --git a/test/test_meal_plan.py b/test/test_meal_plan.py index b294447..ca45b53 100644 --- a/test/test_meal_plan.py +++ b/test/test_meal_plan.py @@ -1,6 +1,9 @@ +import datetime + import pytest from pygrocy.data_models.meal_items import MealPlanItemType, MealPlanSection, RecipeItem +from pygrocy.errors import GrocyError class TestMealPlan: @@ -56,3 +59,19 @@ def test_get_meal_plan_with_product(self, grocy): item for item in meal_plan if item.type == MealPlanItemType.PRODUCT ) assert product_entry.product_id == 3 + + @pytest.mark.vcr + def test_get_meal_plan_filters_valid(self, grocy): + query_filter = ["day>=2022-06-15", "product_amount>0"] + meal_plans = grocy.meal_plan(get_details=True, query_filters=query_filter) + + for item in meal_plans: + assert item.day >= datetime.date(2022, 6, 15) + + @pytest.mark.vcr + def test_get_meal_plan_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.meal_plan(get_details=True, query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_meal_plan_sections.py b/test/test_meal_plan_sections.py index 29e763b..0c7d85a 100644 --- a/test/test_meal_plan_sections.py +++ b/test/test_meal_plan_sections.py @@ -2,6 +2,8 @@ import pytest +from pygrocy.errors import GrocyError + class TestMealPlanSections: @pytest.mark.vcr @@ -25,3 +27,19 @@ def test_get_section_by_id_valid(self, grocy): def test_get_section_by_id_invalid(self, grocy): section = grocy.meal_plan_section(1000) assert section is None + + @pytest.mark.vcr + def test_get_sections_filters_valid(self, grocy): + query_filter = ["name=Breakfast"] + sections = grocy.meal_plan_sections(query_filters=query_filter) + + for item in sections: + assert item.name == "Breakfast" + + @pytest.mark.vcr + def test_get_sections_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.meal_plan_sections(query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_product_groups.py b/test/test_product_groups.py index 846a5c1..7e06818 100644 --- a/test/test_product_groups.py +++ b/test/test_product_groups.py @@ -1,6 +1,7 @@ import pytest from pygrocy.data_models.product import Group +from pygrocy.errors import GrocyError class TestProductGroups: @@ -20,3 +21,19 @@ def test_get_product_groups_valid(self, grocy): group = next(group for group in product_groups_list if group.id == 1) assert group.name == "01 Sweets" assert group.description is None + + @pytest.mark.vcr + def test_get_product_groups_filters_valid(self, grocy): + query_filter = ["id=6"] + product_groups = grocy.product_groups(query_filters=query_filter) + + for item in product_groups: + assert item.id == 6 + + @pytest.mark.vcr + def test_get_product_groups_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.product_groups(query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_shoppinglist.py b/test/test_shoppinglist.py index faabe2e..2cc40c6 100644 --- a/test/test_shoppinglist.py +++ b/test/test_shoppinglist.py @@ -60,3 +60,21 @@ def test_clear_shopping_list_valid(self, grocy): @pytest.mark.vcr def test_remove_product_in_shopping_list_valid(self, grocy): grocy.remove_product_in_shopping_list(20) + + @pytest.mark.vcr + def test_get_shopping_list_filters_valid(self, grocy): + query_filter = ["note~snacks"] + shopping_list = grocy.shopping_list( + get_details=True, query_filters=query_filter + ) + + for item in shopping_list: + assert "snacks" in item.note + + @pytest.mark.vcr + def test_get_shopping_list_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.shopping_list(get_details=True, query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500 diff --git a/test/test_stock.py b/test/test_stock.py index 4fe1ced..e8a23e1 100644 --- a/test/test_stock.py +++ b/test/test_stock.py @@ -1,6 +1,7 @@ import pytest from pygrocy.data_models.product import Product +from pygrocy.errors import GrocyError class TestStock: @@ -54,3 +55,15 @@ def test_get_overdue_products_valid(self, grocy): assert len(overdue_products) == 4 for prod in overdue_products: assert isinstance(prod, Product) + + @pytest.mark.vcr + def test_open_product_valid(self, grocy): + grocy.open_product(13, 1) + + @pytest.mark.vcr + def test_open_product_error(self, grocy): + with pytest.raises(GrocyError) as exc_info: + grocy.open_product(13, 0) + + error = exc_info.value + assert error.status_code == 400 diff --git a/test/test_system.py b/test/test_system.py index 6102f5c..0879c00 100644 --- a/test/test_system.py +++ b/test/test_system.py @@ -1,6 +1,7 @@ -from datetime import datetime +from datetime import date, datetime import pytest +from pygrocy.data_models.system import SystemConfig, SystemInfo, SystemTime class TestSystem: @@ -12,3 +13,37 @@ def test_get_last_db_changed_valid(self, grocy): assert timestamp.year == 2022 assert timestamp.month == 4 assert timestamp.day == 22 + + @pytest.mark.vcr + def test_get_system_info_valid(self, grocy): + system_info = grocy.get_system_info() + + assert isinstance(system_info, SystemInfo) + assert isinstance(system_info.grocy_release_date, date) + assert system_info.grocy_version == "3.3.1" + assert system_info.php_version == "8.0.20" + assert system_info.sqlite_version == "3.38.5" + + @pytest.mark.vcr + def test_get_system_time_valid(self, grocy): + system_time = grocy.get_system_time() + + assert isinstance(system_time, SystemTime) + assert isinstance(system_time.time_local, datetime) + assert isinstance(system_time.time_local_sqlite3, datetime) + assert isinstance(system_time.time_utc, datetime) + + assert system_time.timezone == "UTC" + assert system_time.timestamp == 1658679505 + + @pytest.mark.vcr + def test_get_system_config_valid(self, grocy): + system_config = grocy.get_system_config() + + assert isinstance(system_config, SystemConfig) + + assert system_config.username == "Demo User" + assert system_config.currency == "USD" + assert system_config.locale == "en" + assert "FEATURE_FLAG_TASKS" in system_config.enabled_features + assert "FEATURE_FLAG_THERMAL_PRINTER" not in system_config.enabled_features diff --git a/test/test_tasks.py b/test/test_tasks.py index 0dd149b..a0e80ca 100644 --- a/test/test_tasks.py +++ b/test/test_tasks.py @@ -47,3 +47,19 @@ def test_complete_task_invalid(self, grocy): error = exc_info.value assert error.status_code == 400 + + @pytest.mark.vcr + def test_get_tasks_filters_valid(self, grocy): + query_filter = ["category_id=1"] + tasks = grocy.tasks(query_filters=query_filter) + + for item in tasks: + assert item.category_id == 1 + + @pytest.mark.vcr + def test_get_tasks_filters_invalid(self, grocy, invalid_query_filter): + with pytest.raises(GrocyError) as exc_info: + grocy.tasks(query_filters=invalid_query_filter) + + error = exc_info.value + assert error.status_code == 500