Skip to content

Answer with a list, not a bare array - #1409

Open
darksidemilk wants to merge 1 commit into
working-1.6from
openapi-named-array-responses
Open

Answer with a list, not a bare array#1409
darksidemilk wants to merge 1 commit into
working-1.6from
openapi-named-array-responses

Conversation

@darksidemilk

Copy link
Copy Markdown
Member

This is the blocker. 108 operations answer with a bare top-level JSON array, and no code generator can model that — so no generated client of FOG's API compiles, in any language. Unlike #1408 (which is cosmetic), nothing downstream works until this lands.

Until now nobody had tried. Generating FOG's document has always reported success — but generating and compiling are different gates, and only the first was ever checked. The C# does not compile: 333 errors, from a run with zero warnings.

AutoRest builds no model for a top-level array, falls back to the only named schema on the operation (Error), then writes .Count, [0] and foreach against it. Its own comment on the generated line:

// (await response) // should be FogApi.Models.IError
// response should be returning an array of some kind.

Measured before changing a wire format

Naming the schema was tried first, and does not work. Five response shapes were generated and compiled:

shape result
inline array, items: {type: object}
$ref to a named top-level array schema
inline array whose items are a named object
inline array of integers
an object wrapping the array ✓ compiles

So there is no document-only fix. The rows have to sit under a property — which is where every list route already puts them. /list has always returned an envelope with data; these routes were the inconsistent ones.

What changed

  • /{class}/ids and /{class}/names — 104 routes, now {"data": [...]}
  • /availablekernels, /availableinitrds — now {"data": [...]}

Two more were not wire changes at all, but the document lying about its own server:

  • /pendingmacs delegates to Route::listem(), so it has always answered with a full page. It was documented as a bare array. Now points at MACAddressAssociationPage.
  • POST /{class}/join was documented as returning an array of ids. It never has — Route::joining() ends at sendResponse($code) with no body, so a generated client was told to wait for a list that never arrives. Now documented as 201 with no content.

/system/export also loses format: binary. AutoRest cannot model a binary response and emits File.WriteAllBytes(path, result) with result typed as the error schema. The content type is irrelevant — application/sql, application/octet-stream and text/plain all compile once binary is dropped, and a SQL dump is text, so a plain string is the honest declaration.

Consumers

One first-party caller reads a changed route. fog.host.list.js drives the group typeahead on the Host list page from /group/names, doing $.map(data, …) and data.length. Updated — and written to accept both shapes, so the page keeps working against a server that has not been updated yet.

Result

Generated, compiled and imported, which had never been done before:

before after
compile errors 333 0
FogApi.private.dll builds, 20.6 MB
module import 474 commands, all Verb-Fog*, no collisions
Get-FogHostId / Get-FogHostName uncompilable exist
Get-Help Get-FogHost returns FOG's own descriptions

Two multipart/form-data routes remain ungeneratable and are excluded client-side. That one is not fixable here — a minimal probe (one object, one file property) fails identically, so AutoRest cannot do multipart at all.

Independent of #1408; both touch openapi.class.php and may need a trivial rebase depending on merge order.

darksidemilk added a commit to darksidemilk/FogApi that referenced this pull request Aug 27, 2026
Building the generated module for the first time turned up three things
the generator cannot do. Two are handled here; the third is upstream in
FOGProject/fogproject#1409.

MULTIPART

Snapin_CreateWithFile and Storagegroup_UploadSnapinFile are removed.
AutoRest cannot generate compilable code for multipart/form-data at all,
and this is not FOG's document being wrong: a minimal probe -- one
object with a single `file` property, type string, format binary --
fails identically, 6 CS0411 errors on Extensions.AddIf<T> and
Enumerable.Select. An array of files fails the same way. Reach these two
through Invoke-FogApi, which exists for exactly this.

TWO CMDLETS THAT WERE REALLY FOUR OPERATIONS

