Conversation
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Several new contracts incorrectly exclude valid absent, array-valued, or null HAL states, and nullable transport errors can still crash error handling.
Review effort: Balanced
Findings: 5
Open (5)
What changed in this PR
Types the frontend HAL resource layer, building on #25657 to reduce unsafe any usage without intended runtime changes.
Changes:
- Adds typed HAL links, sources, embedded resources, schemas, services, and errors.
- Declares concrete resource attributes and removes obsolete casts and suppressions.
- Updates dependent frontend code and tests for stricter HAL types.
| File | Description |
|---|---|
modules/documents/frontend/module/hal/resources/document-resource.ts |
Types attachment links. |
modules/budgets/frontend/module/hal/resources/budget-resource.ts |
Types attachment links. |
frontend/src/typings/open-project.typings.d.ts |
Removes obsolete function link typing. |
frontend/src/app/shared/components/storages/storage/storage.component.ts |
Uses typed file links. |
frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-single-context-menu.ts |
Uses typed links and promises. |
frontend/src/app/shared/components/op-context-menu/handlers/wp-status-dropdown-menu.directive.ts |
Types status updates and errors. |
frontend/src/app/shared/components/op-context-menu/handlers/wp-create-settings-menu.directive.ts |
Types configuration links. |
frontend/src/app/shared/components/op-context-menu/handlers/op-settings-dropdown-menu.directive.ts |
Types custom-field links. |
frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts |
Removes redundant attachment cast. |
frontend/src/app/shared/components/fields/field.base.ts |
Builds field schemas on HAL schemas. |
frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts |
Types saved resources. |
frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts |
Types allowed-value links. |
frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.ts |
Types allowed-value loading. |
frontend/src/app/shared/components/fields/edit/field-types/project-edit-field.component.ts |
Types project-value links. |
frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts |
Narrows allowed-value resources. |
frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.ts |
Uses typed schema options. |
frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts |
Uses typed schema proxies. |
frontend/src/app/shared/components/fields/display/field-types/render-hierarchy-item.ts |
Types hierarchy loading. |
frontend/src/app/shared/components/fields/display/field-types/excluded-icon-helper.service.ts |
Uses typed status resources. |
frontend/src/app/shared/components/fields/changeset/resource-changeset.ts |
Types forms, schemas, and payloads. |
frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts |
Uses typed project links. |
frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts |
Removes project casts. |
frontend/src/app/features/work-packages/components/wp-single-view-tabs/files-tab/op-files-tab.component.ts |
Uses typed projects. |
frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts |
Removes unsafe project suppression. |
frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts |
Removes unsafe project suppression. |
frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts |
Types synthetic creation links. |
frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts |
Uses typed query update links. |
frontend/src/app/features/work-packages/components/wp-edit/work-package-changeset.ts |
Types schema proxy and description. |
frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.spec.ts |
Updates resource test casts. |
frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts |
Uses typed project IDs. |
frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts |
Uses typed status names. |
frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts |
Marks floating load promise. |
frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts |
Removes HAL relationship casts. |
frontend/src/app/features/hal/services/hal-resource.service.ts |
Types resource construction and requests. |
frontend/src/app/features/hal/services/hal-resource-notification.service.ts |
Types error and toast handling. |
frontend/src/app/features/hal/schemas/work-package-schema-proxy.ts |
Narrows proxy handler types. |
frontend/src/app/features/hal/schemas/schema-proxy.ts |
Types proxy dispatch and binding. |
frontend/src/app/features/hal/schemas/hal-payload.helper.ts |
Types schema-driven payloads. |
frontend/src/app/features/hal/resources/wp-collection-resource.ts |
Declares collection links and fields. |
frontend/src/app/features/hal/resources/work-package-timestamp-resource.ts |
Reuses base link typing. |
frontend/src/app/features/hal/resources/work-package-resource.ts |
Types work-package relationships and links. |
frontend/src/app/features/hal/resources/work-package-resource.spec.ts |
Updates callable-link fixture. |
frontend/src/app/features/hal/resources/wiki-page-resource.ts |
Types attachment links. |
frontend/src/app/features/hal/resources/user-resource.ts |
Narrows cached state. |
frontend/src/app/features/hal/resources/type-resource.ts |
Narrows cached state. |
frontend/src/app/features/hal/resources/time-entry-resource.ts |
Declares fields and delete link. |
frontend/src/app/features/hal/resources/status-resource.ts |
Narrows cached state. |
frontend/src/app/features/hal/resources/share-resource.ts |
Declares embedded share fields. |
frontend/src/app/features/hal/resources/schema-resource.ts |
Types schema attributes and state. |
frontend/src/app/features/hal/resources/schema-dependency-resource.ts |
Types dependency maps. |
frontend/src/app/features/hal/resources/relation-resource.ts |
Types relation operations and fields. |
frontend/src/app/features/hal/resources/query-sort-by-resource.ts |
Types embedded query sorting. |
frontend/src/app/features/hal/resources/query-resource.ts |
Declares query operation links. |
frontend/src/app/features/hal/resources/query-operator-resource.ts |
Narrows source ID. |
frontend/src/app/features/hal/resources/query-form-resource.ts |
Types nested query schemas. |
frontend/src/app/features/hal/resources/query-filter-resource.ts |
Types filter values and IDs. |
frontend/src/app/features/hal/resources/query-filter-instance-schema-resource.ts |
Types filter schema links and sources. |
frontend/src/app/features/hal/resources/query-filter-instance-resource.ts |
Types dynamic schema access. |
frontend/src/app/features/hal/resources/project-resource.ts |
Types project state. |
frontend/src/app/features/hal/resources/post-resource.ts |
Types attachment links. |
frontend/src/app/features/hal/resources/placeholder-user-resource.ts |
Narrows cached state. |
frontend/src/app/features/hal/resources/mixins/attachable-mixin.ts |
Uses base attachment-link typing. |
frontend/src/app/features/hal/resources/membership-resource.ts |
Declares membership fields and links. |
frontend/src/app/features/hal/resources/meeting-resource.ts |
Types attachment links. |
frontend/src/app/features/hal/resources/hal-resource.ts |
Defines typed HAL source maps. |
frontend/src/app/features/hal/resources/hal-resource.spec.ts |
Updates tests for stricter maps. |
frontend/src/app/features/hal/resources/grid-resource.ts |
Types grid links and attachments. |
frontend/src/app/features/hal/resources/form-resource.ts |
Types form schemas and payloads. |
frontend/src/app/features/hal/resources/error-resource.ts |
Types API error details. |
frontend/src/app/features/hal/resources/custom-action-resource.ts |
Types custom-action operations. |
frontend/src/app/features/hal/resources/attachment-collection-resource.ts |
Declares attachment elements. |
frontend/src/app/features/hal/resources/activity-comment-resource.ts |
Types embedded data and links. |
frontend/src/app/features/hal/http/openproject-header-interceptor.ts |
Replaces HTTP event any. |
frontend/src/app/features/hal/http/http.interfaces.ts |
Narrows HTTP options. |
frontend/src/app/features/hal/helpers/lazy-accessor.ts |
Makes lazy accessors generic. |
frontend/src/app/features/hal/helpers/hal-resource-builder.ts |
Types HAL transformation internals. |
frontend/src/app/features/hal/hal-link/hal-link.ts |
Adds generic callable links. |
frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts |
Removes project cast. |
frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts |
Uses typed work-package links. |
frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts |
Adapts viewpoint typing. |
frontend/src/app/core/state/attachments/attachments.service.ts |
Uses base HAL links. |
frontend/src/app/core/schemas/schema-cache.service.ts |
Uses typed schema links. |
frontend/src/app/core/path-helper/apiv3-paths.ts |
Uses typed project IDs. |
frontend/src/app/core/global_search/input/global-search-input.component.spec.ts |
Types test HAL sources. |
frontend/src/app/core/apiv3/virtual/apiv3-boards-paths.ts |
Narrows grid form payload. |
frontend/src/app/core/apiv3/cache/cachable-apiv3-resource.ts |
Narrows schema-link href. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } catch (e:unknown) { | ||
| this.loading$?.complete(); | ||
| this.halNotification.showError((e as HalError).resource, changeset.projectedResource); | ||
| this.halNotification.showError((e as HalError).resource!, changeset.projectedResource); |
There was a problem hiding this comment.
Pre-existing: before this PR the same null reached showError untyped, so the ! only makes the existing assumption visible. Routing these through handleRawError changes runtime behaviour, so it is tracked separately in OP-20361 to keep this PR type-only.
| }) | ||
| .catch((e:unknown) => { | ||
| this.workPackageNotificationService.showError((e as HalError).resource, change.projectedResource); | ||
| this.workPackageNotificationService.showError((e as HalError).resource!, change.projectedResource); |
There was a problem hiding this comment.
Pre-existing: before this PR the same null reached showError untyped, so the ! only makes the existing assumption visible. Routing these through handleRawError changes runtime behaviour, so it is tracked separately in OP-20361 to keep this PR type-only.
721c8e0 to
8d2e4e0
Compare
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
Deletes WorkPackageResource#updateLinkedResources. It has no callers and calls an undeclared wpCacheService, so it only compiled because of the resource's `any` index signature. https://community.openproject.org/wp/OP-20344
Makes HalLink#$fetch and #$callable generic and gives CallableHalLink its call signature and a HalLink-typed $link, so link functions no longer rely on the global `Function.$link?:any` augmentation, which is removed. HalResource#$links is now a map of CallableHalLink and #$embedded a map of unknown values. Resource-specific link interfaces declare their action links as CallableHalLink<T> instead of `Promise<any>` methods, and are intersected with the base maps on `$links`/`$embedded` so the resource interfaces they are merged into keep the `any` index signature until it is flipped separately. WorkPackageResourceEmbedded drops its `HalResource|any` unions and the link interface no longer inherits the embedded attributes. The unused QueryFilterResource embedded declaration is removed. Callers that relied on the untyped maps get boundary casts. https://community.openproject.org/wp/OP-20344
Types HalResource#$source as HalSource and accepts an unknown source in the constructor and $initialize, which already unwrap a HalResource passed in place of a raw source. The initializer callback takes a HalResource, $copy and $plain drop their `any`, and lazy() becomes generic over the value it defines. initializeHalProperties now reads the source through typed views of `_links` and `_embedded` and passes the target maps to setupProperty directly instead of looking them up by a computed `$`-name, removing the builder's `any` casts. Runtime behaviour is unchanged. Callers and specs that relied on the untyped source get boundary casts. https://community.openproject.org/wp/OP-20344
Types ResourceChangeset#schema and WorkPackageChangeset#schema as ISchemaProxy, which is what both getters return. FormResource#schema stays the embedded form schema and gets its own FormSchemaResource type, a SchemaResource whose attributes are field schemas, and its commit link becomes a CallableHalLink. IFieldSchema is now derived from IOPFieldSchema instead of duplicating it with `any`-typed allowedValues and options. Fields that load allowed values through the link cast it at the boundary. SchemaProxy dispatches its proxied methods by name through proxyMethod, which keeps them bound to the handler and still honours subclass overrides, instead of wrapping unbound method references in a Function proxy. The payload helper checks own schema keys with Object.hasOwn. https://community.openproject.org/wp/OP-20344
Declares attributes and link callables that callers already read through the `any` index signature: FormResource#payload, #commit and #configureForm, QueryResource#star, #unstar, #icalUrl and #updatedAt, TimeEntryResource#hours, WorkPackageCollectionResource#customFields and #createWorkPackage, and WorkPackageResource#configureForm and #bcfViewpoints. WorkPackageResource#description becomes Formattable and RelationResource#type a string. Class and interface declaration merges are replaced by member declarations on the class (with `implements` for the exported shape), which emit no code under useDefineForClassFields: false. Resource state getters cast through unknown instead of `any`. Callers whose values are now typed get boundary casts. https://community.openproject.org/wp/OP-20344
Types the request data, created sources and registered classes in HalResourceService, the HTTP client options and interceptor, and ErrorResource's errors and details. HalResourceNotificationService takes ErrorResource where it reads error attributes and treats raw responses as unknown. HalError#resource is nullable, so the two callers passing it to showError assert it at the boundary, as the service already assumed. HTTPClientParamMap keeps its `any` values: query and BCF callers pass untyped objects as request params. https://community.openproject.org/wp/OP-20344
Types the remaining `any` members of the query filter, filter schema, schema dependency and grid resources, lets attachable resources read the now typed addAttachment link directly, and casts the two schema and parent reads that go through the resource index signature. https://community.openproject.org/wp/OP-20344
Removes type assertions, eslint-disable directives and imports that the typed HAL links, sources, schemas and resource attributes made unnecessary, and marks the two now typed $load() calls whose promises were already discarded with `void`. https://community.openproject.org/wp/OP-20344
Declares the base link map as holding a callable link, an array of links, or nothing, since the builder resolves array-valued HAL links to arrays and resources only carry the links the API sent. The self link is always present, as the builder creates one when missing. Call sites that read a link they have already checked, or that the resource always carries, cast it at the point of use. https://community.openproject.org/wp/OP-20344
8d2e4e0 to
03912a1
Compare

Ticket
https://community.openproject.org/wp/OP-20344
What are you trying to accomplish?
Part of OP-20341: types the HAL resource layer, the main source of
anyin the frontend, without changing behaviour. Theanyindex signature onHalResourceitself stays; OP-20349 replaces it on top of this.$fetch),$sourceasHalSource,$embedded, and the lazy accessor.FormResource#schemastays a raw form schema.IFieldSchemanow builds onIOPFieldSchema.selfalways present), marks nullable embedded links (assignee,responsible,category,version) and the optionalstar/unstarlinks.WorkPackageResource#updateLinkedResources, which referenced an undeclared service.Application-code typing violations drop from 1,174 to 664;
features/halgoes from 364 to 6.What approach did you choose and why?
Changes are types and boundary casts only; no runtime narrowing was added. Resource link and embedded interfaces are intersected with the base maps rather than extending them, so they don't add a second index signature before the index flip. About 17 files outside
features/halneeded small compile fixes for the stricter types.A few behaviour-neutral edits: now-typed floating
$load()/refresh()promises are marked withvoid, andSchemaProxybinds proxied methods by name through its existingproxyMethod, so subclass overrides still apply.The six remaining
features/halviolations exist only because of the index signature, or belong to the work package views (GroupObject.value).Error handlers that assert
HalError#resourceon network failures are tracked in OP-20361.Stacked on #25657, which types the hook and global declarations this builds on.
AI involvement
Directed – I specified the requirements and AI implemented most of it; I validated via testing rather than a full line-by-line review.
Merge checklist
Browser JavaScript diff (19 files, type-only changes omitted)
Each changed browser file (TypeScript and templates, excluding specs, test helpers and declaration files) is transpiled on its own with
frontend/tsconfig.json, before and after; this is the emitted JavaScript that differs.