Skip to content

Commit 64a9fb4

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
chore(powerbi): incorporate staging CLI fixes
2 parents 96b8c7f + 401e039 commit 64a9fb4

9 files changed

Lines changed: 252 additions & 68 deletions

File tree

‎apps/docs/content/docs/cli/files.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ sim files versions list <fileId> [options]
247247

248248
</CommandTable>
249249

250-
## Read the text content of one version of a file
250+
## Read the text content of one version of a file as JSON or YAML
251251

252252
```bash
253253
sim files versions read <fileId> <version> [options]
@@ -474,7 +474,7 @@ Also available as `sim files mv`.
474474

475475
</CommandTable>
476476

477-
## Read a file’s text content
477+
## Read a file’s text content as JSON or YAML
478478

479479
```bash
480480
sim files read <fileId> [options]

‎apps/docs/content/docs/cli/reference.mdx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,7 @@ sim files versions list <fileId> [options]
10151015

10161016
### sim files versions read
10171017

1018-
Read the text content of one version of a file
1018+
Read the text content of one version of a file as JSON or YAML
10191019

10201020
```bash
10211021
sim files versions read <fileId> <version> [options]
@@ -1258,7 +1258,7 @@ Also available as `sim files mv`.
12581258

12591259
### sim files read
12601260

1261-
Read a file’s text content
1261+
Read a file’s text content as JSON or YAML
12621262

12631263
```bash
12641264
sim files read <fileId> [options]
@@ -6400,7 +6400,7 @@ sim workflows create [options]
64006400
| --- | --- | --- |
64016401
| `--name <value>` | Yes | Workflow name. |
64026402
| `--description <value>` | No | Optional workflow description. |
6403-
| `--folder <value>` | No | Folder path as shown in the app; the leading / is optional. |
6403+
| `--folder <value>` | No | Existing folder path (leading / optional); create it first with sim workflows mkdir &lt;path&gt;. |
64046404

64056405
</CommandTable>
64066406

‎apps/docs/content/docs/cli/workflows.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ sim workflows create [options]
352352
| --- | --- | --- |
353353
| `--name <value>` | Yes | Workflow name. |
354354
| `--description <value>` | No | Optional workflow description. |
355-
| `--folder <value>` | No | Folder path as shown in the app; the leading / is optional. |
355+
| `--folder <value>` | No | Existing folder path (leading / optional); create it first with sim workflows mkdir &lt;path&gt;. |
356356

357357
</CommandTable>
358358

‎packages/sim-cli/README.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,16 @@ For each setting, the CLI uses the first available value in this order:
187187
4. built-in default
188188

189189
`sim whoami` shows both the resolved values and where each one came from.
190+
Its JSON and YAML `authenticated` field is `true` after the server accepts the
191+
credential, `false` when it is missing or rejected, and `null` when authentication
192+
could not be checked (including `--no-verify`). `verification.status` separately
193+
reports whether the configured workspace is accessible; a valid credential can
194+
still have `no-workspace` or `rejected` workspace verification.
195+
196+
`sim files read` and `sim files versions read` print the complete text response as
197+
JSON, or YAML with `--output yaml`, including in table and text display modes.
198+
The `truncated` field describes server extraction limits, not display clipping.
199+
For the original file bytes, use `sim files get`.
190200

191201
## Useful commands
192202

‎packages/sim-cli/src/commands/auth.test.ts‎

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,150 @@ describe('whoami command', () => {
617617
expect(output).not.toContain('secret')
618618
})
619619

