# Copyright (c) 2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from pathlib import Path import json from api.client import Client from typing import Dict, List class JSONClient(Client): def __init__(self, file_path): super().__init__() self.file_path = Path("database/") / file_path self.resources = self._get_resources(self.file_path) def _get_resources(self, path: Path) -> List[Dict]: """ Retrieves the resources from the JSON file. :param path: The path to the JSON file. :return: The resources as a JSON string. """ with open(path) as f: return json.load(f) def find_resource(self, query: Dict) -> Dict: """ Finds a resource within a list of resources based on the provided query. :param query: The query object containing the search criteria. :return: The resource that matches the query. """ found_resources = [] for resource in self.resources: if ( "resource_version" not in query or query["resource_version"] == "" or query["resource_version"] == "Latest" ): if resource["id"] == query["id"]: found_resources.append(resource) else: if ( resource["id"] == query["id"] and resource["resource_version"] == query["resource_version"] ): return resource if not found_resources: return {"exists": False} return max( found_resources, key=lambda resource: tuple( map(int, resource["resource_version"].split(".")) ), ) def get_versions(self, query: Dict) -> List[Dict]: """ Retrieves all versions of a resource with the given ID from the list of resources. :param query: The query object containing the search criteria. :return: A list of all versions of the resource. """ versions = [] for resource in self.resources: if resource["id"] == query["id"]: versions.append( {"resource_version": resource["resource_version"]} ) versions.sort( key=lambda resource: tuple( map(int, resource["resource_version"].split(".")) ), reverse=True, ) return versions def update_resource(self, query: Dict) -> Dict: """ Updates a resource within a list of resources based on the provided query. The function iterates over the resources and checks if the "id" and "resource_version" of a resource match the values in the query. If there is a match, it removes the existing resource from the list and appends the updated resource. After updating the resources, the function saves the updated list to the specified file path. :param query: The query object containing the resource identification criteria. :return: A dictionary indicating that the resource was updated. """ original_resource = query["original_resource"] modified_resource = query["resource"] if ( original_resource["id"] != modified_resource["id"] and original_resource["resource_version"] != modified_resource["resource_version"] ): return {"status": "Cannot change resource id"} for resource in self.resources: if ( resource["id"] == original_resource["id"] and resource["resource_version"] == original_resource["resource_version"] ): self.resources.remove(resource) self.resources.append(modified_resource) self.write_to_file() return {"status": "Updated"} def check_resource_exists(self, query: Dict) -> Dict: """ Checks if a resource exists within a list of resources based on the provided query. The function iterates over the resources and checks if the "id" and "resource_version" of a resource match the values in the query. If a matching resource is found, it returns a dictionary indicating that the resource exists. If no matching resource is found, it returns a dictionary indicating that the resource does not exist. :param query: The query object containing the resource identification criteria. :return: A dictionary indicating whether the resource exists. """ for resource in self.resources: if ( resource["id"] == query["id"] and resource["resource_version"] == query["resource_version"] ): return {"exists": True} return {"exists": False} def insert_resource(self, query: Dict) -> Dict: """ Inserts a new resource into a list of resources. The function appends the query (new resource) to the resources list, indicating the insertion. It then writes the updated resources to the specified file path. :param query: The query object containing the resource identification criteria. :return: A dictionary indicating that the resource was inserted. """ if self.check_resource_exists(query)["exists"]: return {"status": "Resource already exists"} self.resources.append(query) self.write_to_file() return {"status": "Inserted"} def delete_resource(self, query: Dict) -> Dict: """ This function deletes a resource from the list of resources based on the provided query. :param query: The query object containing the resource identification criteria. :return: A dictionary indicating that the resource was deleted. """ for resource in self.resources: if ( resource["id"] == query["id"] and resource["resource_version"] == query["resource_version"] ): self.resources.remove(resource) self.write_to_file() return {"status": "Deleted"} def write_to_file(self) -> None: """ This function writes the list of resources to a file at the specified file path. :return: None """ with Path(self.file_path).open("w") as outfile: json.dump(self.resources, outfile, indent=4) def save_session(self) -> Dict: """ This function saves the client session to a dictionary. :return: A dictionary containing the client session. """ session = { "client": "json", "filename": self.file_path.name, } return session