Skip to content

Fixes 35909: report bulk-create outcomes and recover conflicts - #35910

Merged
harshach merged 8 commits into
mainfrom
agent/can-we-annalyze-this-error-a
Oct 22, 2026
Merged

harshach merged 8 commits into
mainfrom
agent/can-we-annalyze-this-error-a

Conversation

@harshach

@harshach harshach commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Describe your changes:

Fixes #35909

Bulk creation could roll back unrelated new entities when one FQN already existed. Recover those conflicts centrally, preserve existing entities, and let callers request an explicit outcome for every input.

For test cases and glossary terms, POST .../createMany?onConflict=skip|fail returns createdCount, skippedCount, failedCount, and ordered results. Each result has an input index, status, resolvable FQN, and a reason/message for skips or failures. IDs are returned only for created entities.

  • skip: conflicts are skipped; other valid inputs are created.
  • fail: conflicts are reported as failed; other valid inputs are still created. This is not an atomic request-wide failure policy.
  • Omitted parameter: preserve the existing created-entity array and validation errors before writes. The existing Java SDK method and Collate AI caller remain compatible; a new SDK overload accepts the policy and returns the detailed result.

Python now exposes TestCases.create_many(requests, on_conflict=ConflictPolicy.skip) and GlossaryTerms.create_many(...). Java exposes TestCases.createMany(requests).onConflict(BulkCreateConflictPolicy.FAIL).execute() and the equivalent glossary builder. Both default to skip and return the generated typed result, including reasons. New fluent calls require a server supporting onConflict.

Type of change:

  • Bug fix
  • Improvement

High-level design:

EntityRepository delegates to a small shared BulkEntityCreator. It maps inputs, checks existing FQNs in a batch, validates new candidates, and creates transaction chunks of at most 100 entities through the existing repository lifecycle. The first valid occurrence of each new FQN wins. Detailed responses isolate expected mapping/preparation validation failures; legacy responses retain their original failure behavior.

A concurrent winner can appear during preparation or insertion. Recovery confirms the exact FQN belongs to a different stored ID, removes that candidate, and retries only the failed transaction's remaining entries after rollback. Every retry removes an entry. Unrelated constraints, connectivity errors, and failures after our own rows commit still propagate. Each committed chunk completes relationships, indexing, and creation hooks before the next chunk starts.

Reason codes are ALREADY_EXISTS, DUPLICATE_IN_REQUEST, and VALIDATION_ERROR. Existing and soft-deleted entities are neither overwritten nor restored. Authorization and request limits remain HTTP errors. Glossary creation now enforces the normal CREATE permission before conflict lookup; conflict results do not expose stored IDs to callers without read permission.

Explicit policy selection also selects the detailed response, preserving the live Collate AI client's array parser without an extra request option or coordinated rollout. Single-create and bulk create-or-update keep their existing contracts. The new JSON schema describes a response only, so no database migration is required. Java, Python, and TypeScript models are generated from it.

See ADR:2026-10-21-bulk-test-case-creation-skips-existing-fqns.

Performance: normal creation adds one batched FQN lookup and O(N) bookkeeping. Reporting adds no entity fetches. Conflicts can add lookups/retries; multi-chunk requests complete search refreshes per chunk. No latency benchmark was run.

The Python facades share a small serialization helper; the Java facades share one request builder and reuse the service methods. Python sends an empty batch as [] instead of letting the REST client's empty-JSON handling drop the body. SDK tests now use the parent's JUnit version; the previous SDK override mixed JUnit 6.1.3 with managed Platform 1.11.4 and prevented test discovery.

Tests:

