Add team integration endpoints

- Add new endpoints for managing team integrations:
  - GET /id/<team_id>/integrations
  - POST /id/<team_id>/integrations/<integration_type>
  - DELETE /id/<team_id>/integrations/<integration_id>
  - PATCH /id/<team_id>/integrations/<integration_id>
- Introduce schemas for TeamIntegration and TeamDiscordIntegration
- Update models to include nullable webhook_url
master
John Montagu, the 4th Earl of Sandvich 2024-11-18 18:28:24 -08:00
parent 3cb9084a69
commit 8a00c53479
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
2 changed files with 155 additions and 1 deletions

View File

@ -1,8 +1,10 @@
#from typing import cast, override
from sqlalchemy.orm import mapped_column, relationship
from sqlalchemy.orm.attributes import Mapped
from sqlalchemy.orm.properties import ForeignKey
from sqlalchemy.types import Integer, String
import app_db
import spec
class TeamIntegration(app_db.BaseModel):
@ -23,10 +25,35 @@ class TeamDiscordIntegration(TeamIntegration):
__tablename__ = "team_discord_integrations"
integration_id: Mapped[int] = mapped_column(ForeignKey("team_integrations.id"), primary_key=True)
webhook_url: Mapped[str] = mapped_column(String(255))
webhook_url: Mapped[str] = mapped_column(String(255), nullable=True)
__mapper_args__ = {
"polymorphic_identity": "team_discord_integrations",
}
class TeamIntegrationSchema(spec.BaseModel):
id: int
team_id: int
integration_type: str
@classmethod
def from_model(cls, model: TeamIntegration):
if model.integration_type == "team_discord_integrations":
if isinstance(model, TeamDiscordIntegration):
return TeamDiscordIntegrationSchema._from_model_discord(model)
raise TypeError()
class TeamDiscordIntegrationSchema(TeamIntegrationSchema):
webhook_url: str
@classmethod
def _from_model_discord(cls, model: TeamDiscordIntegration):
assert model.integration_id != None
return cls(
id=model.integration_id,
team_id=model.team_id,
integration_type=model.integration_type,
webhook_url=model.webhook_url
)
from models.team import Team

View File

@ -14,6 +14,7 @@ from models.player_team_availability import PlayerTeamAvailability
from models.player_team_role import PlayerTeamRole, RoleSchema
from models.team import Team, TeamSchema
from models.team_invite import TeamInvite, TeamInviteSchema
from models.team_integration import TeamDiscordIntegration, TeamDiscordIntegrationSchema, TeamIntegration, TeamIntegrationSchema
from middleware import assert_team_authority, requires_authentication, requires_team_membership
import models
from spec import spec, BaseModel
@ -561,3 +562,129 @@ def revoke_invite(player: Player, team_id: int, key: str, **kwargs):
db.session.delete(invite)
db.session.commit()
return make_response({ }, 204)
@api_team.get("/id/<team_id>/integrations")
@spec.validate(
resp=Response(
HTTP_200=list[TeamIntegrationSchema],
HTTP_404=None,
),
operation_id="get_integrations"
)
@requires_authentication
def get_integrations(player: Player, team_id: int, **kwargs):
player_team = db.session.query(
PlayerTeam
).where(
PlayerTeam.player_id == player.steam_id
).where(
PlayerTeam.team_id == team_id
).one_or_none()
if not player_team:
abort(404)
integrations = db.session.query(
TeamIntegration
).where(
TeamIntegration.team_id == team_id
).all()
def map_integration_to_schema(integration: TeamIntegration):
return TeamIntegrationSchema.from_model(
integration
).dict(by_alias=True)
return list(map(map_integration_to_schema, integrations))
@api_team.post("/id/<team_id>/integrations/<integration_type>")
@spec.validate(
resp=Response(
HTTP_200=TeamIntegrationSchema,
),
operation_id="create_integration"
)
@requires_authentication
@requires_team_membership
def create_integration(player_team: PlayerTeam, integration_type: str, **_):
assert_team_authority(player_team)
if integration_type == "discord":
integration = TeamDiscordIntegration()
integration.team_id = player_team.team_id
integration.webhook_url = ""
else:
abort(404)
db.session.add(integration)
db.session.commit()
return TeamIntegrationSchema.from_model(
integration
).dict(by_alias=True), 200
@api_team.delete("/id/<team_id>/integrations/<integration_id>")
@spec.validate(
resp=Response(
HTTP_204=None,
),
operation_id="delete_integration"
)
def delete_integration(player_team: PlayerTeam, integration_id: int):
assert_team_authority(player_team)
integration = db.session.query(
TeamIntegration
).where(
TeamIntegration.team_id == player_team.team_id
).where(
TeamIntegration.id == integration_id
).one_or_none()
if not integration:
abort(404)
db.session.delete(integration)
db.session.commit()
return make_response({ }, 204)
@api_team.patch("/id/<team_id>/integrations/<integration_id>")
@spec.validate(
resp=Response(
HTTP_200=TeamIntegrationSchema,
),
operation_id="update_integration"
)
def update_integration(
player_team: PlayerTeam,
integration_id: int,
json: TeamIntegrationSchema,
**_
):
assert_team_authority(player_team)
integration = db.session.query(
TeamIntegration
).where(
TeamIntegration.team_id == player_team.team_id
).where(
TeamIntegration.id == integration_id
).one_or_none()
if not integration:
abort(404)
if isinstance(integration, TeamDiscordIntegration):
if isinstance(json, TeamDiscordIntegrationSchema):
integration.webhook_url = json.webhook_url
else:
abort(400)
else:
abort(404)
db.session.commit()
return TeamIntegrationSchema.from_model(
integration
).dict(by_alias=True), 200