[FEATURE REQ] Store subscription ownerId relative to the service, the way scope already is

未关闭
#877 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
55/100
Issue 类型
功能
描述清晰度
描述清楚
活跃度
活跃
技术栈
csharp
领域
api, backend

调研方向

从 common/Resource.cs 中的 IResourceWithReference 和 common/Resource.FileSystem.cs 中的 DTO 格式化路径开始,然后比较 common/Subscription.cs 和 common/WorkspaceSubscription.cs 中的引用声明。跟踪这些声明如何传递给 FormatInformationFileDto 和 Relationships.cs getReferences。完成标准是:两个 subscription 资源的 ownerId 都以相对形式持久化,且不创建用户关系依赖,同时测试覆盖提取和发布行为。

由索引模型根据 Issue 内容生成。

描述

v7 relativizes cross-resource references so an artifact can be published to any instance, which is a great improvement. Subscription scope is covered, but ownerId is not, so subscription artifacts remain pinned to the instance they were extracted from.

What the extractor writes today

Extracting a subscription produces:

{
  "properties": {
    "displayName": "Unlimited",
    "scope": "/products/unlimited",
    "ownerId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-dev/providers/Microsoft.ApiManagement/service/apim-dev/users/1",
    "state": "active"
  }
}

scope is relative and publishes anywhere. ownerId still carries the source instance's subscription id, resource group and service name, so publishing this artifact to another instance requires a configuration override for every subscription:

subscriptions:
  - name: unlimited
    properties:
      ownerId: "/subscriptions/{#SUB#}/resourceGroups/{#RG#}/providers/Microsoft.ApiManagement/service/{#SVC#}/users/1"

That override is pure boilerplate. It says nothing except "the same user id, on whichever instance this is", which is exactly what a relative id already expresses.

The relative form is the documented contract

This is not a request to invent a new format. Microsoft documents ownerId as relative:

ownerId — The user resource identifier of the subscription owner. The value is a valid relative URL in the format of /users/{userId} where {userId} is a user identifier.

SubscriptionContract.ownerId

APIM returns the expanded absolute form on GET, the same as it does for scope, but the documented canonical format is relative. So the extractor is persisting a shape that is less portable than the contract allows.

Why the existing mechanism cannot simply be extended

The obvious change is to add ownerId to OptionalReferencedResourceDtoProperties in SubscriptionResource, next to the two Scope entries. That does not work, for two reasons:

  1. That dictionary is keyed by IResource, and there is no UserResource. Introducing one would mean APIOps starts modelling users as an extractable and publishable resource type, which is a much larger change than this problem warrants.

  2. The same dictionaries feed the publisher's relationship graph in Relationships.cs (getReferences). A UserResource key would create a subscription-to-user dependency edge, so the publisher would expect the user in source and STRICT_VALIDATION=true would report a missing predecessor. Users are typically not managed as artifacts, so that would be a false failure.

The two concerns are currently coupled: "this property holds an absolute id that should be stored relative" and "this property references a resource we manage". ownerId is the first case without the second.

Proposed change

Separate the two by adding a relativization-only declaration.

common/Resource.cs, on IResourceWithReference:

/// <summary>
/// DTO properties holding an absolute resource id that should be persisted relative to the
/// service, but whose target is not a resource APIOps manages. These take part in id
/// relativization only; they never contribute to the relationship graph.
/// </summary>
ImmutableHashSet<string> RelativeIdDtoProperties => [];

common/Resource.FileSystem.cs, include them when formatting the information file:

private static JsonObject FormatInformationFileDto(this IResourceWithReference resource, JsonObject dto) =>
    resource.MandatoryReferencedResourceDtoProperties
            .Union(resource.OptionalReferencedResourceDtoProperties)
            .Select(kvp => kvp.Value)
            .Union(resource.RelativeIdDtoProperties)
            .Aggregate(dto, SetAbsoluteToRelativeId);

Union on the projected property names also removes the need for the current DistinctBy(kvp => kvp.Value), which exists because Scope is declared twice.

common/Subscription.cs and common/WorkspaceSubscription.cs:

public ImmutableHashSet<string> RelativeIdDtoProperties { get; } =
    [nameof(SubscriptionDto.Properties.OwnerId)];

The interface member defaults to empty, so nothing else changes and no existing resource is affected. nameof follows the convention already used for Scope and LoggerId, which works because DTOs are parsed with JsonSerializerOptions.Web.

Scope
  • SubscriptionResource.OwnerId
  • WorkspaceSubscriptionResource.OwnerId

I looked for other properties in the same category and did not find an obvious one, but the new member gives a place for them if they turn up.

Effect

Subscription artifacts become fully instance-agnostic, and the per-subscription ownerId override disappears from every environment configuration file. In our own repo that is 21 override lines in one operator repository and 3 in each of two environment files in another.

Happy to implement

I am glad to send the PR with tests if you agree with the shape. The main open question is naming and whether you would rather see this as a separate member as proposed, or handled some other way, for example a marker on the DTO property or widening the existing dictionaries to accept a null resource key.

主要语言
C#
星标
448
派生
247
PR 合并指标
30 天内没有已合并 PR

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

Azure/apiops 的其他 Issue

查看 Azure/apiops 的全部 Issue

相似的 Issue

更多 C# Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。