Found while working #10712 (PR #11059), which gave generateObjectDraft a
${namespace}_ prefix derived from the datasource's owning package. Filed rather than
fixed there: #10712 is scoped to the draft output, and this is a different path with
a different question behind it. Unassigned, awaiting first-touch grading.
What
ExternalDatasourceService.importObject reuses the draft pipeline, so it inherits the
prefix — but only when the caller does not name the object itself:
constname=opts.name??draft.name;
opts.name is taken verbatim. A caller passing { "name": "customers" } gets an object
persisted as customers, with no ${namespace}_ prefix, via
persistObject → metadata.register('object', name, definition).
Reachable today
Not hypothetical, and not dependent on #10712's change. ImportObjectOpts extends GenerateDraftOpts with name?: string ("Override the auto-derived object name"), and
the import route forwards the request body as opts unfiltered —
packages/rest/src/external-datasource-routes.ts:462:
constresult=awaitsvc.importObject(req.params.name,/* remote */…,(req.bodyasRecord<string,unknown>)??{},);So POST /api/v1/datasources/:name/external/tables/:remote/import with
{"name": "customers"} reaches it.
Why this is not simply "the caller owns the name"
That reading is defensible on its own, and may well be the right ruling — but the
runtime write path this lands on does not apply the check that would catch it.
validateObjectNamespacePrefix runs in two places: defineStack() (compile time) and
MetadataManager.publishPackage / the protocol's publish pre-flight (the Studio
"publish" path, NAMESPACE_PREFIX). metadata.register('object', …) is neither. So an
imported object with an explicitly unprefixed name is registered and served without any
gate having looked at the name.
Note the in-tree callers do not currently exercise this: objectui's
ImportObjectDialog posts no name, and os datasource introspect does not import at
all. The exposure is the route body.
The question a first-touch should answer
Three shapes, none of which a dev should pick unilaterally:
- A. Apply the prefix to
opts.name too (same validateObjectNamespacePrefix
treatment as the derived name). Consistent, but silently rewrites a name the caller
explicitly asked for. - B. Refuse an
opts.name that fails the prefix rule, with the rule's own actionable
message. Contract-first, and matches what the publish gate does with the same
violation — but it turns a currently-accepted request into a 4xx. - C. Leave it: an explicit override is the caller's decision. Then the gap worth
closing is arguably the broader one — that metadata.register('object', …) applies no
namespace check at all — which is a much wider question than this seam.
Related: #10712 / PR #11059 (the draft half, where the prefix derivation lives),
#11000 (the unauthorable primaryKey key in the same generator).
Found while working #10712 (PR #11059), which gave
generateObjectDrafta${namespace}_prefix derived from the datasource's owning package. Filed rather thanfixed there: #10712 is scoped to the draft output, and this is a different path with
a different question behind it. Unassigned, awaiting first-touch grading.
What
ExternalDatasourceService.importObjectreuses the draft pipeline, so it inherits theprefix — but only when the caller does not name the object itself:
opts.nameis taken verbatim. A caller passing{ "name": "customers" }gets an objectpersisted as
customers, with no${namespace}_prefix, viapersistObject→metadata.register('object', name, definition).Reachable today
Not hypothetical, and not dependent on #10712's change.
ImportObjectOpts extends GenerateDraftOptswithname?: string("Override the auto-derived object name"), andthe import route forwards the request body as opts unfiltered —
packages/rest/src/external-datasource-routes.ts:462:So
POST /api/v1/datasources/:name/external/tables/:remote/importwith{"name": "customers"}reaches it.Why this is not simply "the caller owns the name"
That reading is defensible on its own, and may well be the right ruling — but the
runtime write path this lands on does not apply the check that would catch it.
validateObjectNamespacePrefixruns in two places:defineStack()(compile time) andMetadataManager.publishPackage/ the protocol's publish pre-flight (the Studio"publish" path,
NAMESPACE_PREFIX).metadata.register('object', …)is neither. So animported object with an explicitly unprefixed name is registered and served without any
gate having looked at the name.
Note the in-tree callers do not currently exercise this: objectui's
ImportObjectDialogposts noname, andos datasource introspectdoes not import atall. The exposure is the route body.
The question a first-touch should answer
Three shapes, none of which a dev should pick unilaterally:
opts.nametoo (samevalidateObjectNamespacePrefixtreatment as the derived name). Consistent, but silently rewrites a name the caller
explicitly asked for.
opts.namethat fails the prefix rule, with the rule's own actionablemessage. Contract-first, and matches what the publish gate does with the same
violation — but it turns a currently-accepted request into a 4xx.
closing is arguably the broader one — that
metadata.register('object', …)applies nonamespace check at all — which is a much wider question than this seam.
Related: #10712 / PR #11059 (the draft half, where the prefix derivation lives),
#11000 (the unauthorable
primaryKeykey in the same generator).