async def repository_queues( owner: github_types.GitHubLogin = fastapi.Path( # noqa: B008 ..., description="The owner of the repository"), repository: github_types.GitHubRepositoryName = fastapi.Path( # noqa: B008 ..., description="The name of the repository"), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context), ) -> Queues: queues = Queues() async for train in merge_train.Train.iter_trains(repository_ctxt): queue_rules = await train.get_queue_rules() if queue_rules is None: # The train is going the be deleted, so skip it. continue queue = Queue(Branch(train.ref)) for position, (embarked_pull, car) in enumerate(train._iter_embarked_pulls()): if car is None: speculative_check_pull_request = None elif car.creation_state in ["created", "updated"]: if car.queue_pull_request_number is None: raise RuntimeError( f"car state is {car.creation_state}, but queue_pull_request_number is None" ) speculative_check_pull_request = SpeculativeCheckPullRequest( in_place=car.creation_state == "updated", number=car.queue_pull_request_number, started_at=car.creation_date, ended_at=car.checks_ended_timestamp, state=car.checks_conclusion.value or "pending", checks=car.last_checks, evaluated_conditions=car.last_evaluated_conditions, ) elif car.creation_state in ("failed", "pending"): speculative_check_pull_request = None else: raise RuntimeError( f"Car creation state unknown: {car.creation_state}") try: queue_rule = queue_rules[embarked_pull.config["name"]] except KeyError: # This car is going to be deleted so skip it continue queue.pull_requests.append( PullRequestQueued( embarked_pull.user_pull_request_number, position, embarked_pull.config["priority"], QueueRule(name=embarked_pull.config["name"], config=queue_rule.config), embarked_pull.queued_at, speculative_check_pull_request, )) queues.queues.append(queue) return queues
async def badge( owner: github_types.GitHubLogin = fastapi.Path( # noqa: B008 ..., description="The owner of the repository" ), repository: github_types.GitHubRepositoryName = fastapi.Path( # noqa: B008 ..., description="The name of the repository" ), ) -> responses.RedirectResponse: return responses.RedirectResponse( url=f"https://dashboard.mergify.com/badges/{owner}/{repository}" )
async def badge_svg( owner: github_types.GitHubLogin = fastapi.Path( # noqa: B008 ..., description="The owner of the repository" ), repository: github_types.GitHubRepositoryName = fastapi.Path( # noqa: B008 ..., description="The name of the repository" ), style: str = fastapi.Query( # noqa: B008 default="flat", description="The style of the button, more details on https://shields.io/.", ), ) -> responses.RedirectResponse: # pragma: no cover return _get_badge_url(owner, repository, "svg", style)
def get_by_action( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), action: str = f.Path( ..., description= "The action to get audit logs about. Uses SQL 'like' syntax. Case " "insensitive."), limit: int = f.Query( 500, description="The number of objects that will be returned.", ge=0), offset: int = f.Query( 0, description= "The starting object from which the others will be returned.", ge=0), order: models.enums.TimestampOrdering = f.Query( models.enums.TimestampOrdering.ANY, description="The order you want the objects to be returned in."), ): """ Get an array of the audit logs that match the passed `action` pattern, in pages of `limit` elements and starting at the element number `offset`. To avoid denial of service attacks, `limit` cannot be greater than 500. """ return (ordered(ls.session.query(tables.AuditLog).filter( tables.AuditLog.action.ilike(action)), tables.AuditLog.timestamp, order=order).limit(limit).offset(offset).all())
def get_by_user( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), user_id: int = f.Path( ..., description="The id of the user to get audit logs about."), limit: int = f.Query( 500, description="The number of objects that will be returned.", ge=0), offset: int = f.Query( 0, description= "The starting object from which the others will be returned.", ge=0), order: models.enums.TimestampOrdering = f.Query( models.enums.TimestampOrdering.ANY, description="The order you want the objects to be returned in."), ): """ Get an array of the audit logs in which the specified user is involved, in pages of `limit` elements and starting at the element number `offset`. To avoid denial of service attacks, `limit` cannot be greater than 500. """ user = ls.get(tables.User, user_id) return (ordered(ls.session.query(tables.AuditLog).filter_by(user=user), tables.AuditLog.timestamp, order=order).limit(limit).offset(offset).all())
async def user_auth_message( message_id: uuid.UUID = fastapi.Path(...), auth: dto_models.AuthUser = fastapi.Depends(refs.UserAuthProto), database: sql_api.DatabaseHandler = fastapi.Depends(refs.DatabaseProto), ) -> dao_protos.Message: if stored_message := await database.get_message(message_id, auth.id): return stored_message
def lfg_delete( *, ls: LoginSession = f.Depends(dep_loginsession), aid: int = f.Path(..., description="The aid of the LFG to delete."), ): """ Quietly delete a LFG without triggering any webhook or notification. Follows the `DELETE` specification: it will return a success even if the LFG does not exist. Requires the `delete:lfg_admin` scope. """ if "delete:lfg_admin" not in ls.cu.permissions: raise f.HTTPException(403, "Missing `delete:lfg_admin` scope.") lfg = ls.session.execute( ss.select(database.Announcement).where(database.Announcement.aid == aid) ).scalar() if lfg is not None: ls.session.delete(lfg) ls.session.commit() if lfg.state == database.AnnouncementState.PLANNED: planned_event.set() elif lfg.state == database.AnnouncementState.OPEN: open_event.set() return f.Response(status_code=204)
async def delete_queue_freeze( application: application_mod.Application = fastapi.Depends( # noqa: B008 security.get_application ), queue_name: rules.QueueName = fastapi.Path( # noqa: B008 ..., description="The name of the queue" ), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), ) -> fastapi.Response: qf = freeze.QueueFreeze( repository=repository_ctxt, name=queue_name, application_name=application.name, application_id=application.id, ) if not await qf.delete(): raise fastapi.HTTPException( status_code=404, detail=f'The queue "{queue_name}" does not exist or is not currently frozen.', ) return fastapi.Response(status_code=HTTP_204_NO_CONTENT)
async def simulator_pull( body: SimulatorPayload, # noqa: B008 repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), number: int = fastapi.Path( # noqa: B008 ..., description="The pull request number" ), ) -> SimulatorResponse: config = body.get_config() try: ctxt = await repository_ctxt.get_pull_request_context( github_types.GitHubPullRequestNumber(number) ) except http.HTTPClientSideError as e: raise fastapi.HTTPException(status_code=e.status_code, detail=e.message) ctxt.sources = [{"event_type": "mergify-simulator", "data": [], "timestamp": ""}] # type: ignore[typeddict-item] match = await config["pull_request_rules"].get_pull_request_rule(ctxt) title, summary = await actions_runner.gen_summary( ctxt, config["pull_request_rules"], match ) return SimulatorResponse( title=title, summary=summary, )
async def get_queue_freeze( queue_name: rules.QueueName = fastapi.Path( # noqa: B008 ..., description="The name of the queue" ), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), ) -> QueueFreezeResponse: qf = await freeze.QueueFreeze.get(repository_ctxt, queue_name) if qf is None: raise fastapi.HTTPException( status_code=404, detail=f'The queue "{queue_name}" does not exist or is not currently frozen.', ) return QueueFreezeResponse( queue_freezes=[ QueueFreeze( name=qf.name, reason=qf.reason, application_name=qf.application_name, application_id=qf.application_id, freeze_date=qf.freeze_date, ) ], )
async def get_user( request: fastapi.Request, _id: OID = fastapi.Path(...), users_handler: UsersHandler = fastapi.Depends(UsersHandler), ): result = await users_handler.retrieve_user(request=request, query={"_id": _id}) return result.dict()
async def get_shared_message( _: dto_models.LinkAuth = fastapi.Depends(refs.LinkAuthProto), message_id: uuid.UUID = fastapi.Path(...), database: sql_api.DatabaseHandler = fastapi.Depends(refs.DatabaseProto), metadata: utilities.Metadata = fastapi.Depends(utilities.Metadata), ) -> dto_models.Message: if message := await database.get_message(message_id): return await get_message(message, database, metadata)
def get_single( ls: dependencies.LoginSession = f.Depends(dependencies.dependency_login_session), song_id: int = f.Path(..., description="The id of the song to be retrieved.") ): """ Get full information for the song with the specified `song_id`. """ return ls.get(tables.Song, song_id)
async def update_user( request: fastapi.Request, _id: OID = fastapi.Path(...), update: UserUpdateSchema = fastapi.Body(...), users_handler: UsersHandler = fastapi.Depends(UsersHandler), ): return await users_handler.update_user(request=request, _id=_id, update=update)
async def create_queue_freeze( queue_freeze_payload: QueueFreezePayload, application: application_mod.Application = fastapi.Depends( # noqa: B008 security.get_application ), queue_name: rules.QueueName = fastapi.Path( # noqa: B008 ..., description="The name of the queue" ), repository_ctxt: context.Repository = fastapi.Depends( # noqa: B008 security.get_repository_context ), ) -> QueueFreezeResponse: if queue_freeze_payload.reason == "": queue_freeze_payload.reason = "No freeze reason was specified." config_file = await repository_ctxt.get_mergify_config_file() if config_file is None: raise fastapi.HTTPException( status_code=404, detail="Mergify configuration file is missing." ) config = get_mergify_config(config_file) queue_rules = config["queue_rules"] if all(queue_name != rule.name for rule in queue_rules): raise fastapi.HTTPException( status_code=404, detail=f'The queue "{queue_name}" does not exist.' ) qf = await freeze.QueueFreeze.get(repository_ctxt, queue_name) if qf is None: qf = freeze.QueueFreeze( repository=repository_ctxt, name=queue_name, reason=queue_freeze_payload.reason, application_name=application.name, application_id=application.id, freeze_date=date.utcnow(), ) await qf.save() elif qf.reason != queue_freeze_payload.reason: qf.reason = queue_freeze_payload.reason await qf.save() return QueueFreezeResponse( queue_freezes=[ QueueFreeze( name=qf.name, reason=qf.reason, application_name=qf.application_name, application_id=qf.application_id, freeze_date=qf.freeze_date, ) ], )
async def put_message_view( device_name: str = fastapi.Path(..., min_length=validation.MINIMUM_NAME_LENGTH, max_length=validation.MAXIMUM_NAME_LENGTH), message: dao_protos.Message = fastapi.Depends(user_auth_message), auth: dto_models.AuthUser = fastapi.Depends(refs.UserAuthProto), database: sql_api.DatabaseHandler = fastapi.Depends(refs.DatabaseProto), ) -> typing.Union[dto_models.View, fastapi.Response]: if not (device := await database.get_device_by_name(auth.id, device_name)): raise fastapi.exceptions.HTTPException( 404, detail="Device not found.") from None
def lfg_answer_put( *, ls: LoginSession = f.Depends(dep_loginsession), fr: f.Response, aid: int = f.Path(..., description="The id of the LFG that should be answered."), user: t.Optional[str] = f.Query(None, description="The id of the user you are answering on behalf of."), data: models.ResponseEditable = f.Body(..., description="The data of the response."), ): """ Respond to a LFG, or edit the response if it had already been sent, sending notifications via the webhooks. Requires the `answer:lfg` scope. Additionally requires the `answer:lfg_sudo` scope if you are answering on behalf of another user. """ if "answer:lfg" not in ls.cu.permissions: raise f.HTTPException(403, "Missing `answer:lfg` scope.") if user is None: user = ls.cu.sub if "answer:lfg_sudo" not in ls.cu.permissions and user != ls.cu.sub: raise f.HTTPException(403, "Missing `answer:lfg_sudo` scope.") response = ls.session.execute( ss.select(database.Response).where( ss.and_( database.Response.aid == aid, database.Response.partecipant_id == ls.cu.sub, ) ) ).scalar() if response is None: # noinspection PyArgumentList response = database.Response(**data.dict(), aid=aid, partecipant_id=user) ls.session.add(response) fr.status_code = 201 else: response.update(**data.dict()) fr.status_code = 200 ls.session.commit() send_message(ls.session, models.EventResponse( type="answer", code=fr.status_code, response=models.ResponseFull.from_orm(response), ).json()) return response
def edit_single( ls: dependencies.LoginSession = f.Depends(dependencies.dependency_login_session), song_id: int = f.Path(..., description="The id of the song to be edited."), data: models.SongInput = f.Body(..., description="The new data the song should have."), ): """ Replace the data of the song with the specified `song_id` with the data passed in the request body. """ song = ls.get(tables.Song, song_id) song.update(**data.dict()) ls.log("song.edit.single", obj=song.id) ls.session.commit() return song
async def get_repository_context( owner: github_types.GitHubLogin = fastapi.Path( # noqa: B008 ..., description="The owner of the repository"), repository: github_types.GitHubRepositoryName = fastapi.Path( # noqa: B008 ..., description="The name of the repository"), redis_links: redis_utils.RedisLinks = fastapi.Depends( # noqa: B008 redis.get_redis_links), installation_json: github_types.GitHubInstallation = fastapi. Depends( # noqa: B008 get_installation), ) -> typing.AsyncGenerator[context.Repository, None]: async with github.aget_client(installation_json) as client: try: # Check this token has access to this repository repo = typing.cast( github_types.GitHubRepository, await client.item(f"/repos/{owner}/{repository}"), ) except (http.HTTPNotFound, http.HTTPForbidden, http.HTTPUnauthorized): raise fastapi.HTTPException(status_code=404) sub = await subscription.Subscription.get_subscription( redis_links.cache, installation_json["account"]["id"]) installation = context.Installation(installation_json, sub, client, redis_links) repository_ctxt = installation.get_repository_from_github_data(repo) # NOTE(sileht): Since this method is used as fastapi Depends only, it's safe to set this # for the ongoing http request sentry_sdk.set_user( {"username": repository_ctxt.installation.owner_login}) sentry_sdk.set_tag("gh_owner", repository_ctxt.installation.owner_login) sentry_sdk.set_tag("gh_repo", repository_ctxt.repo["name"]) yield repository_ctxt
def delete( ls: dependencies.LoginSession = f.Depends(dependencies.dependency_login_session), song_id: int = f.Path(..., description="The id of the song to be deleted.") ): """ Delete the song having the specified `song_id`. Note that the contained layers will not be deleted; they will become orphaned instead. """ song = ls.get(tables.Song, song_id) ls.session.delete(song) ls.log("song.delete", obj=song.id) ls.session.commit() return f.Response(status_code=204)
def delete(ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), layer_id: int = f.Path( ..., description="The id of the layer to be deleted.")): """ Delete the layer having the specified `layer_id`. Note that the associated file **WON'T** be deleted, it will instead become orphaned and unable to be used. """ layer = ls.get(tables.Layer, layer_id) ls.session.delete(layer) ls.log("layer.delete", obj=layer.id) ls.session.commit() return f.Response(status_code=204)
def lfg_cancel( *, ls: LoginSession = f.Depends(dep_loginsession), aid: int = f.Path(..., description="The id of the LFG that you want to cancel."), user: t.Optional[str] = f.Query(None, description="The id of the user you are acting on behalf of."), ): """ Cancel a LFG, sending notifications via the webhooks. Requires the `cancel:lfg` scope. Additionally requires the `cancel:lfg_sudo` if you're acting on behalf of another user. Additionally requires the `cancel:lfg_admin` if you're trying to start another user's LFG. """ if "cancel:lfg" not in ls.cu.permissions: raise f.HTTPException(403, "Missing `cancel:lfg` scope.") if user is None: user = ls.cu.sub if "cancel:lfg_sudo" not in ls.cu.permissions and user != ls.cu.sub: raise f.HTTPException(403, "Missing `cancel:lfg_sudo` scope.") lfg = ls.session.execute( ss.select(database.Announcement).where(database.Announcement.aid == aid) ).scalar() if lfg is None: raise f.HTTPException(404, "No such LFG.") if "cancel:lfg_admin" not in ls.cu.permissions and user != lfg.creator_id: raise f.HTTPException(403, "Missing `cancel:lfg_admin` scope.") if lfg.state > database.AnnouncementState.OPEN: raise f.HTTPException(409, "LFG has already closed.") lfg.state = database.AnnouncementState.CANCELLED lfg.closure_time = datetime.datetime.now(tz=datetime.timezone.utc) lfg.closure_id = user ls.session.commit() send_message(ls.session, models.EventAnnouncement( type="cancel", announcement=models.AnnouncementFull.from_orm(lfg), ).json()) return lfg
def delete( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), genre_id: int = f.Path(..., description="The id of the genre to be deleted."), ): """ Delete the genre having the specified `genre_id`, also disconnecting all songs from it. """ genre = ls.get(tables.Genre, genre_id) ls.session.delete(genre) ls.log("genre.delete", obj=genre.id) ls.session.commit() return f.Response(status_code=204)
def edit_single( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), layer_id: int = f.Path(..., description="The id of the layer to be edited."), data: models.LayerInput = f.Body( ..., description="The new data the layer should have."), ): """ Replace the data of the layer with the specified `layer_id` with the data passed in the request body. """ layer = ls.get(tables.Layer, layer_id) layer.update(**data.__dict__) ls.log("layer.edit.single", obj=layer.id) ls.session.commit() return layer
def edit_single( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), album_id: int = f.Path(..., description="The id of the album to be edited."), data: models.AlbumInput = f.Body( ..., description="The new data the album should have."), ): """ Replace the data of the album with the specified `album_id` with the data passed in the request body. """ album = ls.get(tables.Album, album_id) album.update(**data.__dict__) ls.log("album.edit.single", obj=album.id) ls.session.commit() return album
def edit_single( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), genre_id: int = f.Path(..., description="The id of the genre to be edited."), data: models.GenreInput = f.Body( ..., description="The new data the genre should have."), ): """ Replace the data of the genre with the specified `genre_id` with the data passed in the request body. """ genre = ls.get(tables.Genre, genre_id) genre.update(**data.dict()) ls.log("genre.edit.single", obj=genre.id) ls.session.commit() return genre
def delete( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), person_id: int = f.Path(..., description="The id of the person to be edited."), ): """ Delete the person having the specified `album_id`. All their involvements will be deleted. """ person = ls.get(tables.Person, person_id) ls.session.delete(person) ls.log("person.delete", obj=person.id) ls.session.commit() return f.Response(status_code=204)
async def download(ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), layer_id: int = f.Path( ..., description="The id of the layer to be downloaded.")): """ Download a single raw file from the database, without any tags applied. """ layer = ls.get(tables.Layer, layer_id) if layer.file is None: raise f.HTTPException(404, "Layer doesn't have an associated file.") if not os.path.exists(layer.file.location): raise f.HTTPException(404, "File doesn't exist on the server filesystem.") return starlette.responses.FileResponse(layer.file.location, media_type=layer.file.mime_type)
def edit_single( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), person_id: int = f.Path(..., description="The id of the person to be edited."), data: models.PersonInput = f.Body( ..., description="The new data the person should have."), ): """ Replace the data of the person with the specified `person_id` with the data passed in the request body. """ person = ls.get(tables.Person, person_id) person.update(**data.__dict__) ls.log("person.edit.single", obj=person.id) ls.session.commit() return person
def delete( ls: dependencies.LoginSession = f.Depends( dependencies.dependency_login_session), album_id: int = f.Path(..., description="The id of the album to be deleted."), ): """ Delete the album having the specified `album_id`. Note that the contained songs will not be deleted; they will become orphaned instead. """ album = ls.get(tables.Album, album_id) ls.session.delete(album) ls.log("album.delete", obj=album.id) ls.session.commit() return f.Response(status_code=204)