Exemple #1
0
def get_assignees(payload: WildValue) -> Union[List[WildValue], WildValue]:
    assignee_details = payload.get("assignees")
    if not assignee_details:
        single_assignee_details = payload.get("assignee")
        if not single_assignee_details:
            transformed_assignee_details = []
        else:
            transformed_assignee_details = [single_assignee_details]
        return transformed_assignee_details
    return assignee_details
Exemple #2
0
def get_push_tag_body(payload: WildValue, change: WildValue) -> str:
    if change.get("new"):
        tag = change["new"]
        action = "pushed"
    elif change.get("old"):
        tag = change["old"]
        action = "removed"

    return get_push_tag_event_message(
        get_actor_info(payload),
        tag["name"].tame(check_string),
        tag_url=tag["links"]["html"]["href"].tame(check_string),
        action=action,
    )
Exemple #3
0
def get_event(request: HttpRequest, payload: WildValue,
              branches: Optional[str]) -> Optional[str]:
    event = validate_extract_webhook_http_header(request, "X_GITLAB_EVENT",
                                                 "GitLab")
    if event == "System Hook":
        # Convert the event name to a GitLab event title
        event_name = payload.get("event_name",
                                 payload["object_kind"]).tame(check_string)
        event = event_name.split("__")[0].replace("_", " ").title()
        event = f"{event} Hook"
    if event in [
            "Confidential Issue Hook", "Issue Hook", "Merge Request Hook",
            "Wiki Page Hook"
    ]:
        action = payload["object_attributes"].get("action",
                                                  "open").tame(check_string)
        event = f"{event} {action}"
    elif event in ["Confidential Note Hook", "Note Hook"]:
        action = payload["object_attributes"]["noteable_type"].tame(
            check_string)
        event = f"{event} {action}"
    elif event == "Push Hook":
        if branches is not None:
            branch = get_branch_name(payload)
            if branches.find(branch) == -1:
                return None

    if event in list(EVENT_FUNCTION_MAPPER.keys()):
        return event

    raise UnsupportedWebhookEventType(event)
Exemple #4
0
def get_push_event_body(payload: WildValue) -> str:
    after = payload.get("after")
    if after:
        stringified_after = after.tame(check_string)
        if stringified_after == EMPTY_SHA:
            return get_remove_branch_event_body(payload)
    return get_normal_push_event_body(payload)
Exemple #5
0
def render_block(block: WildValue) -> str:
    # https://api.slack.com/reference/block-kit/blocks
    block_type = block["type"].tame(
        check_string_in([
            "actions", "context", "divider", "header", "image", "input",
            "section"
        ]))
    if block_type == "actions":
        # Unhandled
        return ""
    elif block_type == "context" and block.get("elements"):
        pieces = []
        # Slack renders these pieces left-to-right, packed in as
        # closely as possible.  We just render them above each other,
        # for simplicity.
        for element in block["elements"]:
            element_type = element["type"].tame(
                check_string_in(["image", "plain_text", "mrkdwn"]))
            if element_type == "image":
                pieces.append(render_block_element(element))
            else:
                pieces.append(element.tame(check_text_block()))
        return "\n\n".join(piece.strip() for piece in pieces
                           if piece.strip() != "")
    elif block_type == "divider":
        return "----"
    elif block_type == "header":
        return "## " + block["text"].tame(
            check_text_block(plain_text_only=True))
    elif block_type == "image":
        image_url = block["image_url"].tame(check_url)
        alt_text = block["alt_text"].tame(check_string)
        if "title" in block:
            alt_text = block["title"].tame(
                check_text_block(plain_text_only=True))
        return f"[{alt_text}]({image_url})"
    elif block_type == "input":
        # Unhandled
        pass
    elif block_type == "section":
        pieces = []
        if "text" in block:
            pieces.append(block["text"].tame(check_text_block()))

        if "accessory" in block:
            pieces.append(render_block_element(block["accessory"]))

        if "fields" in block:
            # TODO -- these should be rendered in two columns,
            # left-to-right.  We could render them sequentially,
            # except some may be Title1 / Title2 / value1 / value2,
            # which would be nonsensical when rendered sequentially.
            pass

        return "\n\n".join(piece.strip() for piece in pieces
                           if piece.strip() != "")

    return ""