Export-ProxyCmdlet refuses a cmdlet whose parameter has two types, and
two pairs of unrelated operations were being merged into one cmdlet
each:

  Join-FogGroup   PUT /group/join applies one group's fields to a list
                  of ids; POST /group/join resolves names and creates
                  what is missing. -Body ended up with both bodies.

  New-FogTask     `task` is one of Route::$validTaskingClasses, so
                  POST /task/{id}/task exists beside POST /task. Their
                  ids are Task_CreateTask and Task_Create, and AutoRest
                  reads the verb off the front of the action and folds
                  the repeated "Task", so both land on New-FogTask.
                  -Body, -Shutdown and -Wol each ended up with two
                  types.

Naming the bodies upstream does not fix either one -- two named types
are still two types on one parameter. They have to be separate cmdlets,
so each POST is renamed to its own subject: Join-FogGroupByName and
New-FogTaskQueue.

Found by scanning the generated cmdlets for parameters declared with
more than one type, rather than one twelve-minute build at a time.
There were exactly three, all on New-FogTask, and none anywhere else in
474 cmdlets.

surface.txt records the result, and shows the whole change in six lines
-- which is what it is for, now that the generated tree is not
committed.

VERIFIED

With #1409 applied to the document, the module builds and imports for
the first time: 0 compile errors, FogApi.private.dll at 20.6 MB, 474
commands exported, every one Verb-Fog*, no collision with a built-in,
and Get-Help returning FOG's own descriptions.

Co-Authored-By: Claude <noreply@anthropic.com>
108 operations answered with a bare top-level JSON array. No code
generator can model that, and until now nobody had tried: generating
FOG's document has always reported success, but the C# it produces had
never been compiled. It does not compile. 333 errors, from a run with
zero warnings.

AutoRest builds no model for a top-level array, falls back to the only
named schema on the operation -- Error -- and then writes `.Count`,
`[0]` and `foreach` against it. Its own comment on the generated line:

  // (await response) // should be FogApi.Models.IError
  // response should be returning an array of some kind.

MEASURED BEFORE CHANGING A WIRE FORMAT

Naming the schema was tried first and does not work. Five response
shapes were generated and compiled:

  inline array, items {type: object}          fails
  $ref to a NAMED top-level array schema      fails
  inline array whose items are a named object fails
  inline array of integers                    fails
  an object wrapping the array                COMPILES

So this is not a naming problem and there is no document-only fix. The
rows have to sit under a property, which is where every list route
already puts them -- /list has always returned an envelope with `data`,
and these routes were the inconsistent ones.

WHAT CHANGED

  /{class}/ids, /{class}/names   104 routes, now {"data": [...]}
  /availablekernels, /availableinitrds        now {"data": [...]}

Two more were not wire changes at all, but the document lying about its
own server:

  /pendingmacs delegates to Route::listem(), so it has always answered
  with a full page. Documented as a bare array. Now points at
  MACAddressAssociationPage.

  POST /{class}/join was documented as returning an array of ids. It
  never has: Route::joining() ends at sendResponse($code) with no body,
  so a generated client was told to wait for a list that never arrives.
  Now documented as 201 with no content.

/system/export also loses `format: binary` -- AutoRest cannot model a
binary response and emits File.WriteAllBytes(path, result) with result
typed as the error schema. The content type is irrelevant:
application/sql, application/octet-stream and text/plain all compile
once the binary format is dropped, and a SQL dump is text, so a plain
string is the honest declaration.

CONSUMERS

One first-party caller reads a changed route:
fog.host.list.js drives the group typeahead on the host list from
/group/names and did `$.map(data, ...)` plus `data.length`. Updated, and
written to accept both shapes so the page keeps working against a server
that has not been updated yet.

RESULT

Generated, compiled and imported, which had never been done before:

  compile errors    333 -> 0
  FogApi.private.dll         builds, 20.6 MB
  module imports             474 commands, all Verb-Fog*, no collisions
  Get-FogHostId/Name         exist for the first time
  Get-Help Get-FogHost       returns FOG's own descriptions

Two multipart/form-data routes remain ungeneratable and are excluded
client-side. That one is not fixable here: a minimal probe -- one object
with a single `file` property -- fails identically, so AutoRest cannot
do multipart at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant