diff --git a/Octokit.Reactive/Clients/IObservableActionsArtifactsClient.cs b/Octokit.Reactive/Clients/IObservableActionsArtifactsClient.cs new file mode 100644 index 0000000000..38e6a34a9c --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsArtifactsClient.cs @@ -0,0 +1,12 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Artifacts API. + /// + /// + /// See the Actions Artifacts API documentation for more information. + /// + public interface IObservableActionsArtifactsClient + { + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsCacheClient.cs b/Octokit.Reactive/Clients/IObservableActionsCacheClient.cs new file mode 100644 index 0000000000..29f800c427 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsCacheClient.cs @@ -0,0 +1,12 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Cache API. + /// + /// + /// See the Actions Cache API documentation for more information. + /// + public interface IObservableActionsCacheClient + { + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsClient.cs b/Octokit.Reactive/Clients/IObservableActionsClient.cs new file mode 100644 index 0000000000..5397fd80e5 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsClient.cs @@ -0,0 +1,41 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public interface IObservableActionsClient + { + /// + /// Client for the Artifacts API. + /// + IObservableActionsArtifactsClient Artifacts { get; } + + /// + /// Client for the Cache API. + /// + IObservableActionsCacheClient Cache { get; } + + /// + /// Client for the Permissions API. + /// + IObservableActionsPermissionsClient Permissions { get; } + + /// + /// Client for the Self-hosted runner groups API. + /// + IObservableActionsSelfHostedRunnerGroupsClient SelfHostedRunnerGroups { get; } + + /// + /// Client for the Self-hosted runners API. + /// + IObservableActionsSelfHostedRunnersClient SelfHostedRunners { get; } + + /// + /// Client for the Workflows API. + /// + IObservableActionsWorkflowsClient Workflows { get; } + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsPermissionsClient.cs b/Octokit.Reactive/Clients/IObservableActionsPermissionsClient.cs new file mode 100644 index 0000000000..ea3391b0b0 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsPermissionsClient.cs @@ -0,0 +1,12 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Permissions API. + /// + /// + /// See the Actions Permissions API documentation for more information. + /// + public interface IObservableActionsPermissionsClient + { + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsSelfHostedRunnerGroupsClient.cs b/Octokit.Reactive/Clients/IObservableActionsSelfHostedRunnerGroupsClient.cs new file mode 100644 index 0000000000..f8e82dc707 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsSelfHostedRunnerGroupsClient.cs @@ -0,0 +1,12 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Self-hosted runner groups API. + /// + /// + /// See the Actions Self-hosted runner groups API documentation for more information. + /// + public interface IObservableActionsSelfHostedRunnerGroupsClient + { + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsSelfHostedRunnersClient.cs b/Octokit.Reactive/Clients/IObservableActionsSelfHostedRunnersClient.cs new file mode 100644 index 0000000000..52ef545712 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsSelfHostedRunnersClient.cs @@ -0,0 +1,12 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Self-hosted runners API. + /// + /// + /// See the Actions Self-hosted runners API documentation for more information. + /// + public interface IObservableActionsSelfHostedRunnersClient + { + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsWorkflowJobsClient.cs b/Octokit.Reactive/Clients/IObservableActionsWorkflowJobsClient.cs new file mode 100644 index 0000000000..4b1362b0dc --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsWorkflowJobsClient.cs @@ -0,0 +1,108 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Workflows jobs API. + /// + /// + /// See the Actions Workflows jobs API documentation for more information. + /// + public interface IObservableActionsWorkflowJobsClient + { + /// + /// Re-runs a specific workflow job in a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-job-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + IObservable Rerun(string owner, string name, long jobId); + + /// + /// Gets a specific job in a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#get-a-job-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The unique identifier of the job. + IObservable Get(string owner, string name, long jobId); + + /// + /// Gets the plain text log file for a workflow job. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#download-job-logs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + IObservable GetLogs(string owner, string name, long jobId); + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable List(string owner, string name, long runId); + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + IObservable List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest); + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + /// Options to change the API response. + IObservable List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest, ApiOptions options); + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + IObservable List(string owner, string name, long runId, int attemptNumber); + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + /// Options to change the API response. + IObservable List(string owner, string name, long runId, int attemptNumber, ApiOptions options); + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsWorkflowRunsClient.cs b/Octokit.Reactive/Clients/IObservableActionsWorkflowRunsClient.cs new file mode 100644 index 0000000000..968138fe8e --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsWorkflowRunsClient.cs @@ -0,0 +1,265 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Workflows runs API. + /// + /// + /// See the Actions Workflows runs API documentation for more information. + /// + public interface IObservableActionsWorkflowRunsClient + { + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + IObservable List(string owner, string name); + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + IObservable List(string owner, string name, WorkflowRunsRequest workflowRunsRequest); + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + IObservable List(string owner, string name, WorkflowRunsRequest workflowRunsRequest, ApiOptions options); + + /// + /// Gets a specific workflow run in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable Get(string owner, string name, long runId); + + /// + /// Deletes a specific workflow run. Anyone with write access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable Delete(string owner, string name, long runId); + + /// + /// Get the review history for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-the-review-history-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable GetReviewHistory(string owner, string name, long runId); + + /// + /// Approves a workflow run for a pull request from a public fork of a first time contributor. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#approve-a-workflow-run-for-a-fork-pull-request + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable Approve(string owner, string name, long runId); + + /// + /// Gets a specific workflow run attempt. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + IObservable GetAttempt(string owner, string name, long runId, long attemptNumber); + + /// + /// Gets a byte array containing an archive of log files for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-attempt-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + IObservable GetAttemptLogs(string owner, string name, long runId, long attemptNumber); + + /// + /// Cancels a workflow run using its Id. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#cancel-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable Cancel(string owner, string name, long runId); + + /// + /// Gets a byte array containing an archive of log files for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable GetLogs(string owner, string name, long runId); + + /// + /// Deletes all logs for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable DeleteLogs(string owner, string name, long runId); + + /// + /// Approve or reject pending deployments that are waiting on approval by a required reviewer. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#review-pending-deployments-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The review for the pending deployment. + IObservable ReviewPendingDeployments(string owner, string name, long runId, PendingDeploymentReview review); + + /// + /// Re-runs a specific workflow run in a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable Rerun(string owner, string name, long runId); + + /// + /// Re-run all of the failed jobs and their dependent jobs in a workflow run using the Id of the workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-failed-jobs-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable RerunFailedJobs(string owner, string name, long runId); + + /// + /// Gets the number of billable minutes and total run time for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-workflow-run-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + IObservable GetUsage(string owner, string name, long runId); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + IObservable ListByWorkflow(string owner, string name, long workflowId); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + IObservable ListByWorkflow(string owner, string name, string workflowFileName); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + IObservable ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + IObservable ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + IObservable ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest, ApiOptions options); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + IObservable ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest, ApiOptions options); + } +} diff --git a/Octokit.Reactive/Clients/IObservableActionsWorkflowsClient.cs b/Octokit.Reactive/Clients/IObservableActionsWorkflowsClient.cs new file mode 100644 index 0000000000..9c5f055bbc --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableActionsWorkflowsClient.cs @@ -0,0 +1,157 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Workflows API. + /// + /// + /// See the Actions Workflows API documentation for more information. + /// + public interface IObservableActionsWorkflowsClient + { + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// The parameters to use to trigger the workflow run. + IObservable CreateDispatch(string owner, string name, string workflowFileName, CreateWorkflowDispatch createDispatch); + + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// The parameters to use to trigger the workflow run. + IObservable CreateDispatch(string owner, string name, long workflowId, CreateWorkflowDispatch createDispatch); + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + IObservable Disable(string owner, string name, string workflowFileName); + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + IObservable Disable(string owner, string name, long workflowId); + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + IObservable Enable(string owner, string name, string workflowFileName); + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + IObservable Enable(string owner, string name, long workflowId); + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + IObservable Get(string owner, string name, string workflowFileName); + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + IObservable Get(string owner, string name, long workflowId); + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + IObservable GetUsage(string owner, string name, string workflowFileName); + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + IObservable GetUsage(string owner, string name, long workflowId); + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + IObservable List(string owner, string name); + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + /// Options to change the API response. + IObservable List(string owner, string name, ApiOptions options); + + /// + /// Client for the Workflow jobs API. + /// + IObservableActionsWorkflowJobsClient Jobs { get; } + + /// + /// Client for the Workflow runs API. + /// + IObservableActionsWorkflowRunsClient Runs { get; } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsArtifactsClient.cs b/Octokit.Reactive/Clients/ObservableActionsArtifactsClient.cs new file mode 100644 index 0000000000..9f152cbd11 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsArtifactsClient.cs @@ -0,0 +1,18 @@ +namespace Octokit.Reactive +{ + public class ObservableActionsArtifactsClient : IObservableActionsArtifactsClient + { + readonly IActionsArtifactsClient _client; + + /// + /// Instantiate a new GitHub Actions Artifacts API client. + /// + /// A GitHub client. + public ObservableActionsArtifactsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.Artifacts; + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsCacheClient.cs b/Octokit.Reactive/Clients/ObservableActionsCacheClient.cs new file mode 100644 index 0000000000..cefea4b59a --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsCacheClient.cs @@ -0,0 +1,18 @@ +namespace Octokit.Reactive +{ + public class ObservableActionsCacheClient : IObservableActionsCacheClient + { + readonly IActionsCacheClient _client; + + /// + /// Instantiate a new GitHub Actions Cache API client. + /// + /// A GitHub client. + public ObservableActionsCacheClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.Cache; + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsClient.cs b/Octokit.Reactive/Clients/ObservableActionsClient.cs new file mode 100644 index 0000000000..3b0e44620a --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsClient.cs @@ -0,0 +1,57 @@ +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public class ObservableActionsClient : IObservableActionsClient + { + /// + /// Instantiate a new GitHub Actions API client. + /// + /// A GitHub client. + public ObservableActionsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + Artifacts = new ObservableActionsArtifactsClient(client); + Cache = new ObservableActionsCacheClient(client); + Permissions = new ObservableActionsPermissionsClient(client); + SelfHostedRunnerGroups = new ObservableActionsSelfHostedRunnerGroupsClient(client); + SelfHostedRunners = new ObservableActionsSelfHostedRunnersClient(client); + Workflows = new ObservableActionsWorkflowsClient(client); + } + + /// + /// Client for the Artifacts API. + /// + public IObservableActionsArtifactsClient Artifacts { get; private set; } + + /// + /// Client for the Cache API. + /// + public IObservableActionsCacheClient Cache { get; private set; } + + /// + /// Client for the Permissions API. + /// + public IObservableActionsPermissionsClient Permissions { get; private set; } + + /// + /// Client for the Self-hosted runner groups API. + /// + public IObservableActionsSelfHostedRunnerGroupsClient SelfHostedRunnerGroups { get; private set; } + + /// + /// Client for the Self-hosted runners API. + /// + public IObservableActionsSelfHostedRunnersClient SelfHostedRunners { get; private set; } + + /// + /// Client for the Workflows API. + /// + public IObservableActionsWorkflowsClient Workflows { get; private set; } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsPermissionsClient.cs b/Octokit.Reactive/Clients/ObservableActionsPermissionsClient.cs new file mode 100644 index 0000000000..50bbec5947 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsPermissionsClient.cs @@ -0,0 +1,18 @@ +namespace Octokit.Reactive +{ + public class ObservableActionsPermissionsClient : IObservableActionsPermissionsClient + { + readonly IActionsPermissionsClient _client; + + /// + /// Instantiate a new GitHub Actions Permissions API client. + /// + /// A GitHub client. + public ObservableActionsPermissionsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.Permissions; + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsSelfHostedRunnerGroupsClient.cs b/Octokit.Reactive/Clients/ObservableActionsSelfHostedRunnerGroupsClient.cs new file mode 100644 index 0000000000..e94e6a38b7 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsSelfHostedRunnerGroupsClient.cs @@ -0,0 +1,18 @@ +namespace Octokit.Reactive +{ + public class ObservableActionsSelfHostedRunnerGroupsClient : IObservableActionsSelfHostedRunnerGroupsClient + { + readonly IActionsSelfHostedRunnerGroupsClient _client; + + /// + /// Instantiate a new GitHub Actions Self-hosted runner groups API client. + /// + /// A GitHub client. + public ObservableActionsSelfHostedRunnerGroupsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.SelfHostedRunnerGroups; + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsSelfHostedRunnersClient.cs b/Octokit.Reactive/Clients/ObservableActionsSelfHostedRunnersClient.cs new file mode 100644 index 0000000000..052b6f90cb --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsSelfHostedRunnersClient.cs @@ -0,0 +1,18 @@ +namespace Octokit.Reactive +{ + public class ObservableActionsSelfHostedRunnersClient : IObservableActionsSelfHostedRunnersClient + { + readonly IActionsSelfHostedRunnersClient _client; + + /// + /// Instantiate a new GitHub Actions Self-hosted runners API client. + /// + /// A GitHub client. + public ObservableActionsSelfHostedRunnersClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.SelfHostedRunners; + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsWorkflowJobsClient.cs b/Octokit.Reactive/Clients/ObservableActionsWorkflowJobsClient.cs new file mode 100644 index 0000000000..0d8da048e1 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsWorkflowJobsClient.cs @@ -0,0 +1,172 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Workflow jobs API. + /// + /// + /// See the Actions Workflow jobs API documentation for more information. + /// + public class ObservableActionsWorkflowJobsClient : IObservableActionsWorkflowJobsClient + { + readonly IActionsWorkflowJobsClient _client; + + /// + /// Instantiate a new GitHub Actions Workflows jobs API client. + /// + /// A GitHub client. + public ObservableActionsWorkflowJobsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.Workflows.Jobs; + } + + /// + /// Re-runs a specific workflow job in a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-job-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + public IObservable Rerun(string owner, string name, long jobId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Rerun(owner, name, jobId).ToObservable(); + } + + /// + /// Gets a specific job in a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#get-a-job-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The unique identifier of the job. + public IObservable Get(string owner, string name, long jobId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Get(owner, name, jobId).ToObservable(); + } + + /// + /// Gets the plain text log file for a workflow job. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#download-job-logs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + public IObservable GetLogs(string owner, string name, long jobId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.GetLogs(owner, name, jobId).ToObservable(); + } + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable List(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.List(owner, name, runId).ToObservable(); + } + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + public IObservable List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunJobsRequest, nameof(workflowRunJobsRequest)); + + return _client.List(owner, name, runId, workflowRunJobsRequest).ToObservable(); + } + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + /// Options to change the API response. + public IObservable List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunJobsRequest, nameof(workflowRunJobsRequest)); + + return _client.List(owner, name, runId, workflowRunJobsRequest, options).ToObservable(); + } + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + public IObservable List(string owner, string name, long runId, int attemptNumber) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.List(owner, name, runId, attemptNumber).ToObservable(); + } + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + /// Options to change the API response. + public IObservable List(string owner, string name, long runId, int attemptNumber, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.List(owner, name, runId, attemptNumber, options).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsWorkflowRunsClient.cs b/Octokit.Reactive/Clients/ObservableActionsWorkflowRunsClient.cs new file mode 100644 index 0000000000..59fe32017a --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsWorkflowRunsClient.cs @@ -0,0 +1,427 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Actions Workflow runs API. + /// + /// + /// See the Actions Workflow runs API documentation for more information. + /// + public class ObservableActionsWorkflowRunsClient : IObservableActionsWorkflowRunsClient + { + readonly IActionsWorkflowRunsClient _client; + readonly IConnection _connection; + + /// + /// Instantiate a new GitHub Actions Workflows runs API client. + /// + /// A GitHub client. + public ObservableActionsWorkflowRunsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.Workflows.Runs; + _connection = client.Connection; + } + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + public IObservable List(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.List(owner, name).ToObservable(); + } + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + public IObservable List(string owner, string name, WorkflowRunsRequest workflowRunsRequest) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + + return _client.List(owner, name, workflowRunsRequest).ToObservable(); + } + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + public IObservable List(string owner, string name, WorkflowRunsRequest workflowRunsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _client.List(owner, name, workflowRunsRequest, options).ToObservable(); + } + + /// + /// Gets a specific workflow run in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable Get(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Get(owner, name, runId).ToObservable(); + } + + /// + /// Deletes a specific workflow run. Anyone with write access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable Delete(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Delete(owner, name, runId).ToObservable(); + } + + /// + /// Get the review history for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-the-review-history-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable GetReviewHistory(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _connection.GetAndFlattenAllPages(ApiUrls.ActionsWorkflowRunApprovals(owner, name, runId)); + } + + /// + /// Approves a workflow run for a pull request from a public fork of a first time contributor. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#approve-a-workflow-run-for-a-fork-pull-request + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable Approve(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Approve(owner, name, runId).ToObservable(); + } + + /// + /// Gets a specific workflow run attempt. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + public IObservable GetAttempt(string owner, string name, long runId, long attemptNumber) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.GetAttempt(owner, name, runId, attemptNumber).ToObservable(); + } + + /// + /// Gets a byte array containing an archive of log files for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-attempt-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + public IObservable GetAttemptLogs(string owner, string name, long runId, long attemptNumber) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.GetAttemptLogs(owner, name, runId, attemptNumber).ToObservable(); + } + + /// + /// Cancels a workflow run using its Id. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#cancel-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable Cancel(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Cancel(owner, name, runId).ToObservable(); + } + + /// + /// Gets a byte array containing an archive of log files for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable GetLogs(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.GetLogs(owner, name, runId).ToObservable(); + } + + /// + /// Deletes all logs for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable DeleteLogs(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.DeleteLogs(owner, name, runId).ToObservable(); + } + + /// + /// Approve or reject pending deployments that are waiting on approval by a required reviewer. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#review-pending-deployments-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The review for the pending deployment. + public IObservable ReviewPendingDeployments(string owner, string name, long runId, PendingDeploymentReview review) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(review, nameof(review)); + + return _client.ReviewPendingDeployments(owner, name, runId, review).ToObservable(); + } + + /// + /// Re-runs a specific workflow run in a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable Rerun(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Rerun(owner, name, runId).ToObservable(); + } + + /// + /// Re-run all of the failed jobs and their dependent jobs in a workflow run using the Id of the workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-failed-jobs-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable RerunFailedJobs(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.RerunFailedJobs(owner, name, runId).ToObservable(); + } + + /// + /// Gets the number of billable minutes and total run time for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-workflow-run-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + public IObservable GetUsage(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.GetUsage(owner, name, runId).ToObservable(); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + public IObservable ListByWorkflow(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.ListByWorkflow(owner, name, workflowId).ToObservable(); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + public IObservable ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + + return _client.ListByWorkflow(owner, name, workflowId, workflowRunsRequest).ToObservable(); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + public IObservable ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _client.ListByWorkflow(owner, name, workflowId, workflowRunsRequest, options).ToObservable(); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + public IObservable ListByWorkflow(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return _client.ListByWorkflow(owner, name, workflowFileName).ToObservable(); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + public IObservable ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + + return _client.ListByWorkflow(owner, name, workflowFileName, workflowRunsRequest).ToObservable(); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + public IObservable ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _client.ListByWorkflow(owner, name, workflowFileName, workflowRunsRequest, options).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableActionsWorkflowsClient.cs b/Octokit.Reactive/Clients/ObservableActionsWorkflowsClient.cs new file mode 100644 index 0000000000..bd6bb02224 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableActionsWorkflowsClient.cs @@ -0,0 +1,248 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + public class ObservableActionsWorkflowsClient : IObservableActionsWorkflowsClient + { + readonly IActionsWorkflowsClient _client; + + /// + /// Instantiate a new GitHub Actions Workflows API client. + /// + /// A GitHub client. + public ObservableActionsWorkflowsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Actions.Workflows; + + Jobs = new ObservableActionsWorkflowJobsClient(client); + Runs = new ObservableActionsWorkflowRunsClient(client); + } + + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// The parameters to use to trigger the workflow run. + public IObservable CreateDispatch(string owner, string name, string workflowFileName, CreateWorkflowDispatch createDispatch) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + Ensure.ArgumentNotNull(createDispatch, nameof(createDispatch)); + + return _client.CreateDispatch(owner, name, workflowFileName, createDispatch).ToObservable(); + } + + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// The parameters to use to trigger the workflow run. + public IObservable CreateDispatch(string owner, string name, long workflowId, CreateWorkflowDispatch createDispatch) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(createDispatch, nameof(createDispatch)); + + return _client.CreateDispatch(owner, name, workflowId, createDispatch).ToObservable(); + } + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + public IObservable Disable(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return _client.Disable(owner, name, workflowFileName).ToObservable(); + } + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + public IObservable Disable(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Disable(owner, name, workflowId).ToObservable(); + } + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + public IObservable Enable(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return _client.Enable(owner, name, workflowFileName).ToObservable(); + } + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + public IObservable Enable(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Enable(owner, name, workflowId).ToObservable(); + } + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + public IObservable Get(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return _client.Get(owner, name, workflowFileName).ToObservable(); + } + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + public IObservable Get(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.Get(owner, name, workflowId).ToObservable(); + } + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + public IObservable GetUsage(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return _client.GetUsage(owner, name, workflowFileName).ToObservable(); + } + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + public IObservable GetUsage(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.GetUsage(owner, name, workflowId).ToObservable(); + } + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + public IObservable List(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return _client.List(owner, name).ToObservable(); + } + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + /// Options to change the API response. + public IObservable List(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(options, nameof(options)); + + return _client.List(owner, name, options).ToObservable(); + } + + /// + /// Client for the Workflow jobs API. + /// + public IObservableActionsWorkflowJobsClient Jobs { get; private set; } + + /// + /// Client for the Workflow runs API. + /// + public IObservableActionsWorkflowRunsClient Runs { get; private set; } + } +} diff --git a/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs index 833a88514a..072dd79d41 100644 --- a/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs +++ b/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit.Reactive +namespace Octokit.Reactive { /// /// A client for GitHub's Org Actions API. diff --git a/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs b/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs index 631bbf4bf4..b49162026e 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit.Reactive +namespace Octokit.Reactive { /// /// A client for GitHub's Repository Actions API. diff --git a/Octokit.Reactive/IObservableGitHubClient.cs b/Octokit.Reactive/IObservableGitHubClient.cs index f5ed4393d1..6532de7994 100644 --- a/Octokit.Reactive/IObservableGitHubClient.cs +++ b/Octokit.Reactive/IObservableGitHubClient.cs @@ -41,5 +41,6 @@ public interface IObservableGitHubClient : IApiInfoProvider IObservableLicensesClient Licenses { get; } IObservableRateLimitClient RateLimit { get; } IObservableMetaClient Meta { get; } + IObservableActionsClient Actions { get; } } } \ No newline at end of file diff --git a/Octokit.Reactive/ObservableGitHubClient.cs b/Octokit.Reactive/ObservableGitHubClient.cs index 648360f4c8..c3899ffc77 100644 --- a/Octokit.Reactive/ObservableGitHubClient.cs +++ b/Octokit.Reactive/ObservableGitHubClient.cs @@ -56,6 +56,7 @@ public ObservableGitHubClient(IGitHubClient gitHubClient) Licenses = new ObservableLicensesClient(gitHubClient); RateLimit = new ObservableRateLimitClient(gitHubClient); Meta = new ObservableMetaClient(gitHubClient); + Actions = new ObservableActionsClient(gitHubClient); } public IConnection Connection @@ -102,6 +103,7 @@ public void SetRequestTimeout(TimeSpan timeout) public IObservableLicensesClient Licenses { get; private set; } public IObservableRateLimitClient RateLimit { get; private set; } public IObservableMetaClient Meta { get; private set; } + public IObservableActionsClient Actions { get; private set; } /// /// Gets the latest API Info - this will be null if no API calls have been made diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 0ba912abf4..e8de439ecb 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -21,7 +21,7 @@ - 1591;1701;1702;1705 + $(NoWarn);1591;1701;1702;1705 diff --git a/Octokit.Tests.Integration/Clients/ActionsWorkflowJobsClientTests.cs b/Octokit.Tests.Integration/Clients/ActionsWorkflowJobsClientTests.cs new file mode 100644 index 0000000000..77adf8a478 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/ActionsWorkflowJobsClientTests.cs @@ -0,0 +1,177 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +public class ActionsWorkflowJobsClientTests +{ + private const string HelloWorldWorkflow = @" +name: hello +on: [ push, workflow_dispatch ] +jobs: + world: + runs-on: [ ubuntu-latest ] + steps: + - run: echo ""Hello world."""; + + [IntegrationTest] + public async Task CanListWorkflowJobs() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Jobs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForCompletion(github, context); + + var jobs = await fixture.List(owner, name, runId); + + Assert.NotNull(jobs); + Assert.NotEqual(0, jobs.TotalCount); + Assert.NotNull(jobs.Jobs); + Assert.NotEmpty(jobs.Jobs); + + jobs = await fixture.List(owner, name, runId, new WorkflowRunJobsRequest() { Filter = WorkflowRunJobsFilter.All }); + + Assert.NotNull(jobs); + Assert.NotEqual(0, jobs.TotalCount); + Assert.NotNull(jobs.Jobs); + Assert.NotEmpty(jobs.Jobs); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowJob() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Jobs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForCompletion(github, context); + + var jobs = await fixture.List(owner, name, runId); + + Assert.NotNull(jobs); + Assert.NotNull(jobs.Jobs); + Assert.Single(jobs.Jobs); + + var job = await fixture.Get(owner, name, jobs.Jobs[0].Id); + + Assert.NotNull(job); + Assert.Equal(jobs.Jobs[0].Id, job.Id); + } + } + + [IntegrationTest] + public async Task CanListWorkflowJobsForAttempt() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Jobs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForCompletion(github, context); + + var jobs = await fixture.List(owner, name, runId, 1); + + Assert.NotNull(jobs); + Assert.NotEqual(0, jobs.TotalCount); + Assert.NotNull(jobs.Jobs); + Assert.NotEmpty(jobs.Jobs); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowJobLogs() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Jobs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForCompletion(github, context); + + var jobs = await fixture.List(owner, name, runId); + + Assert.NotNull(jobs); + Assert.NotNull(jobs.Jobs); + var job = Assert.Single(jobs.Jobs); + + var logs = await fixture.GetLogs(owner, name, job.Id); + + Assert.NotNull(logs); + Assert.NotEmpty(logs); + } + } + + [IntegrationTest] + public async Task CanRerunJob() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Jobs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForCompletion(github, context); + + var jobs = await fixture.List(owner, name, runId); + + Assert.NotNull(jobs); + Assert.NotNull(jobs.Jobs); + var job = Assert.Single(jobs.Jobs); + + await fixture.Rerun(owner, name, job.Id); + } + } + + private static async Task<(string WorkflowFileName, long RunId)> CreateWorkflowAndWaitForCompletion( + IGitHubClient github, + RepositoryContext context) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + var workflowFileName = ".github/workflows/hello-world.yml"; + + _ = await github.Repository.Content.CreateFile( + owner, + name, + workflowFileName, + new CreateFileRequest("Create test workflow", HelloWorldWorkflow)); + + var totalTimeout = TimeSpan.FromMinutes(1); + var loopDelay = TimeSpan.FromSeconds(2); + + using (var cts = new CancellationTokenSource(totalTimeout)) + { + while (!cts.IsCancellationRequested) + { + cts.Token.ThrowIfCancellationRequested(); + + var runs = await github.Actions.Workflows.Runs.List(owner, name); + + await Task.Delay(loopDelay); + + if (runs.TotalCount > 0 && runs.WorkflowRuns[0].Status == WorkflowRunStatus.Completed) + { + return (workflowFileName, runs.WorkflowRuns[0].Id); + } + } + } + + throw new InvalidOperationException("Timed out waiting for workflow run."); + } +} diff --git a/Octokit.Tests.Integration/Clients/ActionsWorkflowRunsClientTests.cs b/Octokit.Tests.Integration/Clients/ActionsWorkflowRunsClientTests.cs new file mode 100644 index 0000000000..1feab89e4c --- /dev/null +++ b/Octokit.Tests.Integration/Clients/ActionsWorkflowRunsClientTests.cs @@ -0,0 +1,407 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Threading; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +public class ActionsWorkflowRunsClientTests +{ + private const string HelloWorldWorkflow = @" +name: hello +on: [ push, workflow_dispatch ] +jobs: + world: + runs-on: [ ubuntu-latest ] + steps: + - run: echo ""Hello world."""; + + private const string BrokenWorkflow = @" +name: hello +on: [ push, workflow_dispatch ] +jobs: + world: + runs-on: [ ubuntu-latest ] + steps: + - run: exit 1"; + + [IntegrationTest] + public async Task CanListWorkflowRuns() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, _) = await CreateWorkflowAndWaitForFirstRun(github, context); + + var runs = await fixture.List(owner, name); + + Assert.NotNull(runs); + Assert.NotEqual(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.NotEmpty(runs.WorkflowRuns); + + runs = await fixture.List(owner, name, new WorkflowRunsRequest() { Branch = "main" }); + + Assert.NotNull(runs); + Assert.NotEqual(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.NotEmpty(runs.WorkflowRuns); + + runs = await fixture.List(owner, name, new WorkflowRunsRequest() { Branch = "not-main" }); + + Assert.NotNull(runs); + Assert.Equal(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.Empty(runs.WorkflowRuns); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowRun() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context); + + var run = await fixture.Get(owner, name, runId); + + Assert.NotNull(run); + Assert.Equal(runId, run.Id); + } + } + + [IntegrationTest] + public async Task CanDeleteWorkflowRun() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context, WorkflowRunStatus.Completed); + + await fixture.Delete(owner, name, runId); + await Assert.ThrowsAsync(() => fixture.Get(owner, name, runId)); + } + } + + [IntegrationTest] + public async Task CanDeleteWorkflowRunLogs() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context, WorkflowRunStatus.Completed); + + await fixture.DeleteLogs(owner, name, runId); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowRunReviewHistory() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context); + + var reviews = await fixture.GetReviewHistory(owner, name, runId); + Assert.NotNull(reviews); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowRunAttempt() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context); + + var run = await fixture.GetAttempt(owner, name, runId, 1); + + Assert.NotNull(run); + Assert.Equal(runId, run.Id); + Assert.Equal(1, run.RunAttempt); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowRunAttemptLogs() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context, WorkflowRunStatus.Completed); + + var logs = await fixture.GetAttemptLogs(owner, name, runId, 1); + + Assert.NotNull(logs); + Assert.NotEmpty(logs); + + var tempFile = Path.GetTempFileName(); + + try + { + File.WriteAllBytes(tempFile, logs); + + using (var archive = ZipFile.OpenRead(tempFile)) + { + Assert.NotEmpty(archive.Entries); + } + } + finally + { + File.Delete(tempFile); + } + } + } + + [IntegrationTest] + public async Task CanGetWorkflowRunLogs() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context, WorkflowRunStatus.Completed); + + var logs = await fixture.GetLogs(owner, name, runId); + + Assert.NotNull(logs); + Assert.NotEmpty(logs); + + var tempFile = Path.GetTempFileName(); + + try + { + File.WriteAllBytes(tempFile, logs); + + using (var archive = ZipFile.OpenRead(tempFile)) + { + Assert.NotEmpty(archive.Entries); + } + } + finally + { + File.Delete(tempFile); + } + } + } + + [IntegrationTest] + public async Task CanCancelWorkflowRun() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context); + + await fixture.Cancel(owner, name, runId); + } + } + + [IntegrationTest] + public async Task CanRerunWorkflow() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context, WorkflowRunStatus.Completed); + + await fixture.Rerun(owner, name, runId); + } + } + + [IntegrationTest] + public async Task CanRerunFailedJobs() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun( + github, + context, + WorkflowRunStatus.Completed, + BrokenWorkflow); + + await fixture.RerunFailedJobs(owner, name, runId); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowRunUsage() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + (var workflowFileName, var runId) = await CreateWorkflowAndWaitForFirstRun(github, context, WorkflowRunStatus.Completed); + + var usage = await fixture.GetUsage(owner, name, runId); + + Assert.NotNull(usage); + Assert.NotEqual(0, usage.RunDurationMs); + Assert.NotNull(usage.Billable); + } + } + + [IntegrationTest] + public async Task CanListWorkflowRunsByWorkflow() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows.Runs; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + (var workflowFileName, _) = await CreateWorkflowAndWaitForFirstRun(github, context); + + var runs = await fixture.ListByWorkflow(owner, name, workflowFileName); + + Assert.NotNull(runs); + Assert.NotEqual(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.NotEmpty(runs.WorkflowRuns); + + runs = await fixture.ListByWorkflow(owner, name, workflowFileName, new WorkflowRunsRequest() { Branch = "main" }); + + Assert.NotNull(runs); + Assert.NotEqual(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.NotEmpty(runs.WorkflowRuns); + + runs = await fixture.ListByWorkflow(owner, name, workflowFileName, new WorkflowRunsRequest() { Branch = "not-main" }); + + Assert.NotNull(runs); + Assert.Equal(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.Empty(runs.WorkflowRuns); + + var workflowId = await GetWorkflowId(github, context, workflowFileName); + + runs = await fixture.ListByWorkflow(owner, name, workflowId); + + Assert.NotNull(runs); + Assert.NotEqual(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.NotEmpty(runs.WorkflowRuns); + + runs = await fixture.ListByWorkflow(owner, name, workflowId, new WorkflowRunsRequest() { Branch = "main" }); + + Assert.NotNull(runs); + Assert.NotEqual(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.NotEmpty(runs.WorkflowRuns); + + runs = await fixture.ListByWorkflow(owner, name, workflowId, new WorkflowRunsRequest() { Branch = "not-main" }); + + Assert.NotNull(runs); + Assert.Equal(0, runs.TotalCount); + Assert.NotNull(runs.WorkflowRuns); + Assert.Empty(runs.WorkflowRuns); + } + } + + private static async Task<(string WorkflowFileName, long RunId)> CreateWorkflowAndWaitForFirstRun( + IGitHubClient github, + RepositoryContext context, + WorkflowRunStatus? statusToWaitFor = null, + string workflowFile = HelloWorldWorkflow) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + var workflowFileName = ".github/workflows/hello-world.yml"; + + _ = await github.Repository.Content.CreateFile( + owner, + name, + workflowFileName, + new CreateFileRequest("Create test workflow", workflowFile)); + + var totalTimeout = TimeSpan.FromMinutes(1); + var loopDelay = TimeSpan.FromSeconds(1); + + using (var cts = new CancellationTokenSource(totalTimeout)) + { + while (!cts.IsCancellationRequested) + { + cts.Token.ThrowIfCancellationRequested(); + + var runs = await github.Actions.Workflows.Runs.List(owner, name); + + await Task.Delay(loopDelay); + + if (runs.TotalCount > 0 && (statusToWaitFor == null || runs.WorkflowRuns[0].Status == statusToWaitFor)) + { + return (workflowFileName, runs.WorkflowRuns[0].Id); + } + } + } + + throw new InvalidOperationException("Timed out waiting for workflow run."); + } + + private static async Task GetWorkflowId( + IGitHubClient github, + RepositoryContext context, + string workflowFileName) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + var workflow = await github.Actions.Workflows.Get(owner, name, workflowFileName); + return workflow.Id; + } +} diff --git a/Octokit.Tests.Integration/Clients/ActionsWorkflowsClientTests.cs b/Octokit.Tests.Integration/Clients/ActionsWorkflowsClientTests.cs new file mode 100644 index 0000000000..59f9e923a5 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/ActionsWorkflowsClientTests.cs @@ -0,0 +1,195 @@ +using System; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +public class ActionsWorkflowsClientTests +{ + private static readonly string HelloWorldWorkflow = @" +name: hello +on: [ push, workflow_dispatch ] +jobs: + world: + runs-on: [ ubuntu-latest ] + steps: + - run: echo ""Hello world."""; + + [IntegrationTest] + public async Task CanGetWorkflow() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + var workflowFileName = await CreateWorkflow(github, context); + + var workflowByName = await fixture.Get(owner, name, workflowFileName); + Assert.NotNull(workflowByName); + Assert.Equal(workflowFileName, workflowByName.Path); + + var workflowById = await fixture.Get(owner, name, workflowByName.Id); + Assert.NotNull(workflowById); + Assert.Equal(workflowByName.Id, workflowById.Id); + } + } + + [IntegrationTest] + public async Task CanGetWorkflowUsage() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + var workflowFileName = await CreateWorkflow(github, context); + + var usage = await fixture.GetUsage(owner, name, workflowFileName); + Assert.NotNull(usage); + Assert.NotNull(usage.Billable); + + var workflowId = await GetWorkflowId(github, context, workflowFileName); + + usage = await fixture.GetUsage(owner, name, workflowId); + Assert.NotNull(usage); + Assert.NotNull(usage.Billable); + } + } + + [IntegrationTest] + public async Task CanListWorkflows() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + var workflows = await fixture.List(owner, name); + + Assert.NotNull(workflows); + Assert.Equal(0, workflows.TotalCount); + Assert.NotNull(workflows.Workflows); + Assert.Empty(workflows.Workflows); + + var workflowFileName = await CreateWorkflow(github, context); + + workflows = await fixture.List(owner, name); + + Assert.NotNull(workflows); + Assert.Equal(1, workflows.TotalCount); + Assert.NotNull(workflows.Workflows); + + var workflow = Assert.Single(workflows.Workflows); + + Assert.NotNull(workflow); + Assert.Equal(workflowFileName, workflow.Path); + } + } + + [IntegrationTest] + public async Task CanDispatchWorkflow() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + var workflowFileName = await CreateWorkflow(github, context); + var reference = "main"; + + await fixture.CreateDispatch(owner, name, workflowFileName, new CreateWorkflowDispatch(reference)); + + var workflowId = await GetWorkflowId(github, context, workflowFileName); + + await fixture.CreateDispatch(owner, name, workflowId, new CreateWorkflowDispatch(reference)); + } + } + + [IntegrationTest] + public async Task CanEnableAndDisableWorkflow() + { + var github = Helper.GetAuthenticatedClient(); + var fixture = github.Actions.Workflows; + + using (var context = await github.CreateRepositoryContextWithAutoInit()) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + var workflowFileName = await CreateWorkflow(github, context); + var workflowId = await AssertWorkflowState(github, context, workflowFileName, "active"); + + await fixture.Disable(owner, name, workflowId); + + await AssertWorkflowState(github, context, workflowFileName, "disabled_manually"); + + await fixture.Enable(owner, name, workflowId); + + await AssertWorkflowState(github, context, workflowFileName, "active"); + + await fixture.Disable(owner, name, workflowFileName); + + await AssertWorkflowState(github, context, workflowFileName, "disabled_manually"); + + await fixture.Enable(owner, name, workflowFileName); + + await AssertWorkflowState(github, context, workflowFileName, "active"); + } + } + + private static async Task CreateWorkflow(IGitHubClient github, RepositoryContext context) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + var workflowFileName = ".github/workflows/hello-world.yml"; + + _ = await github.Repository.Content.CreateFile( + owner, + name, + workflowFileName, + new CreateFileRequest("Create test workflow", HelloWorldWorkflow)); + + await Task.Delay(TimeSpan.FromSeconds(1.5)); + + return workflowFileName; + } + + private static async Task AssertWorkflowState( + IGitHubClient github, + RepositoryContext context, + string workflowFileName, + string expected) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + var workflow = await github.Actions.Workflows.Get(owner, name, workflowFileName); + Assert.NotNull(workflow); + Assert.Equal(expected, workflow.State); + + return workflow.Id; + } + + private static async Task GetWorkflowId( + IGitHubClient github, + RepositoryContext context, + string workflowFileName) + { + var owner = context.Repository.Owner.Login; + var name = context.Repository.Name; + + var workflow = await github.Actions.Workflows.Get(owner, name, workflowFileName); + return workflow.Id; + } +} diff --git a/Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs deleted file mode 100644 index 20548d7b1f..0000000000 --- a/Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit.Tests.Integration.Clients -{ - public class OrganizationActionsClientTests - { - } -} diff --git a/Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs deleted file mode 100644 index 95f60732d1..0000000000 --- a/Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit.Tests.Integration.Clients -{ - public class RepositoryActionsClientTests - { - } -} diff --git a/Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs deleted file mode 100644 index e05a22c110..0000000000 --- a/Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit.Tests.Integration.Reactive -{ - public class ObservableOrganizationActionsClientTests - { - } -} diff --git a/Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs deleted file mode 100644 index 3e2ca85d2e..0000000000 --- a/Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit.Tests.Integration.Reactive -{ - public class ObservableRepositoryActionsClientTests - { - } -} diff --git a/Octokit.Tests/Clients/ActionsWorkflowJobsClientTests.cs b/Octokit.Tests/Clients/ActionsWorkflowJobsClientTests.cs new file mode 100644 index 0000000000..1ec99df17b --- /dev/null +++ b/Octokit.Tests/Clients/ActionsWorkflowJobsClientTests.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ActionsWorkflowJobsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ActionsWorkflowJobsClient(null)); + } + } + + public class TheRerunMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await client.Rerun("fake", "repo", 123); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/jobs/123/rerun")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.Rerun(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Rerun("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.Rerun("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Rerun("fake", "", 123)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await client.Get("fake", "repo", 123); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/jobs/123")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.Get(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Get("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.Get("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Get("fake", "", 123)); + } + } + + public class TheGetLogsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await client.GetLogs("fake", "repo", 123); + + connection.Connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/jobs/123/logs"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.GetLogs(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.GetLogs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.GetLogs("", "repo", 123)); + await Assert.ThrowsAsync(() => client.GetLogs("fake", "", 123)); + } + } + + public class TheListMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await client.List("fake", "repo", 123); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/jobs"), + Args.EmptyDictionary, + Args.ApiOptions); + + await client.List("fake", "repo", 123, 456); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/attempts/456/jobs"), + null, + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRequest() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + var request = new WorkflowRunJobsRequest + { + Filter = WorkflowRunJobsFilter.All, + }; + + await client.List("fake", "repo", 123, request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/jobs"), + Arg.Is>(x => + x.Count == 1 + && x["filter"] == "all"), + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRequestWithApiOptions() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + var request = new WorkflowRunJobsRequest { Filter = WorkflowRunJobsFilter.Latest }; + var options = new ApiOptions { PageSize = 1 }; + + await client.List("fake", "repo", 123, request, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/jobs"), + Arg.Is>(x => + x.Count == 1 + && x["filter"] == "latest"), + options); + + await client.List("fake", "repo", 123, 456, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/attempts/456/jobs"), + null, + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.List(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.List("fake", null, 123)); + + await Assert.ThrowsAsync(() => client.List(null, "repo", 123, 456)); + await Assert.ThrowsAsync(() => client.List("fake", null, 123, 456)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowJobsClient(connection); + + await Assert.ThrowsAsync(() => client.List("", "repo", 123)); + await Assert.ThrowsAsync(() => client.List("fake", "", 123)); + + await Assert.ThrowsAsync(() => client.List("", "repo", 123, 456)); + await Assert.ThrowsAsync(() => client.List("fake", "", 123, 456)); + } + } + } +} diff --git a/Octokit.Tests/Clients/ActionsWorkflowRunsClientTests.cs b/Octokit.Tests/Clients/ActionsWorkflowRunsClientTests.cs new file mode 100644 index 0000000000..21b159e152 --- /dev/null +++ b/Octokit.Tests/Clients/ActionsWorkflowRunsClientTests.cs @@ -0,0 +1,839 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ActionsWorkflowRunsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ActionsWorkflowRunsClient(null)); + } + } + + public class TheApproveMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.Approve("fake", "repo", 123); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/approve")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Approve(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Approve("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Approve("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Approve("fake", "", 123)); + } + } + + public class TheCancelMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.Cancel("fake", "repo", 123); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/cancel")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Cancel(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Cancel("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Cancel("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Cancel("fake", "", 123)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.Delete("fake", "repo", 123); + + connection.Received().Delete( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Delete(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Delete("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Delete("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Delete("fake", "", 123)); + } + } + + public class TheDeleteLogsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.DeleteLogs("fake", "repo", 123); + + connection.Received().Delete( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/logs")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.DeleteLogs(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.DeleteLogs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.DeleteLogs("", "repo", 123)); + await Assert.ThrowsAsync(() => client.DeleteLogs("fake", "", 123)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.Get("fake", "repo", 123); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Get(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Get("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Get("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Get("fake", "", 123)); + } + } + + public class TheGetLogsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var headers = new Dictionary(); + var response = TestSetup.CreateResponse(HttpStatusCode.OK, new byte[] { 1, 2, 3, 4 }, headers); + var responseTask = Task.FromResult>(new ApiResponse(response)); + + var connection = Substitute.For(); + connection.GetRaw(Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/logs"), null) + .Returns(responseTask); + + var apiConnection = Substitute.For(); + apiConnection.Connection.Returns(connection); + + var client = new ActionsWorkflowRunsClient(apiConnection); + + var actual = await client.GetLogs("fake", "repo", 123); + + Assert.Equal(new byte[] { 1, 2, 3, 4 }, actual); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetLogs(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.GetLogs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetLogs("", "repo", 123)); + await Assert.ThrowsAsync(() => client.GetLogs("fake", "", 123)); + } + } + + public class TheGetAttemptMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.GetAttempt("fake", "repo", 123, 456); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/attempts/456"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAttempt(null, "repo", 123, 456)); + await Assert.ThrowsAsync(() => client.GetAttempt("fake", null, 123, 456)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAttempt("", "repo", 123, 456)); + await Assert.ThrowsAsync(() => client.GetAttempt("fake", "", 123, 456)); + } + } + + public class TheGetAttemptLogsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var headers = new Dictionary(); + var response = TestSetup.CreateResponse(HttpStatusCode.OK, new byte[] { 1, 2, 3, 4 }, headers); + var responseTask = Task.FromResult>(new ApiResponse(response)); + + var connection = Substitute.For(); + connection.GetRaw(Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/attempts/456/logs"), null) + .Returns(responseTask); + + var apiConnection = Substitute.For(); + apiConnection.Connection.Returns(connection); + + var client = new ActionsWorkflowRunsClient(apiConnection); + + var actual = await client.GetAttemptLogs("fake", "repo", 123, 456); + + Assert.Equal(new byte[] { 1, 2, 3, 4 }, actual); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAttemptLogs(null, "repo", 123, 456)); + await Assert.ThrowsAsync(() => client.GetAttemptLogs("fake", null, 123, 456)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetAttemptLogs("", "repo", 123, 456)); + await Assert.ThrowsAsync(() => client.GetAttemptLogs("fake", "", 123, 456)); + } + } + + public class TheGetReviewHistoryMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.GetReviewHistory("fake", "repo", 123); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/approvals")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var workflowRunsRequest = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + await Assert.ThrowsAsync(() => client.GetReviewHistory(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.GetReviewHistory("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var workflowRunsRequest = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + await Assert.ThrowsAsync(() => client.GetReviewHistory("", "repo", 123)); + await Assert.ThrowsAsync(() => client.GetReviewHistory("fake", "", 123)); + } + } + + public class TheGetUsageMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.GetUsage("fake", "repo", 123); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/timing"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetUsage(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.GetUsage("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.GetUsage("", "repo", 123)); + await Assert.ThrowsAsync(() => client.GetUsage("fake", "", 123)); + } + } + + public class TheListMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.List("fake", "repo"); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs"), + Args.EmptyDictionary, + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRequest() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest + { + Actor = "octocat", + Branch = "main", + CheckSuiteId = 42, + Created = "2020-2022", + Event = "push", + ExcludePullRequests = true, + Status = CheckRunStatusFilter.InProgress, + }; + + await client.List("fake", "repo", request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs"), + Arg.Is>(x => + x.Count == 7 + && x["actor"] == "octocat" + && x["branch"] == "main" + && x["check_suite_id"] == "42" + && x["created"] == "2020-2022" + && x["event"] == "push" + && x["branch"] == "main" + && x["exclude_pull_requests"] == "true" + && x["status"] == "in_progress"), + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRequestWithApiOptions() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest { Branch = "main", CheckSuiteId = 42, Status = CheckRunStatusFilter.InProgress }; + var options = new ApiOptions { PageSize = 1 }; + + await client.List("fake", "repo", request, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs"), + Arg.Is>(x => + x.Count == 3 + && x["branch"] == "main" + && x["status"] == "in_progress" + && x["check_suite_id"] == "42"), + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var workflowRunsRequest = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + await Assert.ThrowsAsync(() => client.List(null, "repo")); + await Assert.ThrowsAsync(() => client.List("fake", null)); + + await Assert.ThrowsAsync(() => client.List(null, "repo", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.List("fake", null, workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.List("fake", "repo", null)); + + await Assert.ThrowsAsync(() => client.List(null, "repo", workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.List("fake", null, workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.List("fake", "repo", null, options)); + await Assert.ThrowsAsync(() => client.List("fake", "repo", workflowRunsRequest, null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var workflowRunsRequest = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + await Assert.ThrowsAsync(() => client.List("", "repo")); + await Assert.ThrowsAsync(() => client.List("fake", "")); + + await Assert.ThrowsAsync(() => client.List("", "repo", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.List("fake", "", workflowRunsRequest)); + + await Assert.ThrowsAsync(() => client.List("", "repo", workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.List("fake", "", workflowRunsRequest, options)); + } + } + + public class TheListByWorkflowMethod + { + [Fact] + public async Task RequestsCorrectUrlWithId() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.ListByWorkflow("fake", "repo", 123); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/runs"), + Args.EmptyDictionary, + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithName() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await client.ListByWorkflow("fake", "repo", "main.yml"); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yml/runs"), + Args.EmptyDictionary, + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithIdWithRequest() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest + { + Actor = "octocat", + Branch = "main", + CheckSuiteId = 42, + Created = "2020-2022", + Event = "push", + ExcludePullRequests = true, + Status = CheckRunStatusFilter.InProgress, + }; + + await client.ListByWorkflow("fake", "repo", 123, request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/runs"), + Arg.Is>(x => + x.Count == 7 + && x["actor"] == "octocat" + && x["branch"] == "main" + && x["check_suite_id"] == "42" + && x["created"] == "2020-2022" + && x["event"] == "push" + && x["branch"] == "main" + && x["exclude_pull_requests"] == "true" + && x["status"] == "in_progress"), + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithNameWithRequest() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest + { + Actor = "octocat", + Branch = "main", + CheckSuiteId = 42, + Created = "2020-2022", + Event = "push", + ExcludePullRequests = true, + Status = CheckRunStatusFilter.InProgress, + }; + + await client.ListByWorkflow("fake", "repo", "main.yml", request); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yml/runs"), + Arg.Is>(x => + x.Count == 7 + && x["actor"] == "octocat" + && x["branch"] == "main" + && x["check_suite_id"] == "42" + && x["created"] == "2020-2022" + && x["event"] == "push" + && x["branch"] == "main" + && x["exclude_pull_requests"] == "true" + && x["status"] == "in_progress"), + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithIdWithRequestWithApiOptions() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest { Branch = "main", CheckSuiteId = 42, Status = CheckRunStatusFilter.InProgress }; + var options = new ApiOptions { PageSize = 1 }; + + await client.ListByWorkflow("fake", "repo", 123, request, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/runs"), + Arg.Is>(x => + x.Count == 3 + && x["branch"] == "main" + && x["status"] == "in_progress" + && x["check_suite_id"] == "42"), + options); + } + + [Fact] + public async Task RequestsCorrectUrlWithNameWithRequestWithApiOptions() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest { Branch = "main", CheckSuiteId = 42, Status = CheckRunStatusFilter.InProgress }; + var options = new ApiOptions { PageSize = 1 }; + + await client.ListByWorkflow("fake", "repo", "main.yml", request, options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yml/runs"), + Arg.Is>(x => + x.Count == 3 + && x["branch"] == "main" + && x["status"] == "in_progress" + && x["check_suite_id"] == "42"), + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var workflowRunsRequest = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + await Assert.ThrowsAsync(() => client.ListByWorkflow(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", null, 123)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow(null, "repo", "main.yml")); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", null, "main.yml")); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", null)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow(null, "repo", 123, workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", null, 123, workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", 123, null)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow(null, "repo", "main.yml", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", null, "main.yml", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", null, workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "main.yml", null)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow(null, "repo", 123, workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", null, 123, workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", 123, null, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", 123, workflowRunsRequest, null)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow(null, "repo", "main.yml", workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", null, "main.yml", workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", null, workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "main.yml", null, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "main.yml", workflowRunsRequest, null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var workflowRunsRequest = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + await Assert.ThrowsAsync(() => client.ListByWorkflow("", "repo", 123)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "", 123)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow("", "repo", "main.yml")); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "", "main.yml")); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "")); + + await Assert.ThrowsAsync(() => client.ListByWorkflow("", "repo", 123, workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "", 123, workflowRunsRequest)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow("", "repo", "main.yml", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "", "main.yml", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "", workflowRunsRequest)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow("", "repo", "main.yml", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "", "main.yml", workflowRunsRequest)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "", workflowRunsRequest)); + + await Assert.ThrowsAsync(() => client.ListByWorkflow("", "repo", "main.yml", workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "", "main.yml", workflowRunsRequest, options)); + await Assert.ThrowsAsync(() => client.ListByWorkflow("fake", "repo", "", workflowRunsRequest, options)); + } + } + + public class TheRerunMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + await client.Rerun("fake", "repo", 123); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/rerun")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Rerun(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Rerun("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.Rerun("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Rerun("fake", "", 123)); + } + } + + public class TheRerunFailedJobsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + await client.RerunFailedJobs("fake", "repo", 123); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/rerun-failed-jobs")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.RerunFailedJobs(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.RerunFailedJobs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + await Assert.ThrowsAsync(() => client.RerunFailedJobs("", "repo", 123)); + await Assert.ThrowsAsync(() => client.RerunFailedJobs("fake", "", 123)); + } + } + + public class TheReviewFailedJobsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var review = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, ""); + + await client.ReviewPendingDeployments("fake", "repo", 123, review); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/pending_deployments"), + review); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var review = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, "Ship it!"); + + await Assert.ThrowsAsync(() => client.ReviewPendingDeployments(null, "repo", 123, review)); + await Assert.ThrowsAsync(() => client.ReviewPendingDeployments("fake", null, 123, review)); + await Assert.ThrowsAsync(() => client.ReviewPendingDeployments("fake", "repo", 123, null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowRunsClient(connection); + + var review = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, "Ship it!"); + + await Assert.ThrowsAsync(() => client.ReviewPendingDeployments("", "repo", 123, review)); + await Assert.ThrowsAsync(() => client.ReviewPendingDeployments("fake", "", 123, review)); + } + } + } +} diff --git a/Octokit.Tests/Clients/ActionsWorkflowsClientTests.cs b/Octokit.Tests/Clients/ActionsWorkflowsClientTests.cs new file mode 100644 index 0000000000..155b7e7786 --- /dev/null +++ b/Octokit.Tests/Clients/ActionsWorkflowsClientTests.cs @@ -0,0 +1,383 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ActionsWorkflowsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ActionsWorkflowsClient(null)); + } + } + + public class TheClientProperties + { + [Fact] + public void AreNotNull() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + Assert.NotNull(client.Jobs); + Assert.NotNull(client.Runs); + } + } + + public class TheCreateDispatchMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + await client.CreateDispatch("fake", "repo", 123, createDispatch); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/dispatches"), + createDispatch); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + await client.CreateDispatch("fake", "repo", "main.yaml", createDispatch); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yaml/dispatches"), + createDispatch); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + await Assert.ThrowsAsync(() => client.CreateDispatch(null, "repo", 123, createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", null, 123, createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", "repo", 123, null)); + + await Assert.ThrowsAsync(() => client.CreateDispatch(null, "repo", "main.yaml", createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", null, "main.yaml", createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", "repo", null, createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", "repo", "main.yaml", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + await Assert.ThrowsAsync(() => client.CreateDispatch("", "repo", 123, createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", "", 123, createDispatch)); + + await Assert.ThrowsAsync(() => client.CreateDispatch("", "repo", "main.yaml", createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", "", "main.yaml", createDispatch)); + await Assert.ThrowsAsync(() => client.CreateDispatch("fake", "repo", "", createDispatch)); + } + } + + public class TheDisableMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.Disable("fake", "repo", 123); + + connection.Received().Put( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/disable")); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.Disable("fake", "repo", "main.yaml"); + + connection.Received().Put( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yaml/disable")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.Disable(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Disable("fake", null, 123)); + + await Assert.ThrowsAsync(() => client.Disable(null, "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.Disable("fake", null, "main.yaml")); + await Assert.ThrowsAsync(() => client.Disable("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.Disable("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Disable("fake", "", 123)); + + await Assert.ThrowsAsync(() => client.Disable("", "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.Disable("fake", "", "main.yaml")); + await Assert.ThrowsAsync(() => client.Disable("fake", "repo", "")); + } + } + + public class TheEnableMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.Enable("fake", "repo", 123); + + connection.Received().Put( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/enable")); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.Enable("fake", "repo", "main.yaml"); + + connection.Received().Put( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yaml/enable")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.Enable(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Enable("fake", null, 123)); + + await Assert.ThrowsAsync(() => client.Enable(null, "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.Enable("fake", null, "main.yaml")); + await Assert.ThrowsAsync(() => client.Enable("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.Enable("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Enable("fake", "", 123)); + + await Assert.ThrowsAsync(() => client.Enable("", "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.Enable("fake", "", "main.yaml")); + await Assert.ThrowsAsync(() => client.Enable("fake", "repo", "")); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.Get("fake", "repo", 123); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123"), + null); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.Get("fake", "repo", "main.yaml"); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yaml"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.Get(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.Get("fake", null, 123)); + + await Assert.ThrowsAsync(() => client.Get(null, "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.Get("fake", null, "main.yaml")); + await Assert.ThrowsAsync(() => client.Get("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.Get("", "repo", 123)); + await Assert.ThrowsAsync(() => client.Get("fake", "", 123)); + + await Assert.ThrowsAsync(() => client.Get("", "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.Get("fake", "", "main.yaml")); + await Assert.ThrowsAsync(() => client.Get("fake", "repo", "")); + } + } + + public class TheGetUsageMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.GetUsage("fake", "repo", 123); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/123/timing"), + null); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.GetUsage("fake", "repo", "main.yaml"); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows/main.yaml/timing"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.GetUsage(null, "repo", 123)); + await Assert.ThrowsAsync(() => client.GetUsage("fake", null, 123)); + + await Assert.ThrowsAsync(() => client.GetUsage(null, "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.GetUsage("fake", null, "main.yaml")); + await Assert.ThrowsAsync(() => client.GetUsage("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.GetUsage("", "repo", 123)); + await Assert.ThrowsAsync(() => client.GetUsage("fake", "", 123)); + + await Assert.ThrowsAsync(() => client.GetUsage("", "repo", "main.yaml")); + await Assert.ThrowsAsync(() => client.GetUsage("fake", "", "main.yaml")); + await Assert.ThrowsAsync(() => client.GetUsage("fake", "repo", "")); + } + } + + public class TheListMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await client.List("fake", "repo"); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows"), + null, + Args.ApiOptions); + } + + [Fact] + public async Task RequestsCorrectUrlWithRequestWithApiOptions() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + var options = new ApiOptions { PageSize = 1 }; + + await client.List("fake", "repo", options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/workflows"), + null, + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.List(null, "repo")); + await Assert.ThrowsAsync(() => client.List("fake", null)); + + await Assert.ThrowsAsync(() => client.List(null, "repo", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.List("fake", null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.List("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ActionsWorkflowsClient(connection); + + await Assert.ThrowsAsync(() => client.List("", "repo")); + await Assert.ThrowsAsync(() => client.List("fake", "")); + + await Assert.ThrowsAsync(() => client.List("", "repo", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.List("fake", "", ApiOptions.None)); + } + } + } +} diff --git a/Octokit.Tests/Models/CreateWorkflowDispatchTests.cs b/Octokit.Tests/Models/CreateWorkflowDispatchTests.cs new file mode 100644 index 0000000000..a5d6004d40 --- /dev/null +++ b/Octokit.Tests/Models/CreateWorkflowDispatchTests.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class CreateWorkflowDispatchTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new CreateWorkflowDispatch(null)); + } + + [Fact] + public void EnsuresNonEmptyArguments() + { + Assert.Throws(() => new CreateWorkflowDispatch("")); + } + } + + [Fact] + public void CanBeSerialized() + { + var item = new CreateWorkflowDispatch("main"); + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Serialize(item); + + Assert.Equal(@"{""ref"":""main""}", payload); + } + + [Fact] + public void CanBeSerializedWithInputs() + { + var item = new CreateWorkflowDispatch("main"); + + item.Inputs = new Dictionary() + { + ["foo"] = 1, + ["bar"] = "qux", + }; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Serialize(item); + + Assert.Equal(@"{""ref"":""main"",""inputs"":{""foo"":1,""bar"":""qux""}}", payload); + } + } +} diff --git a/Octokit.Tests/Models/EnvironmentApprovalTests.cs b/Octokit.Tests/Models/EnvironmentApprovalTests.cs new file mode 100644 index 0000000000..040dd14691 --- /dev/null +++ b/Octokit.Tests/Models/EnvironmentApprovalTests.cs @@ -0,0 +1,70 @@ +using System; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class EnvironmentApprovalTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""state"": ""approved"", +""comment"": ""Ship it!"", +""environments"": [ + { + ""id"": 161088068, + ""node_id"": ""MDExOkVudmlyb25tZW50MTYxMDg4MDY4"", + ""name"": ""staging"", + ""url"": ""https://api.github.com/repos/github/hello-world/environments/staging"", + ""html_url"": ""https://github.com/github/hello-world/deployments/activity_log?environments_filter=staging"", + ""created_at"": ""2020-11-23T22:00:40Z"", + ""updated_at"": ""2020-11-23T22:00:41Z"" + } +], +""user"": { + ""login"": ""octocat"", + ""id"": 1, + ""node_id"": ""MDQ6VXNlcjE="", + ""avatar_url"": ""https://github.com/images/error/octocat_happy.gif"", + ""gravatar_id"": """", + ""url"": ""https://api.github.com/users/octocat"", + ""html_url"": ""https://github.com/octocat"", + ""followers_url"": ""https://api.github.com/users/octocat/followers"", + ""following_url"": ""https://api.github.com/users/octocat/following{/other_user}"", + ""gists_url"": ""https://api.github.com/users/octocat/gists{/gist_id}"", + ""starred_url"": ""https://api.github.com/users/octocat/starred{/owner}{/repo}"", + ""subscriptions_url"": ""https://api.github.com/users/octocat/subscriptions"", + ""organizations_url"": ""https://api.github.com/users/octocat/orgs"", + ""repos_url"": ""https://api.github.com/users/octocat/repos"", + ""events_url"": ""https://api.github.com/users/octocat/events{/privacy}"", + ""received_events_url"": ""https://api.github.com/users/octocat/received_events"", + ""type"": ""User"", + ""site_admin"": false +} +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal("approved", payload.State); + Assert.Equal("Ship it!", payload.Comment); + Assert.NotNull(payload.User); + Assert.NotNull(payload.Environments); + + var approval = Assert.Single(payload.Environments); + + Assert.NotNull(approval); + Assert.Equal(161088068, approval.Id); + Assert.Equal("MDExOkVudmlyb25tZW50MTYxMDg4MDY4", approval.NodeId); + Assert.Equal("staging", approval.Name); + Assert.Equal("https://api.github.com/repos/github/hello-world/environments/staging", approval.Url); + Assert.Equal("https://github.com/github/hello-world/deployments/activity_log?environments_filter=staging", approval.HtmlUrl); + Assert.Equal(new DateTimeOffset(2020, 11, 23, 22, 00, 40, TimeSpan.Zero), approval.CreatedAt); + Assert.Equal(new DateTimeOffset(2020, 11, 23, 22, 00, 41, TimeSpan.Zero), approval.UpdatedAt); + } + } +} diff --git a/Octokit.Tests/Models/PendingDeploymentReviewTests.cs b/Octokit.Tests/Models/PendingDeploymentReviewTests.cs new file mode 100644 index 0000000000..0340e6629d --- /dev/null +++ b/Octokit.Tests/Models/PendingDeploymentReviewTests.cs @@ -0,0 +1,37 @@ +using System; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class PendingDeploymentReviewTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new PendingDeploymentReview(null, PendingDeploymentReviewState.Approved, "")); + Assert.Throws(() => new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, null)); + } + + [Fact] + public void EnsuresNonEmptyArguments() + { + Assert.Throws(() => new PendingDeploymentReview(new long[0], PendingDeploymentReviewState.Approved, "")); + } + } + + [Fact] + public void CanBeSerialized() + { + var item = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, "Ship it!"); + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Serialize(item); + + Assert.Equal(@"{""environment_ids"":[1],""state"":""approved"",""comment"":""Ship it!""}", payload); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowJobStepTests.cs b/Octokit.Tests/Models/WorkflowJobStepTests.cs new file mode 100644 index 0000000000..9b6410456b --- /dev/null +++ b/Octokit.Tests/Models/WorkflowJobStepTests.cs @@ -0,0 +1,34 @@ +using System; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowJobStepTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""name"": ""Set up job"", +""status"": ""completed"", +""conclusion"": ""success"", +""number"": 1, +""started_at"": ""2020-01-20T09:42:40.000-08:00"", +""completed_at"": ""2020-01-20T09:42:41.000-08:00"" +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal("Set up job", payload.Name); + Assert.Equal(WorkflowJobStatus.Completed, payload.Status); + Assert.Equal(WorkflowJobConclusion.Success, payload.Conclusion); + Assert.Equal(1, payload.Number); + Assert.Equal(new DateTimeOffset(2020, 01, 20, 09, 42, 40, TimeSpan.FromHours(-8)), payload.StartedAt); + Assert.Equal(new DateTimeOffset(2020, 01, 20, 09, 42, 41, TimeSpan.FromHours(-8)), payload.CompletedAt); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowJobTests.cs b/Octokit.Tests/Models/WorkflowJobTests.cs new file mode 100644 index 0000000000..16a8901c43 --- /dev/null +++ b/Octokit.Tests/Models/WorkflowJobTests.cs @@ -0,0 +1,146 @@ +using System; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowJobTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""id"": 399444496, +""run_id"": 29679449, +""run_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/29679449"", +""node_id"": ""MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng=="", +""head_sha"": ""f83a356604ae3c5d03e1b46ef4d1ca77d64a90b0"", +""url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496"", +""html_url"": ""https://github.com/octo-org/octo-repo/runs/399444496"", +""status"": ""completed"", +""conclusion"": ""success"", +""started_at"": ""2020-01-20T17:42:40Z"", +""completed_at"": ""2020-01-20T17:44:39Z"", +""name"": ""build"", +""steps"": [ + { + ""name"": ""Set up job"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 1, + ""started_at"": ""2020-01-20T09:42:40.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:41.000-08:00"" + }, + { + ""name"": ""Run actions/checkout@v2"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 2, + ""started_at"": ""2020-01-20T09:42:41.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:45.000-08:00"" + }, + { + ""name"": ""Set up Ruby"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 3, + ""started_at"": ""2020-01-20T09:42:45.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:45.000-08:00"" + }, + { + ""name"": ""Run actions/cache@v3"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 4, + ""started_at"": ""2020-01-20T09:42:45.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:48.000-08:00"" + }, + { + ""name"": ""Install Bundler"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 5, + ""started_at"": ""2020-01-20T09:42:48.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:52.000-08:00"" + }, + { + ""name"": ""Install Gems"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 6, + ""started_at"": ""2020-01-20T09:42:52.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:53.000-08:00"" + }, + { + ""name"": ""Run Tests"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 7, + ""started_at"": ""2020-01-20T09:42:53.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:59.000-08:00"" + }, + { + ""name"": ""Deploy to Heroku"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 8, + ""started_at"": ""2020-01-20T09:42:59.000-08:00"", + ""completed_at"": ""2020-01-20T09:44:39.000-08:00"" + }, + { + ""name"": ""Post actions/cache@v3"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 16, + ""started_at"": ""2020-01-20T09:44:39.000-08:00"", + ""completed_at"": ""2020-01-20T09:44:39.000-08:00"" + }, + { + ""name"": ""Complete job"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 17, + ""started_at"": ""2020-01-20T09:44:39.000-08:00"", + ""completed_at"": ""2020-01-20T09:44:39.000-08:00"" + } +], +""check_run_url"": ""https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496"", +""labels"": [ + ""self-hosted"", + ""foo"", + ""bar"" +], +""runner_id"": 1, +""runner_name"": ""my runner"", +""runner_group_id"": 2, +""runner_group_name"": ""my runner group"" +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal(399444496, payload.Id); + Assert.Equal("https://api.github.com/repos/octo-org/octo-repo/actions/runs/29679449", payload.RunUrl); + Assert.Equal("MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng==", payload.NodeId); + Assert.Equal("f83a356604ae3c5d03e1b46ef4d1ca77d64a90b0", payload.HeadSha); + Assert.Equal("https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496", payload.Url); + Assert.Equal("https://github.com/octo-org/octo-repo/runs/399444496", payload.HtmlUrl); + Assert.Equal(WorkflowJobStatus.Completed, payload.Status); + Assert.Equal(WorkflowJobConclusion.Success, payload.Conclusion); + Assert.Equal(new DateTimeOffset(2020, 01, 20, 17, 42, 40, TimeSpan.Zero), payload.StartedAt); + Assert.Equal(new DateTimeOffset(2020, 01, 20, 17, 44, 39, TimeSpan.Zero), payload.CompletedAt); + Assert.Equal("build", payload.Name); + Assert.NotNull(payload.Steps); + Assert.NotEmpty(payload.Steps); + Assert.Equal(10, payload.Steps.Count); + Assert.Equal("https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496", payload.CheckRunUrl); + Assert.Equal(new[] { "self-hosted", "foo", "bar" }, payload.Labels); + Assert.Equal(1, payload.RunnerId); + Assert.Equal("my runner", payload.RunnerName); + Assert.Equal(2, payload.RunnerGroupId); + Assert.Equal("my runner group", payload.RunnerGroupName); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowJobsResponseTests.cs b/Octokit.Tests/Models/WorkflowJobsResponseTests.cs new file mode 100644 index 0000000000..1883d5535a --- /dev/null +++ b/Octokit.Tests/Models/WorkflowJobsResponseTests.cs @@ -0,0 +1,134 @@ +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowJobsResponseTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ + ""total_count"": 1, + ""jobs"": [ + { + ""id"": 399444496, + ""run_id"": 29679449, + ""run_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/29679449"", + ""node_id"": ""MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng=="", + ""head_sha"": ""f83a356604ae3c5d03e1b46ef4d1ca77d64a90b0"", + ""url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496"", + ""html_url"": ""https://github.com/octo-org/octo-repo/runs/399444496"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""started_at"": ""2020-01-20T17:42:40Z"", + ""completed_at"": ""2020-01-20T17:44:39Z"", + ""name"": ""build"", + ""steps"": [ + { + ""name"": ""Set up job"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 1, + ""started_at"": ""2020-01-20T09:42:40.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:41.000-08:00"" + }, + { + ""name"": ""Run actions/checkout@v2"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 2, + ""started_at"": ""2020-01-20T09:42:41.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:45.000-08:00"" + }, + { + ""name"": ""Set up Ruby"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 3, + ""started_at"": ""2020-01-20T09:42:45.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:45.000-08:00"" + }, + { + ""name"": ""Run actions/cache@v3"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 4, + ""started_at"": ""2020-01-20T09:42:45.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:48.000-08:00"" + }, + { + ""name"": ""Install Bundler"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 5, + ""started_at"": ""2020-01-20T09:42:48.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:52.000-08:00"" + }, + { + ""name"": ""Install Gems"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 6, + ""started_at"": ""2020-01-20T09:42:52.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:53.000-08:00"" + }, + { + ""name"": ""Run Tests"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 7, + ""started_at"": ""2020-01-20T09:42:53.000-08:00"", + ""completed_at"": ""2020-01-20T09:42:59.000-08:00"" + }, + { + ""name"": ""Deploy to Heroku"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 8, + ""started_at"": ""2020-01-20T09:42:59.000-08:00"", + ""completed_at"": ""2020-01-20T09:44:39.000-08:00"" + }, + { + ""name"": ""Post actions/cache@v3"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 16, + ""started_at"": ""2020-01-20T09:44:39.000-08:00"", + ""completed_at"": ""2020-01-20T09:44:39.000-08:00"" + }, + { + ""name"": ""Complete job"", + ""status"": ""completed"", + ""conclusion"": ""success"", + ""number"": 17, + ""started_at"": ""2020-01-20T09:44:39.000-08:00"", + ""completed_at"": ""2020-01-20T09:44:39.000-08:00"" + } + ], + ""check_run_url"": ""https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496"", + ""labels"": [ + ""self-hosted"", + ""foo"", + ""bar"" + ], + ""runner_id"": 1, + ""runner_name"": ""my runner"", + ""runner_group_id"": 2, + ""runner_group_name"": ""my runner group"" + } + ] +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal(1, payload.TotalCount); + Assert.NotNull(payload.Jobs); + Assert.NotEmpty(payload.Jobs); + Assert.Equal(1, payload.Jobs.Count); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowReferenceTests.cs b/Octokit.Tests/Models/WorkflowReferenceTests.cs new file mode 100644 index 0000000000..040ec2caaf --- /dev/null +++ b/Octokit.Tests/Models/WorkflowReferenceTests.cs @@ -0,0 +1,27 @@ +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowReferenceTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ + ""path"": ""octocat/Hello-World/.github/workflows/deploy.yml@main"", + ""sha"": ""86e8bc9ecf7d38b1ed2d2cfb8eb87ba9b35b01db"", + ""ref"": ""refs/heads/main"" +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal("octocat/Hello-World/.github/workflows/deploy.yml@main", payload.Path); + Assert.Equal("86e8bc9ecf7d38b1ed2d2cfb8eb87ba9b35b01db", payload.Sha); + Assert.Equal("refs/heads/main", payload.Ref); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowRunTests.cs b/Octokit.Tests/Models/WorkflowRunTests.cs new file mode 100644 index 0000000000..57158fbedc --- /dev/null +++ b/Octokit.Tests/Models/WorkflowRunTests.cs @@ -0,0 +1,257 @@ +using System; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowRunTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""id"": 30433642, +""name"": ""Build"", +""node_id"": ""MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ=="", +""check_suite_id"": 42, +""check_suite_node_id"": ""MDEwOkNoZWNrU3VpdGU0Mg=="", +""head_branch"": ""master"", +""head_sha"": ""acb5820ced9479c074f688cc328bf03f341a511d"", +""path"": "".github/workflows/build.yml@main"", +""run_number"": 562, +""event"": ""push"", +""display_title"": ""Update README.md"", +""status"": ""queued"", +""conclusion"": null, +""workflow_id"": 159038, +""url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642"", +""html_url"": ""https://github.com/octo-org/octo-repo/actions/runs/30433642"", +""pull_requests"": [], +""created_at"": ""2020-01-22T19:33:08Z"", +""updated_at"": ""2020-01-22T19:33:08Z"", +""actor"": { +""login"": ""octocat"", +""id"": 1, +""node_id"": ""MDQ6VXNlcjE="", +""avatar_url"": ""https://github.com/images/error/octocat_happy.gif"", +""gravatar_id"": """", +""url"": ""https://api.github.com/users/octocat"", +""html_url"": ""https://github.com/octocat"", +""followers_url"": ""https://api.github.com/users/octocat/followers"", +""following_url"": ""https://api.github.com/users/octocat/following{/other_user}"", +""gists_url"": ""https://api.github.com/users/octocat/gists{/gist_id}"", +""starred_url"": ""https://api.github.com/users/octocat/starred{/owner}{/repo}"", +""subscriptions_url"": ""https://api.github.com/users/octocat/subscriptions"", +""organizations_url"": ""https://api.github.com/users/octocat/orgs"", +""repos_url"": ""https://api.github.com/users/octocat/repos"", +""events_url"": ""https://api.github.com/users/octocat/events{/privacy}"", +""received_events_url"": ""https://api.github.com/users/octocat/received_events"", +""type"": ""User"", +""site_admin"": false +}, +""run_attempt"": 1, +""referenced_workflows"": [ + { + ""path"": ""octocat/Hello-World/.github/workflows/deploy.yml@main"", + ""sha"": ""86e8bc9ecf7d38b1ed2d2cfb8eb87ba9b35b01db"", + ""ref"": ""refs/heads/main"" + }, + { + ""path"": ""octo-org/octo-repo/.github/workflows/report.yml@v2"", + ""sha"": ""79e9790903e1c3373b1a3e3a941d57405478a232"", + ""ref"": ""refs/tags/v2"" + }, + { + ""path"": ""octo-org/octo-repo/.github/workflows/secure.yml@1595d4b6de6a9e9751fb270a41019ce507d4099e"", + ""sha"": ""1595d4b6de6a9e9751fb270a41019ce507d4099e"" + } +], +""run_started_at"": ""2020-01-22T19:33:08Z"", +""triggering_actor"": { +""login"": ""octocat"", +""id"": 1, +""node_id"": ""MDQ6VXNlcjE="", +""avatar_url"": ""https://github.com/images/error/octocat_happy.gif"", +""gravatar_id"": """", +""url"": ""https://api.github.com/users/octocat"", +""html_url"": ""https://github.com/octocat"", +""followers_url"": ""https://api.github.com/users/octocat/followers"", +""following_url"": ""https://api.github.com/users/octocat/following{/other_user}"", +""gists_url"": ""https://api.github.com/users/octocat/gists{/gist_id}"", +""starred_url"": ""https://api.github.com/users/octocat/starred{/owner}{/repo}"", +""subscriptions_url"": ""https://api.github.com/users/octocat/subscriptions"", +""organizations_url"": ""https://api.github.com/users/octocat/orgs"", +""repos_url"": ""https://api.github.com/users/octocat/repos"", +""events_url"": ""https://api.github.com/users/octocat/events{/privacy}"", +""received_events_url"": ""https://api.github.com/users/octocat/received_events"", +""type"": ""User"", +""site_admin"": false +}, +""jobs_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs"", +""logs_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs"", +""check_suite_url"": ""https://api.github.com/repos/octo-org/octo-repo/check-suites/414944374"", +""artifacts_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts"", +""cancel_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel"", +""rerun_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun"", +""workflow_url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/workflows/159038"", +""head_commit"": { +""id"": ""acb5820ced9479c074f688cc328bf03f341a511d"", +""tree_id"": ""d23f6eedb1e1b9610bbc754ddb5197bfe7271223"", +""message"": ""Create linter.yaml"", +""timestamp"": ""2020-01-22T19:33:05Z"", +""author"": { + ""name"": ""Octo Cat"", + ""email"": ""octocat@github.com"" + }, +""committer"": { + ""name"": ""GitHub"", + ""email"": ""noreply@github.com"" + } +}, +""repository"": { +""id"": 1296269, +""node_id"": ""MDEwOlJlcG9zaXRvcnkxMjk2MjY5"", +""name"": ""Hello-World"", +""full_name"": ""octocat/Hello-World"", +""owner"": { + ""login"": ""octocat"", + ""id"": 1, + ""node_id"": ""MDQ6VXNlcjE="", + ""avatar_url"": ""https://github.com/images/error/octocat_happy.gif"", + ""gravatar_id"": """", + ""url"": ""https://api.github.com/users/octocat"", + ""html_url"": ""https://github.com/octocat"", + ""followers_url"": ""https://api.github.com/users/octocat/followers"", + ""following_url"": ""https://api.github.com/users/octocat/following{/other_user}"", + ""gists_url"": ""https://api.github.com/users/octocat/gists{/gist_id}"", + ""starred_url"": ""https://api.github.com/users/octocat/starred{/owner}{/repo}"", + ""subscriptions_url"": ""https://api.github.com/users/octocat/subscriptions"", + ""organizations_url"": ""https://api.github.com/users/octocat/orgs"", + ""repos_url"": ""https://api.github.com/users/octocat/repos"", + ""events_url"": ""https://api.github.com/users/octocat/events{/privacy}"", + ""received_events_url"": ""https://api.github.com/users/octocat/received_events"", + ""type"": ""User"", + ""site_admin"": false +}, +""private"": false, +""html_url"": ""https://github.com/octocat/Hello-World"", +""description"": ""This your first repo!"", +""fork"": false, +""url"": ""https://api.github.com/repos/octocat/Hello-World"", +""archive_url"": ""https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}"", +""assignees_url"": ""https://api.github.com/repos/octocat/Hello-World/assignees{/user}"", +""blobs_url"": ""https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}"", +""branches_url"": ""https://api.github.com/repos/octocat/Hello-World/branches{/branch}"", +""collaborators_url"": ""https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}"", +""comments_url"": ""https://api.github.com/repos/octocat/Hello-World/comments{/number}"", +""commits_url"": ""https://api.github.com/repos/octocat/Hello-World/commits{/sha}"", +""compare_url"": ""https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}"", +""contents_url"": ""https://api.github.com/repos/octocat/Hello-World/contents/{+path}"", +""contributors_url"": ""https://api.github.com/repos/octocat/Hello-World/contributors"", +""deployments_url"": ""https://api.github.com/repos/octocat/Hello-World/deployments"", +""downloads_url"": ""https://api.github.com/repos/octocat/Hello-World/downloads"", +""events_url"": ""https://api.github.com/repos/octocat/Hello-World/events"", +""forks_url"": ""https://api.github.com/repos/octocat/Hello-World/forks"", +""git_commits_url"": ""https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}"", +""git_refs_url"": ""https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}"", +""git_tags_url"": ""https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}"", +""git_url"": ""git:github.com/octocat/Hello-World.git"", +""issue_comment_url"": ""https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}"", +""issue_events_url"": ""https://api.github.com/repos/octocat/Hello-World/issues/events{/number}"", +""issues_url"": ""https://api.github.com/repos/octocat/Hello-World/issues{/number}"", +""keys_url"": ""https://api.github.com/repos/octocat/Hello-World/keys{/key_id}"", +""labels_url"": ""https://api.github.com/repos/octocat/Hello-World/labels{/name}"", +""languages_url"": ""https://api.github.com/repos/octocat/Hello-World/languages"", +""merges_url"": ""https://api.github.com/repos/octocat/Hello-World/merges"", +""milestones_url"": ""https://api.github.com/repos/octocat/Hello-World/milestones{/number}"", +""notifications_url"": ""https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}"", +""pulls_url"": ""https://api.github.com/repos/octocat/Hello-World/pulls{/number}"", +""releases_url"": ""https://api.github.com/repos/octocat/Hello-World/releases{/id}"", +""ssh_url"": ""git@github.com:octocat/Hello-World.git"", +""stargazers_url"": ""https://api.github.com/repos/octocat/Hello-World/stargazers"", +""statuses_url"": ""https://api.github.com/repos/octocat/Hello-World/statuses/{sha}"", +""subscribers_url"": ""https://api.github.com/repos/octocat/Hello-World/subscribers"", +""subscription_url"": ""https://api.github.com/repos/octocat/Hello-World/subscription"", +""tags_url"": ""https://api.github.com/repos/octocat/Hello-World/tags"", +""teams_url"": ""https://api.github.com/repos/octocat/Hello-World/teams"", +""trees_url"": ""https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}"", +""hooks_url"": ""http://api.github.com/repos/octocat/Hello-World/hooks"" +}, +""head_repository"": { +""id"": 217723378, +""node_id"": ""MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg="", +""name"": ""octo-repo"", +""full_name"": ""octo-org/octo-repo"", +""private"": true, +""owner"": { + ""login"": ""octocat"", + ""id"": 1, + ""node_id"": ""MDQ6VXNlcjE="", + ""avatar_url"": ""https://github.com/images/error/octocat_happy.gif"", + ""gravatar_id"": """", + ""url"": ""https://api.github.com/users/octocat"", + ""html_url"": ""https://github.com/octocat"", + ""followers_url"": ""https://api.github.com/users/octocat/followers"", + ""following_url"": ""https://api.github.com/users/octocat/following{/other_user}"", + ""gists_url"": ""https://api.github.com/users/octocat/gists{/gist_id}"", + ""starred_url"": ""https://api.github.com/users/octocat/starred{/owner}{/repo}"", + ""subscriptions_url"": ""https://api.github.com/users/octocat/subscriptions"", + ""organizations_url"": ""https://api.github.com/users/octocat/orgs"", + ""repos_url"": ""https://api.github.com/users/octocat/repos"", + ""events_url"": ""https://api.github.com/users/octocat/events{/privacy}"", + ""received_events_url"": ""https://api.github.com/users/octocat/received_events"", + ""type"": ""User"", + ""site_admin"": false +}, +""html_url"": ""https://github.com/octo-org/octo-repo"", +""description"": null, +""fork"": false, +""url"": ""https://api.github.com/repos/octo-org/octo-repo"", +""forks_url"": ""https://api.github.com/repos/octo-org/octo-repo/forks"", +""keys_url"": ""https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}"", +""collaborators_url"": ""https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}"", +""teams_url"": ""https://api.github.com/repos/octo-org/octo-repo/teams"", +""hooks_url"": ""https://api.github.com/repos/octo-org/octo-repo/hooks"", +""issue_events_url"": ""https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}"", +""events_url"": ""https://api.github.com/repos/octo-org/octo-repo/events"", +""assignees_url"": ""https://api.github.com/repos/octo-org/octo-repo/assignees{/user}"", +""branches_url"": ""https://api.github.com/repos/octo-org/octo-repo/branches{/branch}"", +""tags_url"": ""https://api.github.com/repos/octo-org/octo-repo/tags"", +""blobs_url"": ""https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}"", +""git_tags_url"": ""https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}"", +""git_refs_url"": ""https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}"", +""trees_url"": ""https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}"", +""statuses_url"": ""https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}"", +""languages_url"": ""https://api.github.com/repos/octo-org/octo-repo/languages"", +""stargazers_url"": ""https://api.github.com/repos/octo-org/octo-repo/stargazers"", +""contributors_url"": ""https://api.github.com/repos/octo-org/octo-repo/contributors"", +""subscribers_url"": ""https://api.github.com/repos/octo-org/octo-repo/subscribers"", +""subscription_url"": ""https://api.github.com/repos/octo-org/octo-repo/subscription"", +""commits_url"": ""https://api.github.com/repos/octo-org/octo-repo/commits{/sha}"", +""git_commits_url"": ""https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}"", +""comments_url"": ""https://api.github.com/repos/octo-org/octo-repo/comments{/number}"", +""issue_comment_url"": ""https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}"", +""contents_url"": ""https://api.github.com/repos/octo-org/octo-repo/contents/{+path}"", +""compare_url"": ""https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}"", +""merges_url"": ""https://api.github.com/repos/octo-org/octo-repo/merges"", +""archive_url"": ""https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}"", +""downloads_url"": ""https://api.github.com/repos/octo-org/octo-repo/downloads"", +""issues_url"": ""https://api.github.com/repos/octo-org/octo-repo/issues{/number}"", +""pulls_url"": ""https://api.github.com/repos/octo-org/octo-repo/pulls{/number}"", +""milestones_url"": ""https://api.github.com/repos/octo-org/octo-repo/milestones{/number}"", +""notifications_url"": ""https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}"", +""labels_url"": ""https://api.github.com/repos/octo-org/octo-repo/labels{/name}"", +""releases_url"": ""https://api.github.com/repos/octo-org/octo-repo/releases{/id}"", +""deployments_url"": ""https://api.github.com/repos/octo-org/octo-repo/deployments"" +} +} +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowRunUsageTests.cs b/Octokit.Tests/Models/WorkflowRunUsageTests.cs new file mode 100644 index 0000000000..df434f4efb --- /dev/null +++ b/Octokit.Tests/Models/WorkflowRunUsageTests.cs @@ -0,0 +1,87 @@ +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowRunUsageTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""billable"": { + ""UBUNTU"": { + ""total_ms"": 180000, + ""jobs"": 1, + ""job_runs"": [ + { + ""job_id"": 1, + ""duration_ms"": 180000 + } + ] + }, + ""MACOS"": { + ""total_ms"": 240000, + ""jobs"": 4, + ""job_runs"": [ + { + ""job_id"": 2, + ""duration_ms"": 60000 + }, + { + ""job_id"": 3, + ""duration_ms"": 60000 + }, + { + ""job_id"": 4, + ""duration_ms"": 60000 + }, + { + ""job_id"": 5, + ""duration_ms"": 60000 + } + ] + }, + ""WINDOWS"": { + ""total_ms"": 300000, + ""jobs"": 2, + ""job_runs"": [ + { + ""job_id"": 6, + ""duration_ms"": 150000 + }, + { + ""job_id"": 7, + ""duration_ms"": 150000 + } + ] + } +}, +""run_duration_ms"": 500000 +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal(500000, payload.RunDurationMs); + Assert.NotNull(payload.Billable); + Assert.NotNull(payload.Billable.Ubuntu); + Assert.NotNull(payload.Billable.MacOS); + Assert.NotNull(payload.Billable.Windows); + Assert.Equal(180000, payload.Billable.Ubuntu.TotalMs); + Assert.Equal(1, payload.Billable.Ubuntu.Jobs); + Assert.NotNull(payload.Billable.Ubuntu.JobRuns); + Assert.Equal(1, payload.Billable.Ubuntu.JobRuns.Count); + Assert.Equal(240000, payload.Billable.MacOS.TotalMs); + Assert.Equal(4, payload.Billable.MacOS.Jobs); + Assert.NotNull(payload.Billable.MacOS.JobRuns); + Assert.Equal(4, payload.Billable.MacOS.JobRuns.Count); + Assert.Equal(300000, payload.Billable.Windows.TotalMs); + Assert.Equal(2, payload.Billable.Windows.Jobs); + Assert.NotNull(payload.Billable.Windows.JobRuns); + Assert.Equal(2, payload.Billable.Windows.JobRuns.Count); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowTests.cs b/Octokit.Tests/Models/WorkflowTests.cs new file mode 100644 index 0000000000..a052801082 --- /dev/null +++ b/Octokit.Tests/Models/WorkflowTests.cs @@ -0,0 +1,42 @@ +using System; +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""id"": 161335, +""node_id"": ""MDg6V29ya2Zsb3cxNjEzMzU="", +""name"": ""CI"", +""path"": "".github/workflows/blank.yaml"", +""state"": ""active"", +""created_at"": ""2020-01-08T23:48:37.000-08:00"", +""updated_at"": ""2020-01-08T23:50:21.000-08:00"", +""url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/workflows/161335"", +""html_url"": ""https://github.com/octo-org/octo-repo/blob/master/.github/workflows/161335"", +""badge_url"": ""https://github.com/octo-org/octo-repo/workflows/CI/badge.svg"" +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal(161335, payload.Id); + Assert.Equal("MDg6V29ya2Zsb3cxNjEzMzU=", payload.NodeId); + Assert.Equal("CI", payload.Name); + Assert.Equal(".github/workflows/blank.yaml", payload.Path); + Assert.Equal("active", payload.State); + Assert.Equal(new DateTimeOffset(2020, 01, 08, 23, 48, 37, TimeSpan.FromHours(-8)), payload.CreatedAt); + Assert.Equal(new DateTimeOffset(2020, 01, 08, 23, 50, 21, TimeSpan.FromHours(-8)), payload.UpdatedAt); + Assert.Equal("https://api.github.com/repos/octo-org/octo-repo/actions/workflows/161335", payload.Url); + Assert.Equal("https://github.com/octo-org/octo-repo/blob/master/.github/workflows/161335", payload.HtmlUrl); + Assert.Equal("https://github.com/octo-org/octo-repo/workflows/CI/badge.svg", payload.BadgeUrl); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowUsageTests.cs b/Octokit.Tests/Models/WorkflowUsageTests.cs new file mode 100644 index 0000000000..dbd71f00d4 --- /dev/null +++ b/Octokit.Tests/Models/WorkflowUsageTests.cs @@ -0,0 +1,39 @@ +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowUsageTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""billable"": { + ""UBUNTU"": { + ""total_ms"": 180000 + }, + ""MACOS"": { + ""total_ms"": 240000 + }, + ""WINDOWS"": { + ""total_ms"": 300000 + } +} +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.NotNull(payload.Billable); + Assert.NotNull(payload.Billable.Ubuntu); + Assert.NotNull(payload.Billable.MacOS); + Assert.NotNull(payload.Billable.Windows); + Assert.Equal(180000, payload.Billable.Ubuntu.TotalMs); + Assert.Equal(240000, payload.Billable.MacOS.TotalMs); + Assert.Equal(300000, payload.Billable.Windows.TotalMs); + } + } +} diff --git a/Octokit.Tests/Models/WorkflowsResponseTests.cs b/Octokit.Tests/Models/WorkflowsResponseTests.cs new file mode 100644 index 0000000000..56218f3168 --- /dev/null +++ b/Octokit.Tests/Models/WorkflowsResponseTests.cs @@ -0,0 +1,52 @@ +using Octokit.Internal; +using Xunit; + +namespace Octokit.Tests.Models +{ + public class WorkflowsResponseTests + { + [Fact] + public void CanBeDeserialized() + { + const string json = @"{ +""total_count"": 2, +""workflows"": [ + { + ""id"": 161335, + ""node_id"": ""MDg6V29ya2Zsb3cxNjEzMzU="", + ""name"": ""CI"", + ""path"": "".github/workflows/blank.yaml"", + ""state"": ""active"", + ""created_at"": ""2020-01-08T23:48:37.000-08:00"", + ""updated_at"": ""2020-01-08T23:50:21.000-08:00"", + ""url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/workflows/161335"", + ""html_url"": ""https://github.com/octo-org/octo-repo/blob/master/.github/workflows/161335"", + ""badge_url"": ""https://github.com/octo-org/octo-repo/workflows/CI/badge.svg"" + }, + { + ""id"": 269289, + ""node_id"": ""MDE4OldvcmtmbG93IFNlY29uZGFyeTI2OTI4OQ=="", + ""name"": ""Linter"", + ""path"": "".github/workflows/linter.yaml"", + ""state"": ""active"", + ""created_at"": ""2020-01-08T23:48:37.000-08:00"", + ""updated_at"": ""2020-01-08T23:50:21.000-08:00"", + ""url"": ""https://api.github.com/repos/octo-org/octo-repo/actions/workflows/269289"", + ""html_url"": ""https://github.com/octo-org/octo-repo/blob/master/.github/workflows/269289"", + ""badge_url"": ""https://github.com/octo-org/octo-repo/workflows/Linter/badge.svg"" + } +] +}"; + + var serializer = new SimpleJsonSerializer(); + + var payload = serializer.Deserialize(json); + + Assert.NotNull(payload); + Assert.Equal(2, payload.TotalCount); + Assert.NotNull(payload.Workflows); + Assert.NotEmpty(payload.Workflows); + Assert.Equal(2, payload.Workflows.Count); + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableActionsWorkflowJobsClientTests.cs b/Octokit.Tests/Reactive/ObservableActionsWorkflowJobsClientTests.cs new file mode 100644 index 0000000000..a2f1b7af51 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableActionsWorkflowJobsClientTests.cs @@ -0,0 +1,222 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableActionsWorkflowJobsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableActionsWorkflowJobsClient(null)); + } + } + + public class TheRerunMethod + { + [Fact] + public async Task CallRerunOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + client.Rerun("fake", "repo", 123); + + connection.Received().Actions.Workflows.Jobs.Rerun("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + Assert.Throws(() => client.Rerun(null, "repo", 123)); + Assert.Throws(() => client.Rerun("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + Assert.Throws(() => client.Rerun("", "repo", 123)); + Assert.Throws(() => client.Rerun("fake", "", 123)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task CallGetOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + client.Get("fake", "repo", 123); + + connection.Received().Actions.Workflows.Jobs.Get("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + Assert.Throws(() => client.Get(null, "repo", 123)); + Assert.Throws(() => client.Get("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + Assert.Throws(() => client.Get("", "repo", 123)); + Assert.Throws(() => client.Get("fake", "", 123)); + } + } + + public class TheGetLogsMethod + { + [Fact] + public async Task CallGetLogsOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + client.GetLogs("fake", "repo", 123); + + connection.Received().Actions.Workflows.Jobs.GetLogs("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + Assert.Throws(() => client.GetLogs(null, "repo", 123)); + Assert.Throws(() => client.GetLogs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + Assert.Throws(() => client.GetLogs("", "repo", 123)); + Assert.Throws(() => client.GetLogs("fake", "", 123)); + } + } + + public class TheListMethod + { + [Fact] + public async Task CallListOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + client.List("fake", "repo", 123); + + connection.Received().Actions.Workflows.Jobs.List("fake", "repo", 123); + } + + [Fact] + public async Task CallListOnClientWithRequest() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + var workflowRunJobsRequest = new WorkflowRunJobsRequest(); + + client.List("fake", "repo", 123, workflowRunJobsRequest); + + connection.Received().Actions.Workflows.Jobs.List("fake", "repo", 123, workflowRunJobsRequest); + } + + [Fact] + public async Task CallListOnClientWithRequestAndOptions() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + var workflowRunJobsRequest = new WorkflowRunJobsRequest(); + var options = new ApiOptions(); + + client.List("fake", "repo", 123, workflowRunJobsRequest, options); + + connection.Received().Actions.Workflows.Jobs.List("fake", "repo", 123, workflowRunJobsRequest, options); + } + + [Fact] + public async Task CallListOnClientForAttempt() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + + client.List("fake", "repo", 123, 456); + + connection.Received().Actions.Workflows.Jobs.List("fake", "repo", 123, 456); + } + + [Fact] + public async Task CallListOnClientForAttemptWithOptions() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + var options = new ApiOptions(); + + client.List("fake", "repo", 123, 456, options); + + connection.Received().Actions.Workflows.Jobs.List("fake", "repo", 123, 456, options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + var workflowRunJobsRequest = new WorkflowRunJobsRequest(); + var options = new ApiOptions(); + + Assert.Throws(() => client.List(null, "repo", 123)); + Assert.Throws(() => client.List("fake", null, 123)); + + Assert.Throws(() => client.List(null, "repo", 123, workflowRunJobsRequest)); + Assert.Throws(() => client.List("fake", null, 123, workflowRunJobsRequest)); + Assert.Throws(() => client.List("fake", "repo", 123, null)); + + Assert.Throws(() => client.List(null, "repo", 123, workflowRunJobsRequest, options)); + Assert.Throws(() => client.List("fake", null, 123, workflowRunJobsRequest, options)); + Assert.Throws(() => client.List("fake", "repo", 123, null, options)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowJobsClient(connection); + var workflowRunJobsRequest = new WorkflowRunJobsRequest(); + var options = new ApiOptions(); + + Assert.Throws(() => client.List("", "repo", 123)); + Assert.Throws(() => client.List("fake", "", 123)); + + Assert.Throws(() => client.List("", "repo", 123, workflowRunJobsRequest)); + Assert.Throws(() => client.List("fake", "", 123, workflowRunJobsRequest)); + + Assert.Throws(() => client.List("", "repo", 123, workflowRunJobsRequest, options)); + Assert.Throws(() => client.List("fake", "", 123, workflowRunJobsRequest, options)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableActionsWorkflowRunsClientTests.cs b/Octokit.Tests/Reactive/ObservableActionsWorkflowRunsClientTests.cs new file mode 100644 index 0000000000..0344f91a4c --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableActionsWorkflowRunsClientTests.cs @@ -0,0 +1,700 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableActionsWorkflowRunsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableActionsWorkflowRunsClient(null)); + } + } + + public class TheApproveMethod + { + [Fact] + public async Task CallsApproveOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.Approve("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.Approve("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Approve(null, "repo", 123)); + Assert.Throws(() => client.Approve("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Approve("", "repo", 123)); + Assert.Throws(() => client.Approve("fake", "", 123)); + } + } + + public class TheCancelMethod + { + [Fact] + public async Task CallsCancelOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.Cancel("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.Cancel("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Cancel(null, "repo", 123)); + Assert.Throws(() => client.Cancel("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Cancel("", "repo", 123)); + Assert.Throws(() => client.Cancel("fake", "", 123)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task CallsDeleteOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.Delete("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.Delete("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Delete(null, "repo", 123)); + Assert.Throws(() => client.Delete("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Delete("", "repo", 123)); + Assert.Throws(() => client.Delete("fake", "", 123)); + } + } + + public class TheDeleteLogsMethod + { + [Fact] + public async Task CallsDeleteLogsOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.DeleteLogs("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.DeleteLogs("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.DeleteLogs(null, "repo", 123)); + Assert.Throws(() => client.DeleteLogs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.DeleteLogs("", "repo", 123)); + Assert.Throws(() => client.DeleteLogs("fake", "", 123)); + } + } + + public class TheListMethod + { + [Fact] + public async Task CallsListOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.List("fake", "repo"); + + connection.Received().Actions.Workflows.Runs.List("fake", "repo"); + } + + [Fact] + public async Task CallsListOnClientWithRequest() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + + client.List("fake", "repo", request); + + connection.Received().Actions.Workflows.Runs.List("fake", "repo", request); + } + + [Fact] + public async Task CallsListOnClientWithRequestWithOptions() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + client.List("fake", "repo", request, options); + + connection.Received().Actions.Workflows.Runs.List("fake", "repo", request, options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + Assert.Throws(() => client.List(null, "repo")); + Assert.Throws(() => client.List("fake", null)); + + Assert.Throws(() => client.List(null, "repo", request)); + Assert.Throws(() => client.List("fake", null, request)); + Assert.Throws(() => client.List("fake", "repo", null)); + + Assert.Throws(() => client.List(null, "repo", request, options)); + Assert.Throws(() => client.List("fake", null, request, options)); + Assert.Throws(() => client.List("fake", "repo", null, options)); + Assert.Throws(() => client.List("fake", "repo", request, null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + Assert.Throws(() => client.List("", "repo")); + Assert.Throws(() => client.List("fake", "")); + + Assert.Throws(() => client.List("", "repo", request)); + Assert.Throws(() => client.List("fake", "", request)); + + Assert.Throws(() => client.List("", "repo", request, options)); + Assert.Throws(() => client.List("fake", "", request, options)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task CallsGetOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.Get("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.Get("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Get(null, "repo", 123)); + Assert.Throws(() => client.Get("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Get("", "repo", 123)); + Assert.Throws(() => client.Get("fake", "", 123)); + } + } + + public class TheGetLogsMethod + { + [Fact] + public async Task CallsGetLogsOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.GetLogs("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.GetLogs("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetLogs(null, "repo", 123)); + Assert.Throws(() => client.GetLogs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetLogs("", "repo", 123)); + Assert.Throws(() => client.GetLogs("fake", "", 123)); + } + } + + public class TheGetAttemptMethod + { + [Fact] + public async Task CallsGetAttemptOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.GetAttempt("fake", "repo", 123, 456); + + connection.Received().Actions.Workflows.Runs.GetAttempt("fake", "repo", 123, 456); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetAttempt(null, "repo", 123, 456)); + Assert.Throws(() => client.GetAttempt("fake", null, 123, 456)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetAttempt("", "repo", 123, 456)); + Assert.Throws(() => client.GetAttempt("fake", "", 123, 456)); + } + } + + public class TheGetAttemptLogsMethod + { + [Fact] + public async Task CallsGetAttemptLogsOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.GetAttemptLogs("fake", "repo", 123, 456); + + connection.Received().Actions.Workflows.Runs.GetAttemptLogs("fake", "repo", 123, 456); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetAttemptLogs(null, "repo", 123, 456)); + Assert.Throws(() => client.GetAttemptLogs("fake", null, 123, 456)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetAttemptLogs("", "repo", 123, 456)); + Assert.Throws(() => client.GetAttemptLogs("fake", "", 123, 456)); + } + } + + public class TheGetReviewHistoryMethod + { + [Fact] + public async Task CallsGetReviewHistoryOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.GetReviewHistory("fake", "repo", 123); + + connection.Connection.Received().Get>(Arg.Is(u => u.ToString() == "/repos/fake/repo/actions/runs/123/approvals"), + null); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetReviewHistory(null, "repo", 123)); + Assert.Throws(() => client.GetReviewHistory("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetReviewHistory("", "repo", 123)); + Assert.Throws(() => client.GetReviewHistory("fake", "", 123)); + } + } + + public class TheGetUsageMethod + { + [Fact] + public async Task CallsGetUsageOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.GetUsage("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.GetUsage("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetUsage(null, "repo", 123)); + Assert.Throws(() => client.GetUsage("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.GetUsage("", "repo", 123)); + Assert.Throws(() => client.GetUsage("fake", "", 123)); + } + } + + public class TheRerunMethod + { + [Fact] + public async Task CallsRerunOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.Rerun("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.Rerun("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Rerun(null, "repo", 123)); + Assert.Throws(() => client.Rerun("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.Rerun("", "repo", 123)); + Assert.Throws(() => client.Rerun("fake", "", 123)); + } + } + + public class TheRerunFailedJobsMethod + { + [Fact] + public async Task CallsRerunFailedJobsOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.RerunFailedJobs("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.RerunFailedJobs("fake", "repo", 123); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.RerunFailedJobs(null, "repo", 123)); + Assert.Throws(() => client.RerunFailedJobs("fake", null, 123)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + Assert.Throws(() => client.RerunFailedJobs("", "repo", 123)); + Assert.Throws(() => client.RerunFailedJobs("fake", "", 123)); + } + } + + public class TheReviewPendingDeploymentsMethod + { + [Fact] + public async Task CallsReviewPendingDeploymentsOnClient() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var review = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, ""); + + client.ReviewPendingDeployments("fake", "repo", 123, review); + + connection.Received().Actions.Workflows.Runs.ReviewPendingDeployments("fake", "repo", 123, review); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var review = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, ""); + + Assert.Throws(() => client.ReviewPendingDeployments(null, "repo", 123, review)); + Assert.Throws(() => client.ReviewPendingDeployments("fake", null, 123, review)); + Assert.Throws(() => client.ReviewPendingDeployments("fake", "repo", 123, null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var review = new PendingDeploymentReview(new[] { 1L }, PendingDeploymentReviewState.Approved, ""); + + Assert.Throws(() => client.ReviewPendingDeployments("", "repo", 123, review)); + Assert.Throws(() => client.ReviewPendingDeployments("fake", "", 123, review)); + } + } + + public class TheListByWorkflowMethod + { + [Fact] + public async Task CallsListByWorkflowOnClientById() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.ListByWorkflow("fake", "repo", 123); + + connection.Received().Actions.Workflows.Runs.ListByWorkflow("fake", "repo", 123); + } + + [Fact] + public async Task CallsListByWorkflowOnClientByName() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + client.ListByWorkflow("fake", "repo", "main.yml"); + + connection.Received().Actions.Workflows.Runs.ListByWorkflow("fake", "repo", "main.yml"); + } + + [Fact] + public async Task CallsListByWorkflowOnClientByIdWithRequest() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + + client.ListByWorkflow("fake", "repo", 123, request); + + connection.Received().Actions.Workflows.Runs.ListByWorkflow("fake", "repo", 123, request); + } + + [Fact] + public async Task CallsListByWorkflowOnClientByNameWithRequest() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + + client.ListByWorkflow("fake", "repo", "main.yml", request); + + connection.Received().Actions.Workflows.Runs.ListByWorkflow("fake", "repo", "main.yml", request); + } + + [Fact] + public async Task CallsListByWorkflowOnClientByIdWithRequestWithOptions() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + client.ListByWorkflow("fake", "repo", 123, request, options); + + connection.Received().Actions.Workflows.Runs.ListByWorkflow("fake", "repo", 123, request, options); + } + + [Fact] + public async Task CallsListByWorkflowOnClientByNameWithRequestWithOptions() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + client.ListByWorkflow("fake", "repo", "main.yml", request, options); + + connection.Received().Actions.Workflows.Runs.ListByWorkflow("fake", "repo", "main.yml", request, options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + Assert.Throws(() => client.ListByWorkflow(null, "repo", 123)); + Assert.Throws(() => client.ListByWorkflow("fake", null, 123)); + + Assert.Throws(() => client.ListByWorkflow(null, "repo", "main.yml")); + Assert.Throws(() => client.ListByWorkflow("fake", null, "main.yml")); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", null)); + + Assert.Throws(() => client.ListByWorkflow(null, "repo", 123, request)); + Assert.Throws(() => client.ListByWorkflow("fake", null, 123, request)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", 123, null)); + + Assert.Throws(() => client.ListByWorkflow(null, "repo", "main.yml", request)); + Assert.Throws(() => client.ListByWorkflow("fake", null, "main.yml", request)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", null, request)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", "main.yml", null)); + + Assert.Throws(() => client.ListByWorkflow(null, "repo", 123, request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", null, 123, request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", 123, null, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", 123, request, null)); + + Assert.Throws(() => client.ListByWorkflow(null, "repo", "main.yml", request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", null, "main.yml", request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", null, request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", "main.yml", null, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", "main.yml", request, null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowRunsClient(connection); + + var request = new WorkflowRunsRequest(); + var options = new ApiOptions(); + + Assert.Throws(() => client.ListByWorkflow("", "repo", 123)); + Assert.Throws(() => client.ListByWorkflow("fake", "", 123)); + + Assert.Throws(() => client.ListByWorkflow("", "repo", "main.yml")); + Assert.Throws(() => client.ListByWorkflow("fake", "", "main.yml")); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", "")); + + Assert.Throws(() => client.ListByWorkflow("", "repo", 123, request)); + Assert.Throws(() => client.ListByWorkflow("fake", "", 123, request)); + + Assert.Throws(() => client.ListByWorkflow("", "repo", "main.yml", request)); + Assert.Throws(() => client.ListByWorkflow("fake", "", "main.yml", request)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", "", request)); + + Assert.Throws(() => client.ListByWorkflow("", "repo", 123, request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "", 123, request, options)); + + Assert.Throws(() => client.ListByWorkflow("", "repo", "main.yml", request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "", "main.yml", request, options)); + Assert.Throws(() => client.ListByWorkflow("fake", "repo", "", request, options)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableActionsWorkflowsClientTests.cs b/Octokit.Tests/Reactive/ObservableActionsWorkflowsClientTests.cs new file mode 100644 index 0000000000..0bf644a46f --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableActionsWorkflowsClientTests.cs @@ -0,0 +1,362 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableActionsWorkflowsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableActionsWorkflowsClient(null)); + } + } + + public class TheClientProperties + { + [Fact] + public void AreNotNull() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.NotNull(client.Jobs); + Assert.NotNull(client.Runs); + } + } + + public class TheCreateDispatchMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + client.CreateDispatch("fake", "repo", 123, createDispatch); + + connection.Received().Actions.Workflows.CreateDispatch("fake", "repo", 123, createDispatch); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + client.CreateDispatch("fake", "repo", "main.yaml", createDispatch); + + connection.Received().Actions.Workflows.CreateDispatch("fake", "repo", "main.yaml", createDispatch); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + Assert.Throws(() => client.CreateDispatch(null, "repo", 123, createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", null, 123, createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", "repo", 123, null)); + + Assert.Throws(() => client.CreateDispatch(null, "repo", "main.yaml", createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", null, "main.yaml", createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", "repo", null, createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", "repo", "main.yaml", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + var createDispatch = new CreateWorkflowDispatch("ref"); + + Assert.Throws(() => client.CreateDispatch("", "repo", 123, createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", "", 123, createDispatch)); + + Assert.Throws(() => client.CreateDispatch("", "repo", "main.yaml", createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", "", "main.yaml", createDispatch)); + Assert.Throws(() => client.CreateDispatch("fake", "repo", "", createDispatch)); + } + } + + public class TheDisableMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.Disable("fake", "repo", 123); + + connection.Received().Actions.Workflows.Disable("fake", "repo", 123); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.Disable("fake", "repo", "main.yaml"); + + connection.Received().Actions.Workflows.Disable("fake", "repo", "main.yaml"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.Disable(null, "repo", 123)); + Assert.Throws(() => client.Disable("fake", null, 123)); + + Assert.Throws(() => client.Disable(null, "repo", "main.yaml")); + Assert.Throws(() => client.Disable("fake", null, "main.yaml")); + Assert.Throws(() => client.Disable("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.Disable("", "repo", 123)); + Assert.Throws(() => client.Disable("fake", "", 123)); + + Assert.Throws(() => client.Disable("", "repo", "main.yaml")); + Assert.Throws(() => client.Disable("fake", "", "main.yaml")); + Assert.Throws(() => client.Disable("fake", "repo", "")); + } + } + + public class TheEnableMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.Enable("fake", "repo", 123); + + connection.Received().Actions.Workflows.Enable("fake", "repo", 123); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.Enable("fake", "repo", "main.yaml"); + + connection.Received().Actions.Workflows.Enable("fake", "repo", "main.yaml"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.Enable(null, "repo", 123)); + Assert.Throws(() => client.Enable("fake", null, 123)); + + Assert.Throws(() => client.Enable(null, "repo", "main.yaml")); + Assert.Throws(() => client.Enable("fake", null, "main.yaml")); + Assert.Throws(() => client.Enable("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.Enable("", "repo", 123)); + Assert.Throws(() => client.Enable("fake", "", 123)); + + Assert.Throws(() => client.Enable("", "repo", "main.yaml")); + Assert.Throws(() => client.Enable("fake", "", "main.yaml")); + Assert.Throws(() => client.Enable("fake", "repo", "")); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.Get("fake", "repo", 123); + + connection.Received().Actions.Workflows.Get("fake", "repo", 123); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.Get("fake", "repo", "main.yaml"); + + connection.Received().Actions.Workflows.Get("fake", "repo", "main.yaml"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.Get(null, "repo", 123)); + Assert.Throws(() => client.Get("fake", null, 123)); + + Assert.Throws(() => client.Get(null, "repo", "main.yaml")); + Assert.Throws(() => client.Get("fake", null, "main.yaml")); + Assert.Throws(() => client.Get("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.Get("", "repo", 123)); + Assert.Throws(() => client.Get("fake", "", 123)); + + Assert.Throws(() => client.Get("", "repo", "main.yaml")); + Assert.Throws(() => client.Get("fake", "", "main.yaml")); + Assert.Throws(() => client.Get("fake", "repo", "")); + } + } + + public class TheGetUsageMethod + { + [Fact] + public async Task RequestsCorrectUrlByWorkflowId() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.GetUsage("fake", "repo", 123); + + connection.Received().Actions.Workflows.GetUsage("fake", "repo", 123); + } + + [Fact] + public async Task RequestsCorrectUrlByWorkflowFileName() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.GetUsage("fake", "repo", "main.yaml"); + + connection.Received().Actions.Workflows.GetUsage("fake", "repo", "main.yaml"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.GetUsage(null, "repo", 123)); + Assert.Throws(() => client.GetUsage("fake", null, 123)); + + Assert.Throws(() => client.GetUsage(null, "repo", "main.yaml")); + Assert.Throws(() => client.GetUsage("fake", null, "main.yaml")); + Assert.Throws(() => client.GetUsage("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.GetUsage("", "repo", 123)); + Assert.Throws(() => client.GetUsage("fake", "", 123)); + + Assert.Throws(() => client.GetUsage("", "repo", "main.yaml")); + Assert.Throws(() => client.GetUsage("fake", "", "main.yaml")); + Assert.Throws(() => client.GetUsage("fake", "repo", "")); + } + } + + public class TheListMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + client.List("fake", "repo"); + + connection.Received().Actions.Workflows.List("fake", "repo"); + } + + [Fact] + public async Task RequestsCorrectUrlWithRequestWithApiOptions() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + var options = new ApiOptions { PageSize = 1 }; + + client.List("fake", "repo", options); + + connection.Received().Actions.Workflows.List("fake", "repo", options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.List(null, "repo")); + Assert.Throws(() => client.List("fake", null)); + + Assert.Throws(() => client.List(null, "repo", ApiOptions.None)); + Assert.Throws(() => client.List("fake", null, ApiOptions.None)); + Assert.Throws(() => client.List("fake", "repo", null)); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var connection = Substitute.For(); + var client = new ObservableActionsWorkflowsClient(connection); + + Assert.Throws(() => client.List("", "repo")); + Assert.Throws(() => client.List("fake", "")); + + Assert.Throws(() => client.List("", "repo", ApiOptions.None)); + Assert.Throws(() => client.List("fake", "", ApiOptions.None)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs index 1476c76b4f..d1430a33a3 100644 --- a/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs @@ -1,7 +1,5 @@ -using Octokit.Reactive; -using System; -using System.Collections.Generic; -using System.Text; +using System; +using Octokit.Reactive; using Xunit; namespace Octokit.Tests.Reactive diff --git a/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs index 22b79b8fbc..78cd2746ff 100644 --- a/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs @@ -1,7 +1,5 @@ -using Octokit.Reactive; -using System; -using System.Collections.Generic; -using System.Text; +using System; +using Octokit.Reactive; using Xunit; namespace Octokit.Tests.Reactive diff --git a/Octokit/Clients/ActionsArtifactsClient.cs b/Octokit/Clients/ActionsArtifactsClient.cs new file mode 100644 index 0000000000..cc57d0c433 --- /dev/null +++ b/Octokit/Clients/ActionsArtifactsClient.cs @@ -0,0 +1,19 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions Artifacts API. + /// + /// + /// See the Actions Artifacts API documentation for more information. + /// + public class ActionsArtifactsClient : ApiClient, IActionsArtifactsClient + { + /// + /// Initializes a new GitHub Actions Artifacts API client + /// + /// An API connection + public ActionsArtifactsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + } +} diff --git a/Octokit/Clients/ActionsCacheClient.cs b/Octokit/Clients/ActionsCacheClient.cs new file mode 100644 index 0000000000..f640c845e1 --- /dev/null +++ b/Octokit/Clients/ActionsCacheClient.cs @@ -0,0 +1,19 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions Cache API. + /// + /// + /// See the Actions Cache API documentation for more information. + /// + public class ActionsCacheClient : ApiClient, IActionsCacheClient + { + /// + /// Initializes a new GitHub Actions Cache API client + /// + /// An API connection + public ActionsCacheClient(IApiConnection apiConnection) : base(apiConnection) + { + } + } +} diff --git a/Octokit/Clients/ActionsClient.cs b/Octokit/Clients/ActionsClient.cs new file mode 100644 index 0000000000..9034b14936 --- /dev/null +++ b/Octokit/Clients/ActionsClient.cs @@ -0,0 +1,56 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public class ActionsClient : ApiClient, IActionsClient + { + /// + /// Instantiate a new GitHub Actions API client. + /// + /// An API connection. + public ActionsClient(IApiConnection apiConnection) + : base(apiConnection) + { + Artifacts = new ActionsArtifactsClient(apiConnection); + Cache = new ActionsCacheClient(apiConnection); + Permissions = new ActionsPermissionsClient(apiConnection); + SelfHostedRunnerGroups = new ActionsSelfHostedRunnerGroupsClient(apiConnection); + SelfHostedRunners = new ActionsSelfHostedRunnersClient(apiConnection); + Workflows = new ActionsWorkflowsClient(apiConnection); + } + + /// + /// Client for the Artifacts API. + /// + public IActionsArtifactsClient Artifacts { get; private set; } + + /// + /// Client for the Cache API. + /// + public IActionsCacheClient Cache { get; private set; } + + /// + /// Client for the Permissions API. + /// + public IActionsPermissionsClient Permissions { get; private set; } + + /// + /// Client for the Self-hosted runner groups API. + /// + public IActionsSelfHostedRunnerGroupsClient SelfHostedRunnerGroups { get; private set; } + + /// + /// Client for the Self-hosted runners API. + /// + public IActionsSelfHostedRunnersClient SelfHostedRunners { get; private set; } + + /// + /// Client for the Workflows API. + /// + public IActionsWorkflowsClient Workflows { get; private set; } + } +} diff --git a/Octokit/Clients/ActionsPermissionsClient.cs b/Octokit/Clients/ActionsPermissionsClient.cs new file mode 100644 index 0000000000..13260589c3 --- /dev/null +++ b/Octokit/Clients/ActionsPermissionsClient.cs @@ -0,0 +1,19 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions Permissions API. + /// + /// + /// See the Actions Permissions API documentation for more information. + /// + public class ActionsPermissionsClient : ApiClient, IActionsPermissionsClient + { + /// + /// Initializes a new GitHub Actions Permissions API client + /// + /// An API connection + public ActionsPermissionsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + } +} diff --git a/Octokit/Clients/ActionsSelfHostedRunnerGroupsClient.cs b/Octokit/Clients/ActionsSelfHostedRunnerGroupsClient.cs new file mode 100644 index 0000000000..d960541894 --- /dev/null +++ b/Octokit/Clients/ActionsSelfHostedRunnerGroupsClient.cs @@ -0,0 +1,19 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions Self-hosted runner groups API. + /// + /// + /// See the Actions Self-hosted runner groups API documentation for more information. + /// + public class ActionsSelfHostedRunnerGroupsClient : ApiClient, IActionsSelfHostedRunnerGroupsClient + { + /// + /// Initializes a new GitHub Actions Self-hosted runner groups API client + /// + /// An API connection + public ActionsSelfHostedRunnerGroupsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + } +} diff --git a/Octokit/Clients/ActionsSelfHostedRunnersClient.cs b/Octokit/Clients/ActionsSelfHostedRunnersClient.cs new file mode 100644 index 0000000000..38c86149dd --- /dev/null +++ b/Octokit/Clients/ActionsSelfHostedRunnersClient.cs @@ -0,0 +1,19 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions Self-hosted runners API. + /// + /// + /// See the Actions Self-hosted runners API documentation for more information. + /// + public class ActionsSelfHostedRunnersClient : ApiClient, IActionsSelfHostedRunnersClient + { + /// + /// Initializes a new GitHub Actions Self-hosted runners API client + /// + /// An API connection + public ActionsSelfHostedRunnersClient(IApiConnection apiConnection) : base(apiConnection) + { + } + } +} diff --git a/Octokit/Clients/ActionsWorkflowJobsClient.cs b/Octokit/Clients/ActionsWorkflowJobsClient.cs new file mode 100644 index 0000000000..bdd040a465 --- /dev/null +++ b/Octokit/Clients/ActionsWorkflowJobsClient.cs @@ -0,0 +1,173 @@ +using System.Linq; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Workflows jobs API. + /// + /// + /// See the Actions Workflows jobs API documentation for more information. + /// + public class ActionsWorkflowJobsClient : ApiClient, IActionsWorkflowJobsClient + { + /// + /// Initializes a new GitHub Actions Workflows jobs API client + /// + /// An API connection + public ActionsWorkflowJobsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + /// + /// Re-runs a specific workflow job in a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-job-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/jobs/{job_id}/rerun")] + public Task Rerun(string owner, string name, long jobId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Post(ApiUrls.ActionsRerunWorkflowJob(owner, name, jobId)); + } + + /// + /// Gets a specific job in a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#get-a-job-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The unique identifier of the job. + [ManualRoute("GET", "\n/repos/{owner}/{repo}/actions/jobs/{job_id}")] + public Task Get(string owner, string name, long jobId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Get(ApiUrls.ActionsGetWorkflowJob(owner, name, jobId)); + } + + /// + /// Gets the plain text log file for a workflow job. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#download-job-logs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs")] + public async Task GetLogs(string owner, string name, long jobId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var response = await Connection.Get(ApiUrls.ActionsGetWorkflowJobLogs(owner, name, jobId), null).ConfigureAwait(false); + return response.Body; + } + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/jobs")] + public Task List(string owner, string name, long runId) + { + return List(owner, name, runId, new WorkflowRunJobsRequest(), ApiOptions.None); + } + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/jobs")] + public Task List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest) + { + return List(owner, name, runId, workflowRunJobsRequest, ApiOptions.None); + } + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + /// Options to change the API response. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/jobs")] + public async Task List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunJobsRequest, nameof(workflowRunJobsRequest)); + + var results = await ApiConnection.GetAll(ApiUrls.ActionsListWorkflowJobs(owner, name, runId), workflowRunJobsRequest.ToParametersDictionary(), options).ConfigureAwait(false); + + return new WorkflowJobsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Jobs).ToList()); + } + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs")] + public Task List(string owner, string name, long runId, int attemptNumber) + { + return List(owner, name, runId, attemptNumber, ApiOptions.None); + } + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + /// Options to change the API response. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs")] + public async Task List(string owner, string name, long runId, int attemptNumber, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var results = await ApiConnection.GetAll(ApiUrls.ActionsListWorkflowJobs(owner, name, runId, attemptNumber), null, options).ConfigureAwait(false); + + return new WorkflowJobsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Jobs).ToList()); + } + } +} diff --git a/Octokit/Clients/ActionsWorkflowRunsClient.cs b/Octokit/Clients/ActionsWorkflowRunsClient.cs new file mode 100644 index 0000000000..8570ae38d2 --- /dev/null +++ b/Octokit/Clients/ActionsWorkflowRunsClient.cs @@ -0,0 +1,432 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Workflows runs API. + /// + /// + /// See the Actions Workflows runs API documentation for more information. + /// + public class ActionsWorkflowRunsClient : ApiClient, IActionsWorkflowRunsClient + { + /// + /// Initializes a new GitHub Actions Workflows runs API client + /// + /// An API connection + public ActionsWorkflowRunsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs")] + public Task List(string owner, string name) + { + return List(owner, name, new WorkflowRunsRequest(), ApiOptions.None); + } + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs")] + public Task List(string owner, string name, WorkflowRunsRequest workflowRunsRequest) + { + return List(owner, name, workflowRunsRequest, ApiOptions.None); + } + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs")] + public async Task List(string owner, string name, WorkflowRunsRequest workflowRunsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.ActionsWorkflowRuns(owner, name), workflowRunsRequest.ToParametersDictionary(), options).ConfigureAwait(false); + + return new WorkflowRunsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.WorkflowRuns).ToList()); + } + + /// + /// Gets a specific workflow run in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}")] + public Task Get(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Get(ApiUrls.ActionsWorkflowRun(owner, name, runId), null); + } + + /// + /// Deletes a specific workflow run. Anyone with write access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("DELETE", "/repos/{owner}/{repo}/actions/runs/{run_id}")] + public Task Delete(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Delete(ApiUrls.ActionsWorkflowRun(owner, name, runId)); + } + + /// + /// Get the review history for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-the-review-history-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/approvals")] + public Task> GetReviewHistory(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.GetAll(ApiUrls.ActionsWorkflowRunApprovals(owner, name, runId)); + } + + /// + /// Approves a workflow run for a pull request from a public fork of a first time contributor. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#approve-a-workflow-run-for-a-fork-pull-request + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/runs/{run_id}/approve")] + public Task Approve(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Post(ApiUrls.ActionsApproveWorkflowRun(owner, name, runId)); + } + + /// + /// Gets a specific workflow run attempt. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}")] + public Task GetAttempt(string owner, string name, long runId, long attemptNumber) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Get(ApiUrls.ActionsWorkflowRunAttempt(owner, name, runId, attemptNumber), null); + } + + /// + /// Gets a byte array containing an archive of log files for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-attempt-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}")] + public async Task GetAttemptLogs(string owner, string name, long runId, long attemptNumber) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var response = await Connection.GetRaw(ApiUrls.ActionsGetWorkflowRunAttemptLogs(owner, name, runId, attemptNumber), null).ConfigureAwait(false); + return response.Body; + } + + /// + /// Cancels a workflow run using its Id. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#cancel-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/runs/{run_id}/cancel")] + public Task Cancel(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Post(ApiUrls.ActionsCancelWorkflowRun(owner, name, runId)); + } + + /// + /// Gets a byte array containing an archive of log files for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/logs")] + public async Task GetLogs(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + var response = await Connection.GetRaw(ApiUrls.ActionsGetWorkflowRunLogs(owner, name, runId), null).ConfigureAwait(false); + return response.Body; + } + + /// + /// Deletes all logs for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("DELETE", "/repos/{owner}/{repo}/actions/runs/{run_id}/logs")] + public Task DeleteLogs(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Delete(ApiUrls.ActionsGetWorkflowRunLogs(owner, name, runId)); + } + + /// + /// Approve or reject pending deployments that are waiting on approval by a required reviewer. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#review-pending-deployments-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The review for the pending deployment. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments")] + public Task ReviewPendingDeployments(string owner, string name, long runId, PendingDeploymentReview review) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(review, nameof(review)); + + return ApiConnection.Post(ApiUrls.ActionsWorkflowRunPendingDeployments(owner, name, runId), review); + } + + /// + /// Re-runs your workflow run using its Id. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/runs/{run_id}/rerun")] + public Task Rerun(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Post(ApiUrls.ActionsRerunWorkflowRun(owner, name, runId)); + } + + /// + /// Re-run all of the failed jobs and their dependent jobs in a workflow run using the Id of the workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-failed-jobs-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs")] + public Task RerunFailedJobs(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Post(ApiUrls.ActionsRerunWorkflowRunFailedJobs(owner, name, runId)); + } + + /// + /// Gets the number of billable minutes and total run time for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-workflow-run-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/runs/{run_id}/timing")] + public Task GetUsage(string owner, string name, long runId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Get(ApiUrls.ActionsGetWorkflowRunUsage(owner, name, runId), null); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")] + public Task ListByWorkflow(string owner, string name, long workflowId) + { + return ListByWorkflow(owner, name, workflowId, new WorkflowRunsRequest(), ApiOptions.None); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")] + public Task ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest) + { + return ListByWorkflow(owner, name, workflowId, workflowRunsRequest, ApiOptions.None); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")] + public async Task ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.ActionsListWorkflowRuns(owner, name, workflowId), workflowRunsRequest.ToParametersDictionary(), options).ConfigureAwait(false); + + return new WorkflowRunsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.WorkflowRuns).ToList()); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")] + public Task ListByWorkflow(string owner, string name, string workflowFileName) + { + return ListByWorkflow(owner, name, workflowFileName, new WorkflowRunsRequest(), ApiOptions.None); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")] + public Task ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest) + { + return ListByWorkflow(owner, name, workflowFileName, workflowRunsRequest, ApiOptions.None); + } + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs")] + public async Task ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + Ensure.ArgumentNotNull(workflowRunsRequest, nameof(workflowRunsRequest)); + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.ActionsListWorkflowRuns(owner, name, workflowFileName), workflowRunsRequest.ToParametersDictionary(), options).ConfigureAwait(false); + + return new WorkflowRunsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.WorkflowRuns).ToList()); + } + } +} diff --git a/Octokit/Clients/ActionsWorkflowsClient.cs b/Octokit/Clients/ActionsWorkflowsClient.cs new file mode 100644 index 0000000000..7c72e98337 --- /dev/null +++ b/Octokit/Clients/ActionsWorkflowsClient.cs @@ -0,0 +1,263 @@ +using System.Linq; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Workflows API. + /// + /// + /// See the Actions Workflows API documentation for more information. + /// + public class ActionsWorkflowsClient : ApiClient, IActionsWorkflowsClient + { + /// + /// Initializes a new GitHub Actions Workflows API client + /// + /// An API connection + public ActionsWorkflowsClient(IApiConnection apiConnection) : base(apiConnection) + { + Jobs = new ActionsWorkflowJobsClient(apiConnection); + Runs = new ActionsWorkflowRunsClient(apiConnection); + } + + /// + /// Manually triggers a GitHub Actions workflow run in a repository by file name. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// The parameters to use to trigger the workflow run. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches")] + public Task CreateDispatch(string owner, string name, string workflowFileName, CreateWorkflowDispatch createDispatch) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + Ensure.ArgumentNotNull(createDispatch, nameof(createDispatch)); + + return ApiConnection.Post(ApiUrls.ActionsDispatchWorkflow(owner, name, workflowFileName), createDispatch); + } + + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// The parameters to use to trigger the workflow run. + [ManualRoute("POST", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches")] + public Task CreateDispatch(string owner, string name, long workflowId, CreateWorkflowDispatch createDispatch) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(createDispatch, nameof(createDispatch)); + + return ApiConnection.Post(ApiUrls.ActionsDispatchWorkflow(owner, name, workflowId), createDispatch); + } + + /// + /// Disables a specific workflow in a repository by file name. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + [ManualRoute("PUT", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable")] + public Task Disable(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return ApiConnection.Put(ApiUrls.ActionsDisableWorkflow(owner, name, workflowFileName)); + } + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + [ManualRoute("PUT", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable")] + public Task Disable(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Put(ApiUrls.ActionsDisableWorkflow(owner, name, workflowId)); + } + + /// + /// Enables a specific workflow in a repository by file name. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + [ManualRoute("PUT", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable")] + public Task Enable(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return ApiConnection.Put(ApiUrls.ActionsEnableWorkflow(owner, name, workflowFileName)); + } + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + [ManualRoute("PUT", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable")] + public Task Enable(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Put(ApiUrls.ActionsEnableWorkflow(owner, name, workflowId)); + } + + /// + /// Gets a specific workflow in a repository by file name. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}")] + public Task Get(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return ApiConnection.Get(ApiUrls.ActionsGetWorkflow(owner, name, workflowFileName), null); + } + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}")] + public Task Get(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Get(ApiUrls.ActionsGetWorkflow(owner, name, workflowId), null); + } + + /// + /// Gets useage of a specific workflow in a repository by file name. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing")] + public Task GetUsage(string owner, string name, string workflowFileName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNullOrEmptyString(workflowFileName, nameof(workflowFileName)); + + return ApiConnection.Get(ApiUrls.ActionsGetWorkflowUsage(owner, name, workflowFileName), null); + } + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing")] + public Task GetUsage(string owner, string name, long workflowId) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return ApiConnection.Get(ApiUrls.ActionsGetWorkflowUsage(owner, name, workflowId), null); + } + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows")] + public Task List(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + + return List(owner, name, ApiOptions.None); + } + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + /// Options to change the API response. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/workflows")] + public async Task List(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); + Ensure.ArgumentNotNull(options, nameof(options)); + + var results = await ApiConnection.GetAll(ApiUrls.ActionsListWorkflows(owner, name), null, options).ConfigureAwait(false); + + return new WorkflowsResponse( + results.Count > 0 ? results.Max(x => x.TotalCount) : 0, + results.SelectMany(x => x.Workflows).ToList()); + } + + /// + /// Client for the Workflow jobs API. + /// + public IActionsWorkflowJobsClient Jobs { get; private set; } + + /// + /// Client for the Workflow runs API. + /// + public IActionsWorkflowRunsClient Runs { get; private set; } + } +} diff --git a/Octokit/Clients/IActionsArtifactsClient.cs b/Octokit/Clients/IActionsArtifactsClient.cs new file mode 100644 index 0000000000..9905bad9cb --- /dev/null +++ b/Octokit/Clients/IActionsArtifactsClient.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Artifacts API. + /// + /// + /// See the Actions Artifacts API documentation for more information. + /// + public interface IActionsArtifactsClient + { + } +} diff --git a/Octokit/Clients/IActionsCacheClient.cs b/Octokit/Clients/IActionsCacheClient.cs new file mode 100644 index 0000000000..f0aa775365 --- /dev/null +++ b/Octokit/Clients/IActionsCacheClient.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Cache API. + /// + /// + /// See the Actions Cache API documentation for more information. + /// + public interface IActionsCacheClient + { + } +} diff --git a/Octokit/Clients/IActionsClient.cs b/Octokit/Clients/IActionsClient.cs new file mode 100644 index 0000000000..027cffc00d --- /dev/null +++ b/Octokit/Clients/IActionsClient.cs @@ -0,0 +1,41 @@ +namespace Octokit +{ + /// + /// A client for GitHub's Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public interface IActionsClient + { + /// + /// Client for the Artifacts API. + /// + IActionsArtifactsClient Artifacts { get; } + + /// + /// Client for the Cache API. + /// + IActionsCacheClient Cache { get; } + + /// + /// Client for the Permissions API. + /// + IActionsPermissionsClient Permissions { get; } + + /// + /// Client for the Self-hosted runner groups API. + /// + IActionsSelfHostedRunnerGroupsClient SelfHostedRunnerGroups { get; } + + /// + /// Client for the Self-hosted runners API. + /// + IActionsSelfHostedRunnersClient SelfHostedRunners { get; } + + /// + /// Client for the Workflows API. + /// + IActionsWorkflowsClient Workflows { get; } + } +} diff --git a/Octokit/Clients/IActionsPermissionsClient.cs b/Octokit/Clients/IActionsPermissionsClient.cs new file mode 100644 index 0000000000..d06315bc64 --- /dev/null +++ b/Octokit/Clients/IActionsPermissionsClient.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Permissions API. + /// + /// + /// See the Actions Permissions API documentation for more information. + /// + public interface IActionsPermissionsClient + { + } +} diff --git a/Octokit/Clients/IActionsSelfHostedRunnerGroupsClient.cs b/Octokit/Clients/IActionsSelfHostedRunnerGroupsClient.cs new file mode 100644 index 0000000000..f76ffc4a4e --- /dev/null +++ b/Octokit/Clients/IActionsSelfHostedRunnerGroupsClient.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Self-hosted runner groups API. + /// + /// + /// See the Actions Self-hosted runner groups API documentation for more information. + /// + public interface IActionsSelfHostedRunnerGroupsClient + { + } +} diff --git a/Octokit/Clients/IActionsSelfHostedRunnersClient.cs b/Octokit/Clients/IActionsSelfHostedRunnersClient.cs new file mode 100644 index 0000000000..3c429615a0 --- /dev/null +++ b/Octokit/Clients/IActionsSelfHostedRunnersClient.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Self-hosted runners API. + /// + /// + /// See the Actions Self-hosted runners API documentation for more information. + /// + public interface IActionsSelfHostedRunnersClient + { + } +} diff --git a/Octokit/Clients/IActionsWorkflowJobsClient.cs b/Octokit/Clients/IActionsWorkflowJobsClient.cs new file mode 100644 index 0000000000..a907b3b97e --- /dev/null +++ b/Octokit/Clients/IActionsWorkflowJobsClient.cs @@ -0,0 +1,107 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Workflow jobs API. + /// + /// + /// See the Actions Workflow jobs API documentation for more information. + /// + public interface IActionsWorkflowJobsClient + { + /// + /// Re-runs a specific workflow job in a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-job-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + Task Rerun(string owner, string name, long jobId); + + /// + /// Gets a specific job in a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#get-a-job-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The unique identifier of the job. + Task Get(string owner, string name, long jobId); + + /// + /// Gets the plain text log file for a workflow job. + /// + /// + /// https://developer.github.com/v3/actions/workflow-jobs/#download-job-logs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow job. + Task GetLogs(string owner, string name, long jobId); + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task List(string owner, string name, long runId); + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + Task List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest); + + /// + /// Lists jobs for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// Details to filter the request, such as by when completed. + /// Options to change the API response. + Task List(string owner, string name, long runId, WorkflowRunJobsRequest workflowRunJobsRequest, ApiOptions options); + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + Task List(string owner, string name, long runId, int attemptNumber); + + /// + /// Lists jobs for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-jobs-for-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + /// Options to change the API response. + Task List(string owner, string name, long runId, int attemptNumber, ApiOptions options); + } +} diff --git a/Octokit/Clients/IActionsWorkflowRunsClient.cs b/Octokit/Clients/IActionsWorkflowRunsClient.cs new file mode 100644 index 0000000000..a4b42f61f9 --- /dev/null +++ b/Octokit/Clients/IActionsWorkflowRunsClient.cs @@ -0,0 +1,267 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Workflow runs API. + /// + /// + /// See the Actions Workflow runs API documentation for more information. + /// + public interface IActionsWorkflowRunsClient + { + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + Task List(string owner, string name); + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + Task List(string owner, string name, WorkflowRunsRequest workflowRunsRequest); + + /// + /// Lists all workflow runs for a repository. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository + /// + /// The owner of the repository. + /// The name of the repository. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + Task List(string owner, string name, WorkflowRunsRequest workflowRunsRequest, ApiOptions options); + + /// + /// Gets a specific workflow run in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task Get(string owner, string name, long runId); + + /// + /// Deletes a specific workflow run. Anyone with write access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task Delete(string owner, string name, long runId); + + /// + /// Get the review history for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-the-review-history-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + [ExcludeFromPaginationApiOptionsConventionTest("Pagination not supported by GitHub API (tested 30/03/2022)")] + [ExcludeFromPaginationNamingConventionTest("Pagination not supported by GitHub API (tested 30/03/2022)")] + Task> GetReviewHistory(string owner, string name, long runId); + + /// + /// Approve or reject pending deployments that are waiting on approval by a required reviewer. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#review-pending-deployments-for-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The review for the pending deployment. + Task ReviewPendingDeployments(string owner, string name, long runId, PendingDeploymentReview review); + + /// + /// Approves a workflow run for a pull request from a public fork of a first time contributor. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#approve-a-workflow-run-for-a-fork-pull-request + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task Approve(string owner, string name, long runId); + + /// + /// Gets a specific workflow run attempt. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run-attempt + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + Task GetAttempt(string owner, string name, long runId, long attemptNumber); + + /// + /// Gets a byte array containing an archive of log files for a specific workflow run attempt. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-attempt-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + /// The attempt number of the workflow run. + Task GetAttemptLogs(string owner, string name, long runId, long attemptNumber); + + /// + /// Cancels a workflow run using its Id. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#cancel-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task Cancel(string owner, string name, long runId); + + /// + /// Gets a byte array containing an archive of log files for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task GetLogs(string owner, string name, long runId); + + /// + /// Deletes all logs for a workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#delete-workflow-run-logs + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task DeleteLogs(string owner, string name, long runId); + + /// + /// Re-runs your workflow run using its Id. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task Rerun(string owner, string name, long runId); + + /// + /// Re-run all of the failed jobs and their dependent jobs in a workflow run using the Id of the workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#re-run-failed-jobs-from-a-workflow-run + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task RerunFailedJobs(string owner, string name, long runId); + + /// + /// Gets the number of billable minutes and total run time for a specific workflow run. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#get-workflow-run-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow run. + Task GetUsage(string owner, string name, long runId); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + Task ListByWorkflow(string owner, string name, long workflowId); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + Task ListByWorkflow(string owner, string name, string workflowFileName); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + Task ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + Task ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + Task ListByWorkflow(string owner, string name, long workflowId, WorkflowRunsRequest workflowRunsRequest, ApiOptions options); + + /// + /// List all workflow runs for a workflow. + /// + /// + /// https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// Details to filter the request, such as by check suite Id. + /// Options to change the API response. + Task ListByWorkflow(string owner, string name, string workflowFileName, WorkflowRunsRequest workflowRunsRequest, ApiOptions options); + } +} diff --git a/Octokit/Clients/IActionsWorkflowsClient.cs b/Octokit/Clients/IActionsWorkflowsClient.cs new file mode 100644 index 0000000000..24f0ee791f --- /dev/null +++ b/Octokit/Clients/IActionsWorkflowsClient.cs @@ -0,0 +1,156 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Actions Workflows API. + /// + /// + /// See the Actions Workflows API documentation for more information. + /// + public interface IActionsWorkflowsClient + { + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + /// The parameters to use to trigger the workflow run. + Task CreateDispatch(string owner, string name, string workflowFileName, CreateWorkflowDispatch createDispatch); + + /// + /// Manually triggers a GitHub Actions workflow run in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#create-a-workflow-dispatch-event + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + /// The parameters to use to trigger the workflow run. + Task CreateDispatch(string owner, string name, long workflowId, CreateWorkflowDispatch createDispatch); + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + Task Disable(string owner, string name, string workflowFileName); + + /// + /// Disables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#disable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + Task Disable(string owner, string name, long workflowId); + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + Task Enable(string owner, string name, string workflowFileName); + + /// + /// Enables a specific workflow in a repository by Id. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#enable-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + Task Enable(string owner, string name, long workflowId); + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + Task Get(string owner, string name, string workflowFileName); + + /// + /// Gets a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-a-workflow + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + Task Get(string owner, string name, long workflowId); + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The workflow file name. + Task GetUsage(string owner, string name, string workflowFileName); + + /// + /// Gets useage of a specific workflow in a repository by Id. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#get-workflow-usage + /// + /// The owner of the repository. + /// The name of the repository. + /// The Id of the workflow. + Task GetUsage(string owner, string name, long workflowId); + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + Task List(string owner, string name); + + /// + /// Lists the workflows in a repository. Anyone with read access to the repository can use this endpoint. + /// + /// + /// https://developer.github.com/v3/actions/workflows/#list-repository-workflows + /// + /// The owner of the repository. + /// The name of the repository. + /// Options to change the API response. + Task List(string owner, string name, ApiOptions options); + + /// + /// Client for the Workflow jobs API. + /// + IActionsWorkflowJobsClient Jobs { get; } + + /// + /// Client for the Workflow runs API. + /// + IActionsWorkflowRunsClient Runs { get; } + } +} diff --git a/Octokit/Clients/IOrganizationActionsClient.cs b/Octokit/Clients/IOrganizationActionsClient.cs index 38dd3cd032..c2e35c7070 100644 --- a/Octokit/Clients/IOrganizationActionsClient.cs +++ b/Octokit/Clients/IOrganizationActionsClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit +namespace Octokit { /// /// A client for GitHub's Org Actions API. diff --git a/Octokit/Clients/IRepositoriesClient.cs b/Octokit/Clients/IRepositoriesClient.cs index 5ccb62c744..983fb20298 100644 --- a/Octokit/Clients/IRepositoriesClient.cs +++ b/Octokit/Clients/IRepositoriesClient.cs @@ -1,7 +1,6 @@ -using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using System.Collections.Generic; namespace Octokit { @@ -9,7 +8,7 @@ namespace Octokit /// A client for GitHub's Repositories API. /// /// - /// See the Repositories API documentation for more details. + /// See the Repositories API documentation for more details. /// public interface IRepositoriesClient { diff --git a/Octokit/Clients/IRepositoryActionsClient.cs b/Octokit/Clients/IRepositoryActionsClient.cs index af0aabe896..02e7f3bdfe 100644 --- a/Octokit/Clients/IRepositoryActionsClient.cs +++ b/Octokit/Clients/IRepositoryActionsClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit +namespace Octokit { /// /// A client for GitHub's Repository Actions API. diff --git a/Octokit/Clients/OrganizationActionsClient.cs b/Octokit/Clients/OrganizationActionsClient.cs index 639c728a9b..feec372ff9 100644 --- a/Octokit/Clients/OrganizationActionsClient.cs +++ b/Octokit/Clients/OrganizationActionsClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit +namespace Octokit { /// /// A client for GitHub's Org Actions API. diff --git a/Octokit/Clients/RepositoryActionsClient.cs b/Octokit/Clients/RepositoryActionsClient.cs index 9837448eba..6a442efa57 100644 --- a/Octokit/Clients/RepositoryActionsClient.cs +++ b/Octokit/Clients/RepositoryActionsClient.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Octokit +namespace Octokit { /// /// A client for GitHub's Repository Actions API. diff --git a/Octokit/GitHubClient.cs b/Octokit/GitHubClient.cs index ba2d8a2633..77c8dbba5e 100644 --- a/Octokit/GitHubClient.cs +++ b/Octokit/GitHubClient.cs @@ -118,6 +118,7 @@ public GitHubClient(IConnection connection) Licenses = new LicensesClient(apiConnection); RateLimit = new RateLimitClient(apiConnection); Meta = new MetaClient(apiConnection); + Actions = new ActionsClient(apiConnection); } /// @@ -368,6 +369,14 @@ public Uri BaseAddress /// public IMarkdownClient Markdown { get; private set; } + /// + /// Access GitHub's Actions API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/actions/ + /// + public IActionsClient Actions { get; private set; } + static Uri FixUpBaseUri(Uri uri) { Ensure.ArgumentNotNull(uri, nameof(uri)); diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 88a8b95270..c4ae3b26b9 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -9,7 +9,6 @@ namespace Octokit public static partial class ApiUrls { static readonly Uri _currentUserRepositoriesUrl = new Uri("user/repos", UriKind.Relative); - static readonly Uri _currentUserOrganizationsUrl = new Uri("user/orgs", UriKind.Relative); static readonly Uri _currentUserSshKeys = new Uri("user/keys", UriKind.Relative); static readonly Uri _currentUserGpgKeys = new Uri("user/gpg_keys", UriKind.Relative); static readonly Uri _currentUserStars = new Uri("user/starred", UriKind.Relative); @@ -4776,5 +4775,366 @@ public static Uri PackageVersionRestoreUser(string username, PackageType package { return "/users/{0}/packages/{1}/{2}/versions/{3}/restore".FormatUri(username, packageType.ToParameter(), packageName, packageVersionId); } + + /// + /// Returns the that disables an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsDispatchWorkflow(string owner, string repo, long workflowId) + { + return "/repos/{0}/{1}/actions/workflows/{2}/dispatches".FormatUri(owner, repo, workflowId); + } + + /// + /// Returns the that disables an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The workflow file name. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsDispatchWorkflow(string owner, string repo, string workflowFileName) + { + return "/repos/{0}/{1}/actions/workflows/{2}/dispatches".FormatUri(owner, repo, workflowFileName.UriEncode()); + } + + /// + /// Returns the that disables an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The workflow file name. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsDisableWorkflow(string owner, string repo, string workflowFileName) + { + return "/repos/{0}/{1}/actions/workflows/{2}/disable".FormatUri(owner, repo, workflowFileName.UriEncode()); + } + + /// + /// Returns the that disables an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsDisableWorkflow(string owner, string repo, long workflowId) + { + return "/repos/{0}/{1}/actions/workflows/{2}/disable".FormatUri(owner, repo, workflowId); + } + + /// + /// Returns the that enables an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The workflow file name. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsEnableWorkflow(string owner, string repo, string workflowFileName) + { + return "/repos/{0}/{1}/actions/workflows/{2}/enable".FormatUri(owner, repo, workflowFileName.UriEncode()); + } + + /// + /// Returns the that enables an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsEnableWorkflow(string owner, string repo, long workflowId) + { + return "/repos/{0}/{1}/actions/workflows/{2}/enable".FormatUri(owner, repo, workflowId); + } + + /// + /// Returns the that gets an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The workflow file name. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsGetWorkflow(string owner, string repo, string workflowFileName) + { + return "/repos/{0}/{1}/actions/workflows/{2}".FormatUri(owner, repo, workflowFileName.UriEncode()); + } + + /// + /// Returns the that gets an Actions workflow for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsGetWorkflow(string owner, string repo, long workflowId) + { + return "/repos/{0}/{1}/actions/workflows/{2}".FormatUri(owner, repo, workflowId); + } + + /// + /// Returns the that gets an Actions workflow'usage for a repository. + /// + /// The owner of repo + /// The name of repo + /// The workflow file name. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsGetWorkflowUsage(string owner, string repo, string workflowFileName) + { + return "/repos/{0}/{1}/actions/workflows/{2}/timing".FormatUri(owner, repo, workflowFileName.UriEncode()); + } + + /// + /// Returns the that gets an Actions workflow's usage for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsGetWorkflowUsage(string owner, string repo, long workflowId) + { + return "/repos/{0}/{1}/actions/workflows/{2}/timing".FormatUri(owner, repo, workflowId); + } + + /// + /// Returns the that handles the Actions workflows for the repository. + /// + /// The owner of repo + /// The name of repo + /// The that handles the Actions workflows for the repository. + public static Uri ActionsListWorkflows(string owner, string repo) + { + return "/repos/{0}/{1}/actions/workflows".FormatUri(owner, repo); + } + + /// + /// Returns the that re-runs an Actions workflow job for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow job. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsRerunWorkflowJob(string owner, string repo, long jobId) + { + return "/repos/{0}/{1}/actions/jobs/{2}/rerun".FormatUri(owner, repo, jobId); + } + + /// + /// Returns the that re-runs an Actions workflow job for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow job. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsGetWorkflowJob(string owner, string repo, long jobId) + { + return "/repos/{0}/{1}/actions/jobs/{2}".FormatUri(owner, repo, jobId); + } + + /// + /// Returns the that gets the logs an Actions workflow job for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow job. + /// The that gets an Actions workflow job for a repository. + public static Uri ActionsGetWorkflowJobLogs(string owner, string repo, long jobId) + { + return "/repos/{0}/{1}/actions/jobs/{2}/logs".FormatUri(owner, repo, jobId); + } + + /// + /// Returns the that handles the Actions jobs for a workflow run. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The that handles the Actions workflows runs for a workflow. + public static Uri ActionsListWorkflowJobs(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/jobs".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that handles the Actions jobs for a workflow run. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The attempt number of the workflow job. + /// The that handles the Actions workflows runs for a workflow. + public static Uri ActionsListWorkflowJobs(string owner, string repo, long runId, int attemptNumber) + { + return "/repos/{0}/{1}/actions/runs/{2}/attempts/{3}/jobs".FormatUri(owner, repo, runId, attemptNumber); + } + + /// + /// Returns the that gets Actions workflow runs for a repository. + /// + /// The owner of repo + /// The name of repo + /// The that gets Actions workflow runs for a repository. + public static Uri ActionsWorkflowRuns(string owner, string repo) + { + return "/repos/{0}/{1}/actions/runs".FormatUri(owner, repo); + } + + /// + /// Returns the that gets an Actions workflow run for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The that gets an Actions workflow run for a repository. + public static Uri ActionsWorkflowRun(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that gets an Actions workflow run attempt for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The attempt number of the workflow run. + /// The that gets an Actions workflow run for a repository. + public static Uri ActionsWorkflowRunAttempt(string owner, string repo, long runId, long attemptNumber) + { + return "/repos/{0}/{1}/actions/runs/{2}/attempts/{3}".FormatUri(owner, repo, runId, attemptNumber); + } + + /// + /// Returns the that approves an Actions workflow run for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The that approves an Actions workflow run for a repository. + public static Uri ActionsApproveWorkflowRun(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/approve".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that cancels an Actions workflow run for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The that cancels an Actions workflow run for a repository. + public static Uri ActionsCancelWorkflowRun(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/cancel".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that gets the logs an Actions workflow run attempt for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The that gets an Actions workflow run for a repository. + public static Uri ActionsGetWorkflowRunLogs(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/logs".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that gets the logs an Actions workflow run attempt for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow run. + /// The attempt number of the workflow run. + /// The that gets an Actions workflow run for a repository. + public static Uri ActionsGetWorkflowRunAttemptLogs(string owner, string repo, long runId, long attemptNumber) + { + return "/repos/{0}/{1}/actions/runs/{2}/attempts/{3}/logs".FormatUri(owner, repo, runId, attemptNumber); + } + + /// + /// Returns the that re-runs an Actions workflow run for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow job. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsRerunWorkflowRun(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/rerun".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that re-runs failed jobs of an Actions workflow run for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow job. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsRerunWorkflowRunFailedJobs(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/rerun-failed-jobs".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that gets an Actions workflow's usage for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets an Actions workflow for a repository. + public static Uri ActionsGetWorkflowRunUsage(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/timing".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that gets Actions workflow run approvals for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets Actions workflow run approvals for a repository. + public static Uri ActionsWorkflowRunApprovals(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/approvals".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that gets Actions workflow run pending deployments for a repository. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that gets Actions workflow run pending deployments for a repository. + public static Uri ActionsWorkflowRunPendingDeployments(string owner, string repo, long runId) + { + return "/repos/{0}/{1}/actions/runs/{2}/pending_deployments".FormatUri(owner, repo, runId); + } + + /// + /// Returns the that handles the Actions workflow runs for a workflow. + /// + /// The owner of repo + /// The name of repo + /// The Id of the workflow. + /// The that handles the Actions workflows runs for a workflow. + public static Uri ActionsListWorkflowRuns(string owner, string repo, long workflowId) + { + return "/repos/{0}/{1}/actions/workflows/{2}/runs".FormatUri(owner, repo, workflowId); + } + + /// + /// Returns the that handles the Actions workflow runs for a workflow. + /// + /// The owner of repo + /// The name of repo + /// The workflow file name. + /// The that handles the Actions workflows runs for a workflow. + public static Uri ActionsListWorkflowRuns(string owner, string repo, string workflowFileName) + { + return "/repos/{0}/{1}/actions/workflows/{2}/runs".FormatUri(owner, repo, workflowFileName.UriEncode()); + } } } diff --git a/Octokit/Helpers/StringExtensions.cs b/Octokit/Helpers/StringExtensions.cs index 30526f831f..42ed83361f 100644 --- a/Octokit/Helpers/StringExtensions.cs +++ b/Octokit/Helpers/StringExtensions.cs @@ -73,6 +73,14 @@ public static Uri ExpandUriTemplate(this string template, object values) public static string ToRubyCase(this string propertyName) { Ensure.ArgumentNotNullOrEmptyString(propertyName, nameof(propertyName)); + + // If the entire property is already all upper case, then do not split it across + // word boundaries. For example, "UBUNTU" should not be changed to "u_b_u_n_t_u". + if (string.Equals(propertyName, propertyName.ToUpperInvariant(), StringComparison.Ordinal)) + { + return propertyName; + } + return string.Join("_", propertyName.SplitUpperCase()).ToLowerInvariant(); } diff --git a/Octokit/IGitHubClient.cs b/Octokit/IGitHubClient.cs index 2de41eb246..220cf3c0d7 100644 --- a/Octokit/IGitHubClient.cs +++ b/Octokit/IGitHubClient.cs @@ -39,6 +39,14 @@ public interface IGitHubClient : IApiInfoProvider /// IActivitiesClient Activity { get; } + /// + /// Access GitHub's Actions API. + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/actions/ + /// + IActionsClient Actions { get; } + /// /// Access GitHub's Application API. /// diff --git a/Octokit/Models/Request/CheckRunStatusFilter.cs b/Octokit/Models/Request/CheckRunStatusFilter.cs new file mode 100644 index 0000000000..c136f39856 --- /dev/null +++ b/Octokit/Models/Request/CheckRunStatusFilter.cs @@ -0,0 +1,34 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum CheckRunStatusFilter + { + [Parameter(Value = "success")] + Success, + [Parameter(Value = "failure")] + Failure, + [Parameter(Value = "neutral")] + Neutral, + [Parameter(Value = "cancelled")] + Cancelled, + [Parameter(Value = "timed_out")] + TimedOut, + [Parameter(Value = "action_required")] + ActionRequired, + [Parameter(Value = "stale")] + Stale, + [Parameter(Value = "requested")] + Requested, + [Parameter(Value = "in_progress")] + InProgress, + [Parameter(Value = "completed")] + Completed, + [Parameter(Value = "queued")] + Queued, + [Parameter(Value = "waiting")] + Waiting, + [Parameter(Value = "skipped")] + Skipped, + } +} diff --git a/Octokit/Models/Request/CreateWorkflowDispatch.cs b/Octokit/Models/Request/CreateWorkflowDispatch.cs new file mode 100644 index 0000000000..a570a2415e --- /dev/null +++ b/Octokit/Models/Request/CreateWorkflowDispatch.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Specifies the values used to create a workflow dispatch event. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class CreateWorkflowDispatch + { + /// + /// Creates a new workflow dispatch event. + /// + /// Required. The git reference for the workflow. The reference can be a branch or tag name. + public CreateWorkflowDispatch(string @ref) + { + Ensure.ArgumentNotNullOrEmptyString(@ref, nameof(@ref)); + Ref = @ref; + } + + /// + /// The git reference for the workflow. The reference can be a branch or tag name. + /// + public string Ref { get; private set; } + + /// + /// Input keys and values configured in the workflow file. + /// + public IDictionary Inputs { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Ref: {0}", Ref); + } + } + } +} diff --git a/Octokit/Models/Request/PendingDeploymentReview.cs b/Octokit/Models/Request/PendingDeploymentReview.cs new file mode 100644 index 0000000000..919d3d4236 --- /dev/null +++ b/Octokit/Models/Request/PendingDeploymentReview.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PendingDeploymentReview + { + public PendingDeploymentReview(IList environmentIds, PendingDeploymentReviewState state, string comment) + { + Ensure.ArgumentNotNullOrEmptyEnumerable(environmentIds, nameof(environmentIds)); + Ensure.ArgumentNotNull(comment, nameof(comment)); + + EnvironmentIds = environmentIds; + State = state; + Comment = comment; + } + + /// + /// The list of environment Ids to approve or reject. + /// + public IList EnvironmentIds { get; private set; } + + /// + /// Whether to approve or reject deployment to the specified environments. + /// + public StringEnum State { get; private set; } + + /// + /// A comment to accompany the deployment review. + /// + public string Comment { get; private set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "EnvironmentIds: {0}, State: {1}, Comment: {2}", EnvironmentIds, State, Comment); } + } + } +} diff --git a/Octokit/Models/Request/PendingDeploymentReviewState.cs b/Octokit/Models/Request/PendingDeploymentReviewState.cs new file mode 100644 index 0000000000..b3ac1bc9f7 --- /dev/null +++ b/Octokit/Models/Request/PendingDeploymentReviewState.cs @@ -0,0 +1,12 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum PendingDeploymentReviewState + { + [Parameter(Value = "approved")] + Approved, + [Parameter(Value = "rejected")] + Rejected, + } +} diff --git a/Octokit/Models/Request/WorkflowRunJobsRequest.cs b/Octokit/Models/Request/WorkflowRunJobsRequest.cs new file mode 100644 index 0000000000..d414b8e4ad --- /dev/null +++ b/Octokit/Models/Request/WorkflowRunJobsRequest.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Details to filter a workflow run jobs request, such as by the latest attempt. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunJobsRequest : RequestParameters + { + /// + /// Filters jobs by their completed_at timestamp. + /// + public StringEnum Filter { get; set; } + + internal string DebuggerDisplay => string.Format( + CultureInfo.InvariantCulture, + "Filter: {0}", + Filter); + } +} diff --git a/Octokit/Models/Request/WorkflowRunsRequest.cs b/Octokit/Models/Request/WorkflowRunsRequest.cs new file mode 100644 index 0000000000..a7919033d2 --- /dev/null +++ b/Octokit/Models/Request/WorkflowRunsRequest.cs @@ -0,0 +1,68 @@ +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; + +namespace Octokit +{ + /// + /// Details to filter a workflow runs request, such as by branch or check suite Id. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunsRequest : RequestParameters + { + /// + /// Returns someone's workflow runs. + /// + public string Actor { get; set; } + + /// + /// Returns workflow runs associated with a branch. + /// + public string Branch { get; set; } + + /// + /// Returns workflow run triggered by the event you specify. + /// + public string Event { get; set; } + + /// + /// Only returns workflow runs that are associated with the specified head SHA. + /// + [Parameter(Key = "head_sha")] + public string HeadSha { get; set; } + + /// + /// Returns workflow runs with the check run status or conclusion that you specify. + /// + public StringEnum Status { get; set; } + + /// + /// Returns workflow runs created within the given date-time range. + /// + public string Created { get; set; } + + /// + /// If true pull requests are omitted from the response. + /// + [Parameter(Key = "exclude_pull_requests")] + public bool? ExcludePullRequests { get; set; } + + /// + /// Returns workflow runs with the check_suite_id that you specify. + /// + [Parameter(Key = "check_suite_id")] + public long? CheckSuiteId { get; set; } + + internal string DebuggerDisplay => string.Format( + CultureInfo.InvariantCulture, + "Actor: {0}, Branch: {1}, Event: {2}, Status: {3}, Created: {4}, ExcludePullRequests: {5}, CheckSuiteId: {6}, HeadSha: {7}", + Actor, + Branch, + Event, + Status, + Created, + ExcludePullRequests, + CheckSuiteId, + HeadSha); + } +} diff --git a/Octokit/Models/Response/EnvironmentApproval.cs b/Octokit/Models/Response/EnvironmentApproval.cs new file mode 100644 index 0000000000..1f56025045 --- /dev/null +++ b/Octokit/Models/Response/EnvironmentApproval.cs @@ -0,0 +1,69 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents an environment for a deployment approval. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class EnvironmentApproval + { + public EnvironmentApproval() { } + + public EnvironmentApproval(long id, string nodeId, string name, string url, string htmlUrl, DateTimeOffset createdAt, DateTimeOffset updatedAt) + { + Id = id; + NodeId = nodeId; + Name = name; + Url = url; + HtmlUrl = htmlUrl; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + } + + /// + /// The Id of the environment. + /// + public long Id { get; private set; } + + /// + /// GraphQL Node Id. + /// + public string NodeId { get; private set; } + + /// + /// The name of the environment. + /// + public string Name { get; private set; } + + /// + /// The URL for this environment. + /// + public string Url { get; private set; } + + /// + /// The URL for the HTML view of this environment. + /// + public string HtmlUrl { get; private set; } + + /// + /// The time that the environment was created. + /// + public DateTimeOffset CreatedAt { get; private set; } + + /// + /// The time that the environment was last updated. + /// + public DateTimeOffset UpdatedAt { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Id: {0}, Name: {1}", Id, Name); + } + } + } +} diff --git a/Octokit/Models/Response/EnvironmentApprovalState.cs b/Octokit/Models/Response/EnvironmentApprovalState.cs new file mode 100644 index 0000000000..4144b25f28 --- /dev/null +++ b/Octokit/Models/Response/EnvironmentApprovalState.cs @@ -0,0 +1,12 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum EnvironmentApprovalState + { + [Parameter(Value = "approved")] + Approved, + [Parameter(Value = "rejected")] + Rejected, + } +} diff --git a/Octokit/Models/Response/EnvironmentApprovals.cs b/Octokit/Models/Response/EnvironmentApprovals.cs new file mode 100644 index 0000000000..51915fdd35 --- /dev/null +++ b/Octokit/Models/Response/EnvironmentApprovals.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents an environment for a deployment approval. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class EnvironmentApprovals + { + public EnvironmentApprovals() { } + + public EnvironmentApprovals(IReadOnlyList environments, User user, EnvironmentApprovalState state, string comment) + { + Environments = environments; + User = user; + State = state; + Comment = comment; + } + + /// + /// The list of environments that were approved or rejected. + /// + public IReadOnlyList Environments { get; private set; } + + /// + /// The user that approved or rejected the deployments. + /// + public User User { get; private set; } + + /// + /// Whether deployment to the environment(s) was approved or rejected. + /// + public StringEnum State { get; private set; } + + /// + /// The comment submitted with the deployment review. + /// + public string Comment { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "State: {0}, Comment: {1}", State, Comment); + } + } + } +} diff --git a/Octokit/Models/Response/Workflow.cs b/Octokit/Models/Response/Workflow.cs new file mode 100644 index 0000000000..ec8cdfefb4 --- /dev/null +++ b/Octokit/Models/Response/Workflow.cs @@ -0,0 +1,90 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class Workflow + { + public Workflow() { } + + public Workflow(long id, string nodeId, string name, string path, WorkflowState state, DateTimeOffset createdAt, DateTimeOffset updatedAt, string url, string htmlUrl, string badgeUrl, DateTimeOffset? deletedAt) + { + Id = id; + NodeId = nodeId; + Name = name; + Path = path; + State = state; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + Url = url; + HtmlUrl = htmlUrl; + BadgeUrl = badgeUrl; + DeletedAt = deletedAt; + } + + /// + /// The Id for this workflow. + /// + public long Id { get; private set; } + + /// + /// GraphQL Node Id. + /// + public string NodeId { get; private set; } + + /// + /// Name of the workflow. + /// + public string Name { get; private set; } + + /// + /// The path of the workflow file. + /// + public string Path { get; private set; } + + /// + /// The state of the workflow. + /// + public StringEnum State { get; private set; } + + /// + /// The time that the workflow was created. + /// + public DateTimeOffset CreatedAt { get; private set; } + + /// + /// The time that the workflow was last updated. + /// + public DateTimeOffset UpdatedAt { get; private set; } + + /// + /// The URL for this workflow. + /// + public string Url { get; private set; } + + /// + /// The URL for the HTML view of this workflow. + /// + public string HtmlUrl { get; private set; } + + /// + /// The URL of the badge image for this workflow. + /// + public string BadgeUrl { get; private set; } + + /// + /// The time that the workflow was deleted. + /// + public DateTimeOffset? DeletedAt { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Id: {0} Name: {1}", Id, Name); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowBillable.cs b/Octokit/Models/Response/WorkflowBillable.cs new file mode 100644 index 0000000000..9ab1b7a5f1 --- /dev/null +++ b/Octokit/Models/Response/WorkflowBillable.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowBillable + { + public WorkflowBillable() { } + + public WorkflowBillable(WorkflowBillableTiming ubuntu, WorkflowBillableTiming macOS, WorkflowBillableTiming windows) + { + Ubuntu = ubuntu; + MacOS = macOS; + Windows = windows; + } + + /// + /// The Ubuntu billable timing. + /// + [Parameter(Key = "UBUNTU")] + public WorkflowBillableTiming Ubuntu { get; private set; } + + /// + /// The macOS billable timing. + /// + [Parameter(Key = "MACOS")] + public WorkflowBillableTiming MacOS { get; private set; } + + /// + /// The Windows billable timing. + /// + [Parameter(Key = "WINDOWS")] + public WorkflowBillableTiming Windows { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Ubuntu: {0}, macOS: {1}, Windows: {2}", Ubuntu, MacOS, Windows); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowBillableTiming.cs b/Octokit/Models/Response/WorkflowBillableTiming.cs new file mode 100644 index 0000000000..550ff467be --- /dev/null +++ b/Octokit/Models/Response/WorkflowBillableTiming.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowBillableTiming + { + public WorkflowBillableTiming() { } + + public WorkflowBillableTiming(long totalMs) + { + TotalMs = totalMs; + } + + /// + /// The total billable milliseconds. + /// + public long TotalMs { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "TotalMs: {0}", TotalMs); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowJob.cs b/Octokit/Models/Response/WorkflowJob.cs new file mode 100644 index 0000000000..34f9213a24 --- /dev/null +++ b/Octokit/Models/Response/WorkflowJob.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowJob + { + public WorkflowJob() { } + + public WorkflowJob(long id, long runId, string runUrl, string nodeId, string headSha, string url, string htmlUrl, WorkflowJobStatus status, WorkflowJobConclusion? conclusion, DateTimeOffset startedAt, DateTimeOffset? completedAt, string name, IReadOnlyList steps, string checkRunUrl, IReadOnlyList labels, long runnerId, string runnerName, long runnerGroupId, string runnerGroupName) + { + Id = id; + RunId = runId; + RunUrl = runUrl; + NodeId = nodeId; + HeadSha = headSha; + Url = url; + HtmlUrl = htmlUrl; + Status = status; + Conclusion = conclusion; + StartedAt = startedAt; + CompletedAt = completedAt; + Name = name; + Steps = steps; + CheckRunUrl = checkRunUrl; + Labels = labels; + RunnerId = runnerId; + RunnerName = runnerName; + RunnerGroupId = runnerGroupId; + RunnerGroupName = runnerGroupName; + } + + /// + /// The Id of the job. + /// + public long Id { get; private set; } + + /// + /// The Id of the associated workflow run. + /// + public long RunId { get; private set; } + + /// + /// The run URL for this job. + /// + public string RunUrl { get; private set; } + + /// + /// The SHA of the commit that is being run. + /// + public string HeadSha { get; private set; } + + /// + /// The URL for this job. + /// + public string Url { get; private set; } + + /// + /// The URL for the HTML view of this job. + /// + public string HtmlUrl { get; private set; } + + /// + /// GraphQL Node Id. + /// + public string NodeId { get; private set; } + + /// + /// The phase of the lifecycle that the job is currently in. + /// + public StringEnum Status { get; private set; } + + /// + /// The outcome of the job. + /// + public StringEnum? Conclusion { get; private set; } + + /// + /// The time that the job started. + /// + public DateTimeOffset StartedAt { get; private set; } + + /// + /// The time that the job finished. + /// + public DateTimeOffset? CompletedAt { get; private set; } + + /// + /// Name of the workflow job. + /// + public string Name { get; private set; } + + /// + /// Steps in this job. + /// + public IReadOnlyList Steps { get; private set; } + + /// + /// The URL for the check run for the job. + /// + public string CheckRunUrl { get; private set; } + + /// + /// Labels for the workflow job. + /// + public IReadOnlyList Labels { get; private set; } + + /// + /// The Id of the runner to which this job has been assigned. + /// + public long RunnerId { get; private set; } + + /// + /// The name of the runner to which this job has been assigned. + /// + public string RunnerName { get; private set; } + + /// + /// The Id of the runner group to which this job has been assigned. + /// + public long RunnerGroupId { get; private set; } + + /// + /// The name of the runner group to which this job has been assigned. + /// + public string RunnerGroupName { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Id: {0} Name: {1}", Id, Name); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowJobConclusion.cs b/Octokit/Models/Response/WorkflowJobConclusion.cs new file mode 100644 index 0000000000..e940d307d3 --- /dev/null +++ b/Octokit/Models/Response/WorkflowJobConclusion.cs @@ -0,0 +1,22 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum WorkflowJobConclusion + { + [Parameter(Value = "success")] + Success, + [Parameter(Value = "failure")] + Failure, + [Parameter(Value = "neutral")] + Neutral, + [Parameter(Value = "cancelled")] + Cancelled, + [Parameter(Value = "skipped")] + Skipped, + [Parameter(Value = "timed_out")] + TimedOut, + [Parameter(Value = "action_required")] + ActionRequired, + } +} diff --git a/Octokit/Models/Response/WorkflowJobStatus.cs b/Octokit/Models/Response/WorkflowJobStatus.cs new file mode 100644 index 0000000000..80e2c955ef --- /dev/null +++ b/Octokit/Models/Response/WorkflowJobStatus.cs @@ -0,0 +1,14 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum WorkflowJobStatus + { + [Parameter(Value = "queued")] + Queued, + [Parameter(Value = "in_progress")] + InProgress, + [Parameter(Value = "completed")] + Completed, + } +} diff --git a/Octokit/Models/Response/WorkflowJobStep.cs b/Octokit/Models/Response/WorkflowJobStep.cs new file mode 100644 index 0000000000..4f51e7f3ac --- /dev/null +++ b/Octokit/Models/Response/WorkflowJobStep.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowJobStep + { + public WorkflowJobStep() { } + + public WorkflowJobStep(string name, WorkflowJobStatus status, WorkflowJobConclusion conclusion, int number, DateTimeOffset? startedAt, DateTimeOffset? completedAt) + { + Name = name; + Status = status; + Conclusion = conclusion; + Number = number; + StartedAt = startedAt; + CompletedAt = completedAt; + } + + /// + /// The name of the step. + /// + public string Name { get; private set; } + + /// + /// The number of the step. + /// + public int Number { get; private set; } + + /// + /// The phase of the lifecycle that the job is currently in. + /// + public StringEnum Status { get; private set; } + + /// + /// The outcome of the job. + /// + public StringEnum? Conclusion { get; private set; } + + /// + /// The time that the step started. + /// + public DateTimeOffset? StartedAt { get; private set; } + + /// + /// The time that the step finished. + /// + public DateTimeOffset? CompletedAt { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Number: {0} Name: {1}", Number, Name); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowJobsResponse.cs b/Octokit/Models/Response/WorkflowJobsResponse.cs new file mode 100644 index 0000000000..663425c3b0 --- /dev/null +++ b/Octokit/Models/Response/WorkflowJobsResponse.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowJobsResponse + { + public WorkflowJobsResponse() + { + } + + public WorkflowJobsResponse(int totalCount, IReadOnlyList jobs) + { + TotalCount = totalCount; + Jobs = jobs; + } + + /// + /// The total number of workflow runs. + /// + public int TotalCount { get; private set; } + + /// + /// The retrieved workflow runs. + /// + public IReadOnlyList Jobs { get; private set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "TotalCount: {0}, Jobs: {1}", TotalCount, Jobs.Count); + } +} diff --git a/Octokit/Models/Response/WorkflowReference.cs b/Octokit/Models/Response/WorkflowReference.cs new file mode 100644 index 0000000000..056e61c36c --- /dev/null +++ b/Octokit/Models/Response/WorkflowReference.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowReference + { + public WorkflowReference() { } + + public WorkflowReference(string path, string sha, string @ref) + { + Path = path; + Sha = sha; + Ref = @ref; + } + + /// + /// The path of the workflow file. + /// + public string Path { get; private set; } + + /// + /// The SHA of the workflow file. + /// + public string Sha { get; private set; } + + /// + /// The reference of the workflow file. + /// + public string Ref { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Path: {0} SHA: {1}", Path, Sha); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowRun.cs b/Octokit/Models/Response/WorkflowRun.cs new file mode 100644 index 0000000000..7d1073c717 --- /dev/null +++ b/Octokit/Models/Response/WorkflowRun.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRun + { + public WorkflowRun() { } + + public WorkflowRun(long id, string name, string nodeId, long checkSuiteId, string checkSuiteNodeId, string headBranch, string headSha, string path, long runNumber, string @event, string displayTitle, WorkflowRunStatus status, WorkflowRunConclusion? conclusion, long workflowId, string url, string htmlUrl, IReadOnlyList pullRequests, DateTimeOffset createdAt, DateTimeOffset updatedAt, User actor, long runAttempt, IReadOnlyList referencedWorkflows, DateTimeOffset runStartedAt, User triggeringActor, string jobsUrl, string logsUrl, string checkSuiteUrl, string artifactsUrl, string cancelUrl, string rerunUrl, string previousAttemptUrl, string workflowUrl, Commit headCommit, Repository repository, Repository headRepository, long headRepositoryId) + { + Id = id; + Name = name; + NodeId = nodeId; + CheckSuiteId = checkSuiteId; + CheckSuiteNodeId = checkSuiteNodeId; + HeadBranch = headBranch; + HeadSha = headSha; + Path = path; + RunNumber = runNumber; + Event = @event; + DisplayTitle = displayTitle; + Status = status; + Conclusion = conclusion; + WorkflowId = workflowId; + Url = url; + HtmlUrl = htmlUrl; + PullRequests = pullRequests; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + Actor = actor; + RunAttempt = runAttempt; + ReferencedWorkflows = referencedWorkflows; + RunStartedAt = runStartedAt; + TriggeringActor = triggeringActor; + JobsUrl = jobsUrl; + LogsUrl = logsUrl; + CheckSuiteUrl = checkSuiteUrl; + ArtifactsUrl = artifactsUrl; + CancelUrl = cancelUrl; + RerunUrl = rerunUrl; + PreviousAttemptUrl = previousAttemptUrl; + WorkflowUrl = workflowUrl; + HeadCommit = headCommit; + Repository = repository; + HeadRepository = headRepository; + HeadRepositoryId = headRepositoryId; + } + + /// + /// The ID of the workflow run. + /// + public long Id { get; private set; } + + /// + /// The name of the workflow run. + /// + public string Name { get; private set; } + + /// + /// GraphQL Node Id. + /// + public string NodeId { get; private set; } + + /// + /// The ID of the associated check suite. + /// + public long CheckSuiteId { get; private set; } + + /// + /// The node ID of the associated check suite. + /// + public string CheckSuiteNodeId { get; private set; } + + /// + /// The head branch. + /// + public string HeadBranch { get; private set; } + + /// + /// The SHA of the head commit that points to the version of the workflow being run. + /// + public string HeadSha { get; private set; } + + /// + /// The full path of the workflow. + /// + public string Path { get; private set; } + + /// + /// The auto incrementing run number for the workflow run. + /// + public long RunNumber { get; private set; } + + /// + /// The event that triggered the workflow run. + /// + public string Event { get; private set; } + + /// + /// The event-specific title associated with the run or the run-name if set. + /// + public string DisplayTitle { get; private set; } + + /// + /// The status of the the workflow run. + /// + public StringEnum Status { get; private set; } + + /// + /// The conclusion of the the workflow run. + /// + public StringEnum? Conclusion { get; private set; } + + /// + /// The ID of the parent workflow. + /// + public long WorkflowId { get; private set; } + + /// + /// The URL for this workflow run. + /// + public string Url { get; private set; } + + /// + /// The URL for the HTML view of this workflow run. + /// + public string HtmlUrl { get; private set; } + + /// + /// Any associated pull requests. + /// + public IReadOnlyList PullRequests { get; private set; } + + /// + /// The time that the workflow run was created. + /// + public DateTimeOffset CreatedAt { get; private set; } + + /// + /// The time that the workflow run was last updated. + /// + public DateTimeOffset UpdatedAt { get; private set; } + + /// + /// The actor associated with the workflow run. + /// + public User Actor { get; private set; } + + /// + /// Attempt number of the run, 1 for first attempt and higher if the workflow was re-run. + /// + public long RunAttempt { get; private set; } + + /// + /// Any associated pull requests. + /// + public IReadOnlyList ReferencedWorkflows { get; private set; } + + /// + /// The time that the workflow run started. + /// + public DateTimeOffset RunStartedAt { get; private set; } + + /// + /// The actor that triggered the workflow run. + /// + public User TriggeringActor { get; private set; } + + /// + /// The URL for this workflow run's job. + /// + public string JobsUrl { get; private set; } + + /// + /// The URL for this workflow run's logs. + /// + public string LogsUrl { get; private set; } + + /// + /// The URL for this workflow run's check suite. + /// + public string CheckSuiteUrl { get; private set; } + + /// + /// The URL for this workflow run's artifacts. + /// + public string ArtifactsUrl { get; private set; } + + /// + /// The URL to cancel this workflow run. + /// + public string CancelUrl { get; private set; } + + /// + /// The URL to re-run this workflow run. + /// + public string RerunUrl { get; private set; } + + /// + /// The URL for this workflow run's previous run. + /// + public string PreviousAttemptUrl { get; private set; } + + /// + /// The URL for this workflow run's workflow. + /// + public string WorkflowUrl { get; private set; } + + /// + /// The head commit of the workflow run. + /// + public Commit HeadCommit { get; private set; } + + /// + /// The repository associated with the workflow run. + /// + public Repository Repository { get; private set; } + + /// + /// The head repository associated with the workflow run. + /// + public Repository HeadRepository { get; private set; } + + /// + /// The ID of the head repository. + /// + public long HeadRepositoryId { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Id: {0} Name: {1}", Id, Name); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowRunBillable.cs b/Octokit/Models/Response/WorkflowRunBillable.cs new file mode 100644 index 0000000000..c11d2698c4 --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunBillable.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using System.Globalization; +using Octokit.Internal; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunBillable + { + public WorkflowRunBillable() { } + + public WorkflowRunBillable(WorkflowRunBillableTiming ubuntu, WorkflowRunBillableTiming macOS, WorkflowRunBillableTiming windows) + { + Ubuntu = ubuntu; + MacOS = macOS; + Windows = windows; + } + + /// + /// The Ubuntu billable timing. + /// + [Parameter(Key = "UBUNTU")] + public WorkflowRunBillableTiming Ubuntu { get; private set; } + + /// + /// The macOS billable timing. + /// + [Parameter(Key = "MACOS")] + public WorkflowRunBillableTiming MacOS { get; private set; } + + /// + /// The Windows billable timing. + /// + [Parameter(Key = "WINDOWS")] + public WorkflowRunBillableTiming Windows { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Ubuntu: {0}, macOS: {1}, Windows: {2}", Ubuntu, MacOS, Windows); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowRunBillableTiming.cs b/Octokit/Models/Response/WorkflowRunBillableTiming.cs new file mode 100644 index 0000000000..1a93b99aff --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunBillableTiming.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunBillableTiming + { + public WorkflowRunBillableTiming() { } + + public WorkflowRunBillableTiming(long totalMs, long jobs, IReadOnlyList jobRuns) + { + TotalMs = totalMs; + Jobs = jobs; + JobRuns = jobRuns; + } + + /// + /// The total billable milliseconds. + /// + public long TotalMs { get; private set; } + + /// + /// The total number of jobs. + /// + public long Jobs { get; private set; } + + /// + /// The billable job runs. + /// + public IReadOnlyList JobRuns { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "TotalMs: {0}, Jobs: {1}", TotalMs, Jobs); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowRunConclusion.cs b/Octokit/Models/Response/WorkflowRunConclusion.cs new file mode 100644 index 0000000000..5895a31422 --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunConclusion.cs @@ -0,0 +1,22 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum WorkflowRunConclusion + { + [Parameter(Value = "success")] + Success, + [Parameter(Value = "failure")] + Failure, + [Parameter(Value = "neutral")] + Neutral, + [Parameter(Value = "cancelled")] + Cancelled, + [Parameter(Value = "timed_out")] + TimedOut, + [Parameter(Value = "action_required")] + ActionRequired, + [Parameter(Value = "stale")] + Stale, + } +} diff --git a/Octokit/Models/Response/WorkflowRunJobsFilter.cs b/Octokit/Models/Response/WorkflowRunJobsFilter.cs new file mode 100644 index 0000000000..c6110cf6c3 --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunJobsFilter.cs @@ -0,0 +1,24 @@ +using Octokit.Internal; + +namespace Octokit +{ + /// + /// Filter jobs for a workflow run. + /// + /// + /// See https://developer.github.com/v3/actions/workflow-jobs/#list-jobs-for-a-workflow-run for details. + /// + public enum WorkflowRunJobsFilter + { + /// + /// Returns jobs from the most recent execution of the workflow run. + /// + [Parameter(Value = "latest")] + Latest, + /// + /// Returns all jobs for a workflow run, including from old executions of the workflow run. + /// + [Parameter(Value = "all")] + All, + } +} diff --git a/Octokit/Models/Response/WorkflowRunStatus.cs b/Octokit/Models/Response/WorkflowRunStatus.cs new file mode 100644 index 0000000000..ce0356fe07 --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunStatus.cs @@ -0,0 +1,18 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum WorkflowRunStatus + { + [Parameter(Value = "requested")] + Requested, + [Parameter(Value = "in_progress")] + InProgress, + [Parameter(Value = "completed")] + Completed, + [Parameter(Value = "queued")] + Queued, + [Parameter(Value = "waiting")] + Waiting, + } +} diff --git a/Octokit/Models/Response/WorkflowRunTiming.cs b/Octokit/Models/Response/WorkflowRunTiming.cs new file mode 100644 index 0000000000..fdba9e964d --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunTiming.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunTiming + { + public WorkflowRunTiming() { } + + public WorkflowRunTiming(long jobId, long durationMs) + { + JobId = jobId; + DurationMs = durationMs; + } + + /// + /// The job Id. + /// + public long JobId { get; private set; } + + /// + /// The duration of the job in milliseconds. + /// + public long DurationMs { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "JobId: {0}, DurationMs: {1}", JobId, DurationMs); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowRunUsage.cs b/Octokit/Models/Response/WorkflowRunUsage.cs new file mode 100644 index 0000000000..5a2951e2de --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunUsage.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunUsage + { + public WorkflowRunUsage() { } + + public WorkflowRunUsage(WorkflowRunBillable billable, long runDurationMs) + { + Billable = billable; + RunDurationMs = runDurationMs; + } + + /// + /// The billable usage. + /// + public WorkflowRunBillable Billable { get; private set; } + + /// + /// The total run duration in milliseconds. + /// + public long RunDurationMs { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Billing: {0}", Billable); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowRunsResponse.cs b/Octokit/Models/Response/WorkflowRunsResponse.cs new file mode 100644 index 0000000000..25c9bf32e1 --- /dev/null +++ b/Octokit/Models/Response/WorkflowRunsResponse.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowRunsResponse + { + public WorkflowRunsResponse() + { + } + + public WorkflowRunsResponse(int totalCount, IReadOnlyList workflowRuns) + { + TotalCount = totalCount; + WorkflowRuns = workflowRuns; + } + + /// + /// The total number of workflow runs. + /// + public int TotalCount { get; private set; } + + /// + /// The retrieved workflow runs. + /// + public IReadOnlyList WorkflowRuns { get; private set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "TotalCount: {0}, WorkflowRuns: {1}", TotalCount, WorkflowRuns.Count); + } +} diff --git a/Octokit/Models/Response/WorkflowState.cs b/Octokit/Models/Response/WorkflowState.cs new file mode 100644 index 0000000000..d303fe42aa --- /dev/null +++ b/Octokit/Models/Response/WorkflowState.cs @@ -0,0 +1,18 @@ +using Octokit.Internal; + +namespace Octokit +{ + public enum WorkflowState + { + [Parameter(Value = "active")] + Active, + [Parameter(Value = "deleted")] + Deleted, + [Parameter(Value = "disabled_fork")] + DisabledFork, + [Parameter(Value = "disabled_inactivity")] + DisabledInactivity, + [Parameter(Value = "disabled_manually")] + DisabledManually + } +} diff --git a/Octokit/Models/Response/WorkflowUsage.cs b/Octokit/Models/Response/WorkflowUsage.cs new file mode 100644 index 0000000000..82d868df44 --- /dev/null +++ b/Octokit/Models/Response/WorkflowUsage.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowUsage + { + public WorkflowUsage() { } + + public WorkflowUsage(WorkflowBillable billable) + { + Billable = billable; + } + + /// + /// The billable usage. + /// + public WorkflowBillable Billable { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Billing: {0}", Billable); + } + } + } +} diff --git a/Octokit/Models/Response/WorkflowsResponse.cs b/Octokit/Models/Response/WorkflowsResponse.cs new file mode 100644 index 0000000000..a61ca19572 --- /dev/null +++ b/Octokit/Models/Response/WorkflowsResponse.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class WorkflowsResponse + { + public WorkflowsResponse() + { + } + + public WorkflowsResponse(int totalCount, IReadOnlyList workflows) + { + TotalCount = totalCount; + Workflows = workflows; + } + + /// + /// The total number of workflows. + /// + public int TotalCount { get; private set; } + + /// + /// The retrieved workflows. + /// + public IReadOnlyList Workflows { get; private set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "TotalCount: {0}, Workflows: {1}", TotalCount, Workflows.Count); + } +} diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index b212b79773..a55742391c 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -21,7 +21,7 @@ $(DefineConstants);SIMPLE_JSON_INTERNAL;SIMPLE_JSON_OBJARRAYINTERNAL;SIMPLE_JSON_READONLY_COLLECTIONS;SIMPLE_JSON_TYPEINFO - 1591;1701;1702;1705 + $(NoWarn);1591;1701;1702;1705