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_urlmaster
							parent
							
								
									3cb9084a69
								
							
						
					
					
						commit
						8a00c53479
					
				| 
						 | 
					@ -1,8 +1,10 @@
 | 
				
			||||||
 | 
					#from typing import cast, override
 | 
				
			||||||
from sqlalchemy.orm import mapped_column, relationship
 | 
					from sqlalchemy.orm import mapped_column, relationship
 | 
				
			||||||
from sqlalchemy.orm.attributes import Mapped
 | 
					from sqlalchemy.orm.attributes import Mapped
 | 
				
			||||||
from sqlalchemy.orm.properties import ForeignKey
 | 
					from sqlalchemy.orm.properties import ForeignKey
 | 
				
			||||||
from sqlalchemy.types import Integer, String
 | 
					from sqlalchemy.types import Integer, String
 | 
				
			||||||
import app_db
 | 
					import app_db
 | 
				
			||||||
 | 
					import spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TeamIntegration(app_db.BaseModel):
 | 
					class TeamIntegration(app_db.BaseModel):
 | 
				
			||||||
| 
						 | 
					@ -23,10 +25,35 @@ class TeamDiscordIntegration(TeamIntegration):
 | 
				
			||||||
    __tablename__ = "team_discord_integrations"
 | 
					    __tablename__ = "team_discord_integrations"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    integration_id: Mapped[int] = mapped_column(ForeignKey("team_integrations.id"), primary_key=True)
 | 
					    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__ = {
 | 
					    __mapper_args__ = {
 | 
				
			||||||
        "polymorphic_identity": "team_discord_integrations",
 | 
					        "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
 | 
					from models.team import Team
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ from models.player_team_availability import PlayerTeamAvailability
 | 
				
			||||||
from models.player_team_role import PlayerTeamRole, RoleSchema
 | 
					from models.player_team_role import PlayerTeamRole, RoleSchema
 | 
				
			||||||
from models.team import Team, TeamSchema
 | 
					from models.team import Team, TeamSchema
 | 
				
			||||||
from models.team_invite import TeamInvite, TeamInviteSchema
 | 
					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
 | 
					from middleware import assert_team_authority, requires_authentication, requires_team_membership
 | 
				
			||||||
import models
 | 
					import models
 | 
				
			||||||
from spec import spec, BaseModel
 | 
					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.delete(invite)
 | 
				
			||||||
    db.session.commit()
 | 
					    db.session.commit()
 | 
				
			||||||
    return make_response({ }, 204)
 | 
					    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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue