Azure / Azure/azure-openapi-validator

[RequiredPropertiesMissingInResourceModel][BodyTopLevelProperties] Special-case the `OperationStatus` response

Open
#806 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
53
Forks
57
Avg merge
1d 23h
Merged PRs (30d)
2

Description

This sample shows the recommended TypeSpec (and generated Swagger) for an `OperationStatus` response: [playground](https://azure.github.io/typespec-azure/playground/?options=%7B%22linterRuleSet%22%3A%7B%22extends%22%3A%5B%22%40azure-tools%2Ftypespec-azure-rulesets%2Fresource-manager%22%5D%7D%7D&e=%40azure-tools%2Ftypespec-autorest&vs=%7B%7D&c=aW1wb3J0ICJAdHlwZXNwZWMvaHR0cCI7CtIZcmVzdNUZdmVyc2lvbmluZ8wfYXp1cmUtdG9vbHMvyCstxhVjb3Jl3yvIK3Jlc291cmNlLW1hbmFnZXIiOwoKdXNpbmcgSHR0cDvHDFJlc3TIDFbpAI7IEkHESi5Db3JlzhJSx1xNxls7CgovKiogQ29udG9zb8RUxR4gUHJvdmlkZXIg5gCDbWVudCBBUEkuICovCkBhcm3IIE5hbWVzcGFjZQpAc2VydmljZSgjeyB0aXRsZTogIsdXyC1IdWJDbGllbnQiIH0pCkDnAUNlZCjnAL9zKQpuyFAgTWljcm9zb2Z0LtJG7wC2QVBJIMdNc%2BQAoWVudW3oARNzIHsKICDELjIwMjEtMTAtMDEtcHJldmlld8g1xDQgIOQA10NvbW1vblTkAY%2FHQCj1ATcuyykuyGoudjUpCiAgYNJpYCwKfeYAs0HoALXrAM4g6AHw5QFdcGFyZW50yHAoU3Vic2NyaXB0aW9uTG9jYcQIyB0pCm1vZGVsIE9wZXLFGVN0YXR16gDyVGhlIG%2FIHCBzxh3mAOzqAh8ubHJvxjgKIMckOvYCLMkQ5QC85wJwxDtlOwrLcnVuaXF1ZSBpZGVudGlmaWVyIGZvciB07ACMxnDtAPggIEDEVGJpbGl0eShMaWZlY3ljbOQAhGFk5AFWaWQ6IHN0cuUC4stt5AIwIG9mxV%2FfYN5gQGtlecQHcGF0aMQIc2Vn5AL1KCLPVWVzIsQvxHjxAJHpAYjkAUhydCB0aW3ffMZ8xS5UaW1lPzogdXRjRGF0ZcQO01hjb21wbGV0Zd9byltlbmTbWeQBQnByb2dyZXNzIG1hZGUgdG93YXJkyGjkBDftAbnfc8VzcGVyY2VudEPGQ8R7ZmxvYXQ2NMl3cnAt5ATuaWZpY8R%2FcGVydGll5wKayhA%2FOvAC2VDJHMlLRXJyb3JzIHRoYXQgb2NjdXJyZWQgaeYCEOoAtmVuZGVkIHdpdGggQ2FuY2VsZWQgb3IgRmFpxArtAxn%2BAU7Ebj%2F4Ax9Gb3VuZMVxcy7lAJhEZXRhaWw76AP8RW1wbG95ZeUBc%2BsA9PUDxOoA6%2BkDzkFn5QLqZchG5QCwYWfkAWFpbnQzMukBFENpdHnSKmNpdHk%2F8QKzUHJvZmls01lAZW5jb2RlKCJiYXNlNjR1cmzlAvhwxjA%2FOiBieXTkAYh9CgppbnRlcmbkBbTpAMVzIGV4dGVuZHP3ASvLKXt9CuUGUsgjyhvLWegBO%2BYBDmdldCBpcyBByjXkAaE87wFDPuQAnQ%3D%3D)

This swagger causes the following errors:

```
error | RequiredPropertiesMissingInResourceModel | Model definition 'OperationStatus' must have the properties 'name', 'id' and 'type' in its hierarchy and these properties must be marked as readonly.
- file:///home/mharder/specs-mh/specification/widget/resource-manager/Microsoft.Widget/Widget/stable/2021-11-01/widget.json:154:5
error | BodyTopLevelProperties | Top level properties should be one of name, type, id, location, properties, tags, plan, sku, etag, managedBy, identity, zones. Model definition 'OperationStatus' has extra properties ['status'].
- file:///home/mharder/specs-mh/specification/widget/resource-manager/Microsoft.Widget/Widget/stable/2021-11-01/widget.json:154:5
error | BodyTopLevelProperties | Top level properties should be one of name, type, id, location, properties, tags, plan, sku, etag, managedBy, identity, zones. Model definition 'OperationStatus' has extra properties ['startTime'].
- file:///home/mharder/specs-mh/specification/widget/resource-manager/Microsoft.Widget/Widget/stable/2021-11-01/widget.json:154:5
error | BodyTopLevelProperties | Top level properties should be one of name, type, id, location, properties, tags, plan, sku, etag, managedBy, identity, zones. Model definition 'OperationStatus' has extra properties ['endTime'].
- file:///home/mharder/specs-mh/specification/widget/resource-manager/Microsoft.Widget/Widget/stable/2021-11-01/widget.json:154:5
error | BodyTopLevelProperties | Top level properties should be one of name, type, id, location, properties, tags, plan, sku, etag, managedBy, identity, zones. Model definition 'OperationStatus' has extra properties ['percentComplete'].
- file:///home/mharder/specs-mh/specification/widget/resource-manager/Microsoft.Widget/Widget/stable/2021-11-01/widget.json:154:5
error | BodyTopLevelProperties | Top level properties should be one of name, type, id, location, properties, tags, plan, sku, etag, managedBy, identity, zones. Model definition 'OperationStatus' has extra properties ['error'].
- file:///home/mharder/specs-mh/specification/widget/resource-manager/Microsoft.Widget/Widget/stable/2021-11-01/widget.json:154:5
```

We believe this swagger should cause **no** errors. The rules `RequiredPropertiesMissingInResourceModel` and `BodyTopLevelProperties` should be updated to allow (or require?) the schema in this sample.

Example existing spec, that fails with these errors (they were ignored when the PR was merged to main):

https://github.com/Azure/azure-rest-api-specs/blob/fda1bb1d4593ae819edd699446d0941d1e8f1174/specification/chaos/resource-manager/Microsoft.Chaos/Chaos/stable/2024-01-01/operationStatuses.json#L37-L83

## Examples (from playground)
### TypeSpec
```js
import "@typespec/http";
import "@typespec/rest";
import "@typespec/versioning";
import "@azure-tools/typespec-azure-core";
import "@azure-tools/typespec-azure-resource-manager";

using Http;
using Rest;
using Versioning;
using Azure.Core;
using Azure.ResourceManager;

/** Contoso Resource Provider management API. */
@armProviderNamespace
@service(#{ title: "ContosoProviderHubClient" })
@versioned(Versions)
namespace Microsoft.ContosoProviderHub;

/** Contoso API versions */
enum Versions {
/** 2021-10-01-preview version */
@armCommonTypesVersion(Azure.ResourceManager.CommonTypes.Versions.v5)
`2021-10-01-preview`,
}

/** A ContosoProviderHub resource */
@parentResource(SubscriptionLocationResource)
model OperationStatus {
/** The operation status */
@Azure.Core.lroStatus
status: Azure.ResourceManager.ResourceProvisioningState;

/** The unique identifier for the operationStatus resource */
@visibility(Lifecycle.Read)
id: string;

/** The name of the operationStatus resource */
@visibility(Lifecycle.Read)
@key
@path
@segment("operationStatuses")
name: string;

/** Operation start time */
@visibility(Lifecycle.Read)
startTime?: utcDateTime;

/** Operation complete time */
@visibility(Lifecycle.Read)
endTime?: utcDateTime;

/** The progress made toward completing the operation */
@visibility(Lifecycle.Read)
percentComplete?: float64;

/** rp-specific properties */
properties?: OperationStatusProperties;

/** Errors that occurred if the operation ended with Canceled or Failed status */
@visibility(Lifecycle.Read)
error?: Azure.ResourceManager.Foundations.ErrorDetail;
}

/** Employee properties */
model OperationStatusProperties {
/** Age of employee */
age?: int32;

/** City of employee */
city?: string;

/** Profile of employee */
@encode("base64url")
profile?: bytes;
}

interface Operations extends Azure.ResourceManager.Operations {}

@armResourceOperations
interface Employees {
get is ArmResourceRead;
}
```

### Swagger
```json
{
"swagger": "2.0",
"info": {
"title": "ContosoProviderHubClient",
"version": "2021-10-01-preview",
"description": "Contoso Resource Provider management API.",
"x-typespec-generated": [
{
"emitter": "@azure-tools/typespec-autorest"
}
]
},
"schemes": [
"https"
],
"host": "management.azure.com",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"security": [
{
"azure_auth": [
"user_impersonation"
]
}
],
"securityDefinitions": {
"azure_auth": {
"type": "oauth2",
"description": "Azure Active Directory OAuth2 Flow.",
"flow": "implicit",
"authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize",
"scopes": {
"user_impersonation": "impersonate your user account"
}
}
},
"tags": [
{
"name": "Operations"
},
{
"name": "Employees"
}
],
"paths": {
"/providers/Microsoft.ContosoProviderHub/operations": {
"get": {
"operationId": "Operations_List",
"tags": [
"Operations"
],
"description": "List the operations for the provider",
"parameters": [
{
"$ref": "../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter"
}
],
"responses": {
"200": {
"description": "Azure operation completed successfully.",
"schema": {
"$ref": "../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse"
}
}
},
"x-ms-pageable": {
"nextLinkName": "nextLink"
}
}
},
"/subscriptions/{subscriptionId}/providers/Microsoft.ContosoProviderHub/locations/{location}/operationStatuses/{name}": {
"get": {
"operationId": "Employees_Get",
"tags": [
"Employees"
],
"description": "Get a OperationStatus",
"parameters": [
{
"$ref": "../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter"
},
{
"$ref": "../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter"
},
{
"$ref": "../../../common-types/resource-management/v5/types.json#/parameters/LocationParameter"
},
{
"name": "name",
"in": "path",
"description": "The name of the operationStatus resource",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "Azure operation completed successfully.",
"schema": {
"$ref": "#/definitions/OperationStatus"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse"
}
}
}
}
}
},
"definitions": {
"Azure.ResourceManager.ResourceProvisioningState": {
"type": "string",
"description": "The provisioning state of a resource type.",
"enum": [
"Succeeded",
"Failed",
"Canceled"
],
"x-ms-enum": {
"name": "ResourceProvisioningState",
"modelAsString": true,
"values": [
{
"name": "Succeeded",
"value": "Succeeded",
"description": "Resource has been created."
},
{
"name": "Failed",
"value": "Failed",
"description": "Resource creation failed."
},
{
"name": "Canceled",
"value": "Canceled",
"description": "Resource creation was canceled."
}
]
}
},
"OperationStatus": {
"type": "object",
"description": "A ContosoProviderHub resource",
"properties": {
"status": {
"$ref": "#/definitions/Azure.ResourceManager.ResourceProvisioningState",
"description": "The operation status"
},
"id": {
"type": "string",
"description": "The unique identifier for the operationStatus resource",
"readOnly": true
},
"name": {
"type": "string",
"description": "The name of the operationStatus resource",
"readOnly": true
},
"startTime": {
"type": "string",
"format": "date-time",
"description": "Operation start time",
"readOnly": true
},
"endTime": {
"type": "string",
"format": "date-time",
"description": "Operation complete time",
"readOnly": true
},
"percentComplete": {
"type": "number",
"format": "double",
"description": "The progress made toward completing the operation",
"readOnly": true
},
"properties": {
"$ref": "#/definitions/OperationStatusProperties",
"description": "rp-specific properties"
},
"error": {
"$ref": "../../../common-types/resource-management/v5/types.json#/definitions/ErrorDetail",
"description": "Errors that occurred if the operation ended with Canceled or Failed status",
"readOnly": true
}
},
"required": [
"status",
"id",
"name"
]
},
"OperationStatusProperties": {
"type": "object",
"description": "Employee properties",
"properties": {
"age": {
"type": "integer",
"format": "int32",
"description": "Age of employee"
},
"city": {
"type": "string",
"description": "City of employee"
},
"profile": {
"type": "string",
"format": "base64url",
"description": "Profile of employee"
}
}
}
},
"parameters": {}
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.