From 48f80ae480e2fc0eabbdb5041e841b80c0f788f4 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Thu, 29 Sep 2022 12:52:54 +0200 Subject: [proxy] display some more details in mapping/resource version view in the web UI --- src/hydrilla/proxy/state_impl/items.py | 209 +++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) (limited to 'src/hydrilla/proxy/state_impl/items.py') diff --git a/src/hydrilla/proxy/state_impl/items.py b/src/hydrilla/proxy/state_impl/items.py index d312db9..010b8ca 100644 --- a/src/hydrilla/proxy/state_impl/items.py +++ b/src/hydrilla/proxy/state_impl/items.py @@ -39,12 +39,30 @@ import typing as t import dataclasses as dc from contextlib import contextmanager +from urllib.parse import urljoin from ... import item_infos from .. import state as st from . import base +def _get_item_id(cursor: sqlite3.Cursor, item_type: str, identifier: str) \ + -> str: + cursor.execute( + 'SELECT item_id FROM items WHERE identifier = ? AND type = ?;', + (identifier, item_type) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (item_id,), = rows + + return str(item_id) + + def _get_parent_item_id(cursor: sqlite3.Cursor, version_id: str) -> str: cursor.execute( ''' @@ -66,6 +84,7 @@ def _get_parent_item_id(cursor: sqlite3.Cursor, version_id: str) -> str: return str(item_id) + def _set_installed_status(cursor: sqlite3.Cursor, id: str, new_status: str) \ -> None: cursor.execute( @@ -138,6 +157,71 @@ def _uninstall_version(ref: VersionRefVar) -> t.Optional[VersionRefVar]: return ref if version_still_present else None +def _get_file(ref: VersionRefVar, name: str, file_type: str = 'L') \ + -> st.FileData: + with ref.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + f.data, fu.mime_type + FROM + item_versions AS iv + JOIN items AS i USING (item_id) + JOIN file_uses AS fu USING (item_version_id) + JOIN files AS f USING (file_id) + WHERE + (iv.item_version_id = ? AND iv.installed = 'I') AND + i.type = ? AND + (fu.name = ? AND fu.type = ?) AND + f.data IS NOT NULL; + ''', + (ref.id, ref.type.value[0].upper(), name, file_type) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (data, mime_type), = rows + + return st.FileData(mime_type, name, data) + + +def _get_upstream_file_url( + ref: VersionRefVar, + name: str, + file_type: str = 'L' +) -> str: + with ref.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + f.sha256, r.url + FROM + item_versions AS iv + JOIN repo_iterations AS ri USING(repo_iteration_id) + JOIN repos AS r USING(repo_id) + JOIN file_uses AS fu USING(item_version_id) + JOIN files AS f USING(file_id) + WHERE + iv.item_version_id = ? AND + (fu.name = ? AND fu.type = ?) AND + r.url IS NOT NULL; + ''', + (ref.id, name, file_type) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (sha256, repo_url), = rows + + return urljoin(repo_url, f'file/sha256/{sha256}') + + @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteMappingRef(st.MappingRef): state: base.HaketiloStateWithFields = dc.field(hash=False, compare=False) @@ -405,6 +489,12 @@ class ConcreteMappingStore(st.MappingStore): return sorted(result, key=(lambda di: di.identifier)) + def get_by_identifier(self, identifier: str) -> st.MappingRef: + with self.state.cursor() as cursor: + item_id = _get_item_id(cursor, 'M', identifier) + + return ConcreteMappingRef(item_id, self.state) + @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteMappingVersionRef(st.MappingVersionRef): @@ -472,6 +562,76 @@ class ConcreteMappingVersionRef(st.MappingVersionRef): mapping_ref.update_status(enabled, frozen, id_to_pass) + def get_license_file(self, name: str) -> st.FileData: + return _get_file(self, name, 'L') + + def get_upstream_license_file_url(self, name: str) -> str: + return _get_upstream_file_url(self, name, 'L') + + def get_required_mapping(self, identifier: str) \ + -> 'ConcreteMappingVersionRef': + with self.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + iv2.item_version_id + FROM + item_versions AS iv1 + JOIN resolved_required_mappings AS rrm + ON iv1.item_version_id = + rrm.requiring_mapping_id + JOIN item_versions AS iv2 + ON rrm.required_mapping_id = + iv2.item_version_id + JOIN items AS i + ON iv2.item_id = i.item_id + WHERE + iv1.item_version_id = ? AND + i.identifier = ?; + ''', + (self.id, identifier) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (required_id,), = rows + + return ConcreteMappingVersionRef(str(required_id), self.state) + + def get_payload_resource(self, pattern: str, identifier: str) \ + -> 'ConcreteResourceVersionRef': + with self.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + iv.item_version_id + FROM + payloads AS p + JOIN resolved_depended_resources AS rdd + USING(payload_id) + JOIN item_versions AS iv + ON rdd.resource_item_id = iv.item_version_id + JOIN items AS i + USING (item_id) + WHERE + (p.mapping_item_id = ? AND p.pattern = ?) AND + i.identifier = ?; + ''', + (self.id, pattern, identifier) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (resource_ver_id,), = rows + + return ConcreteResourceVersionRef(str(resource_ver_id), self.state) + def get_item_display_info(self) -> st.RichMappingDisplayInfo: with self._mapping_ref() as mapping_ref: return mapping_ref.get_display_info() @@ -578,6 +738,12 @@ class ConcreteResourceStore(st.ResourceStore): return sorted(result, key=(lambda di: di.identifier)) + def get_by_identifier(self, identifier: str) -> st.ResourceRef: + with self.state.cursor() as cursor: + item_id = _get_item_id(cursor, 'R', identifier) + + return ConcreteResourceRef(item_id, self.state) + @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteResourceVersionRef(st.ResourceVersionRef): @@ -589,6 +755,49 @@ class ConcreteResourceVersionRef(st.ResourceVersionRef): def uninstall(self) -> t.Optional['ConcreteResourceVersionRef']: return _uninstall_version(self) + def get_license_file(self, name: str) -> st.FileData: + return _get_file(self, name, 'L') + + def get_resource_file(self, name: str) -> st.FileData: + return _get_file(self, name, 'W') + + def get_upstream_license_file_url(self, name: str) -> str: + return _get_upstream_file_url(self, name, 'L') + + def get_upstream_resource_file_url(self, name: str) -> str: + return _get_upstream_file_url(self, name, 'W') + + def get_dependency(self, identifier: str) -> st.ResourceVersionRef: + with self.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + iv.item_version_id + FROM + resolved_depended_resources AS rdd1 + JOIN payloads AS p + ON rdd1.payload_id = p.payload_id + JOIN resolved_depended_resources AS rdd2 + ON p.payload_id = rdd2.payload_id + JOIN item_versions AS iv + ON rdd2.resource_item_id = iv.item_version_id + JOIN items AS i + USING (item_id) + WHERE + rdd1.resource_item_id = ? AND i.identifier = ?; + ''', + (self.id, identifier) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (dep_id,), = rows + + return ConcreteResourceVersionRef(str(dep_id), self.state) + def get_item_display_info(self) -> st.RichResourceDisplayInfo: with self.state.cursor() as cursor: resource_id = _get_parent_item_id(cursor, self.id) -- cgit v1.2.3