-
-
Notifications
You must be signed in to change notification settings - Fork 424
fix: pass expected @scope:team format to backend
#90
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
Changes from all commits
e829139
de872d3
80a6e84
ebe6ee7
7a48fbb
361adc3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /** | ||
| * Constructs a scope:team string in the format expected by npm. | ||
| * npm operations require the format @scope:team (with @ prefix). | ||
| * | ||
| * @param orgName - The organization name (with or without @) | ||
| * @param teamName - The team name | ||
| * @returns The scope:team string in @scope:team format | ||
| */ | ||
| export function buildScopeTeam(orgName: string, teamName: string): `@${string}:${string}` { | ||
| return `@${orgName.replace(/^@/, '')}:${teamName}` | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ import { H3, HTTPError, handleCors, type H3Event } from 'h3-next' | |
| import type { CorsOptions } from 'h3-next' | ||
|
|
||
| import type { ConnectorState, PendingOperation, OperationType, ApiResponse } from './types.ts' | ||
| import { logDebug, logError } from './logger.ts' | ||
| import { | ||
| getNpmUser, | ||
| orgAddUser, | ||
|
|
@@ -20,6 +21,7 @@ import { | |
| ownerAdd, | ||
| ownerRemove, | ||
| packageInit, | ||
| validateScopeTeam, | ||
| type NpmExecResult, | ||
| } from './npm-client.ts' | ||
|
|
||
|
|
@@ -447,6 +449,17 @@ export function createConnectorApp(expectedToken: string) { | |
| // Decode the team name (handles encoded colons like nuxt%3Adevelopers) | ||
| const scopeTeam = decodeURIComponent(scopeTeamRaw) | ||
|
|
||
| try { | ||
| validateScopeTeam(scopeTeam) | ||
| } catch (err) { | ||
| logError('scope:team validation failed') | ||
| logDebug(err, { scopeTeamRaw, scopeTeam }) | ||
| throw new HTTPError({ | ||
| statusCode: 400, | ||
| message: `Invalid scope:team format: ${scopeTeam}. Expected @scope:team`, | ||
| }) | ||
|
Comment on lines
+455
to
+460
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may look a little funny at a glance, but the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it help putting quotes around the invalid input for clarity? |
||
| } | ||
|
|
||
| const result = await teamListUsers(scopeTeam) | ||
| if (result.exitCode !== 0) { | ||
| return { | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import { createConnectorApp } from '../../cli/src/server.ts' | ||
|
|
||
| const TEST_TOKEN = 'test-token-123' | ||
|
|
||
| describe('connector server', () => { | ||
| describe('GET /team/:scopeTeam/users', () => { | ||
| it('returns 400 for invalid scope:team format (missing @ prefix)', async () => { | ||
| const app = createConnectorApp(TEST_TOKEN) | ||
|
|
||
| const response = await app.fetch( | ||
| new Request('http://localhost/team/netlify%3Adevelopers/users', { | ||
| headers: { Authorization: `Bearer ${TEST_TOKEN}` }, | ||
| }), | ||
| ) | ||
|
|
||
| expect(response.status).toBe(400) | ||
| const body = await response.json() | ||
| expect(body.message).toContain('Invalid scope:team format') | ||
| }) | ||
|
|
||
| it('returns 401 without auth token', async () => { | ||
| const app = createConnectorApp(TEST_TOKEN) | ||
|
|
||
| const response = await app.fetch( | ||
| new Request('http://localhost/team/@netlify%3Adevelopers/users'), | ||
| ) | ||
|
|
||
| expect(response.status).toBe(401) | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
|
|
||
| import { buildScopeTeam } from '../../app/utils/npm' | ||
| import { validateScopeTeam } from '../../cli/src/npm-client' | ||
|
|
||
| describe('buildScopeTeam', () => { | ||
| it('constructs scope:team with @ prefix', () => { | ||
| expect(buildScopeTeam('netlify', 'developers')).toBe('@netlify:developers') | ||
| expect(buildScopeTeam('nuxt', 'core')).toBe('@nuxt:core') | ||
| }) | ||
|
|
||
| it('strips existing @ prefix from orgName', () => { | ||
| expect(buildScopeTeam('@netlify', 'developers')).toBe('@netlify:developers') | ||
| expect(buildScopeTeam('@nuxt', 'core')).toBe('@nuxt:core') | ||
| }) | ||
|
|
||
| it('produces format accepted by validateScopeTeam', () => { | ||
| expect(() => validateScopeTeam(buildScopeTeam('netlify', 'developers'))).not.toThrow() | ||
| expect(() => validateScopeTeam(buildScopeTeam('nuxt', 'core'))).not.toThrow() | ||
| expect(() => validateScopeTeam(buildScopeTeam('my-org', 'my-team'))).not.toThrow() | ||
| }) | ||
| }) |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be missing something, but debugging was difficult without stack traces and support for non-string objects in general. Given that clack doesn't have affordances for this, debug/obug was the least intrusive approach I could think of.