Exemple #6
0
def api_bitbucket_webhook(
    request: HttpRequest,
    user_profile: UserProfile,
    payload: WildValue = REQ(converter=to_wild_value),
    branches: Optional[str] = REQ(default=None),
) -> HttpResponse:
    repository = payload["repository"]

    commits = [
        {
            "name": commit["author"].tame(check_string)
            if "author" in commit
            else payload.get("user", "Someone").tame(check_string),
            "sha": commit["raw_node"].tame(check_string),
            "message": commit["message"].tame(check_string),
            "url": "{}{}commits/{}".format(
                payload["canon_url"].tame(check_string),
                repository["absolute_url"].tame(check_string),
                commit["raw_node"].tame(check_string),
            ),
        }
        for commit in payload["commits"]
    ]

    if len(commits) == 0:
        # Bitbucket doesn't give us enough information to really give
        # a useful message :/
        subject = repository["name"].tame(check_string)
        content = "{} [force pushed]({}).".format(
            payload.get("user", "Someone").tame(check_string),
            payload["canon_url"].tame(check_string) + repository["absolute_url"].tame(check_string),
        )
    else:
        branch = payload["commits"][-1]["branch"].tame(check_string)
        if branches is not None and branches.find(branch) == -1:
            return json_success(request)

        committer = payload.get("user", "Someone").tame(check_string)
        content = get_push_commits_event_message(committer, None, branch, commits)
        subject = TOPIC_WITH_BRANCH_TEMPLATE.format(
            repo=repository["name"].tame(check_string), branch=branch
        )

    check_send_webhook_message(request, user_profile, subject, content, unquote_url_parameters=True)
    return json_success(request)
Exemple #7
0
def extract_friendly_name(payload: WildValue) -> str:
    tentative_job_name = payload.get("friendly_name", "").tame(check_string)
    if not tentative_job_name:
        url = payload["url"].tame(check_string)
        segments = url.split("/")
        tentative_job_name = segments[-3]
        if tentative_job_name == "jobs":
            tentative_job_name = "Job"
    return tentative_job_name
Exemple #8
0
def get_update_name_body(payload: WildValue, action: WildValue, entity: str) -> str:
    name = action["changes"]["name"]
    kwargs = {
        "entity": entity,
        "new": name["new"].tame(check_string),
        "old": name["old"].tame(check_string),
        "name_template": get_name_template(entity).format(
            name=action["name"].tame(check_string),
            app_url=action.get("app_url").tame(check_none_or(check_string)),
        ),
    }

    return NAME_CHANGED_TEMPLATE.format(**kwargs)
Exemple #9
0
def get_comment_added_body(payload: WildValue, action: WildValue, entity: str) -> str:
    actions = payload["actions"]
    kwargs = {"entity": entity}
    for action in actions:
        if action["id"] == payload["primary_id"]:
            kwargs["text"] = action["text"].tame(check_string)
        elif action["entity_type"] == entity:
            name_template = get_name_template(entity).format(
                name=action["name"].tame(check_string),
                app_url=action.get("app_url").tame(check_none_or(check_string)),
            )
            kwargs["name_template"] = name_template

    return COMMENT_ADDED_TEMPLATE.format(**kwargs)
Exemple #10
0
def get_subject_based_on_type(payload: WildValue, event: str) -> str:
    if "pull_request" in event:
        return TOPIC_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
            repo=get_repository_name(payload),
            type="PR",
            id=payload["pull_request"]["number"].tame(check_int),
            title=payload["pull_request"]["title"].tame(check_string),
        )
    elif event.startswith("issue"):
        return TOPIC_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
            repo=get_repository_name(payload),
            type="issue",
            id=payload["issue"]["number"].tame(check_int),
            title=payload["issue"]["title"].tame(check_string),
        )
    elif event.startswith("deployment"):
        return "{} / Deployment on {}".format(
            get_repository_name(payload),
            payload["deployment"]["environment"].tame(check_string),
        )
    elif event == "membership":
        return "{} organization".format(
            payload["organization"]["login"].tame(check_string))
    elif event == "team":
        return "team {}".format(payload["team"]["name"].tame(check_string))
    elif event == "push_commits":
        return TOPIC_WITH_BRANCH_TEMPLATE.format(
            repo=get_repository_name(payload),
            branch=get_branch_name_from_ref(payload["ref"].tame(check_string)),
        )
    elif event == "gollum":
        return TOPIC_WITH_BRANCH_TEMPLATE.format(
            repo=get_repository_name(payload),
            branch="wiki pages",
        )
    elif event == "ping":
        if not payload.get("repository"):
            return get_organization_name(payload)
    elif event == "check_run":
        return f"{get_repository_name(payload)} / checks"
    elif event.startswith("discussion"):
        return TOPIC_FOR_DISCUSSION.format(
            repo=get_repository_name(payload),
            number=payload["discussion"]["number"].tame(check_int),
            title=payload["discussion"]["title"].tame(check_string),
        )

    return get_repository_name(payload)
Exemple #11
0
def get_update_archived_body(payload: WildValue, action: WildValue, entity: str) -> str:
    archived = action["changes"]["archived"]
    if archived["new"]:
        operation = "archived"
    else:
        operation = "unarchived"

    kwargs = {
        "entity": entity,
        "name_template": get_name_template(entity).format(
            name=action["name"].tame(check_string),
            app_url=action.get("app_url").tame(check_none_or(check_string)),
        ),
        "operation": operation,
    }

    return ARCHIVED_TEMPLATE.format(**kwargs)
