Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/12928.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the AppConfig fragment write REST v2 API: create / get / update / purge plus bulk update and bulk delete of raw config fragments, open to any authenticated user and gated by RBAC per scope (BEP-1052).
300 changes: 300 additions & 0 deletions docs/manager/rest-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,127 @@
"title": "AppConfigScopeType",
"type": "string"
},
"CreateAppConfigFragmentInput": {
"description": "Input for creating a new app config fragment at a given scope.",
"properties": {
"config_name": {
"description": "Registered config name.",
"maxLength": 128,
"minLength": 1,
"title": "Config Name",
"type": "string"
},
"scope_type": {
"$ref": "#/components/schemas/AppConfigScopeType",
"description": "Scope the fragment is written at (public | domain | user)."
},
"scope_id": {
"anyOf": [
{
"format": "uuid",
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Scope identifier: the domain id (domain scope) or the user id (user scope). Null for public scope, which has no owner.",
"title": "Scope Id"
},
"config": {
"additionalProperties": true,
"description": "The fragment's JSON config document.",
"title": "Config",
"type": "object"
}
},
"required": [
"config_name",
"scope_type",
"config"
],
"title": "CreateAppConfigFragmentInput",
"type": "object"
},
"AppConfigFragmentUpdateItem": {
"description": "One item of a bulk update, carrying its own target id.\n\nBulk requests address many fragments in a single call, so the id belongs in the body\nhere β€” unlike the single-fragment :class:`UpdateAppConfigFragmentInput`.",
"properties": {
"id": {
"description": "App config fragment id to update.",
"format": "uuid",
"title": "Id",
"type": "string"
},
"config": {
"additionalProperties": true,
"description": "The replacement JSON config document.",
"title": "Config",
"type": "object"
}
},
"required": [
"id",
"config"
],
"title": "AppConfigFragmentUpdateItem",
"type": "object"
},
"BulkUpdateAppConfigFragmentInput": {
"description": "Input for updating many fragments' config documents (per-item partial success).",
"properties": {
"items": {
"description": "Fragments to update, each identified by its id.",
"items": {
"$ref": "#/components/schemas/AppConfigFragmentUpdateItem"
},
"minItems": 1,
"title": "Items",
"type": "array"
}
},
"required": [
"items"
],
"title": "BulkUpdateAppConfigFragmentInput",
"type": "object"
},
"BulkPurgeAppConfigFragmentInput": {
"description": "Input for purging many fragments (per-item partial success).",
"properties": {
"ids": {
"description": "Fragment ids to purge.",
"items": {
"format": "uuid",
"type": "string"
},
"minItems": 1,
"title": "Ids",
"type": "array"
}
},
"required": [
"ids"
],
"title": "BulkPurgeAppConfigFragmentInput",
"type": "object"
},
"UpdateAppConfigFragmentInput": {
"description": "Input for updating one app config fragment's config document.\n\nThe target fragment is identified by the request path, not by this body.",
"properties": {
"config": {
"additionalProperties": true,
"description": "The replacement JSON config document.",
"title": "Config",
"type": "object"
}
},
"required": [
"config"
],
"title": "UpdateAppConfigFragmentInput",
"type": "object"
},
"CreateAppConfigAllowListInput": {
"description": "Input for registering a new app config allow-list entry.",
"properties": {
Expand Down Expand Up @@ -39583,6 +39704,185 @@
"description": "Retrieve aggregate resource capacity/usage across all agents (superadmin only).\n\n**Preconditions:**\n* Superadmin privilege required.\n"
}
},
"/v2/app-config-fragments/": {
"post": {
"operationId": "v2/app-config-fragments.create",
"tags": [
"v2/app-config-fragments"
],
"responses": {
"200": {
"description": "Successful response"
}
},
"security": [
{
"TokenAuth": []
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateAppConfigFragmentInput"
}
}
}
},
"parameters": [],
"description": "Create a fragment at the caller's authorized scope (auth required, RBAC-gated).\n\n**Preconditions:**\n* User privilege required.\n"
}
},
"/v2/app-config-fragments/bulk-update": {
"post": {
"operationId": "v2/app-config-fragments.bulk_update",
"tags": [
"v2/app-config-fragments"
],
"responses": {
"200": {
"description": "Successful response"
}
},
"security": [
{
"TokenAuth": []
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BulkUpdateAppConfigFragmentInput"
}
}
}
},
"parameters": [],
"description": "Update many fragments' configs by id, with per-item partial success (auth, RBAC).\n\n**Preconditions:**\n* User privilege required.\n"
}
},
"/v2/app-config-fragments/bulk-delete": {
"post": {
"operationId": "v2/app-config-fragments.bulk_purge",
"tags": [
"v2/app-config-fragments"
],
"responses": {
"200": {
"description": "Successful response"
}
},
"security": [
{
"TokenAuth": []
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BulkPurgeAppConfigFragmentInput"
}
}
}
},
"parameters": [],
"description": "Purge many fragments by id, with per-item partial success (auth, RBAC).\n\n**Preconditions:**\n* User privilege required.\n"
}
},
"/v2/app-config-fragments/{fragment_id}": {
"get": {
"operationId": "v2/app-config-fragments.get",
"tags": [
"v2/app-config-fragments"
],
"responses": {
"200": {
"description": "Successful response"
}
},
"security": [
{
"TokenAuth": []
}
],
"parameters": [
{
"name": "fragment_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"description": "Get a fragment by id (auth required, RBAC-gated).\n\n**Preconditions:**\n* User privilege required.\n"
},
"patch": {
"operationId": "v2/app-config-fragments.update",
"tags": [
"v2/app-config-fragments"
],
"responses": {
"200": {
"description": "Successful response"
}
},
"security": [
{
"TokenAuth": []
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateAppConfigFragmentInput"
}
}
}
},
"parameters": [
{
"name": "fragment_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"description": "Update a fragment's config document by id (auth required, RBAC-gated).\n\n**Preconditions:**\n* User privilege required.\n"
},
"delete": {
"operationId": "v2/app-config-fragments.purge",
"tags": [
"v2/app-config-fragments"
],
"responses": {
"200": {
"description": "Successful response"
}
},
"security": [
{
"TokenAuth": []
}
],
"parameters": [
{
"name": "fragment_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"description": "Purge a fragment by id (auth required, RBAC-gated).\n\n**Preconditions:**\n* User privilege required.\n"
}
},
"/v2/app-config-allow-list/": {
"post": {
"operationId": "v2/app-config-allow-list.admin_create",
Expand Down
4 changes: 2 additions & 2 deletions src/ai/backend/common/data/app_config/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import enum

from ai.backend.common.data.permission.types import RBACElementType, ScopeType
from ai.backend.common.identifier.app_config import AppConfigScopeIdentifier
from ai.backend.common.identifier.app_config import AppConfigScopeID

__all__ = ("AppConfigScopeType",)

Expand Down Expand Up @@ -50,7 +50,7 @@ def to_rbac_element_type(self) -> RBACElementType | None:
case AppConfigScopeType.USER:
return RBACElementType.USER

def to_rbac_scope_id(self, scope_id: AppConfigScopeIdentifier | None) -> str:
def to_rbac_scope_id(self, scope_id: AppConfigScopeID | None) -> str:
"""The RBAC scope id for a write at this fragment scope, in RBAC's string form.

``public`` is system-wide and names no owner.
Expand Down
Empty file.
Loading
Loading