Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List issues #1349

Merged
merged 16 commits into from
Jul 20, 2023
64 changes: 64 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,59 @@ impl Api {
Ok(rv)
}

/// List all issues associated with an organization and a project
pub fn list_organization_project_issues(
&self,
org: &str,
project: &str,
max_pages: usize,
query: Option<String>,
) -> ApiResult<Vec<Issue>> {
let mut rv = vec![];
let mut cursor = "".to_string();
let mut requests_no = 0;

let url = if let Some(query) = query {
format!(
"/projects/{}/{}/issues/?query={}&",
PathArg(org),
PathArg(project),
QueryArg(&query),
)
} else {
format!("/projects/{}/{}/issues/?", PathArg(org), PathArg(project),)
};

loop {
requests_no += 1;

let resp = self.get(&format!("{}cursor={}", url, QueryArg(&cursor)))?;

if resp.status() == 404 || (resp.status() == 400 && !cursor.is_empty()) {
if rv.is_empty() {
return Err(ApiErrorKind::OrganizationNotFound.into());
} else {
break;
}
}

let pagination = resp.pagination();
rv.extend(resp.convert::<Vec<Issue>>()?.into_iter());

if requests_no == max_pages {
break;
}

if let Some(next) = pagination.into_next_cursor() {
cursor = next;
} else {
break;
}
}

Ok(rv)
}