Exemple #12
0
def get_story_joined_label_list(
    payload: WildValue, action: WildValue, label_ids_added: List[int]
) -> str:
    labels = []

    for label_id in label_ids_added:
        label_name = ""

        for action in payload["actions"]:
            if action["id"].tame(check_int) == label_id:
                label_name = action.get("name", "").tame(check_string)

        if label_name == "":
            reference = get_reference_by_id(payload, label_id)
            label_name = "" if reference is None else reference["name"].tame(check_string)

        labels.append(LABEL_TEMPLATE.format(name=label_name))

    return ", ".join(labels)
Exemple #13
0
def get_update_description_body(payload: WildValue, action: WildValue, entity: str) -> str:
    desc = action["changes"]["description"]

    kwargs = {
        "entity": entity,
        "new": desc["new"].tame(check_string),
        "old": desc["old"].tame(check_string),
        "name_template": get_name_template(entity).format(
            name=action["name"].tame(check_string),
            app_url=action.get("app_url").tame(check_none_or(check_string)),
        ),
    }

    if kwargs["new"] and kwargs["old"]:
        body = DESC_CHANGED_TEMPLATE.format(**kwargs)
    elif kwargs["new"]:
        body = NEW_DESC_ADDED_TEMPLATE.format(**kwargs)
    else:
        body = DESC_REMOVED_TEMPLATE.format(**kwargs)

    return body
Exemple #14
0
def get_story_update_state_body(payload: WildValue, action: WildValue) -> str:
    workflow_state_id = action["changes"]["workflow_state_id"]
    references = payload["references"]

    state = {}
    for ref in references:
        if ref["id"].tame(check_string_or_int) == workflow_state_id["new"].tame(check_int):
            state["new"] = ref["name"].tame(check_string)
        if ref["id"].tame(check_string_or_int) == workflow_state_id["old"].tame(check_int):
            state["old"] = ref["name"].tame(check_string)

    kwargs = {
        "entity": "story",
        "new": state["new"],
        "old": state["old"],
        "name_template": STORY_NAME_TEMPLATE.format(
            name=action["name"].tame(check_string),
            app_url=action.get("app_url").tame(check_none_or(check_string)),
        ),
    }

    return STATE_CHANGED_TEMPLATE.format(**kwargs)
Exemple #15
0
def get_tag_push_event_body(payload: WildValue) -> str:
    return get_push_tag_event_message(
        get_user_name(payload),
        get_tag_name(payload),
        action="pushed" if payload.get("checkout_sha") else "removed",
    )
Exemple #16
0
def get_zulip_event_name(
    header_event: str,
    payload: WildValue,
    branches: Optional[str],
) -> Optional[str]:
    """
    Usually, we return an event name that is a key in EVENT_FUNCTION_MAPPER.

    We return None for an event that we know we don't want to handle.
    """
    if header_event == "pull_request":
        action = payload["action"].tame(check_string)
        if action in ("opened", "synchronize", "reopened", "edited"):
            return "opened_or_update_pull_request"
        if action in ("assigned", "unassigned"):
            return "assigned_or_unassigned_pull_request"
        if action == "closed":
            return "closed_pull_request"
        if action == "review_requested":
            return "pull_request_review_requested"
        if action == "ready_for_review":
            return "pull_request_ready_for_review"
        if action in ("locked", "unlocked"):
            return "locked_or_unlocked_pull_request"
        if action in ("auto_merge_enabled", "auto_merge_disabled"):
            return "pull_request_auto_merge"
        if action in IGNORED_PULL_REQUEST_ACTIONS:
            return None
    elif header_event == "push":
        if is_commit_push_event(payload):
            if branches is not None:
                branch = get_branch_name_from_ref(
                    payload["ref"].tame(check_string))
                if branches.find(branch) == -1:
                    return None
            return "push_commits"
        else:
            return "push_tags"
    elif header_event == "check_run":
        if payload["check_run"]["status"].tame(check_string) != "completed":
            return None
        return header_event
    elif header_event == "team":
        action = payload["action"].tame(check_string)
        if action == "edited":
            return "team"
        if action in IGNORED_TEAM_ACTIONS:
            # no need to spam our logs, we just haven't implemented it yet
            return None
        else:
            # this means GH has actually added new actions since September 2020,
            # so it's a bit more cause for alarm
            raise UnsupportedWebhookEventType(
                f"unsupported team action {action}")
    elif header_event in list(EVENT_FUNCTION_MAPPER.keys()):
        return header_event
    elif header_event in IGNORED_EVENTS:
        return None

    complete_event = "{}:{}".format(
        header_event,
        payload.get("action", "???").tame(check_string))  # nocoverage
    raise UnsupportedWebhookEventType(complete_event)