Use cases covered

  • Python and Java fluent SDK public entry points: exact POST paths, array bodies, default/explicit conflict policy, typed indexes/statuses/reasons/IDs, Unicode, empty batches, and HTTP error propagation.
  • Mixed created/skipped/failed inputs, ordered indexes, reason codes/messages, both conflict policies, empty input, and invalid policy rejection before writes.
  • Existing and soft-deleted test cases/glossary terms, repeated FQNs, and independent names under different tables/glossaries.
  • A mapping or preparation failure followed by a valid input with the same FQN; the valid occurrence is created.
  • Omitted-parameter array responses, both Java SDK signatures, and legacy validation rejection before inserts.
  • Denied CREATE requests and callers allowed CREATE but denied VIEW_BASIC; existing IDs stay absent under both policies.
  • Real concurrent inserts during preparation and insertion, later-chunk conflicts, winner preservation, relationships, and search visibility.
  • Unrelated foreign-key, unique-constraint, connection, and later-chunk errors propagate; bulk upserts still update existing tables and bulk alerts remain scheduled.

Unit tests

  • IntegrityConstraintMapperTest: 12 passed.
  • TestCaseRepositoryTest: 11 passed.
  • MCP: CreateEntityToolTest 24 passed, CreateTestCaseToolTest 4 passed, PatchEntityToolTest 31 passed.
  • 82 selected backend/MCP unit tests passed, with no failures or skips.
  • Full Java SDK suite: 302 passed, no failures or skips; includes all 10 new BulkCreateFluentAPITest cases.
  • Full Python SDK suite: 472 passed, 1 existing skip (Module structure test not relevant for SDK); includes all 10 new test_bulk_create.py cases. Scoped Ruff and basedpyright checks passed.
  • These runs exercised a2ac1594dbef2f5460a85b83875967f84de25536.

Review follow-up at c2824d23cc08c5add4e5b02c8e5bc86c819d7850 (Python SDK and ADR only):

  • PYTHONPATH=ingestion/src python -m pytest -q ingestion/tests/unit/sdk --cov=metadata.sdk.entities.base --cov-report=term: 486 passed, 1 existing skip, including 24 bulk-create cases. The 14 added cases reproduced failures before the fix.
  • Covers 503/504 without replay under both policies and both facades, empty/null responses, and swallowed client errors. The changed _create_many helper has 7/7 lines covered; the complete base.py remains 89.1% (279/313).
  • Scoped Ruff lint/format, basedpyright src/metadata/sdk/entities/base.py, decision-record validation, git diff --check, and commit hooks passed. An additional type-check attempt including the test module reported 34 errors in its unchanged fixtures/assertions; tests are outside the configured include = ["src"] type-check scope.
  • Java/backend suites were not rerun for this follow-up; their results below remain attributed to a2ac1594dbef2f5460a85b83875967f84de25536.

JaCoCo from the targeted MySQL run: BulkEntityCreator 91/91 lines (100%) and 34/36 branches (94.4%); its candidate record 2/2 lines. The unexercised branches guard against treating our own stored ID as a concurrent winner. Whole-class line coverage is EntityRepository 46.5% (2727/5870), TestCaseResource 14.9% (58/388), and GlossaryTermResource 14.4% (40/277). The existing large classes remain below the repository's 90% whole-class target.

SDK coverage: BulkCreateRequest 7/7 lines, both new Java createMany methods 3/3 lines, and all new Python bulk-create methods 100%. Whole existing Python files: base.py 89%, glossaryterms.py 95%, testcases.py 100%; whole Java facade classes: GlossaryTerms 69.2%, TestCases 38.9%. These existing whole-class gaps are not presented as meeting the 90% target.

Backend integration tests

  • Added real HTTP/database regressions in TestCaseResourceIT, GlossaryTermResourceIT, and EntityBulkCreateRecoveryIT.

40 passed on PostgreSQL + OpenSearch and 40 passed on MySQL + Elasticsearch, with no failures or skips, on reviewed commit a2ac1594dbef2f5460a85b83875967f84de25536. Both Maven builds succeeded.

Integration class (selected methods) PostgreSQL MySQL
TestCaseResourceIT 12 12
GlossaryTermResourceIT 12 12
EntityBulkCreateRecoveryIT 12 12
TableResourceIT 3 3
AlertStateIT 1 1
Total passed 40 40

