Added dropdown to technicianId input #344 #1.1.0.rc4

This commit is contained in:
Sven Heidemann 2023-08-17 08:37:04 +02:00
parent 10c20621a8
commit 45a3127696
40 changed files with 272 additions and 93 deletions

View File

@ -0,0 +1,53 @@
type Discord {
guilds(filter: GuildFilter): [Guild]
users(filter: DiscordUserFilter): [DiscordUser]
}
type Guild {
id: ID
name: String
channels(filter: ChannelFilter): [Channel]
roles: [Role]
emojis: [Emoji]
}
input GuildFilter {
id: ID
name: String
}
type Channel {
id: String
name: String
type: String
}
input ChannelFilter {
id: String
name: String
type: String
}
type Role {
id: String
name: String
}
type DiscordUser {
id: String
name: String
bot: Boolean
}
input DiscordUserFilter {
id: ID
name: String
bot: Boolean
}
type Emoji {
id: String
name: String
url: String
}

View File

@ -38,7 +38,6 @@ type Query {
achievementOperators: [String] achievementOperators: [String]
technicianConfig: TechnicianConfig technicianConfig: TechnicianConfig
guilds(filter: GuildFilter): [Guild]
possibleFeatureFlags: [String] possibleFeatureFlags: [String]
discord: Discord
} }

View File

@ -37,6 +37,12 @@ from bot_graphql.queries.auto_role_rule_history_query import AutoRoleRuleHistory
from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery from bot_graphql.queries.auto_role_rule_query import AutoRoleRuleQuery
from bot_graphql.queries.client_history_query import ClientHistoryQuery from bot_graphql.queries.client_history_query import ClientHistoryQuery
from bot_graphql.queries.client_query import ClientQuery from bot_graphql.queries.client_query import ClientQuery
from bot_graphql.queries.discord.channel_query import ChannelQuery
from bot_graphql.queries.discord.discord_query import DiscordQuery
from bot_graphql.queries.discord.discord_user_query import DiscordUserQuery
from bot_graphql.queries.discord.emoji_query import EmojiQuery
from bot_graphql.queries.discord.guild_query import GuildQuery
from bot_graphql.queries.discord.role_query import RoleQuery
from bot_graphql.queries.game_server_query import GameServerQuery from bot_graphql.queries.game_server_query import GameServerQuery
from bot_graphql.queries.known_user_history_query import KnownUserHistoryQuery from bot_graphql.queries.known_user_history_query import KnownUserHistoryQuery
from bot_graphql.queries.known_user_query import KnownUserQuery from bot_graphql.queries.known_user_query import KnownUserQuery
@ -105,6 +111,13 @@ class GraphQLModule(ModuleABC):
services.add_transient(QueryABC, UserJoinedGameServerHistoryQuery) services.add_transient(QueryABC, UserJoinedGameServerHistoryQuery)
services.add_transient(QueryABC, UserJoinedGameServerQuery) services.add_transient(QueryABC, UserJoinedGameServerQuery)
services.add_transient(QueryABC, DiscordQuery)
services.add_transient(QueryABC, GuildQuery)
services.add_transient(QueryABC, ChannelQuery)
services.add_transient(QueryABC, RoleQuery)
services.add_transient(QueryABC, EmojiQuery)
services.add_transient(QueryABC, DiscordUserQuery)
# filters # filters
services.add_transient(FilterABC, AutoRoleFilter) services.add_transient(FilterABC, AutoRoleFilter)
services.add_transient(FilterABC, AutoRoleRuleFilter) services.add_transient(FilterABC, AutoRoleRuleFilter)

View File

@ -0,0 +1 @@
# imports

View File

@ -1,29 +0,0 @@
type Guild {
id: ID
name: String
channels: [Channel]
roles: [Role]
emojis: [Emoji]
}
input GuildFilter {
id: ID
}
type Channel {
id: String
name: String
type: String
}
type Role {
id: String
name: String
}
type Emoji {
id: String
name: String
url: String
}

View File

