Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | 8x 1x 7x 7x 7x 3x 3x 2x 1x 1x 1x 4x 2x 2x 4x 5x 4x 1x 3x 4x 3x 1x 2x 8x 8x 8x 6x 8x 8x 6x 6x 8x | /**
* Project Resolver
*
* Resolves DSN to org/project information with caching.
* Uses cached resolution when available to avoid API calls.
*/
import {
findProjectByDsnKey,
listOrganizations,
listProjects,
resolveOrgDisplayName,
} from "../api-client.js";
import { getCachedDsn, updateCachedResolution } from "../db/dsn-cache.js";
import { getDsnSourceDescription } from "./detector.js";
import type {
DetectedDsn,
ResolvedProject,
ResolvedProjectInfo,
} from "./types.js";
/**
* Resolve a detected DSN to full project information
*
* Uses cached resolution if available, otherwise fetches from API.
* Updates cache with resolution for future use.
*
* @param cwd - Directory where DSN was detected
* @param dsn - Detected DSN to resolve
* @returns Resolved project with org/project slugs and names
* @throws Error if DSN cannot be resolved (no org ID, API error, etc.)
*/
export async function resolveProject(
cwd: string,
dsn: DetectedDsn
): Promise<ResolvedProject> {
// Check if we have cached resolution
if (dsn.resolved) {
return {
...dsn.resolved,
dsn,
sourceDescription: getDsnSourceDescription(dsn),
};
}
// Check cache for resolution
const cached = getCachedDsn(cwd);
Iif (cached?.resolved && cached.dsn === dsn.raw) {
return {
...cached.resolved,
dsn,
sourceDescription: getDsnSourceDescription(dsn),
};
}
// Need to fetch from API
// For DSNs without orgId, try to resolve by searching with the public key
if (!dsn.orgId) {
const project = await findProjectByDsnKey(dsn.publicKey);
if (!project?.organization) {
throw new Error(
"Cannot resolve project: DSN could not be matched to any accessible project. " +
"You may not have access, or specify the target explicitly: sentry <command> <org>/<project>"
);
}
const resolved: ResolvedProjectInfo = {
orgSlug: project.organization.slug,
orgName: resolveOrgDisplayName(
project.organization.slug,
project.organization.name
),
projectSlug: project.slug,
projectName: project.name,
};
updateCachedResolution(cwd, resolved);
return {
...resolved,
dsn,
sourceDescription: getDsnSourceDescription(dsn),
};
}
const resolved = await fetchProjectInfo(dsn.orgId, dsn.projectId);
// Update cache with resolution
updateCachedResolution(cwd, resolved);
return {
...resolved,
dsn,
sourceDescription: getDsnSourceDescription(dsn),
};
}
/**
* Fetch project info from Sentry API
*
* Since we only have orgId (numeric) and projectId (numeric) from the DSN,
* we need to fetch the org and project to get slugs and names.
*/
async function fetchProjectInfo(
orgId: string,
projectId: string
): Promise<ResolvedProjectInfo> {
// Fetch all orgs to find the one matching our orgId
const orgs = await listOrganizations();
// Find org by ID - org.id might be string or number depending on API
const org = orgs.find((o) => String(o.id) === orgId);
if (!org) {
throw new Error(
`Could not find organization with ID ${orgId}. ` +
"You may not have access to this organization."
);
}
// Fetch projects for this org to find the one matching our projectId
const projects = await listProjects(org.slug);
// Find project by ID
const project = projects.find((p) => String(p.id) === projectId);
if (!project) {
throw new Error(
`Could not find project with ID ${projectId} in organization ${org.slug}. ` +
"You may not have access to this project."
);
}
return {
orgSlug: org.slug,
orgName: org.name,
projectSlug: project.slug,
projectName: project.name,
};
}
/** Project reference with org context */
type AccessibleProject = {
org: string;
project: string;
orgName: string;
projectName: string;
};
/**
* Get list of accessible projects for the current user.
* Fetches all projects from all accessible organizations.
*
* Used for "no DSN found" error messages to help user specify project.
*
* @returns Array of org/project pairs
*/
export async function getAccessibleProjects(): Promise<AccessibleProject[]> {
const results: AccessibleProject[] = [];
try {
const orgs = await listOrganizations();
for (const org of orgs) {
try {
const projects = await listProjects(org.slug);
for (const project of projects) {
results.push({
org: org.slug,
project: project.slug,
orgName: org.name,
projectName: project.name,
});
}
} catch {
// Skip orgs we can't access
}
}
} catch {
// Not authenticated or API error
}
return results;
}
|