Before adding the fluent methods, the 10 Python cases failed with missing create_many, and the Java test failed compilation on the missing bulk request type. After implementation and JUnit dependency alignment, both full SDK suites passed.

The initial detailed-response RED run failed both new cases against the old array/validation behavior. The first-valid preparation regression also failed before its fix (zero created instead of one). An earlier 35-case run had one glossary fixture-name collision between parameterized cases; the fixture now uses distinct names. The earlier preparation-race regression also demonstrated the original HTTP 400 failure. Final results below supersede those development runs.

Targeted verification commands
IT_TESTS='TestCaseResourceIT#createMany*+test_createMany,GlossaryTermResourceIT#createMany*+test_glossaryTermNameUniquenessWithinGlossary,EntityBulkCreateRecoveryIT,TableResourceIT#test_bulkCreateOrUpdate_idempotent+test_bulkUpdate_existingEntities+test_bulkCreateOrUpdate_mixedCreateAndUpdate,AlertStateIT#bulkCreateSchedulesEveryAlert'

mvn -B verify -pl :openmetadata-integration-tests -am \
  -Dspring-boot.repackage.skip=true -Ppostgres-opensearch \
  -DintegrationTests.lane=parallel "-Dit.test=$IT_TESTS" \
  -Dfailsafe.failIfNoSpecifiedTests=false \
  -Dtest=BulkCreateFluentAPITest,GlossaryRelationsFluentAPITest,TestCaseMockTest,IntegrityConstraintMapperTest,TestCaseRepositoryTest,CreateEntityToolTest,CreateTestCaseToolTest,PatchEntityToolTest \
  -Dsurefire.failIfNoSpecifiedTests=false -Dspotless.skip=true

# Reuse main classes compiled by the preceding run.
mvn -B verify -pl :openmetadata-integration-tests -am \
  -Dmaven.main.skip=true -Dspring-boot.repackage.skip=true \
  -Pmysql-elasticsearch -DintegrationTests.lane=parallel "-Dit.test=$IT_TESTS" \
  -Dfailsafe.failIfNoSpecifiedTests=false -Dtest=NoUnitTestsInThisRun \
  -Dsurefire.failIfNoSpecifiedTests=false -Dspotless.skip=true

The MySQL run also uses JaCoCo 0.8.13 for BulkEntityCreator, EntityRepository, TestCaseResource, and GlossaryTermResource. The full SDK run uses JaCoCo 0.8.13 for the fluent package.

mvn -B test -pl :openmetadata-sdk -am -Dmaven.main.skip=true \
  '-Dtest=**/sdk/**/*Test' -Dsurefire.failIfNoSpecifiedTests=false -Dspotless.skip=true

# With the Python venv active:
PYTHONPATH=ingestion/src pytest -q ingestion/tests/unit/sdk
cd ingestion
basedpyright src/metadata/sdk/entities/base.py src/metadata/sdk/entities/testcases.py src/metadata/sdk/entities/glossaryterms.py

mvn spotless:apply, mvn spotless:check, scoped Ruff/basedpyright, git diff --check, the impact-map branch check, and commit hooks passed on the SDK follow-up. Earlier make generate, canonical TypeScript generation, Python result-model validation, and generated OpenAPI checks passed on the unchanged schemas. Generated TypeScript is excluded from the repository's handwritten UI lint/format checks.

PENDING. The targeted results above do not replace the full affected-suite selection. make java_affected selects the full service unit suite, full merge-queue integration suites, cache/PostgreSQL lanes, and 28 PostgreSQL/OpenSearch search-it classes. No generated passing affected-suite report is claimed.

Full CI runs on a2ac1594dbef2f5460a85b83875967f84de25536:

These four runs are still in progress as of this update. The 28 selected search-it classes have not been run locally, and the complete affected-suite report and whole-class coverage target remain outstanding. UI and nightly scale suites were not run because this change has no UI behavior or scale-fixture changes.

Ingestion integration tests

Python SDK changes are covered by the full SDK unit suite, with the real REST serializer and a mocked HTTP transport. No connector changes. A live-server Python SDK integration run was not performed; Java API integration tests exercise the matching endpoint contract on both databases.

