示例#1
0
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
示例#2
0
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}"
    )
示例#3
0
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)
示例#4
0
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())
示例#5
0
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())
示例#6
0
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
示例#7
0
文件: __main__.py 项目: RYGhub/ryglfg
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)
示例#8
0
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)
示例#9
0
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,
    )
示例#10
0
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,
            )
        ],
    )
示例#11
0
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()
示例#12
0
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)
示例#13
0
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)
示例#14
0
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)
示例#15
0
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,
            )
        ],
    )
示例#16
0
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
示例#17
0
文件: __main__.py 项目: RYGhub/ryglfg
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
示例#18
0
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
示例#19
0
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
示例#20
0
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)
示例#21
0
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)
示例#22
0
文件: __main__.py 项目: RYGhub/ryglfg
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
示例#23
0
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)
示例#24
0
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
示例#25
0
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
示例#26
0
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
示例#27
0
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)
示例#28
0
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)
示例#29
0
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
示例#30
0
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)