All files / src/utils namespace.ts

100% Statements 28/28
100% Branches 14/14
100% Functions 4/4
100% Lines 24/24

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 786x           6x 79x             6x   79x   50x 50x   4x 4x     2x     29x 29x   3x 3x     2x                     86x 86x 86x   86x           84x     4x               6x       71x 71x          
import { enhancedFetch } from "./fetch";
 
/**
 * Simple heuristic to determine if a path likely represents a project
 * Projects typically contain a slash (group/project), while groups usually don't
 */
export function isLikelyProjectPath(namespacePath: string): boolean {
  return namespacePath.includes("/");
}
 
/**
 * Detect namespace type by attempting to fetch from GitLab API
 * Tries both project and group endpoints to determine which one exists
 */
export async function detectNamespaceType(namespacePath: string): Promise<"project" | "group"> {
  // First try heuristic for common cases
  if (isLikelyProjectPath(namespacePath)) {
    // Try project first, fallback to group if needed
    const isProject = await verifyNamespaceType(namespacePath, "project");
    if (isProject) return "project";
 
    const isGroup = await verifyNamespaceType(namespacePath, "group");
    if (isGroup) return "group";
 
    // Default fallback for paths with slash
    return "project";
  } else {
    // Try group first, fallback to project if needed
    const isGroup = await verifyNamespaceType(namespacePath, "group");
    if (isGroup) return "group";
 
    const isProject = await verifyNamespaceType(namespacePath, "project");
    if (isProject) return "project";
 
    // Default fallback for paths without slash
    return "group";
  }
}
 
/**
 * Verify if a namespace exists as the specified type by making a lightweight API call
 */
async function verifyNamespaceType(
  namespacePath: string,
  type: "project" | "group"
): Promise<boolean> {
  try {
    const entityType = type === "project" ? "projects" : "groups";
    const apiUrl = `${process.env.GITLAB_API_URL}/api/v4/${entityType}/${encodeURIComponent(namespacePath)}`;
 
    const response = await enhancedFetch(apiUrl, {
      headers: {
        Authorization: `Bearer ${process.env.GITLAB_TOKEN}`,
      },
    });
 
    return response.ok;
  } catch {
    // If API call fails, return false
    return false;
  }
}
 
/**
 * Determine the appropriate entity type and path for GitLab API calls
 * Returns the entity type ('projects' or 'groups') and ensures proper encoding
 */
export async function resolveNamespaceForAPI(namespacePath: string): Promise<{
  entityType: "projects" | "groups";
  encodedPath: string;
}> {
  const namespaceType = await detectNamespaceType(namespacePath);
  return {
    entityType: namespaceType === "project" ? "projects" : "groups",
    encodedPath: encodeURIComponent(namespacePath),
  };
}