Playwright (UI) tests

Not applicable — generated type only; no UI behavior changed.

Not applicable — no UI behavior changes.

Manual testing performed

No interactive manual testing. Integration tests start real database/search containers and verify HTTP responses, database state, relationships, rollback, and search visibility.

UI screen recording:

Not applicable — backend/API change with no UI behavior changes.

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes 35909: report bulk-create outcomes and recover conflicts.
  • My PR is linked to a GitHub issue via Fixes #35909 above.
  • Comments explain rollback/progress and the committed-ID guard.
  • JSON Schema: generated models updated; no migration needed for a response-only schema.
  • UI recording: not applicable, as explained above.
  • Added tests and listed their results above.
  • Self-review: fresh-context review of c2824d23cc08c5add4e5b02c8e5bc86c819d7850 confirmed both requested Python/ADR fixes. One additional Java SDK retry finding remains, described below.
  • Decision check: the response contract, conflict policy, compatibility boundary, and concurrency behavior are recorded in the ADR.
  • Bug fix: added regressions for the original batch failure and concurrent creation.

Remaining validation: finish the full affected-test selection and record its generated report; assess the whole-class coverage gap. Targeted tests and review do not replace those gates.

Additional merge risk found during this review (not changed in this Python/ADR follow-up): the Java detailed bulk-create calls in TestCaseService and GlossaryTermService use default OkHttp replay behavior. A local-server reproduction through TestCases.createMany(...).execute() returned 503 with Retry-After: 0, followed by a conflict response: the SDK sent 2 requests and returned created=0, skipped=1. These Java calls need replay prevention and a regression test before merge. This is separate from the two requested review threads; no remaining-risk or full-PR approval is implied by addressing them.

RetriggerView in GreptileConfidence Score: 5/5

The reviewed changes appear safe to merge; no new blocking code issues were found.

Summary

Bulk creation now reports ordered outcomes, preserves existing entities, and recovers concurrent conflicts without dropping unrelated new inputs.

  • Bulk creation keeps valid new entities when names conflict.
  • Java and Python callers can request detailed bulk-create results.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Bulk create request] --> B[Map inputs and check existing names]
  B --> C[Prepare new candidates]
  C --> D[Create transaction chunk]
  D -->|Success| E[Complete creation hooks]
  D -->|Duplicate key| F[Confirm another stored ID]
  F -->|Conflict found| G[Remove conflicting inputs]
  G --> D
  F -->|No matching conflict| H[Propagate error]
  E --> I[Return legacy array or detailed outcomes]
Loading

Reviews (5) · Last reviewed commit: "Prevent automatic retries of Python bulk..." · Reviewed by Greptile

Centralize createMany recovery in EntityRepository. Skip existing and repeated FQNs without overwriting or restoring entities, and return only newly created entities in request order.

After a concurrent duplicate rolls back a transaction, warn and retry only the remaining entries in that 100-row chunk. Finish creation hooks for each committed chunk and propagate unrelated failures. Preserve single-create and bulk-upsert behavior.

Add real database regressions for test cases and glossary terms, concurrent conflicts, later chunks, soft deletes, and error propagation. Document the API contract and decision. Verified 23 unit tests and 21 integration tests on each of PostgreSQL and MySQL.
Recheck the exact FQN when preparation rejects a bulk-create entry. Skip a concurrently created entity while preserving validation errors for different FQNs and validating all remaining entries before writes.

Add a deterministic glossary preparation race regression and a different-parent name validation regression. Extend the decision record to cover preparation recovery.
@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Oct 21, 2026
@harshach
harshach marked this pull request as ready for review October 21, 2026 17:45
@harshach
harshach requested a review from a team as a code owner October 21, 2026 17:45
@harshach
harshach requested a review from TeddyCr October 21, 2026 17:45
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

