defmodule GitHub.Pulls do @moduledoc """ Provides API endpoints related to pulls """ @default_client GitHub.Client @doc """ Check if a pull request has been merged ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#check-if-a-pull-request-has-been-merged) """ @spec check_if_merged(String.t(), String.t(), integer, keyword) :: :ok | {:error, GitHub.Error.t()} def check_if_merged(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :check_if_merged}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/merge", method: :get, response: [{204, nil}, {404, nil}], opts: opts }) end @doc """ Create a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#create-a-pull-request) """ @spec create(String.t(), String.t(), map, keyword) :: {:ok, GitHub.PullRequest.t()} | {:error, GitHub.Error.t()} def create(owner, repo, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, body: body], call: {GitHub.Pulls, :create}, url: "/repos/#{owner}/#{repo}/pulls", body: body, method: :post, request: [{"application/json", :map}], response: [ {201, {GitHub.PullRequest, :t}}, {403, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :t}} ], opts: opts }) end @doc """ Create a reply for a review comment ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#create-a-reply-for-a-review-comment) """ @spec create_reply_for_review_comment(String.t(), String.t(), integer, integer, map, keyword) :: {:ok, GitHub.PullRequest.ReviewComment.t()} | {:error, GitHub.Error.t()} def create_reply_for_review_comment(owner, repo, pull_number, comment_id, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [ owner: owner, repo: repo, pull_number: pull_number, comment_id: comment_id, body: body ], call: {GitHub.Pulls, :create_reply_for_review_comment}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/comments/#{comment_id}/replies", body: body, method: :post, request: [{"application/json", :map}], response: [{201, {GitHub.PullRequest.ReviewComment, :t}}, {404, {GitHub.BasicError, :t}}], opts: opts }) end @doc """ Create a review for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#create-a-review-for-a-pull-request) """ @spec create_review(String.t(), String.t(), integer, map, keyword) :: {:ok, GitHub.PullRequest.Review.t()} | {:error, GitHub.Error.t()} def create_review(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :create_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews", body: body, method: :post, request: [{"application/json", :map}], response: [ {200, {GitHub.PullRequest.Review, :t}}, {403, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :simple}} ], opts: opts }) end @doc """ Create a review comment for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#create-a-review-comment-for-a-pull-request) """ @spec create_review_comment(String.t(), String.t(), integer, map, keyword) :: {:ok, GitHub.PullRequest.ReviewComment.t()} | {:error, GitHub.Error.t()} def create_review_comment(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :create_review_comment}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/comments", body: body, method: :post, request: [{"application/json", :map}], response: [ {201, {GitHub.PullRequest.ReviewComment, :t}}, {403, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :t}} ], opts: opts }) end @doc """ Delete a pending review for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#delete-a-pending-review-for-a-pull-request) """ @spec delete_pending_review(String.t(), String.t(), integer, integer, keyword) :: {:ok, GitHub.PullRequest.Review.t()} | {:error, GitHub.Error.t()} def delete_pending_review(owner, repo, pull_number, review_id, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, review_id: review_id], call: {GitHub.Pulls, :delete_pending_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews/#{review_id}", method: :delete, response: [ {200, {GitHub.PullRequest.Review, :t}}, {404, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :simple}} ], opts: opts }) end @doc """ Delete a review comment for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#delete-a-review-comment-for-a-pull-request) """ @spec delete_review_comment(String.t(), String.t(), integer, keyword) :: :ok | {:error, GitHub.Error.t()} def delete_review_comment(owner, repo, comment_id, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, comment_id: comment_id], call: {GitHub.Pulls, :delete_review_comment}, url: "/repos/#{owner}/#{repo}/pulls/comments/#{comment_id}", method: :delete, response: [{204, nil}, {404, {GitHub.BasicError, :t}}], opts: opts }) end @doc """ Dismiss a review for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#dismiss-a-review-for-a-pull-request) """ @spec dismiss_review(String.t(), String.t(), integer, integer, map, keyword) :: {:ok, GitHub.PullRequest.Review.t()} | {:error, GitHub.Error.t()} def dismiss_review(owner, repo, pull_number, review_id, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, review_id: review_id, body: body], call: {GitHub.Pulls, :dismiss_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews/#{review_id}/dismissals", body: body, method: :put, request: [{"application/json", :map}], response: [ {200, {GitHub.PullRequest.Review, :t}}, {404, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :simple}} ], opts: opts }) end @doc """ Get a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#get-a-pull-request) """ @spec get(String.t(), String.t(), integer, keyword) :: {:ok, GitHub.PullRequest.t()} | {:error, GitHub.Error.t()} def get(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :get}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}", method: :get, response: [ {200, {GitHub.PullRequest, :t}}, {304, nil}, {404, {GitHub.BasicError, :t}}, {500, {GitHub.BasicError, :t}}, {503, :map} ], opts: opts }) end @doc """ Get a review for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#get-a-review-for-a-pull-request) """ @spec get_review(String.t(), String.t(), integer, integer, keyword) :: {:ok, GitHub.PullRequest.Review.t()} | {:error, GitHub.Error.t()} def get_review(owner, repo, pull_number, review_id, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, review_id: review_id], call: {GitHub.Pulls, :get_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews/#{review_id}", method: :get, response: [{200, {GitHub.PullRequest.Review, :t}}, {404, {GitHub.BasicError, :t}}], opts: opts }) end @doc """ Get a review comment for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#get-a-review-comment-for-a-pull-request) """ @spec get_review_comment(String.t(), String.t(), integer, keyword) :: {:ok, GitHub.PullRequest.ReviewComment.t()} | {:error, GitHub.Error.t()} def get_review_comment(owner, repo, comment_id, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, comment_id: comment_id], call: {GitHub.Pulls, :get_review_comment}, url: "/repos/#{owner}/#{repo}/pulls/comments/#{comment_id}", method: :get, response: [{200, {GitHub.PullRequest.ReviewComment, :t}}, {404, {GitHub.BasicError, :t}}], opts: opts }) end @doc """ List pull requests ## Options * `state` (String.t()): Either `open`, `closed`, or `all` to filter by state. * `head` (String.t()): Filter pulls by head user or head organization and branch name in the format of `user:ref-name` or `organization:ref-name`. For example: `github:new-script-format` or `octocat:test-branch`. * `base` (String.t()): Filter pulls by base branch name. Example: `gh-pages`. * `sort` (String.t()): What to sort results by. `popularity` will sort by the number of comments. `long-running` will sort by date created and will limit the results to pull requests that have been open for more than a month and have had activity within the past month. * `direction` (String.t()): The direction of the sort. Default: `desc` when sort is `created` or sort is not specified, otherwise `asc`. * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#list-pull-requests) """ @spec list(String.t(), String.t(), keyword) :: {:ok, [GitHub.PullRequest.simple()]} | {:error, GitHub.Error.t()} def list(owner, repo, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:base, :direction, :head, :page, :per_page, :sort, :state]) client.request(%{ args: [owner: owner, repo: repo], call: {GitHub.Pulls, :list}, url: "/repos/#{owner}/#{repo}/pulls", method: :get, query: query, response: [ {200, {:array, {GitHub.PullRequest, :simple}}}, {304, nil}, {422, {GitHub.ValidationError, :t}} ], opts: opts }) end @doc """ List comments for a pull request review ## Options * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#list-comments-for-a-pull-request-review) """ @spec list_comments_for_review(String.t(), String.t(), integer, integer, keyword) :: {:ok, [GitHub.ReviewComment.t()]} | {:error, GitHub.Error.t()} def list_comments_for_review(owner, repo, pull_number, review_id, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:page, :per_page]) client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, review_id: review_id], call: {GitHub.Pulls, :list_comments_for_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews/#{review_id}/comments", method: :get, query: query, response: [{200, {:array, {GitHub.ReviewComment, :t}}}, {404, {GitHub.BasicError, :t}}], opts: opts }) end @doc """ List commits on a pull request ## Options * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#list-commits-on-a-pull-request) """ @spec list_commits(String.t(), String.t(), integer, keyword) :: {:ok, [GitHub.Commit.t()]} | {:error, GitHub.Error.t()} def list_commits(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:page, :per_page]) client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :list_commits}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/commits", method: :get, query: query, response: [{200, {:array, {GitHub.Commit, :t}}}], opts: opts }) end @doc """ List pull requests files ## Options * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#list-pull-requests-files) """ @spec list_files(String.t(), String.t(), integer, keyword) :: {:ok, [GitHub.DiffEntry.t()]} | {:error, GitHub.Error.t()} def list_files(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:page, :per_page]) client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :list_files}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/files", method: :get, query: query, response: [ {200, {:array, {GitHub.DiffEntry, :t}}}, {422, {GitHub.ValidationError, :t}}, {500, {GitHub.BasicError, :t}}, {503, :map} ], opts: opts }) end @doc """ Get all requested reviewers for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/review-requests#get-all-requested-reviewers-for-a-pull-request) """ @spec list_requested_reviewers(String.t(), String.t(), integer, keyword) :: {:ok, GitHub.PullRequest.ReviewRequest.t()} | {:error, GitHub.Error.t()} def list_requested_reviewers(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :list_requested_reviewers}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/requested_reviewers", method: :get, response: [{200, {GitHub.PullRequest.ReviewRequest, :t}}], opts: opts }) end @doc """ List review comments on a pull request ## Options * `sort` (String.t()): The property to sort the results by. * `direction` (String.t()): The direction to sort results. Ignored without `sort` parameter. * `since` (String.t()): Only show results that were last updated after the given time. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`. * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#list-review-comments-on-a-pull-request) """ @spec list_review_comments(String.t(), String.t(), integer, keyword) :: {:ok, [GitHub.PullRequest.ReviewComment.t()]} | {:error, GitHub.Error.t()} def list_review_comments(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:direction, :page, :per_page, :since, :sort]) client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :list_review_comments}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/comments", method: :get, query: query, response: [{200, {:array, {GitHub.PullRequest.ReviewComment, :t}}}], opts: opts }) end @doc """ List review comments in a repository ## Options * `sort` (String.t()): * `direction` (String.t()): The direction to sort results. Ignored without `sort` parameter. * `since` (String.t()): Only show results that were last updated after the given time. This is a timestamp in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`. * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#list-review-comments-in-a-repository) """ @spec list_review_comments_for_repo(String.t(), String.t(), keyword) :: {:ok, [GitHub.PullRequest.ReviewComment.t()]} | {:error, GitHub.Error.t()} def list_review_comments_for_repo(owner, repo, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:direction, :page, :per_page, :since, :sort]) client.request(%{ args: [owner: owner, repo: repo], call: {GitHub.Pulls, :list_review_comments_for_repo}, url: "/repos/#{owner}/#{repo}/pulls/comments", method: :get, query: query, response: [{200, {:array, {GitHub.PullRequest.ReviewComment, :t}}}], opts: opts }) end @doc """ List reviews for a pull request ## Options * `per_page` (integer): The number of results per page (max 100). * `page` (integer): Page number of the results to fetch. ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#list-reviews-for-a-pull-request) """ @spec list_reviews(String.t(), String.t(), integer, keyword) :: {:ok, [GitHub.PullRequest.Review.t()]} | {:error, GitHub.Error.t()} def list_reviews(owner, repo, pull_number, opts \\ []) do client = opts[:client] || @default_client query = Keyword.take(opts, [:page, :per_page]) client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number], call: {GitHub.Pulls, :list_reviews}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews", method: :get, query: query, response: [{200, {:array, {GitHub.PullRequest.Review, :t}}}], opts: opts }) end @doc """ Merge a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#merge-a-pull-request) """ @spec merge(String.t(), String.t(), integer, map | nil, keyword) :: {:ok, GitHub.PullRequest.MergeResult.t()} | {:error, GitHub.Error.t()} def merge(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :merge}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/merge", body: body, method: :put, request: [{"application/json", {:nullable, :map}}], response: [ {200, {GitHub.PullRequest.MergeResult, :t}}, {403, {GitHub.BasicError, :t}}, {404, {GitHub.BasicError, :t}}, {405, :map}, {409, :map}, {422, {GitHub.ValidationError, :t}} ], opts: opts }) end @doc """ Remove requested reviewers from a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/review-requests#remove-requested-reviewers-from-a-pull-request) """ @spec remove_requested_reviewers(String.t(), String.t(), integer, map, keyword) :: {:ok, GitHub.PullRequest.simple()} | {:error, GitHub.Error.t()} def remove_requested_reviewers(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :remove_requested_reviewers}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/requested_reviewers", body: body, method: :delete, request: [{"application/json", :map}], response: [{200, {GitHub.PullRequest, :simple}}, {422, {GitHub.ValidationError, :t}}], opts: opts }) end @doc """ Request reviewers for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/review-requests#request-reviewers-for-a-pull-request) """ @spec request_reviewers(String.t(), String.t(), integer, map, keyword) :: {:ok, GitHub.PullRequest.simple()} | {:error, GitHub.Error.t()} def request_reviewers(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :request_reviewers}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/requested_reviewers", body: body, method: :post, request: [{"application/json", :map}], response: [{201, {GitHub.PullRequest, :simple}}, {403, {GitHub.BasicError, :t}}, {422, nil}], opts: opts }) end @doc """ Submit a review for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#submit-a-review-for-a-pull-request) """ @spec submit_review(String.t(), String.t(), integer, integer, map, keyword) :: {:ok, GitHub.PullRequest.Review.t()} | {:error, GitHub.Error.t()} def submit_review(owner, repo, pull_number, review_id, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, review_id: review_id, body: body], call: {GitHub.Pulls, :submit_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews/#{review_id}/events", body: body, method: :post, request: [{"application/json", :map}], response: [ {200, {GitHub.PullRequest.Review, :t}}, {403, {GitHub.BasicError, :t}}, {404, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :simple}} ], opts: opts }) end @doc """ Update a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#update-a-pull-request) """ @spec update(String.t(), String.t(), integer, map, keyword) :: {:ok, GitHub.PullRequest.t()} | {:error, GitHub.Error.t()} def update(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :update}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}", body: body, method: :patch, request: [{"application/json", :map}], response: [ {200, {GitHub.PullRequest, :t}}, {403, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :t}} ], opts: opts }) end @doc """ Update a pull request branch ## Resources * [API method documentation](https://docs.github.com/rest/pulls/pulls#update-a-pull-request-branch) """ @spec update_branch(String.t(), String.t(), integer, map | nil, keyword) :: {:ok, map} | {:error, GitHub.Error.t()} def update_branch(owner, repo, pull_number, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, body: body], call: {GitHub.Pulls, :update_branch}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/update-branch", body: body, method: :put, request: [{"application/json", {:nullable, :map}}], response: [{202, :map}, {403, {GitHub.BasicError, :t}}, {422, {GitHub.ValidationError, :t}}], opts: opts }) end @doc """ Update a review for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/reviews#update-a-review-for-a-pull-request) """ @spec update_review(String.t(), String.t(), integer, integer, map, keyword) :: {:ok, GitHub.PullRequest.Review.t()} | {:error, GitHub.Error.t()} def update_review(owner, repo, pull_number, review_id, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, pull_number: pull_number, review_id: review_id, body: body], call: {GitHub.Pulls, :update_review}, url: "/repos/#{owner}/#{repo}/pulls/#{pull_number}/reviews/#{review_id}", body: body, method: :put, request: [{"application/json", :map}], response: [{200, {GitHub.PullRequest.Review, :t}}, {422, {GitHub.ValidationError, :simple}}], opts: opts }) end @doc """ Update a review comment for a pull request ## Resources * [API method documentation](https://docs.github.com/rest/pulls/comments#update-a-review-comment-for-a-pull-request) """ @spec update_review_comment(String.t(), String.t(), integer, map, keyword) :: {:ok, GitHub.PullRequest.ReviewComment.t()} | {:error, GitHub.Error.t()} def update_review_comment(owner, repo, comment_id, body, opts \\ []) do client = opts[:client] || @default_client client.request(%{ args: [owner: owner, repo: repo, comment_id: comment_id, body: body], call: {GitHub.Pulls, :update_review_comment}, url: "/repos/#{owner}/#{repo}/pulls/comments/#{comment_id}", body: body, method: :patch, request: [{"application/json", :map}], response: [{200, {GitHub.PullRequest.ReviewComment, :t}}], opts: opts }) end end