This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 27eed7ecba [#12609] docs(open-api): Add Semantic Model REST API
(#12627)
27eed7ecba is described below
commit 27eed7ecbaa97bf27242cb42ec29f50dad8028ca
Author: mchades <[email protected]>
AuthorDate: Mon Oct 12 10:46:07 2026 +0800
[#12609] docs(open-api): Add Semantic Model REST API (#12627)
### What changes were proposed in this pull request?
Document the Semantic Model list, create, load, alter, and drop APIs and
Apache Ossie import/export, including schemas, media types, errors, and
examples.
Native REST definitions use camelCase fields; standalone Ossie documents
use snake_case fields and string source identifiers. The specification
also covers the reserved properties extension, document version, export
format, and download headers.
Adds an HTTP regression test confirming that an omitted `Content-Type`
is accepted and parsed as YAML.
### Why are the changes needed?
Provide the OpenAPI contract for the Semantic Model REST APIs now
implemented on main.
Fix: #12609
### Does this PR introduce _any_ user-facing change?
Yes. It adds API documentation only.
### How was this patch tested?
- `./gradlew :docs:spotlessApply :docs:build`
- `./gradlew :server:spotlessApply :server:test --tests
org.apache.gravitino.server.web.rest.TestSemanticModelOperations
-PskipITs -PskipDockerTests=true` — 26 REST tests passed; Docker tests
were not run.
- `git diff --check`
- Compared schema fields with the native DTOs and checked 15 schema
examples and request boundaries, including field casing and omitted/null
properties.
---
docs/open-api/openapi.yaml | 20 +
docs/open-api/semantic-models.yaml | 1591 ++++++++++++++++++++
.../web/rest/TestSemanticModelOperations.java | 53 +
3 files changed, 1664 insertions(+)
diff --git a/docs/open-api/openapi.yaml b/docs/open-api/openapi.yaml
index 59bfdebe26..fb426a9a8d 100644
--- a/docs/open-api/openapi.yaml
+++ b/docs/open-api/openapi.yaml
@@ -168,6 +168,18 @@ paths:
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/views/{view}:
$ref:
"./views.yaml#/paths/~1metalakes~1%7Bmetalake%7D~1catalogs~1%7Bcatalog%7D~1schemas~1%7Bschema%7D~1views~1%7Bview%7D"
+ /metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models:
+ $ref:
"./semantic-models.yaml#/paths/~1metalakes~1%7Bmetalake%7D~1catalogs~1%7Bcatalog%7D~1schemas~1%7Bschema%7D~1semantic-models"
+
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models/ossie:
+ $ref:
"./semantic-models.yaml#/paths/~1metalakes~1%7Bmetalake%7D~1catalogs~1%7Bcatalog%7D~1schemas~1%7Bschema%7D~1semantic-models~1ossie"
+
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models/{semanticModel}:
+ $ref:
"./semantic-models.yaml#/paths/~1metalakes~1%7Bmetalake%7D~1catalogs~1%7Bcatalog%7D~1schemas~1%7Bschema%7D~1semantic-models~1%7BsemanticModel%7D"
+
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models/{semanticModel}/ossie:
+ $ref:
"./semantic-models.yaml#/paths/~1metalakes~1%7Bmetalake%7D~1catalogs~1%7Bcatalog%7D~1schemas~1%7Bschema%7D~1semantic-models~1%7BsemanticModel%7D~1ossie"
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/functions:
$ref:
"./functions.yaml#/paths/~1metalakes~1%7Bmetalake%7D~1catalogs~1%7Bcatalog%7D~1schemas~1%7Bschema%7D~1functions"
@@ -569,6 +581,14 @@ components:
schema:
type: string
+ semanticModel:
+ name: semanticModel
+ in: path
+ description: The name of the Semantic Model
+ required: true
+ schema:
+ type: string
+
model:
name: model
in: path
diff --git a/docs/open-api/semantic-models.yaml
b/docs/open-api/semantic-models.yaml
new file mode 100644
index 0000000000..d81640988c
--- /dev/null
+++ b/docs/open-api/semantic-models.yaml
@@ -0,0 +1,1591 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+---
+
+paths:
+
+ /metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models:
+ parameters:
+ - $ref: "./openapi.yaml#/components/parameters/metalake"
+ - $ref: "./openapi.yaml#/components/parameters/catalog"
+ - $ref: "./openapi.yaml#/components/parameters/schema"
+
+ get:
+ tags:
+ - semantic-model
+ summary: List Semantic Models
+ description: >-
+ Lists the Semantic Models in a schema. Semantic Models are supported
only for relational
+ catalogs. Each returned identifier uses the full
metalake.catalog.schema.name namespace.
+ operationId: listSemanticModels
+ responses:
+ "200":
+ $ref: "#/components/responses/SemanticModelListResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ $ref: "#/components/responses/SemanticModelParentNotFoundResponse"
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+ post:
+ tags:
+ - semantic-model
+ summary: Create a Semantic Model
+ description: >-
+ Creates a schema-scoped Semantic Model in a relational catalog. The
complete definition is
+ validated before the model is stored, including source identifier
shape. Source existence,
+ source columns, and source authorization checks are not yet
implemented.
+ operationId: createSemanticModel
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/SemanticModelCreateRequest"
+ examples:
+ SemanticModelCreateRequest:
+ $ref: "#/components/examples/SemanticModelCreateRequest"
+ responses:
+ "200":
+ $ref: "#/components/responses/SemanticModelResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ $ref: "#/components/responses/SemanticModelParentNotFoundResponse"
+ "409":
+ $ref: "#/components/responses/SemanticModelAlreadyExistsResponse"
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "502":
+ $ref: "#/components/responses/SemanticModelConnectionFailedResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models/ossie:
+ parameters:
+ - $ref: "./openapi.yaml#/components/parameters/metalake"
+ - $ref: "./openapi.yaml#/components/parameters/catalog"
+ - $ref: "./openapi.yaml#/components/parameters/schema"
+
+ post:
+ tags:
+ - semantic-model
+ summary: Import an Apache Ossie Semantic Model
+ description: >-
+ Imports one standalone Apache Ossie document and creates the
represented Semantic Model.
+ The request body is the raw JSON or YAML document, not a JSON-encoded
string. Content-Type
+ selects the parser; when the header is absent, the body is parsed as
YAML. Parsing does not
+ fall back to another format.
+ operationId: importOssieDocument
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OssieSemanticModelDocument"
+ examples:
+ OssieSemanticModelDocument:
+ $ref: "#/components/examples/OssieSemanticModelDocument"
+ application/yaml:
+ schema:
+ $ref: "#/components/schemas/OssieSemanticModelDocument"
+ examples:
+ OssieSemanticModelDocument:
+ $ref: "#/components/examples/OssieSemanticModelDocument"
+ application/x-yaml:
+ schema:
+ $ref: "#/components/schemas/OssieSemanticModelDocument"
+ examples:
+ OssieSemanticModelDocument:
+ $ref: "#/components/examples/OssieSemanticModelDocument"
+ text/yaml:
+ schema:
+ $ref: "#/components/schemas/OssieSemanticModelDocument"
+ examples:
+ OssieSemanticModelDocument:
+ $ref: "#/components/examples/OssieSemanticModelDocument"
+ responses:
+ "200":
+ $ref: "#/components/responses/SemanticModelResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ $ref: "#/components/responses/SemanticModelParentNotFoundResponse"
+ "409":
+ $ref: "#/components/responses/SemanticModelAlreadyExistsResponse"
+ "415":
+ description: >-
+ Unsupported Media Type - An explicitly supplied Content-Type is
not one of
+ application/json, application/yaml, application/x-yaml, or
text/yaml
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "502":
+ $ref: "#/components/responses/SemanticModelConnectionFailedResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models/{semanticModel}:
+ parameters:
+ - $ref: "./openapi.yaml#/components/parameters/metalake"
+ - $ref: "./openapi.yaml#/components/parameters/catalog"
+ - $ref: "./openapi.yaml#/components/parameters/schema"
+ - $ref: "./openapi.yaml#/components/parameters/semanticModel"
+
+ get:
+ tags:
+ - semantic-model
+ summary: Load a Semantic Model
+ description: Returns the specified Semantic Model with its nested
definition and audit data.
+ operationId: loadSemanticModel
+ responses:
+ "200":
+ $ref: "#/components/responses/SemanticModelResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ $ref: "#/components/responses/SemanticModelNotFoundResponse"
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+ put:
+ tags:
+ - semantic-model
+ summary: Alter a Semantic Model
+ description: >-
+ Applies the ordered updates atomically. The server validates and
converts the complete
+ update batch before dispatch; if any update or the resulting model is
invalid, none of the
+ updates is applied. Only replaceDefinition revalidates the complete
definition. Source
+ existence, source columns, and source authorization checks are not yet
implemented.
+ operationId: alterSemanticModel
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/SemanticModelUpdatesRequest"
+ examples:
+ SemanticModelUpdatesRequest:
+ $ref: "#/components/examples/SemanticModelUpdatesRequest"
+ responses:
+ "200":
+ $ref: "#/components/responses/SemanticModelResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ $ref: "#/components/responses/SemanticModelNotFoundResponse"
+ "409":
+ $ref: "#/components/responses/SemanticModelAlterConflictResponse"
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "502":
+ $ref: "#/components/responses/SemanticModelConnectionFailedResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+ delete:
+ tags:
+ - semantic-model
+ summary: Drop a Semantic Model
+ description: >-
+ Drops the specified Semantic Model. A missing schema or Semantic Model
is reported as a
+ successful response with dropped set to false.
+ operationId: dropSemanticModel
+ responses:
+ "200":
+ $ref: "./openapi.yaml#/components/responses/DropResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ description: Not Found - The target metalake or catalog does not
exist
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ NoSuchMetalakeException:
+ $ref:
"./metalakes.yaml#/components/examples/NoSuchMetalakeException"
+ NoSuchCatalogException:
+ $ref:
"./catalogs.yaml#/components/examples/NoSuchCatalogException"
+ "409":
+ $ref: "#/components/responses/SemanticModelOptimisticLockResponse"
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+
/metalakes/{metalake}/catalogs/{catalog}/schemas/{schema}/semantic-models/{semanticModel}/ossie:
+ parameters:
+ - $ref: "./openapi.yaml#/components/parameters/metalake"
+ - $ref: "./openapi.yaml#/components/parameters/catalog"
+ - $ref: "./openapi.yaml#/components/parameters/schema"
+ - $ref: "./openapi.yaml#/components/parameters/semanticModel"
+
+ get:
+ tags:
+ - semantic-model
+ summary: Export a Semantic Model as an Apache Ossie document
+ description: >-
+ Exports the specified Semantic Model as a standalone Apache Ossie YAML
or JSON document.
+ The response is downloadable document content rather than the native
Gravitino response
+ envelope. The format query parameter selects the representation
independently of the
+ Accept header.
+ operationId: exportOssieDocument
+ parameters:
+ - name: format
+ in: query
+ description: The serialized document format
+ required: false
+ schema:
+ type: string
+ enum:
+ - yaml
+ - json
+ default: yaml
+ responses:
+ "200":
+ $ref: "#/components/responses/OssieSemanticModelDocumentResponse"
+ "400":
+ $ref: "#/components/responses/SemanticModelBadRequestResponse"
+ "403":
+ $ref: "#/components/responses/SemanticModelForbiddenResponse"
+ "404":
+ $ref: "#/components/responses/SemanticModelNotFoundResponse"
+ "501":
+ $ref:
"#/components/responses/SemanticModelUnsupportedCatalogResponse"
+ "5XX":
+ $ref: "./openapi.yaml#/components/responses/ServerErrorResponse"
+
+components:
+
+ schemas:
+ AIContext:
+ description: Additional context for AI tools, encoded as exactly one
string or object value
+ oneOf:
+ - type: string
+ - $ref: "#/components/schemas/AIContextObject"
+
+ AIContextObject:
+ type: object
+ description: >-
+ Structured context for AI tools. Unknown properties may contain any
JSON value and are
+ retained losslessly in input order.
+ properties:
+ instructions:
+ type: string
+ description: Instructions for AI tools on how to use the entity
+ synonyms:
+ type: array
+ description: >-
+ Alternative names and terms. Omitting the property differs from
supplying an empty
+ array.
+ items:
+ type: string
+ examples:
+ type: array
+ description: >-
+ Sample questions or use cases. Omitting the property differs from
supplying an empty
+ array.
+ items:
+ type: string
+ additionalProperties: true
+
+ SemanticModelSource:
+ type: object
+ description: >-
+ A governed Table or logical View expressed as catalog.schema.name. The
endpoint metalake is
+ inherited and therefore is not included in this identifier.
+ required:
+ - namespace
+ - name
+ properties:
+ namespace:
+ type: array
+ minItems: 2
+ maxItems: 2
+ description: Exactly the catalog and schema containing the source,
in that order
+ items:
+ type: string
+ minLength: 1
+ name:
+ type: string
+ minLength: 1
+ description: The source Table or logical View name
+ additionalProperties: false
+
+ Dataset:
+ type: object
+ description: A logical dataset backed by a governed Gravitino Table or
logical View
+ required:
+ - name
+ - source
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The dataset name, unique within the Semantic Model
+ source:
+ $ref: "#/components/schemas/SemanticModelSource"
+ primaryKey:
+ type: array
+ description: >-
+ Source columns that form the primary key. The property may be
omitted; an explicitly
+ supplied empty array is retained as empty.
+ items:
+ type: string
+ minLength: 1
+ uniqueKeys:
+ type: array
+ description: >-
+ Source column sets that form unique keys. The property may be
omitted; an explicitly
+ supplied empty array is retained as empty.
+ items:
+ type: array
+ minItems: 1
+ items:
+ type: string
+ minLength: 1
+ description:
+ type: string
+ description: The human-readable dataset description
+ aiContext:
+ $ref: "#/components/schemas/AIContext"
+ fields:
+ type: array
+ description: >-
+ Ordered fields in the dataset. The property may be omitted; an
explicitly supplied
+ empty array is retained as empty.
+ items:
+ $ref: "#/components/schemas/Field"
+ customExtensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/CustomExtension"
+ additionalProperties: false
+
+ OssieDataset:
+ type: object
+ description: >-
+ An Apache Ossie dataset backed by a governed Gravitino Table or
logical View
+ required:
+ - name
+ - source
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The dataset name, unique within the Semantic Model
+ source:
+ type: string
+ minLength: 1
+ description: >-
+ A catalog.schema.name source identifier. Segments containing dots
or backticks use
+ backtick quoting, and a backtick inside a quoted segment is
doubled. Query sources are
+ not supported.
+ primary_key:
+ type: array
+ description: >-
+ Source columns that form the primary key. The property may be
omitted; an explicitly
+ supplied empty array is retained as empty.
+ items:
+ type: string
+ minLength: 1
+ unique_keys:
+ type: array
+ description: >-
+ Source column sets that form unique keys. The property may be
omitted; an explicitly
+ supplied empty array is retained as empty.
+ items:
+ type: array
+ minItems: 1
+ items:
+ type: string
+ minLength: 1
+ description:
+ type: string
+ description: The human-readable dataset description
+ ai_context:
+ $ref: "#/components/schemas/AIContext"
+ fields:
+ type: array
+ description: >-
+ Ordered fields in the dataset. The property may be omitted; an
explicitly supplied
+ empty array is retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieField"
+ custom_extensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieCustomExtension"
+ additionalProperties: false
+
+ Field:
+ type: object
+ description: A row-level semantic attribute in a dataset
+ required:
+ - name
+ - expression
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The field name, unique within its dataset
+ expression:
+ $ref: "#/components/schemas/Expression"
+ dimension:
+ $ref: "#/components/schemas/Dimension"
+ label:
+ type: string
+ description: The display label used to categorize the field
+ description:
+ type: string
+ description: The human-readable field description
+ datatype:
+ $ref: "#/components/schemas/SemanticModelDataType"
+ aiContext:
+ $ref: "#/components/schemas/AIContext"
+ customExtensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/CustomExtension"
+ additionalProperties: false
+
+ Relationship:
+ type: object
+ description: A relationship between two datasets in the same Semantic
Model
+ required:
+ - name
+ - from
+ - to
+ - fromColumns
+ - toColumns
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The relationship name, unique within the Semantic Model
+ from:
+ type: string
+ minLength: 1
+ description: The source dataset name
+ to:
+ type: string
+ minLength: 1
+ description: The target dataset name
+ fromColumns:
+ type: array
+ minItems: 1
+ description: >-
+ Non-empty source columns. The array must have the same number of
entries as toColumns.
+ items:
+ type: string
+ minLength: 1
+ toColumns:
+ type: array
+ minItems: 1
+ description: >-
+ Non-empty target columns. The array must have the same number of
entries as
+ fromColumns.
+ items:
+ type: string
+ minLength: 1
+ aiContext:
+ $ref: "#/components/schemas/AIContext"
+ customExtensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/CustomExtension"
+ additionalProperties: false
+
+ Metric:
+ type: object
+ description: A model-scoped quantitative measure
+ required:
+ - name
+ - expression
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The metric name, unique within the Semantic Model
+ expression:
+ $ref: "#/components/schemas/Expression"
+ description:
+ type: string
+ description: The human-readable metric description
+ datatype:
+ $ref: "#/components/schemas/SemanticModelDataType"
+ aiContext:
+ $ref: "#/components/schemas/AIContext"
+ customExtensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/CustomExtension"
+ additionalProperties: false
+
+ Expression:
+ type: object
+ description: An expression with one or more ordered dialect-specific
representations
+ required:
+ - dialects
+ properties:
+ dialects:
+ type: array
+ minItems: 1
+ description: Each dialect identifier may occur at most once
+ items:
+ $ref: "#/components/schemas/DialectExpression"
+ additionalProperties: false
+
+ DialectExpression:
+ type: object
+ description: An expression written in a specific dialect
+ required:
+ - dialect
+ - expression
+ properties:
+ dialect:
+ type: string
+ minLength: 1
+ description: >-
+ An open dialect identifier. ANSI_SQL, SNOWFLAKE, MDX, TABLEAU,
DATABRICKS, MAQL, and
+ BIGQUERY are well-known Apache Ossie values; other non-empty
identifiers are accepted
+ and preserved without conversion or fallback.
+ expression:
+ type: string
+ minLength: 1
+ description: The non-empty dialect-specific expression text
+ additionalProperties: false
+
+ Dimension:
+ type: object
+ description: >-
+ Dimension metadata for a field. An empty object is valid and remains
distinct from an
+ omitted dimension.
+ properties:
+ isTime:
+ type: boolean
+ nullable: true
+ description: Whether consumers should treat the field as a time
dimension
+ additionalProperties: false
+
+ CustomExtension:
+ type: object
+ description: Vendor-specific attributes retained for Semantic Model
interchange
+ required:
+ - vendorName
+ - data
+ properties:
+ vendorName:
+ type: string
+ description: The extension vendor name
+ data:
+ type: string
+ description: Opaque vendor-specific data retained as a string
+ additionalProperties: false
+
+ OssieField:
+ type: object
+ description: A row-level semantic attribute in a dataset
+ required:
+ - name
+ - expression
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The field name, unique within its dataset
+ expression:
+ $ref: "#/components/schemas/Expression"
+ dimension:
+ $ref: "#/components/schemas/OssieDimension"
+ label:
+ type: string
+ description: The display label used to categorize the field
+ description:
+ type: string
+ description: The human-readable field description
+ datatype:
+ $ref: "#/components/schemas/SemanticModelDataType"
+ ai_context:
+ $ref: "#/components/schemas/AIContext"
+ custom_extensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieCustomExtension"
+ additionalProperties: false
+
+ OssieRelationship:
+ type: object
+ description: A relationship between two datasets in the same Semantic
Model
+ required:
+ - name
+ - from
+ - to
+ - from_columns
+ - to_columns
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The relationship name, unique within the Semantic Model
+ from:
+ type: string
+ minLength: 1
+ description: The source dataset name
+ to:
+ type: string
+ minLength: 1
+ description: The target dataset name
+ from_columns:
+ type: array
+ minItems: 1
+ description: >-
+ Non-empty source columns. The array must have the same number of
entries as to_columns.
+ items:
+ type: string
+ minLength: 1
+ to_columns:
+ type: array
+ minItems: 1
+ description: >-
+ Non-empty target columns. The array must have the same number of
entries as
+ from_columns.
+ items:
+ type: string
+ minLength: 1
+ ai_context:
+ $ref: "#/components/schemas/AIContext"
+ custom_extensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieCustomExtension"
+ additionalProperties: false
+
+ OssieMetric:
+ type: object
+ description: A model-scoped quantitative measure
+ required:
+ - name
+ - expression
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The metric name, unique within the Semantic Model
+ expression:
+ $ref: "#/components/schemas/Expression"
+ description:
+ type: string
+ description: The human-readable metric description
+ datatype:
+ $ref: "#/components/schemas/SemanticModelDataType"
+ ai_context:
+ $ref: "#/components/schemas/AIContext"
+ custom_extensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The property may be omitted; an
explicitly supplied empty
+ array is retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieCustomExtension"
+ additionalProperties: false
+
+ OssieDimension:
+ type: object
+ description: >-
+ Dimension metadata for a field. An empty object is valid and remains
distinct from an
+ omitted dimension.
+ properties:
+ is_time:
+ type: boolean
+ description: Whether consumers should treat the field as a time
dimension
+ additionalProperties: false
+
+ OssieCustomExtension:
+ type: object
+ description: Vendor-specific attributes retained for Semantic Model
interchange
+ required:
+ - vendor_name
+ - data
+ properties:
+ vendor_name:
+ type: string
+ description: The extension vendor name
+ data:
+ type: string
+ description: Opaque vendor-specific data retained as a string
+ additionalProperties: false
+
+ SemanticModelDataType:
+ type: string
+ description: The exact wire value for a logical field or metric data type
+ enum:
+ - String
+ - Integer
+ - Decimal
+ - Float
+ - Boolean
+ - Date
+ - Time
+ - DateTime
+ - DateTimeTz
+ - Opaque
+
+ SemanticModelDefinition:
+ type: object
+ description: The complete replaceable semantic definition, without
entity metadata
+ required:
+ - datasets
+ properties:
+ aiContext:
+ $ref: "#/components/schemas/AIContext"
+ datasets:
+ type: array
+ minItems: 1
+ description: The non-empty ordered datasets in the Semantic Model
+ items:
+ $ref: "#/components/schemas/Dataset"
+ relationships:
+ type: array
+ description: >-
+ Ordered relationships. The property may be omitted; an explicitly
supplied empty array
+ is retained as empty.
+ items:
+ $ref: "#/components/schemas/Relationship"
+ metrics:
+ type: array
+ description: >-
+ Ordered metrics. The property may be omitted; an explicitly
supplied empty array is
+ retained as empty.
+ items:
+ $ref: "#/components/schemas/Metric"
+ customExtensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. The root vendorName
GRAVITINO_PROPERTIES is reserved and
+ cannot be supplied in a native definition. The property may be
omitted; an explicitly
+ supplied empty array is retained as empty.
+ items:
+ $ref: "#/components/schemas/CustomExtension"
+ additionalProperties: false
+
+ OssieSemanticModelDocument:
+ type: object
+ description: >-
+ A standalone Apache Ossie document. Unlike the native Gravitino
response, its semantic
+ definition is at the document root and it contains no response
envelope, audit data, or
+ direct properties object.
+ required:
+ - version
+ - name
+ - datasets
+ properties:
+ version:
+ type: string
+ minLength: 1
+ description: >-
+ The non-blank Apache Ossie document version, stored in the
ossie-version model property
+ on import. Export uses that property or defaults to 0.2.0.dev0
when it is absent. The
+ version is preserved without restricting it to a fixed allowlist.
+ name:
+ type: string
+ minLength: 1
+ description: The Semantic Model name
+ description:
+ type: string
+ description: The human-readable Semantic Model description
+ ai_context:
+ $ref: "#/components/schemas/AIContext"
+ datasets:
+ type: array
+ minItems: 1
+ description: The non-empty ordered datasets in the Semantic Model
+ items:
+ $ref: "#/components/schemas/OssieDataset"
+ relationships:
+ type: array
+ description: >-
+ Ordered relationships. The property may be omitted; an explicitly
supplied empty array
+ is retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieRelationship"
+ metrics:
+ type: array
+ description: >-
+ Ordered metrics. The property may be omitted; an explicitly
supplied empty array is
+ retained as empty.
+ items:
+ $ref: "#/components/schemas/OssieMetric"
+ custom_extensions:
+ type: array
+ description: >-
+ Vendor-specific extensions. At most one root GRAVITINO_PROPERTIES
extension carries
+ Gravitino-specific properties as a JSON object encoded in a
string, with string values
+ only. Import consumes this extension into model properties; an
ossie-version value in
+ its data must match the document version. Export omits
ossie-version from the extension
+ and writes it as version instead. Other extensions and nested
GRAVITINO_PROPERTIES
+ extensions remain opaque. The property may be omitted; an
explicitly supplied empty
+ array is normalized to an absent customExtensions in the native
definition on import.
+ items:
+ $ref: "#/components/schemas/OssieCustomExtension"
+ additionalProperties: false
+
+ SemanticModel:
+ type: object
+ description: A schema-scoped analytical Semantic Model with a nested
semantic definition
+ required:
+ - name
+ - definition
+ - properties
+ - audit
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The Semantic Model name
+ comment:
+ type: string
+ nullable: true
+ description: The comment; the property is omitted when no comment is
set
+ definition:
+ $ref: "#/components/schemas/SemanticModelDefinition"
+ properties:
+ type: object
+ description: Gravitino-specific Semantic Model properties, including
the Ossie version
+ additionalProperties:
+ type: string
+ audit:
+ $ref: "./openapi.yaml#/components/schemas/Audit"
+ additionalProperties: false
+
+ SemanticModelCreateRequest:
+ type: object
+ description: The request to create a Semantic Model
+ required:
+ - name
+ - definition
+ properties:
+ name:
+ type: string
+ minLength: 1
+ description: The non-blank Semantic Model name
+ comment:
+ type: string
+ nullable: true
+ description: The optional Semantic Model comment
+ definition:
+ $ref: "#/components/schemas/SemanticModelDefinition"
+ properties:
+ type: object
+ nullable: true
+ description: >-
+ Optional Gravitino-specific properties. Omitted or null properties
use the defaults.
+ The ossie-version property must be non-blank and defaults to
0.2.0.dev0.
+ additionalProperties:
+ type: string
+ additionalProperties: false
+
+ SemanticModelUpdatesRequest:
+ type: object
+ description: A non-empty ordered update batch applied atomically
+ required:
+ - updates
+ properties:
+ updates:
+ type: array
+ minItems: 1
+ items:
+ $ref: "#/components/schemas/SemanticModelUpdateRequest"
+ additionalProperties: false
+
+ SemanticModelUpdateRequest:
+ description: One polymorphic Semantic Model update
+ oneOf:
+ - $ref: "#/components/schemas/RenameSemanticModelRequest"
+ - $ref: "#/components/schemas/UpdateSemanticModelCommentRequest"
+ - $ref: "#/components/schemas/SetSemanticModelPropertyRequest"
+ - $ref: "#/components/schemas/RemoveSemanticModelPropertyRequest"
+ - $ref: "#/components/schemas/ReplaceSemanticModelDefinitionRequest"
+ discriminator:
+ propertyName: "@type"
+ mapping:
+ rename: "#/components/schemas/RenameSemanticModelRequest"
+ updateComment:
"#/components/schemas/UpdateSemanticModelCommentRequest"
+ setProperty: "#/components/schemas/SetSemanticModelPropertyRequest"
+ removeProperty:
"#/components/schemas/RemoveSemanticModelPropertyRequest"
+ replaceDefinition:
"#/components/schemas/ReplaceSemanticModelDefinitionRequest"
+
+ RenameSemanticModelRequest:
+ type: object
+ description: Renames the Semantic Model
+ required:
+ - "@type"
+ - newName
+ properties:
+ "@type":
+ type: string
+ enum:
+ - rename
+ newName:
+ type: string
+ minLength: 1
+ description: The new non-blank Semantic Model name
+ example: {
+ "@type": "rename",
+ "newName": "sales_model_v2"
+ }
+
+ UpdateSemanticModelCommentRequest:
+ type: object
+ description: >-
+ Replaces the comment. An omitted or null newComment clears the
comment; an explicit empty
+ string is stored as an empty comment.
+ required:
+ - "@type"
+ properties:
+ "@type":
+ type: string
+ enum:
+ - updateComment
+ newComment:
+ type: string
+ nullable: true
+ description: The new comment, null to clear it, or an empty string
to retain empty
+ example: {
+ "@type": "updateComment",
+ "newComment": "Updated sales definitions"
+ }
+
+ SetSemanticModelPropertyRequest:
+ type: object
+ description: Sets or replaces one Gravitino-specific property
+ required:
+ - "@type"
+ - property
+ - value
+ properties:
+ "@type":
+ type: string
+ enum:
+ - setProperty
+ property:
+ type: string
+ minLength: 1
+ description: The non-blank property name
+ value:
+ type: string
+ description: The property value; an empty string is valid
+ example: {
+ "@type": "setProperty",
+ "property": "domain",
+ "value": "sales"
+ }
+
+ RemoveSemanticModelPropertyRequest:
+ type: object
+ description: Removes one Gravitino-specific property if it exists
+ required:
+ - "@type"
+ - property
+ properties:
+ "@type":
+ type: string
+ enum:
+ - removeProperty
+ property:
+ type: string
+ minLength: 1
+ description: The non-blank property name
+ example: {
+ "@type": "removeProperty",
+ "property": "legacy"
+ }
+
+ ReplaceSemanticModelDefinitionRequest:
+ type: object
+ description: Replaces and validates the complete nested definition
atomically
+ required:
+ - "@type"
+ - definition
+ properties:
+ "@type":
+ type: string
+ enum:
+ - replaceDefinition
+ definition:
+ $ref: "#/components/schemas/SemanticModelDefinition"
+ example: {
+ "@type": "replaceDefinition",
+ "definition": {
+ "datasets": [
+ {
+ "name": "orders",
+ "source": {
+ "namespace": ["sales", "mart"],
+ "name": "orders"
+ },
+ "fields": []
+ }
+ ],
+ "relationships": [],
+ "metrics": [],
+ "customExtensions": []
+ }
+ }
+
+ responses:
+ SemanticModelListResponse:
+ description: Returns the Semantic Model identifiers in the schema
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ type: object
+ required:
+ - code
+ - identifiers
+ properties:
+ code:
+ type: integer
+ format: int32
+ description: Status code of the response
+ enum:
+ - 0
+ identifiers:
+ type: array
+ description: Full Semantic Model identifiers
+ items:
+ $ref: "./openapi.yaml#/components/schemas/NameIdentifier"
+ additionalProperties: false
+ examples:
+ SemanticModelListResponse:
+ $ref: "#/components/examples/SemanticModelListResponse"
+
+ SemanticModelResponse:
+ description: Returns the Semantic Model object with its nested definition
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ type: object
+ required:
+ - code
+ - semanticModel
+ properties:
+ code:
+ type: integer
+ format: int32
+ description: Status code of the response
+ enum:
+ - 0
+ semanticModel:
+ $ref: "#/components/schemas/SemanticModel"
+ additionalProperties: false
+ examples:
+ SemanticModelResponse:
+ $ref: "#/components/examples/SemanticModelResponse"
+
+ OssieSemanticModelDocumentResponse:
+ description: Returns a standalone Apache Ossie document in the requested
format
+ headers:
+ Content-Disposition:
+ description: >-
+ Attachment filename derived from the Semantic Model name, with
characters outside
+ A-Za-z0-9._- replaced by underscores, ending in .ossie.yaml or
.ossie.json
+ schema:
+ type: string
+ example: attachment; filename="sales_model.ossie.yaml"
+ content:
+ application/yaml:
+ schema:
+ $ref: "#/components/schemas/OssieSemanticModelDocument"
+ examples:
+ OssieSemanticModelDocument:
+ $ref: "#/components/examples/OssieSemanticModelDocument"
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OssieSemanticModelDocument"
+ examples:
+ OssieSemanticModelDocument:
+ $ref: "#/components/examples/OssieSemanticModelDocument"
+
+ SemanticModelBadRequestResponse:
+ description: >-
+ Bad Request - The request is malformed or a Semantic Model definition,
source, or update is
+ invalid
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ IllegalSemanticModelException:
+ $ref: "#/components/examples/IllegalSemanticModelException"
+
+ SemanticModelForbiddenResponse:
+ description: Forbidden - The caller cannot access the requested or
referenced metadata
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ ForbiddenException:
+ $ref: "#/components/examples/ForbiddenException"
+
+ SemanticModelParentNotFoundResponse:
+ description: Not Found - The target metalake, catalog, or schema does
not exist
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ NoSuchMetalakeException:
+ $ref:
"./metalakes.yaml#/components/examples/NoSuchMetalakeException"
+ NoSuchCatalogException:
+ $ref:
"./catalogs.yaml#/components/examples/NoSuchCatalogException"
+ NoSuchSchemaException:
+ $ref: "./schemas.yaml#/components/examples/NoSuchSchemaException"
+
+ SemanticModelNotFoundResponse:
+ description: Not Found - The parent or target Semantic Model does not
exist
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ NoSuchMetalakeException:
+ $ref:
"./metalakes.yaml#/components/examples/NoSuchMetalakeException"
+ NoSuchCatalogException:
+ $ref:
"./catalogs.yaml#/components/examples/NoSuchCatalogException"
+ NoSuchSchemaException:
+ $ref: "./schemas.yaml#/components/examples/NoSuchSchemaException"
+ NoSuchSemanticModelException:
+ $ref: "#/components/examples/NoSuchSemanticModelException"
+
+ SemanticModelUnsupportedCatalogResponse:
+ description: Not Implemented - Semantic Models are supported only by
relational catalogs
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ UnsupportedOperationException:
+ $ref: "#/components/examples/UnsupportedOperationException"
+
+ SemanticModelAlreadyExistsResponse:
+ description: Conflict - The target Semantic Model already exists
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ SemanticModelAlreadyExistsException:
+ $ref: "#/components/examples/SemanticModelAlreadyExistsException"
+
+ SemanticModelAlterConflictResponse:
+ description: >-
+ Conflict - A rename target already exists or the internal transaction
lost an
+ optimistic-lock race
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ SemanticModelAlreadyExistsException:
+ $ref: "#/components/examples/SemanticModelAlreadyExistsException"
+ OptimisticLockException:
+ $ref: "#/components/examples/OptimisticLockException"
+
+ SemanticModelOptimisticLockResponse:
+ description: Conflict - The internal transaction lost an optimistic-lock
race
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ OptimisticLockException:
+ $ref: "#/components/examples/OptimisticLockException"
+
+ SemanticModelConnectionFailedResponse:
+ description: Bad Gateway - A referenced source catalog is temporarily
unavailable
+ content:
+ application/vnd.gravitino.v1+json:
+ schema:
+ $ref: "./openapi.yaml#/components/schemas/ErrorModel"
+ examples:
+ ConnectionFailedException:
+ $ref: "#/components/examples/ConnectionFailedException"
+
+ examples:
+ OssieSemanticModelDocument:
+ value: {
+ "version": "0.2.0.dev0",
+ "name": "sales_model",
+ "description": "Governed sales definitions",
+ "ai_context": {
+ "instructions": "Use certified sales metrics for revenue questions"
+ },
+ "datasets": [
+ {
+ "name": "orders",
+ "source": "sales.mart.orders",
+ "primary_key": ["order_id"],
+ "fields": [
+ {
+ "name": "order_amount",
+ "expression": {
+ "dialects": [
+ {
+ "dialect": "ANSI_SQL",
+ "expression": "order_amount"
+ }
+ ]
+ },
+ "datatype": "Decimal"
+ }
+ ]
+ }
+ ],
+ "relationships": [],
+ "metrics": [
+ {
+ "name": "total_revenue",
+ "expression": {
+ "dialects": [
+ {
+ "dialect": "ANSI_SQL",
+ "expression": "SUM(orders.order_amount)"
+ }
+ ]
+ },
+ "datatype": "Decimal"
+ }
+ ],
+ "custom_extensions": [
+ {
+ "vendor_name": "GRAVITINO_PROPERTIES",
+ "data": "{\"domain\":\"sales\"}"
+ }
+ ]
+ }
+
+ SemanticModelListResponse:
+ value: {
+ "code": 0,
+ "identifiers": [
+ {
+ "namespace": ["lake", "sales", "mart"],
+ "name": "sales_model"
+ }
+ ]
+ }
+
+ SemanticModelCreateRequest:
+ value: {
+ "name": "sales_model",
+ "comment": "Governed sales definitions",
+ "definition": {
+ "aiContext": {
+ "instructions": "Use certified sales metrics for revenue
questions",
+ "synonyms": ["sales semantics"],
+ "confidence": 0.95
+ },
+ "datasets": [
+ {
+ "name": "orders",
+ "source": {
+ "namespace": ["sales", "mart"],
+ "name": "orders"
+ },
+ "primaryKey": ["order_id"],
+ "uniqueKeys": [],
+ "description": "Governed order facts",
+ "aiContext": "Use completed orders for revenue",
+ "fields": [
+ {
+ "name": "order_time",
+ "expression": {
+ "dialects": [
+ {
+ "dialect": "ANSI_SQL",
+ "expression": "order_time"
+ }
+ ]
+ },
+ "dimension": {
+ "isTime": true
+ },
+ "label": "Order time",
+ "datatype": "DateTimeTz"
+ },
+ {
+ "name": "order_amount",
+ "expression": {
+ "dialects": [
+ {
+ "dialect": "TRINO",
+ "expression": "order_amount"
+ }
+ ]
+ },
+ "description": "Order amount before refunds",
+ "datatype": "Decimal",
+ "customExtensions": [
+ {
+ "vendorName": "example",
+ "data": "{\"semantic_type\":\"money\"}"
+ }
+ ]
+ }
+ ],
+ "customExtensions": []
+ },
+ {
+ "name": "customers",
+ "source": {
+ "namespace": ["sales", "mart"],
+ "name": "customers"
+ },
+ "fields": []
+ }
+ ],
+ "relationships": [
+ {
+ "name": "orders_to_customers",
+ "from": "orders",
+ "to": "customers",
+ "fromColumns": ["customer_id"],
+ "toColumns": ["customer_id"]
+ }
+ ],
+ "metrics": [
+ {
+ "name": "total_revenue",
+ "expression": {
+ "dialects": [
+ {
+ "dialect": "ANSI_SQL",
+ "expression": "SUM(orders.order_amount)"
+ }
+ ]
+ },
+ "description": "Total revenue across all orders",
+ "datatype": "Decimal",
+ "aiContext": {
+ "examples": ["What was revenue last month?"]
+ }
+ }
+ ],
+ "customExtensions": [
+ {
+ "vendorName": "example",
+ "data": "{\"certified\":true}"
+ }
+ ]
+ },
+ "properties": {
+ "domain": "sales"
+ }
+ }
+
+ SemanticModelUpdatesRequest:
+ value: {
+ "updates": [
+ {
+ "@type": "rename",
+ "newName": "sales_model_v2"
+ },
+ {
+ "@type": "updateComment",
+ "newComment": ""
+ },
+ {
+ "@type": "setProperty",
+ "property": "domain",
+ "value": "commerce"
+ },
+ {
+ "@type": "removeProperty",
+ "property": "legacy"
+ },
+ {
+ "@type": "replaceDefinition",
+ "definition": {
+ "datasets": [
+ {
+ "name": "orders",
+ "source": {
+ "namespace": ["sales", "mart"],
+ "name": "orders"
+ },
+ "fields": []
+ }
+ ],
+ "relationships": [],
+ "metrics": [],
+ "customExtensions": []
+ }
+ }
+ ]
+ }
+
+ SemanticModelResponse:
+ value: {
+ "code": 0,
+ "semanticModel": {
+ "name": "sales_model",
+ "comment": "Governed sales definitions",
+ "definition": {
+ "aiContext": "Use certified sales definitions",
+ "datasets": [
+ {
+ "name": "orders",
+ "source": {
+ "namespace": ["sales", "mart"],
+ "name": "orders"
+ },
+ "fields": []
+ }
+ ],
+ "relationships": [],
+ "metrics": [],
+ "customExtensions": []
+ },
+ "properties": {
+ "domain": "sales"
+ },
+ "audit": {
+ "creator": "gravitino",
+ "createTime": "2026-01-01T00:00:00Z",
+ "lastModifier": "gravitino",
+ "lastModifiedTime": "2026-01-01T00:00:00Z"
+ }
+ }
+ }
+
+ IllegalSemanticModelException:
+ value: {
+ "code": 1001,
+ "type": "IllegalSemanticModelException",
+ "message": "datasets[0].source: source must use catalog.schema.name",
+ "stack": [
+ "org.apache.gravitino.exceptions.IllegalSemanticModelException:
datasets[0].source: source must use catalog.schema.name",
+ "..."
+ ]
+ }
+
+ NoSuchSemanticModelException:
+ value: {
+ "code": 1003,
+ "type": "NoSuchSemanticModelException",
+ "message": "Semantic Model does not exist: sales_model",
+ "stack": [
+ "org.apache.gravitino.exceptions.NoSuchSemanticModelException:
Semantic Model does not exist: sales_model",
+ "..."
+ ]
+ }
+
+ SemanticModelAlreadyExistsException:
+ value: {
+ "code": 1004,
+ "type": "SemanticModelAlreadyExistsException",
+ "message": "Semantic Model already exists: sales_model",
+ "stack": [
+
"org.apache.gravitino.exceptions.SemanticModelAlreadyExistsException: Semantic
Model already exists: sales_model",
+ "..."
+ ]
+ }
+
+ UnsupportedOperationException:
+ value: {
+ "code": 1006,
+ "type": "UnsupportedOperationException",
+ "message": "The catalog does not support Semantic Model operations",
+ "stack": [
+ "java.lang.UnsupportedOperationException: The catalog does not
support Semantic Model operations",
+ "..."
+ ]
+ }
+
+ ConnectionFailedException:
+ value: {
+ "code": 1007,
+ "type": "ConnectionFailedException",
+ "message": "A referenced source catalog is unavailable",
+ "stack": [
+ "org.apache.gravitino.exceptions.ConnectionFailedException: A
referenced source catalog is unavailable",
+ "..."
+ ]
+ }
+
+ ForbiddenException:
+ value: {
+ "code": 1008,
+ "type": "ForbiddenException",
+ "message": "The caller cannot access referenced source metadata",
+ "stack": [
+ "org.apache.gravitino.exceptions.ForbiddenException: The caller
cannot access referenced source metadata",
+ "..."
+ ]
+ }
+
+ OptimisticLockException:
+ value: {
+ "code": 1012,
+ "type": "OptimisticLockException",
+ "message": "The Semantic Model changed during the internal
transaction",
+ "stack": [
+ "org.apache.gravitino.exceptions.OptimisticLockException: The
Semantic Model changed during the internal transaction",
+ "..."
+ ]
+ }
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestSemanticModelOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestSemanticModelOperations.java
index 3fd2028c54..07d7c7ca50 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestSemanticModelOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestSemanticModelOperations.java
@@ -36,6 +36,10 @@ import static org.mockito.Mockito.when;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.json.JsonMapper;
import java.io.IOException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
@@ -44,6 +48,7 @@ import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -76,6 +81,7 @@ import org.apache.gravitino.exceptions.NoSuchTableException;
import org.apache.gravitino.exceptions.NoSuchViewException;
import org.apache.gravitino.exceptions.OptimisticLockException;
import org.apache.gravitino.exceptions.SemanticModelAlreadyExistsException;
+import org.apache.gravitino.json.JsonUtils;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.SemanticModelEntity;
import org.apache.gravitino.rel.Column;
@@ -1013,6 +1019,53 @@ public class TestSemanticModelOperations extends
BaseOperationsTest {
verifyNoMoreInteractions(dispatcher);
}
+ @Test
+ void testImportOssieDocumentWithoutContentTypeDefaultsToYaml()
+ throws IOException, InterruptedException {
+ NameIdentifier ident = semanticModelIdentifier("marketing");
+ when(dispatcher.createSemanticModel(
+ eq(ident),
+ eq(null),
+ any(SemanticModelDefinition.class),
+ eq(Map.of(PROPERTY_OSSIE_VERSION, DEFAULT_VERSION))))
+ .thenReturn(semanticModel("marketing", null));
+
+ String yaml =
+ """
+ version: 0.2.0.dev0
+ name: marketing
+ datasets:
+ - name: campaigns
+ source: semantic_model_catalog.semantic_model_schema.campaigns
+ fields: []
+ """;
+ // The Jersey client may supply an entity media type, so send a raw HTTP
request instead.
+ HttpRequest request =
+ HttpRequest.newBuilder(target(semanticModelPath() + "/ossie").getUri())
+ .timeout(Duration.ofSeconds(10))
+ .header(HttpHeaders.ACCEPT, VND_V1_JSON)
+ .POST(HttpRequest.BodyPublishers.ofString(yaml))
+ .build();
+
Assertions.assertTrue(request.headers().firstValue(HttpHeaders.CONTENT_TYPE).isEmpty());
+
+ HttpResponse<String> response =
+ HttpClient.newHttpClient().send(request,
HttpResponse.BodyHandlers.ofString());
+
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
response.statusCode());
+ SemanticModelResponse body =
+ JsonUtils.objectMapper().readValue(response.body(),
SemanticModelResponse.class);
+ body.validate();
+ Assertions.assertEquals("marketing", body.getSemanticModel().name());
+ verify(dispatcher).importOssieDocument(namespace,
OssieDocument.yaml(yaml));
+ verify(dispatcher)
+ .createSemanticModel(
+ eq(ident),
+ eq(null),
+ any(SemanticModelDefinition.class),
+ eq(Map.of(PROPERTY_OSSIE_VERSION, DEFAULT_VERSION)));
+ verifyNoMoreInteractions(dispatcher);
+ }
+
@Test
void testExportOssieYamlAndJsonDocuments() throws Exception {
NameIdentifier ident = semanticModelIdentifier("sales");