Share the existing-FQN lookup across filtering and conflict recovery, prepare candidates through a per-entity predicate, and use the existing partition utility. Remove the temporary mutable singleton list and manual chunk indexing while preserving validation, retry bounds, and committed-ID protection.
Return created, skipped, and failed counts with ordered reasons and stored IDs. Keep skip as the default and allow fail to mark conflicts as item failures while creating other valid inputs. Move shared preparation and duplicate recovery into BulkEntityCreator, regenerate the public models, and update both REST endpoints, SDK services, and database regressions.
@harshach harshach changed the title Fixes 35909: recover bulk creates from duplicate FQNs Fixes 35909: report bulk-create outcomes and recover conflicts Oct 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 79%
79.01% (128161/162207) 65.36% (44943/68761) 68.39% (25688/37560)

@harshach
harshach requested a review from a team as a code owner October 21, 2026 19:06
Comment on lines +193 to +204
@classmethod
def _create_many(cls, requests: Sequence[TCreate], on_conflict: ConflictPolicy) -> BulkCreateResult:
client = cls._get_client()
endpoint = cls._get_endpoint_path(client)
# REST drops empty json lists; data preserves an empty batch as [].
payload = json.dumps(
[request.model_dump(mode="json", by_alias=True, exclude_none=True) for request in requests]
)
response = cls._get_rest_client(client).post(
f"{endpoint}/createMany?onConflict={on_conflict.value}", data=payload
)
return BulkCreateResult.model_validate(response)

@gitar-bot gitar-bot Bot Oct 21, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Python create_many retries non-idempotent POST on 503/504

_create_many calls rest_client.post(...) with the client's default retry budget (retry=3, retry_codes=[503, 504], see ingestion/src/metadata/ingestion/ometa/client.py:186-189,386). Suppose a proxy returns 504 after the server has already committed some chunks. The SDK then sends the same batch again, and the retry reports every entity the first attempt created as skipped/ALREADY_EXISTS (or failed under fail) with no IDs. The caller gets a result that says nothing was created. Separately, if the retries run out or the catch-all in _one_request swallows an error, _request returns None and BulkCreateResult.model_validate(None) raises a pydantic ValidationError instead of APIError. The fix is to pass retries=0 for this POST and to raise a clear error when the response is empty.

Disable automatic retries for the non-idempotent bulk POST and fail clearly on an empty response:

response = cls._get_rest_client(client).post(
    f"{endpoint}/createMany?onConflict={on_conflict.value}", data=payload, retries=0
)
if response is None:
    raise RuntimeError(f"No response from {endpoint}/createMany")
return BulkCreateResult.model_validate(response)

✅ Fixed by @harshach in c2824d2

Reply for a change | Was this helpful? React with 👍 / 👎

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c2824d2. The shared Python bulk-create helper now sends the POST with retries=0. A missing response raises a clear RuntimeError naming the endpoint and noting that creation may have completed, rather than a Pydantic validation error. HTTP errors still propagate.

Added 14 regression cases through the real REST client for both facades: 503/504 are not replayed under either conflict policy, and empty/null responses or swallowed client errors fail clearly. All 14 failed before the fix and now pass. Full Python SDK suite: 486 passed, 1 existing skip.

