From 9fb7bfc2d896ca384f0e8cbb1dd06ec7065e4947 Mon Sep 17 00:00:00 2001 From: edraft Date: Wed, 12 Mar 2025 12:42:45 +0100 Subject: [PATCH] Some improvements & bugfixes --- .../api_graphql/abc/db_model_filter_abc.py | 3 +- .../api_graphql/filter/short_url_filter.py | 5 ++ api/src/api_graphql/graphql/short_url.gql | 2 + .../mutations/short_url_mutation.py | 2 +- .../database/abc/data_access_object_abc.py | 10 ++- api/src/data/schemas/public/short_url_dao.py | 2 + api/src/redirector.py | 11 ++- web/src/app/core/base/page-base.ts | 1 + .../short-url-form-page.component.ts | 10 +-- .../short-urls/short-urls.data.service.ts | 75 +++++++++++-------- .../admin/short-urls/short-urls.module.ts | 32 ++++---- 11 files changed, 92 insertions(+), 61 deletions(-) diff --git a/api/src/api_graphql/abc/db_model_filter_abc.py b/api/src/api_graphql/abc/db_model_filter_abc.py index 1a8931b..d78b1a3 100644 --- a/api/src/api_graphql/abc/db_model_filter_abc.py +++ b/api/src/api_graphql/abc/db_model_filter_abc.py @@ -13,10 +13,11 @@ class DbModelFilterABC[T](FilterABC[T]): obj: Optional[dict], ): FilterABC.__init__(self, obj) + from api_graphql.filter.user_filter import UserFilter self.add_field("id", IntFilter) self.add_field("deleted", BoolFilter) - self.add_field("editor", IntFilter) + self.add_field("editor", UserFilter) self.add_field("createdUtc", StringFilter, "created") self.add_field("updatedUtc", StringFilter, "updated") diff --git a/api/src/api_graphql/filter/short_url_filter.py b/api/src/api_graphql/filter/short_url_filter.py index 812fce6..02a5097 100644 --- a/api/src/api_graphql/filter/short_url_filter.py +++ b/api/src/api_graphql/filter/short_url_filter.py @@ -1,5 +1,7 @@ from api_graphql.abc.db_model_filter_abc import DbModelFilterABC from api_graphql.abc.filter.string_filter import StringFilter +from api_graphql.filter.domain_filter import DomainFilter +from api_graphql.filter.group_filter import GroupFilter class ShortUrlFilter(DbModelFilterABC): @@ -12,3 +14,6 @@ class ShortUrlFilter(DbModelFilterABC): self.add_field("shortUrl", StringFilter, db_name="short_url") self.add_field("targetUrl", StringFilter, db_name="target_url") self.add_field("description", StringFilter) + + self.add_field("group", GroupFilter) + self.add_field("domain", DomainFilter) diff --git a/api/src/api_graphql/graphql/short_url.gql b/api/src/api_graphql/graphql/short_url.gql index 1ab6144..a9753bd 100644 --- a/api/src/api_graphql/graphql/short_url.gql +++ b/api/src/api_graphql/graphql/short_url.gql @@ -50,6 +50,8 @@ input ShortUrlFilter { targetUrl: StringFilter description: StringFilter loadingScreen: BooleanFilter + group: GroupFilter + domain: DomainFilter fuzzy: ShortUrlFuzzy diff --git a/api/src/api_graphql/mutations/short_url_mutation.py b/api/src/api_graphql/mutations/short_url_mutation.py index e91c0ba..d07478d 100644 --- a/api/src/api_graphql/mutations/short_url_mutation.py +++ b/api/src/api_graphql/mutations/short_url_mutation.py @@ -115,5 +115,5 @@ class ShortUrlMutation(MutationABC): @staticmethod async def resolve_track_visit(*_, id: int, agent: str): logger.debug(f"track visit: {id} -- {agent}") - await shortUrlVisitDao.create(ShortUrlVisit(0, id, agent)) + x = await shortUrlVisitDao.create(ShortUrlVisit(0, id, agent)) return True diff --git a/api/src/core/database/abc/data_access_object_abc.py b/api/src/core/database/abc/data_access_object_abc.py index 9c510bc..98f4a62 100644 --- a/api/src/core/database/abc/data_access_object_abc.py +++ b/api/src/core/database/abc/data_access_object_abc.py @@ -132,11 +132,15 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]): async def count(self, filters: AttributeFilters = None) -> int: query = f"SELECT COUNT(*) FROM {self._table_name}" + for join in self.__joins: + query += f" {self.__joins[join]}" if filters is not None and (not isinstance(filters, list) or len(filters) > 0): query += f" WHERE {self._build_conditions(filters)}" result = await self._db.select_map(query) + if len(result) == 0: + return 0 return result[0]["count"] async def get_all(self) -> list[T_DBM]: @@ -583,14 +587,16 @@ class DataAccessObjectABC(ABC, Database, Generic[T_DBM]): if isinstance(sub_values, dict): for operator, value in sub_values.items(): - conditions.append(self._build_condition(db_name, operator, value)) + conditions.append( + f"({self._build_condition(db_name, operator, value)} OR {self._build_condition(db_name, "isNull", None)})") elif isinstance(sub_values, list): sub_conditions = [] for value in sub_values: if isinstance(value, dict): for operator, val in value.items(): sub_conditions.append( - self._build_condition(db_name, operator, val) + f"({self._build_condition(db_name, operator, val)} OR {self._build_condition(db_name, "isNull", None)})" + ) else: sub_conditions.append( diff --git a/api/src/data/schemas/public/short_url_dao.py b/api/src/data/schemas/public/short_url_dao.py index 43c5a26..6866d5c 100644 --- a/api/src/data/schemas/public/short_url_dao.py +++ b/api/src/data/schemas/public/short_url_dao.py @@ -13,7 +13,9 @@ class ShortUrlDao(DbModelDaoABC[ShortUrl]): self.attribute(ShortUrl.target_url, str) self.attribute(ShortUrl.description, str) self.attribute(ShortUrl.group_id, int) + self.reference("group", "id", ShortUrl.group_id, "public.groups") self.attribute(ShortUrl.domain_id, int) + self.reference("domain", "id", ShortUrl.domain_id, "public.domains") self.attribute(ShortUrl.loading_screen, bool) diff --git a/api/src/redirector.py b/api/src/redirector.py index 6f11a03..0b9f1b1 100644 --- a/api/src/redirector.py +++ b/api/src/redirector.py @@ -83,7 +83,7 @@ def _find_short_url_by_path(path: str) -> Optional[dict]: json={ "query": f""" query getShortUrlByPath($path: String!) {{ - shortUrls(filter: {{ shortUrl: {{ equal: $path }}, deleted: {{ equal: false }} }}) {{ + shortUrls(filter: {{ shortUrl: {{ equal: $path }}, deleted: {{ equal: false }}, group: {{ deleted: {{ equal: false }} }} }}) {{ nodes {{ id shortUrl @@ -107,7 +107,14 @@ def _find_short_url_by_path(path: str) -> Optional[dict]: }, headers={"Authorization": f"API-Key {api_key}"}, ) - data = request.json()["data"]["shortUrls"]["nodes"] + data = request.json() + if "errors" in data: + logger.warning(f"Failed to find short url by path {path} -> {data["errors"]}") + + if "data" not in data or "shortUrls" not in data["data"] or "nodes" not in data["data"]["shortUrls"]: + return None + + data = data["data"]["shortUrls"]["nodes"] if len(data) == 0: return None diff --git a/web/src/app/core/base/page-base.ts b/web/src/app/core/base/page-base.ts index f9233e3..3423063 100644 --- a/web/src/app/core/base/page-base.ts +++ b/web/src/app/core/base/page-base.ts @@ -115,6 +115,7 @@ export abstract class PageBase< .onChange() .pipe(takeUntil(this.unsubscribe$)) .subscribe(() => { + logger.debug('Reload data'); this.load(true); }); } diff --git a/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts b/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts index 1143817..fa7ea84 100644 --- a/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts +++ b/web/src/app/modules/admin/short-urls/form-page/short-url-form-page.component.ts @@ -41,12 +41,10 @@ export class ShortUrlFormPageComponent extends FormPageBase< return; } - this.dataService - .load([{ id: { equal: this.nodeId } }]) - .subscribe(apiKey => { - this.node = apiKey.nodes[0]; - this.setForm(this.node); - }); + this.dataService.loadById(this.nodeId).subscribe(node => { + this.node = node; + this.setForm(this.node); + }); } new(): ShortUrl { diff --git a/web/src/app/modules/admin/short-urls/short-urls.data.service.ts b/web/src/app/modules/admin/short-urls/short-urls.data.service.ts index 7dca99f..a43033d 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.data.service.ts +++ b/web/src/app/modules/admin/short-urls/short-urls.data.service.ts @@ -1,5 +1,5 @@ import { Injectable, Provider } from '@angular/core'; -import { Observable } from 'rxjs'; +import { merge, Observable } from 'rxjs'; import { Create, Delete, @@ -47,13 +47,8 @@ export class ShortUrlsDataService return this.apollo .query<{ shortUrls: QueryResult }>({ query: gql` - query getShortUrls( - $filter: [ShortUrlFilter] - $sort: [ShortUrlSort] - $skip: Int - $take: Int - ) { - shortUrls(filter: $filter, sort: $sort, skip: $skip, take: $take) { + query getShortUrls($filter: [ShortUrlFilter], $sort: [ShortUrlSort]) { + shortUrls(filter: $filter, sort: $sort) { count totalCount nodes { @@ -80,7 +75,7 @@ export class ShortUrlsDataService ${DB_MODEL_FRAGMENT} `, variables: { - filter: filter, + filter: [{ group: { deleted: { equal: false } } }, ...(filter ?? [])], sort: sort, skip: skip, take: take, @@ -100,23 +95,25 @@ export class ShortUrlsDataService .query<{ shortUrls: QueryResult }>({ query: gql` query getShortUrl($id: Int) { - shortUrl(filter: { id: { equal: $id } }) { - id - shortUrl - targetUrl - description - loadingScreen - visits - group { + shortUrls(filter: { id: { equal: $id } }) { + nodes { id - name - } - domain { - id - name - } + shortUrl + targetUrl + description + loadingScreen + visits + group { + id + name + } + domain { + id + name + } - ...DB_MODEL + ...DB_MODEL + } } } @@ -136,15 +133,27 @@ export class ShortUrlsDataService } onChange(): Observable { - return this.apollo - .subscribe<{ shortUrlChange: void }>({ - query: gql` - subscription onShortUrlChange { - shortUrlChange - } - `, - }) - .pipe(map(result => result.data?.shortUrlChange)); + return merge( + this.apollo + .subscribe<{ shortUrlChange: void }>({ + query: gql` + subscription onShortUrlChange { + shortUrlChange + } + `, + }) + .pipe(map(result => result.data?.shortUrlChange)), + + this.apollo + .subscribe<{ groupChange: void }>({ + query: gql` + subscription onGroupChange { + groupChange + } + `, + }) + .pipe(map(result => result.data?.groupChange)) + ).pipe(map(() => {})); } create(object: ShortUrlCreateInput): Observable { diff --git a/web/src/app/modules/admin/short-urls/short-urls.module.ts b/web/src/app/modules/admin/short-urls/short-urls.module.ts index d095c69..6e5b0e2 100644 --- a/web/src/app/modules/admin/short-urls/short-urls.module.ts +++ b/web/src/app/modules/admin/short-urls/short-urls.module.ts @@ -1,34 +1,34 @@ -import { NgModule } from "@angular/core"; -import { CommonModule } from "@angular/common"; -import { SharedModule } from "src/app/modules/shared/shared.module"; -import { RouterModule, Routes } from "@angular/router"; -import { PermissionGuard } from "src/app/core/guard/permission.guard"; -import { PermissionsEnum } from "src/app/model/auth/permissionsEnum"; -import { ShortUrlsPage } from "src/app/modules/admin/short-urls/short-urls.page"; -import { ShortUrlFormPageComponent } from "src/app/modules/admin/short-urls/form-page/short-url-form-page.component"; -import { ShortUrlsDataService } from "src/app/modules/admin/short-urls/short-urls.data.service"; -import { ShortUrlsColumns } from "src/app/modules/admin/short-urls/short-urls.columns"; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from 'src/app/modules/shared/shared.module'; +import { RouterModule, Routes } from '@angular/router'; +import { PermissionGuard } from 'src/app/core/guard/permission.guard'; +import { PermissionsEnum } from 'src/app/model/auth/permissionsEnum'; +import { ShortUrlsPage } from 'src/app/modules/admin/short-urls/short-urls.page'; +import { ShortUrlFormPageComponent } from 'src/app/modules/admin/short-urls/form-page/short-url-form-page.component'; +import { ShortUrlsDataService } from 'src/app/modules/admin/short-urls/short-urls.data.service'; +import { ShortUrlsColumns } from 'src/app/modules/admin/short-urls/short-urls.columns'; const routes: Routes = [ { - path: "", - title: "ShortUrls", + path: '', + title: 'ShortUrls', component: ShortUrlsPage, children: [ { - path: "create", + path: 'create', component: ShortUrlFormPageComponent, canActivate: [PermissionGuard], data: { - permissions: [PermissionsEnum.apiKeysCreate], + permissions: [PermissionsEnum.shortUrlsCreate], }, }, { - path: "edit/:id", + path: 'edit/:id', component: ShortUrlFormPageComponent, canActivate: [PermissionGuard], data: { - permissions: [PermissionsEnum.apiKeysUpdate], + permissions: [PermissionsEnum.shortUrlsUpdate], }, }, ],