@ -0,0 +1,16 @@
from cpl_query.extension import List
from discord import Guild, User
class Discord:
def __init__(self, guilds: List[Guild], users: List[User]):
self._guilds = guilds
self._users = users
@property
def guilds(self) -> List[Guild]:
return self._guilds
@property
def users(self) -> List[User]:
return self._users

View File

@ -1,10 +1,10 @@
from bot_graphql.abc.data_query_abc import DataQueryABC from bot_graphql.abc.query_abc import QueryABC
class ChannelQuery(DataQueryABC): class ChannelQuery(QueryABC):
def __init__(self): def __init__(self):
DataQueryABC.__init__(self, "Channel") QueryABC.__init__(self, "Channel")
self.set_field("id", lambda c, *_: c.id) self.set_field("id", lambda c, *_: c.id)
self.set_field("name", lambda c, *_: c.name) self.set_field("name", lambda c, *_: c.name)
self.set_field("type", lambda c, *_: type(c)) self.set_field("type", lambda c, *_: type(c).__name__)

View File

@ -0,0 +1,48 @@
from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_graphql.abc.query_abc import QueryABC
class DiscordQuery(QueryABC):
def __init__(
self,
bot: DiscordBotServiceABC,
):
QueryABC.__init__(self, "Discord")
self._bot = bot
self.set_field("guilds", self._resolve_guilds)
self.set_field("users", self._resolve_users)
def _resolve_guilds(self, *_, filter=None):
guilds = self._bot.guilds
if filter is None:
return guilds
if "id" in filter:
guilds = self._bot.guilds.where(lambda g: g.id == int(filter["id"]))
if "name" in filter:
guilds = self._bot.guilds.where(lambda g: g.name == filter["name"])
return guilds
def _resolve_users(self, *_, filter=None):
users = List(any).extend(self._bot.users)
if filter is None:
return users
if "id" in filter:
users = users.where(lambda g: g.id == int(filter["id"]))
if "name" in filter:
users = users.where(lambda g: g.name == filter["name"])
if "bot" in filter:
users = users.where(lambda g: g.bot == bool(filter["bot"]))
return users

View File

@ -0,0 +1,10 @@
from bot_graphql.abc.query_abc import QueryABC
class DiscordUserQuery(QueryABC):
def __init__(self):
QueryABC.__init__(self, "DiscordUser")
self.set_field("id", lambda r, *_: r.id)
self.set_field("name", lambda r, *_: r.name)
self.set_field("bot", lambda r, *_: r.bot)

View File

@ -1,9 +1,9 @@
from bot_graphql.abc.data_query_abc import DataQueryABC from bot_graphql.abc.query_abc import QueryABC
class RoleQuery(DataQueryABC): class EmojiQuery(QueryABC):
def __init__(self): def __init__(self):
DataQueryABC.__init__(self, "Emoji") QueryABC.__init__(self, "Emoji")
self.set_field("id", lambda e, *_: e.id) self.set_field("id", lambda e, *_: e.id)
self.set_field("name", lambda e, *_: e.name) self.set_field("name", lambda e, *_: e.name)

View File

@ -1,12 +1,38 @@
from bot_graphql.abc.data_query_abc import DataQueryABC from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from discord import Guild
from bot_graphql.abc.query_abc import QueryABC
class GuildQuery(DataQueryABC): class GuildQuery(QueryABC):
def __init__(self): def __init__(
DataQueryABC.__init__(self, "Guild") self,
bot: DiscordBotServiceABC,
):
QueryABC.__init__(self, "Guild")
self._bot = bot
self.set_field("id", lambda g, *_: g.id) self.set_field("id", lambda g, *_: g.id)
self.set_field("name", lambda g, *_: g.name) self.set_field("name", lambda g, *_: g.name)
self.set_field("channels", lambda g, *_: g.channels) self.set_field("channels", self._resolve_channels)
self.set_field("roles", lambda g, *_: g.roles) self.set_field("roles", lambda g, *_: g.roles)
self.set_field("emojis", lambda g, *_: g.emojis) self.set_field("emojis", lambda g, *_: g.emojis)
def _resolve_channels(self, g: Guild, *_, filter=None):
users = List(any).extend(g.channels)
if filter is None:
return users
if "id" in filter:
users = users.where(lambda c: c.id == int(filter["id"]))
if "name" in filter:
users = users.where(lambda c: c.id == filter["name"])
if "type" in filter:
users = users.where(lambda c: type(c).__name__ == filter["type"])
return users