/// List all repos associated with an organization
pub fn list_organization_repos(&self, org: &str) -> ApiResult<Vec<Repo>> {
let mut rv = vec![];
Expand Down Expand Up @@ -2356,6 +2409,17 @@ struct MissingChecksumsResponse {
missing: HashSet<Digest>,
}

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Issue {
pub id: String,
pub short_id: String,
pub title: String,
pub last_seen: String,
pub status: String,
pub level: String,
}

/// Change information for issue bulk updates.
#[derive(Serialize, Default)]
pub struct IssueChanges {
Expand Down
79 changes: 79 additions & 0 deletions src/commands/issues/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use anyhow::Result;
use clap::{Arg, ArgMatches, Command};

use crate::api::Api;
use crate::config::Config;
use crate::utils::formatting::Table;

pub fn make_command(command: Command) -> Command {
command
.about("List all issues in your organization.")
.arg(
Arg::new("max_rows")
.long("max-rows")
.value_name("MAX_ROWS")
.value_parser(clap::value_parser!(usize))
.help("Maximum number of rows to print."),
)
.arg(
Arg::new("pages")
.long("pages")
.value_name("PAGES")
.default_value("5")
.value_parser(clap::value_parser!(usize))
.help("Maximum number of pages to fetch (100 issues/page)."),
)
.arg(
Arg::new("query")
.long("query")
.value_name("QUERY")
.default_value("")
.help("Query to pass at the request. An example is \"is:unresolved\""),
)
}

pub fn execute(matches: &ArgMatches) -> Result<()> {
let config = Config::current();
let org = config.get_org(matches)?;
let project = config.get_project(matches)?;
let pages = *matches.get_one("pages").unwrap();
let query = matches.get_one::<String>("query").cloned();
let api = Api::current();

let issues = api.list_organization_project_issues(&org, &project, pages, query)?;

let mut table = Table::new();
table
.title_row()
.add("Issue ID")
.add("Short ID")
.add("Title")
.add("Last seen")
.add("Status")
.add("Level");

let max_rows = std::cmp::min(
issues.len(),
*matches.get_one("max_rows").unwrap_or(&std::usize::MAX),
);

if let Some(issues) = issues.get(..max_rows) {
for issue in issues {
let row = table.add_row();
row.add(&issue.id)
.add(&issue.short_id)
.add(&issue.title)
.add(&issue.last_seen)
.add(&issue.status)
.add(&issue.level);
}
}

if table.is_empty() {
println!("No issues found");
} else {
table.print();
}

Ok(())
}
2 changes: 2 additions & 0 deletions src/commands/issues/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ use clap::{Arg, ArgAction, ArgMatches, Command};

use crate::utils::args::ArgExt;

pub mod list;
pub mod mute;
pub mod resolve;
pub mod unresolve;

macro_rules! each_subcommand {
($mac:ident) => {
$mac!(list);
$mac!(mute);
$mac!(resolve);
$mac!(unresolve);
Expand Down
2 changes: 1 addition & 1 deletion src/utils/sourcemaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ impl SourceMapProcessor {
.filter_map(|artifact| Digest::from_str(&artifact.sha1).ok())
.collect();

for mut source in self.sources.values_mut() {
for source in self.sources.values_mut() {
if let Ok(checksum) = source.checksum() {
if already_uploaded_checksums.contains(&checksum) {
source.already_uploaded = true;
Expand Down
10 changes: 10 additions & 0 deletions tests/integration/_cases/issues/issues-display-with-query.trycmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
```
$ sentry-cli issues list --query is:resolved
? success
+------------+-----------+-----------+-----------------------------+----------+-------+
| Issue ID | Short ID | Title | Last seen | Status | Level |
+------------+-----------+-----------+-----------------------------+----------+-------+
| 4242424242 | SEN-CLI-H | N+1 Query | 2023-07-18T00:10:01.222387Z | resolved | info |
+------------+-----------+-----------+-----------------------------+----------+-------+

```
12 changes: 12 additions & 0 deletions tests/integration/_cases/issues/issues-display.trycmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
```
$ sentry-cli issues list
? success
+------------+-----------+---------------------------------------------------------+-----------------------------+------------+-------+
| Issue ID | Short ID | Title | Last seen | Status | Level |
+------------+-----------+---------------------------------------------------------+-----------------------------+------------+-------+
| 4242424243 | SEN-CLI-L | ProgrammingError: column users_user.role does not exist | 2023-07-18T00:12:01.222387Z | unresolved | error |
| 4242424242 | SEN-CLI-H | N+1 Query | 2023-07-18T00:10:01.222387Z | resolved | info |
| 4242424241 | SEN-CLI-1 | NameError: name 'jobs' is not defined | 2023-07-18T00:00:01.222387Z | ignored | error |
+------------+-----------+---------------------------------------------------------+-----------------------------+------------+-------+

```
33 changes: 33 additions & 0 deletions tests/integration/_cases/issues/issues-help.trycmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

```
$ sentry-cli issues --help
? success
Manage issues in Sentry.

Usage: sentry-cli[EXE] issues [OPTIONS] <COMMAND>

Commands:
list List all issues in your organization.
mute Bulk mute all selected issues.
resolve Bulk resolve all selected issues.
unresolve Bulk unresolve all selected issues.
help Print this message or the help of the given subcommand(s)

Options:
-o, --org <ORG> The organization slug
boozec marked this conversation as resolved.
Show resolved Hide resolved
--header <KEY:VALUE> Custom headers that should be attached to all requests
in key:value format.
-p, --project <PROJECT> The project slug.
--auth-token <AUTH_TOKEN> Use the given Sentry auth token.
-s, --status <STATUS> Select all issues matching a given status. [possible values:
resolved, muted, unresolved]
-a, --all Select all issues (this might be limited).
-i, --id <ID> Select the issue with the given ID.
--log-level <LOG_LEVEL> Set the log output verbosity. [possible values: trace, debug, info,
warn, error]
--quiet Do not print any output while preserving correct exit code. This
flag is currently implemented only for selected subcommands.
[aliases: silent]
-h, --help Print help

```
6 changes: 6 additions & 0 deletions tests/integration/_cases/issues/issues-list-empty.trycmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```
$ sentry-cli issues list
? success
No issues found

```
29 changes: 29 additions & 0 deletions tests/integration/_cases/issues/issues-list-help.trycmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
```
$ sentry-cli issues list --help
? success
List all issues in your organization.

Usage: sentry-cli[EXE] issues list [OPTIONS]

Options:
--max-rows <MAX_ROWS> Maximum number of rows to print.
-o, --org <ORG> The organization slug
--header <KEY:VALUE> Custom headers that should be attached to all requests
in key:value format.
-p, --project <PROJECT> The project slug.
--pages <PAGES> Maximum number of pages to fetch (100 issues/page). [default: 5]
--auth-token <AUTH_TOKEN> Use the given Sentry auth token.
--query <QUERY> Query to pass at the request. An example is "is:unresolved"
[default: ]
-s, --status <STATUS> Select all issues matching a given status. [possible values:
resolved, muted, unresolved]
-a, --all Select all issues (this might be limited).
-i, --id <ID> Select the issue with the given ID.
--log-level <LOG_LEVEL> Set the log output verbosity. [possible values: trace, debug, info,
warn, error]
--quiet Do not print any output while preserving correct exit code. This
flag is currently implemented only for selected subcommands.
[aliases: silent]
-h, --help Print help

```
26 changes: 26 additions & 0 deletions tests/integration/_responses/issues/get-issues.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"id": "4242424243",
"shortId": "SEN-CLI-L",
"title": "ProgrammingError: column users_user.role does not exist",
"lastSeen": "2023-07-18T00:12:01.222387Z",
"status": "unresolved",
"level": "error"
},
{
"id": "4242424242",
"shortId": "SEN-CLI-H",
"title": "N+1 Query",
"lastSeen": "2023-07-18T00:10:01.222387Z",
"status": "resolved",
"level": "info"
},
{
"id": "4242424241",
"shortId": "SEN-CLI-1",
"title": "NameError: name 'jobs' is not defined",
"lastSeen": "2023-07-18T00:00:01.222387Z",
"status": "ignored",
"level": "error"
}
]
10 changes: 10 additions & 0 deletions tests/integration/_responses/issues/get-resolved-issues.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"id": "4242424242",
"shortId": "SEN-CLI-H",
"title": "N+1 Query",
"lastSeen": "2023-07-18T00:10:01.222387Z",
"status": "resolved",
"level": "info"
}
]
45 changes: 45 additions & 0 deletions tests/integration/issues/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::integration::{mock_endpoint, register_test, EndpointOptions};

#[test]
fn command_issues_list_help() {
register_test("issues/issues-list-help.trycmd");
}

#[test]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to add some "working" tests as well? Maybe with flags behavior that you added as well? :)

fn doesnt_fail_with_empty_response() {
let _server = mock_endpoint(
EndpointOptions::new(
"GET",
"/api/0/projects/wat-org/wat-project/issues/?query=&cursor=",
200,
)
.with_response_body("[]"),
);
register_test("issues/issues-list-empty.trycmd");
}

#[test]
fn display_issues() {
let _server = mock_endpoint(
EndpointOptions::new(
"GET",
"/api/0/projects/wat-org/wat-project/issues/?query=&cursor=",
200,
)
.with_response_file("issues/get-issues.json"),
);
register_test("issues/issues-display.trycmd");
}

#[test]
fn display_resolved_issues() {
let _server = mock_endpoint(
EndpointOptions::new(
"GET",
"/api/0/projects/wat-org/wat-project/issues/?query=is:resolved&cursor=",
200,
)
.with_response_file("issues/get-resolved-issues.json"),
);
register_test("issues/issues-display-with-query.trycmd");
}
8 changes: 8 additions & 0 deletions tests/integration/issues/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::integration::register_test;

mod list;

#[test]
fn command_issues_help() {
register_test("issues/issues-help.trycmd");
}
1 change: 1 addition & 0 deletions tests/integration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod deploys;
mod events;
mod help;
mod info;
mod issues;
mod login;
mod monitors;
mod org_tokens;
Expand Down
Loading