620+
it.each([
621+
{ workspaceId: null, status: 401 },
622+
{ workspaceId: null, status: 403 },
623+
{ workspaceId: 'ws_1', status: 401 },
624+
{ workspaceId: 'ws_1', status: 403 },
625+
])(
626+
'reports credential rejection with workspace=$workspaceId and HTTP $status',
627+
async ({ workspaceId, status }) => {
628+
mocks.profileFrom.mockReturnValue(configured({ workspaceId, output: 'json' }))
629+
mocks.request.mockRejectedValue(new SimApiError('Credential rejected', status))
630+
631+
await whoami()
632+
633+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
634+
expect(result.authenticated).toBe(false)
635+
expect(result.verification.status).toBe('rejected')
636+
expect(result.verification.detail).toBe('Credential rejected')
637+
expect(process.exitCode).toBe(1)
638+
}
639+
)
640+
641+
it.each([0, 404, 429, 502])(
642+
'preserves setup guidance without claiming authentication when metadata is unavailable with HTTP %s',
643+
async (status) => {
644+
mocks.profileFrom.mockReturnValue(configured({ workspaceId: null, output: 'json' }))
645+
mocks.request.mockRejectedValue(new SimApiError('Metadata unavailable', status))
646+
647+
await whoami()
648+
649+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
650+
expect(result.authenticated).toBeNull()
651+
expect(result.verification.status).toBe('no-workspace')
652+
expect(result.verification.detail).toContain(
653+
'sim configure --profile default --set-workspace'
654+
)
655+
expect(process.exitCode).toBe(2)
656+
}
657+
)
658+
659+
it('distinguishes accepted credentials from missing workspace configuration', async () => {
660+
mocks.profileFrom.mockReturnValue(configured({ workspaceId: null, output: 'json' }))
661+
662+
await whoami()
663+
664+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
665+
expect(result.authenticated).toBe(true)
666+
expect(result.verification.status).toBe('no-workspace')
667+
expect(process.exitCode).toBe(2)
668+
})
669+
670+
it('keeps authentication separate from workspace access', async () => {
671+
mocks.profileFrom.mockReturnValue(configured({ output: 'json' }))
672+
mocks.request.mockImplementation(async (path: string) => {
673+
if (path === '/api/v2/meta') return { data: { keyType: 'workspace' } }
674+
throw new SimApiError('Workspace not found', 404)
675+
})
676+
677+
await whoami()
678+
679+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
680+
expect(result.authenticated).toBe(true)
681+
expect(result.verification.status).toBe('rejected')
682+
expect(process.exitCode).toBe(1)
683+
})
684+
685+
it('leaves authentication unknown when verification is explicitly skipped', async () => {
686+
mocks.profileFrom.mockReturnValue(configured({ output: 'json' }))
687+
mocks.request.mockImplementation(async () => {
688+
throw new Error('Verification must be skipped')
689+
})
690+
691+
await whoami('--no-verify')
692+
693+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
694+
expect(result.authenticated).toBeNull()
695+
expect(result.verification.status).toBe('disabled')
696+
expect(process.exitCode).toBeUndefined()
697+
})
698+
699+
it('still verifies a workspace when an older server has no metadata endpoint', async () => {
700+
mocks.profileFrom.mockReturnValue(configured({ output: 'json' }))
701+
mocks.request.mockImplementation(async (path: string) => {
702+
if (path === '/api/v2/meta') throw new SimApiError('Not found', 404)
703+
return { data: { id: 'ws_1', name: 'Workspace', memberCount: 1 } }
704+
})
705+
706+
await whoami()
707+
708+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
709+
expect(result.authenticated).toBe(true)
710+
expect(result.verification.status).toBe('verified')
711+
expect(process.exitCode).toBeUndefined()
712+
})
713+
714+
it.each([
715+
undefined,
716+
null,
717+
{},
718+
{ data: null },
719+
{ data: {} },
720+
{ data: { keyType: 'unsupported' } },
721+
])('falls back to workspace verification for malformed metadata %j', async (meta) => {
722+
mocks.profileFrom.mockReturnValue(configured({ output: 'json' }))
723+
mocks.request.mockImplementation(async (path: string) =>
724+
path === '/api/v2/meta' ? meta : { data: { id: 'ws_1', name: 'Workspace', memberCount: 1 } }
725+
)
726+
727+
await whoami()
728+
729+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
730+
expect(result.authenticated).toBe(true)
731+
expect(result.verification.status).toBe('verified')
732+
expect(result.verification.keyType).toBeNull()
733+
expect(process.exitCode).toBeUndefined()
734+
})
735+
736+
it('keeps authentication unknown when malformed metadata is the only possible check', async () => {
737+
mocks.profileFrom.mockReturnValue(configured({ workspaceId: null, output: 'json' }))
738+
respond({ data: { keyType: 'unsupported' } })
739+
740+
await whoami()
741+
742+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
743+
expect(result.authenticated).toBeNull()
744+
expect(result.verification.status).toBe('no-workspace')
745+
expect(result.verification.keyType).toBeNull()
746+
expect(process.exitCode).toBe(2)
747+
})
748+
749+
it('reports a key revoked between the metadata and workspace reads as unauthenticated', async () => {
750+
mocks.profileFrom.mockReturnValue(configured({ output: 'json' }))
751+
mocks.request.mockImplementation(async (path: string) => {
752+
if (path === '/api/v2/meta') return { data: { keyType: 'personal' } }
753+
throw new SimApiError('Invalid API key', 401)
754+
})
755+
756+
await whoami()
757+
758+
const result = JSON.parse(vi.mocked(console.log).mock.calls.flat().join('\n'))
759+
expect(result.authenticated).toBe(false)
760+
expect(result.verification.status).toBe('rejected')
761+
expect(process.exitCode).toBe(1)
762+
})
763+
620764
it('exits 1 when the API rejects the key, without hiding the resolved settings', async () => {
621765
mocks.request.mockRejectedValue(
622766
new SimApiError('Invalid API key — run: sim login --profile default', 401)

0 commit comments

Comments
 (0)