View File

@ -1,9 +1,9 @@
from bot_graphql.abc.data_query_abc import DataQueryABC from bot_graphql.abc.query_abc import QueryABC
class RoleQuery(DataQueryABC): class RoleQuery(QueryABC):
def __init__(self): def __init__(self):
DataQueryABC.__init__(self, "Role") QueryABC.__init__(self, "Role")
self.set_field("id", lambda r, *_: r.id) self.set_field("id", lambda r, *_: r.id)
self.set_field("name", lambda r, *_: r.name) self.set_field("name", lambda r, *_: r.name)

View File

@ -1,4 +1,5 @@
from cpl_discord.service import DiscordBotServiceABC from cpl_discord.service import DiscordBotServiceABC
from cpl_query.extension import List
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC
@ -24,6 +25,7 @@ from bot_graphql.filter.user_filter import UserFilter
from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter from bot_graphql.filter.user_joined_game_server_filter import UserJoinedGameServerFilter
from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter from bot_graphql.filter.user_joined_server_filter import UserJoinedServerFilter
from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter from bot_graphql.filter.user_joined_voice_channel_filter import UserJoinedVoiceChannelFilter
from bot_graphql.model.discord import Discord
from modules.achievements.achievement_service import AchievementService from modules.achievements.achievement_service import AchievementService
@ -47,8 +49,6 @@ class Query(QueryABC):
): ):
QueryABC.__init__(self, "Query") QueryABC.__init__(self, "Query")
self._bot = bot
self.add_collection("autoRole", lambda *_: auto_roles.get_auto_roles(), AutoRoleFilter) self.add_collection("autoRole", lambda *_: auto_roles.get_auto_roles(), AutoRoleFilter)
self.add_collection("autoRoleRule", lambda *_: auto_roles.get_auto_role_rules(), AutoRoleRuleFilter) self.add_collection("autoRoleRule", lambda *_: auto_roles.get_auto_role_rules(), AutoRoleRuleFilter)
self.add_collection("client", lambda *_: clients.get_clients(), ClientFilter) self.add_collection("client", lambda *_: clients.get_clients(), ClientFilter)
@ -73,13 +73,7 @@ class Query(QueryABC):
self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter) self.add_collection("achievement", lambda *_: achievements.get_achievements(), AchievementFilter)
self.set_field("technicianConfig", lambda *_: technician_config.get_technician_config()) self.set_field("technicianConfig", lambda *_: technician_config.get_technician_config())
self.set_field("guilds", self._resolve_guilds) self.set_field("achievementAttributes", lambda *_: achievement_service.get_attributes())
self.set_field("achievementAttributes", lambda x, *_: achievement_service.get_attributes()) self.set_field("achievementOperators", lambda *_: achievement_service.get_operators())
self.set_field("achievementOperators", lambda x, *_: achievement_service.get_operators()) self.set_field("possibleFeatureFlags", lambda *_: [e.value for e in FeatureFlagsEnum])
self.set_field("possibleFeatureFlags", lambda x, *_: [e.value for e in FeatureFlagsEnum]) self.set_field("discord", lambda *_: Discord(bot.guilds, List(any).extend(bot.users)))
def _resolve_guilds(self, *_, filter=None):
if filter is None or "id" not in filter:
return self._bot.guilds
return self._bot.guilds.where(lambda g: g.id == int(filter["id"]))

View File