Comment on lines +8 to +11
/** Bulk creation with per-input outcomes. Defaults to skipping conflicts. */
public final class BulkCreateRequest {
private final Function<BulkCreateConflictPolicy, BulkCreateResult> createFunction;
private BulkCreateConflictPolicy onConflict = BulkCreateConflictPolicy.SKIP;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Quality: Fluent/Python SDK default SKIP + forced detailed mode not in ADR

This commit adds a new default for SDK callers. TestCases.createMany(...) and GlossaryTerms.createMany(...) (Java fluent) and create_many() (Python) always send onConflict=skip unless the caller chooses otherwise. Because a policy is always sent, they always get the detailed response and need a server that supports onConflict (READMEs: "Both builders default to SKIP and explicitly request detailed outcomes… These methods require a server that supports onConflict"). The ADR 2026-10-21-bulk-test-case-creation-skips-existing-fqns.md (lines 79-83) only records the Java service-level overloads. It says nothing about the fluent/Python facades, their SKIP default, or that they don't work with older servers. The record should say: "The Java fluent and Python SDK bulk-create facades default to onConflict=skip, always request the detailed BulkCreateResult, and are unsupported against servers without onConflict."

Was this helpful? React with 👍 / 👎

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c2824d2: appended Amendment v5. It explicitly records that both Java fluent and Python bulk-create facades default to onConflict=skip, always request detailed BulkCreateResult outcomes, and are unsupported against servers without onConflict.

The amendment distinguishes the existing Java service overload's array response and records the Python REST retry policy. No Java runtime change was needed.

@gitar-bot

gitar-bot Bot commented Oct 21, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 2 closed / 2 findings

🔴 High risk · Changing shared EntityRepository bulk-create conflict recovery affects persisted entity writes; faulty duplicate detection could skip valid entities or misreport creations.

Adds per-input outcome reporting and conflict recovery to bulk create so unrelated new entities are no longer rolled back when one FQN already exists, with explicit skip/fail policies exposed in the Python and Java SDKs. Two issues were resolved: Python create_many no longer retries non-idempotent POSTs on 503/504, and the SDK default of SKIP with forced detailed mode is now documented in the ADR. No open findings remain.

✅ 2 closed
✅ Bug: Python create_many retries non-idempotent POST on 503/504

📄 ingestion/src/metadata/sdk/entities/base.py:193-204
_create_many calls rest_client.post(...) with the client's default retry budget (retry=3, retry_codes=[503, 504], see ingestion/src/metadata/ingestion/ometa/client.py:186-189,386). Suppose a proxy returns 504 after the server has already committed some chunks. The SDK then sends the same batch again, and the retry reports every entity the first attempt created as skipped/ALREADY_EXISTS (or failed under fail) with no IDs. The caller gets a result that says nothing was created. Separately, if the retries run out or the catch-all in _one_request swallows an error, _request returns None and BulkCreateResult.model_validate(None) raises a pydantic ValidationError instead of APIError. The fix is to pass retries=0 for this POST and to raise a clear error when the response is empty.

✅ Quality: Fluent/Python SDK default SKIP + forced detailed mode not in ADR

📄 openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/request/BulkCreateRequest.java:8-11 📄 ingestion/src/metadata/sdk/entities/testcases.py:21-26 📄 ingestion/src/metadata/sdk/entities/glossaryterms.py:34-39 📄 docs/decisions/2026-10-21-bulk-test-case-creation-skips-existing-fqns.md:79-83
This commit adds a new default for SDK callers. TestCases.createMany(...) and GlossaryTerms.createMany(...) (Java fluent) and create_many() (Python) always send onConflict=skip unless the caller chooses otherwise. Because a policy is always sent, they always get the detailed response and need a server that supports onConflict (READMEs: "Both builders default to SKIP and explicitly request detailed outcomes… These methods require a server that supports onConflict"). The ADR 2026-10-21-bulk-test-case-creation-skips-existing-fqns.md (lines 79-83) only records the Java service-level overloads. It says nothing about the fluent/Python facades, their SKIP default, or that they don't work with older servers. The record should say: "The Java fluent and Python SDK bulk-create facades default to onConflict=skip, always request the detailed BulkCreateResult, and are unsupported against servers without onConflict."

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@harshach
harshach added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 0b68d6b Oct 22, 2026
93 of 98 checks passed
@harshach
harshach deleted the agent/can-we-annalyze-this-error-a branch October 22, 2026 01:27
sonika-shah added a commit that referenced this pull request Oct 26, 2026
createMany mapped and stored the terms without any authorization check, so a
caller without CREATE could bulk-create terms in any glossary. Each term is now
checked for limits and CREATE before anything is written, as in main (#35910).

This branch was successfully deployed

1 active deployment
test — c2824d23 Deployed Oct 21, 2026 by harshach via RDF Playwright execution #36639
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bulk entity creation fails new entries when an FQN already exists

2 participants