@ -10,7 +10,7 @@ from bot_graphql.query import Query
class Schema: class Schema:
def __init__(self, query: Query, mutation: Mutation, queries: list[QueryABC]): def __init__(self, query: Query, mutation: Mutation, queries: list[QueryABC]):
type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "model/")) type_defs = load_schema_from_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), "graphql/"))
self._schema = make_executable_schema(type_defs, query, mutation, *queries) self._schema = make_executable_schema(type_defs, query, mutation, *queries)
@property @property

View File

@ -1,3 +1,8 @@
export interface Discord {
guilds?: Guild[];
users?: DiscordUser[];
}
export interface Guild { export interface Guild {
id?: string; id?: string;
name?: string; name?: string;
@ -14,9 +19,9 @@ export interface Channel {
} }
export enum ChannelType { export enum ChannelType {
category = "category", category = "CategoryChannel",
text = "text", text = "TextChannel",
voice = "voice" voice = "VoiceChannel"
} }
export interface Role { export interface Role {
@ -29,3 +34,9 @@ export interface Emoji {
name?: string; name?: string;
url?: string; url?: string;
} }
export interface DiscordUser {
id?: string;
name?: string;
bot?: boolean;
}

View File

@ -1,12 +1,13 @@
export class Queries { export class Queries {
static guildsQuery = ` static guildsQuery = `
query GuildsQuery($id: ID) { query GuildsQuery($id: ID, $filter: ChannelFilter) {
discord {
guilds(filter: {id: $id}) { guilds(filter: {id: $id}) {
id id
name name
channels { channels(filter: $filter) {
id id
name name
type type
@ -22,6 +23,18 @@ export class Queries {
} }
} }
} }
}
`;
static discordUsersQuery = `
query DiscordUsersQuery {
discord {
users {
id
name
}
}
}
`; `;
static serversQuery = ` static serversQuery = `
@ -29,10 +42,11 @@ export class Queries {
serverCount serverCount
servers(filter: $filter, page: $page, sort: $sort) { servers(filter: $filter, page: $page, sort: $sort) {
id id
discordId
name name
iconURL iconURL
userCount userCount
clients{ clients {
id id
discordId discordId
name name

View File

@ -1,7 +1,7 @@
import { GameServer, Server } from "../data/server.model"; import { GameServer, Server } from "../data/server.model";
import { User } from "../data/user.model"; import { User } from "../data/user.model";
import { AutoRole, AutoRoleRule } from "../data/auto_role.model"; import { AutoRole, AutoRoleRule } from "../data/auto_role.model";
import { Guild } from "../data/discord.model"; import { Discord, Guild } from "../data/discord.model";
import { Level } from "../data/level.model"; import { Level } from "../data/level.model";
import { Achievement, AchievementAttribute } from "../data/achievement.model"; import { Achievement, AchievementAttribute } from "../data/achievement.model";
import { TechnicianConfig } from "../config/technician-config.model"; import { TechnicianConfig } from "../config/technician-config.model";
@ -21,7 +21,7 @@ export interface ServerConfigQuery {
} }
export interface SingleDiscordQuery { export interface SingleDiscordQuery {
guilds: Guild[]; discord: Discord;
} }
export interface UserListQuery { export interface UserListQuery {

View File

@ -162,7 +162,7 @@
<div class="content-divider"></div> <div class="content-divider"></div>
<app-config-list translationKey="admin.settings.bot.ping_urls" [(data)]="config.pingURLs"></app-config-list> <app-config-list translationKey="admin.settings.bot.ping_urls" [(data)]="config.pingURLs"></app-config-list>
<app-config-list translationKey="admin.settings.bot.technician_ids" [(data)]="config.technicianIds"></app-config-list> <app-config-list translationKey="admin.settings.bot.technician_ids" [options]="possibleTechnicians" optionLabel="name" optionValue="id" [(data)]="config.technicianIds"></app-config-list>
<app-feature-flag-list [(data)]="config.featureFlags"></app-feature-flag-list> <app-feature-flag-list [(data)]="config.featureFlags"></app-feature-flag-list>
<div class="content-row"> <div class="content-row">

View File

@ -11,12 +11,13 @@ import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { ToastService } from "src/app/services/toast/toast.service"; import { ToastService } from "src/app/services/toast/toast.service";
import { forkJoin, throwError } from "rxjs"; import { forkJoin, throwError } from "rxjs";
import { TechnicianConfig } from "../../../../../models/config/technician-config.model"; import { TechnicianConfig } from "../../../../../models/config/technician-config.model";
import { TechnicianConfigQuery } from "../../../../../models/graphql/query.model"; import { SingleDiscordQuery, TechnicianConfigQuery } from "../../../../../models/graphql/query.model";
import { Queries } from "../../../../../models/graphql/queries.model"; import { Queries } from "../../../../../models/graphql/queries.model";
import { DataService } from "../../../../../services/data/data.service"; import { DataService } from "../../../../../services/data/data.service";
import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model"; import { TechnicianConfigMutationResult } from "../../../../../models/graphql/result.model";
import { Mutations } from "../../../../../models/graphql/mutations.model"; import { Mutations } from "../../../../../models/graphql/mutations.model";
import { AuthService } from "../../../../../services/auth/auth.service"; import { AuthService } from "../../../../../services/auth/auth.service";
import { ChannelType, DiscordUser } from "../../../../../models/data/discord.model";
@Component({ @Component({
@ -54,6 +55,8 @@ export class SettingsComponent implements OnInit {
technicianIds: [] technicianIds: []
}; };
possibleTechnicians?: DiscordUser[];
constructor( constructor(
private dataService: DataService, private dataService: DataService,
private settingsService: SettingsService, private settingsService: SettingsService,
@ -76,7 +79,8 @@ export class SettingsComponent implements OnInit {
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
return throwError(() => error); return throwError(() => error);
})), })),
this.dataService.query<TechnicianConfigQuery>(Queries.technicianConfigQuery) this.dataService.query<TechnicianConfigQuery>(Queries.technicianConfigQuery),
this.dataService.query<SingleDiscordQuery>(Queries.discordUsersQuery)
]).subscribe(data => { ]).subscribe(data => {
this.data = data[0]; this.data = data[0];
this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? "0.0.0"; this.data.webVersion = this.settingsService.getWebVersion()?.getVersionString() ?? "0.0.0";
@ -86,6 +90,7 @@ export class SettingsComponent implements OnInit {
} }
this.config = data[1].technicianConfig; this.config = data[1].technicianConfig;
this.possibleTechnicians = data[2].discord.users ?? undefined;
this.initForms(); this.initForms();
this.spinnerService.hideSpinner(); this.spinnerService.hideSpinner();
}); });

View File

@ -19,10 +19,13 @@
<td style="flex: 1;"> <td style="flex: 1;">
<p-cellEditor> <p-cellEditor>
<ng-template pTemplate="input"> <ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="value.value"> <p-dropdown *ngIf="options" [options]="options" [optionLabel]="optionLabel ?? ''" [optionValue]="optionValue ?? ''" [(ngModel)]="value.value"></p-dropdown>
<input *ngIf="!options" class="table-edit-input" pInputText type="text" [(ngModel)]="value.value">
</ng-template> </ng-template>
<ng-template pTemplate="output"> <ng-template pTemplate="output">
{{value.value}} <p-dropdown *ngIf="options" [options]="options" [optionLabel]="optionLabel ?? ''" [optionValue]="optionValue ?? ''" [(ngModel)]="value.value" [disabled]="true"></p-dropdown>
<input *ngIf="!options" class="table-edit-input" pInputText type="text" [(ngModel)]="value.value" [disabled]="true">
</ng-template> </ng-template>
</p-cellEditor> </p-cellEditor>
</td> </td>

View File

@ -10,6 +10,9 @@ export class ConfigListComponent {
internal_data: any[] = []; internal_data: any[] = [];
@Input() translationKey: string = ""; @Input() translationKey: string = "";
@Input() options?: Array<any>;
@Input() optionLabel?: string = "";
@Input() optionValue?: string = "";
@Input() @Input()
set data(val: any[]) { set data(val: any[]) {

View File

@ -1,10 +1,11 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router"; import { ActivatedRoute, ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
import { catchError } from "rxjs/operators"; import { catchError } from "rxjs/operators";
import { AuthService } from "src/app/services/auth/auth.service"; import { AuthService } from "src/app/services/auth/auth.service";
import { ThemeService } from "src/app/services/theme/theme.service"; import { ThemeService } from "src/app/services/theme/theme.service";
import { SidebarService } from "../../../../services/sidebar/sidebar.service"; import { SidebarService } from "../../../../services/sidebar/sidebar.service";
import { MemberRoles } from "../../../../models/auth/auth-user.dto"; import { MemberRoles } from "../../../../models/auth/auth-user.dto";
import { DataService } from "../../../../services/data/data.service";
@Injectable({ @Injectable({
providedIn: "root" providedIn: "root"
@ -14,7 +15,9 @@ export class AuthGuard implements CanActivate {
private router: Router, private router: Router,
private authService: AuthService, private authService: AuthService,
private themeService: ThemeService, private themeService: ThemeService,
private sidebarService: SidebarService private sidebarService: SidebarService,
private data: DataService,
private route: ActivatedRoute
) { ) {
} }
@ -47,8 +50,13 @@ export class AuthGuard implements CanActivate {
if (memberRole !== undefined) { if (memberRole !== undefined) {
let userHasAccess = false; let userHasAccess = false;
let authUser = await this.authService.getLoggedInUser(); let authUser = await this.authService.getLoggedInUser();
let server = route.params["serverId"];
if (!authUser || !authUser.users) {
return true;
}
authUser?.users?.forEach(u => { authUser?.users?.forEach(u => {
if (u.server === +(this.sidebarService.server$.value?.id ?? 0)) { if (u.server === +(server ?? 0)) {
if ( if (
memberRole === MemberRoles.Moderator && u.isModerator || memberRole === MemberRoles.Moderator && u.isModerator ||
memberRole === MemberRoles.Admin && u.isAdmin || memberRole === MemberRoles.Admin && u.isAdmin ||

View File

@ -105,9 +105,11 @@ export class AutoRolesRulesComponent extends ComponentWithTable implements OnIni
filter: { filter: {
id: server.discordId id: server.discordId
} }
} },
).subscribe(data => { ).subscribe(data => {
this.guild = data.guilds[0]; if (data.discord.guilds) {
this.guild = data.discord.guilds[0];
}
this.emojis = this.guild.emojis this.emojis = this.guild.emojis
.map(x => { .map(x => {
return { label: x.name, value: x }; return { label: x.name, value: x };

View File

@ -77,7 +77,7 @@ export class AutoRolesComponent extends ComponentWithTable implements OnInit, On
private sidebar: SidebarService, private sidebar: SidebarService,
private route: ActivatedRoute private route: ActivatedRoute
) { ) {
super('auto-role', ['id', 'channel_id', 'channel_name', 'message_id', 'rule_count']) super("auto-role", ["id", "channel_id", "channel_name", "message_id", "rule_count"]);
} }
public ngOnInit(): void { public ngOnInit(): void {
@ -87,14 +87,16 @@ export class AutoRolesComponent extends ComponentWithTable implements OnInit, On
this.server = server; this.server = server;
this.spinner.showSpinner(); this.spinner.showSpinner();
this.data.query<SingleDiscordQuery>(Queries.guildsQuery, { this.data.query<SingleDiscordQuery>(Queries.guildsQuery, {
id: server?.discordId,
filter: { filter: {
id: server?.discordId type: ChannelType.text
} }
} }
).subscribe(data => { ).subscribe(data => {
this.guild = data.guilds[0]; if (data.discord.guilds) {
this.guild = data.discord.guilds[0];
}
this.channels = this.guild.channels this.channels = this.guild.channels
.filter(x => x.type === ChannelType.text)
.map(x => { .map(x => {
return { label: x.name, value: x }; return { label: x.name, value: x };
}); });