Skip to content

feat(ui): standardize schema-driven credential file upload with drag-… - #33000

Merged
sweta1308 merged 22 commits into
mainfrom
feat/standardize-credential-file-upload
Sep 15, 2026
Merged

sweta1308 merged 22 commits into
mainfrom
feat/standardize-credential-file-upload

Conversation

@sweta1308

@sweta1308 sweta1308 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Describe your changes:

Fixes #31677

Replaced the per-surface credential upload affordances with one schema-driven component, because the same JSON Schema annotation was rendering three different ways depending on which form stack drew it — and two of the three could not accept a file at all. A format: password string marked uiFieldType: file | fileOrInput now renders the same drop zone, file picker, filename chip and validation in every connection form, in both the legacy RJSF stack and FormBuilderV1.

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

High-level design:

Key files.

  • credential-file-input.tsx (new) — drop zone and picker share one handler, so the two input paths cannot diverge. Filename chip, remove-to-clear, optional paste box for fileOrInput. Four rejection kinds: unacceptedType, sizeLimit, binary, unreadable; 1 MiB default cap.
  • CredentialFileField.utils.ts + enums/CredentialFileField.enum.ts (new) — the schema→props contract both stacks read, so they cannot drift.
  • PasswordWidget.tsx, CorePasswordWidget.tsx — rewired to it.
  • secure_tempfile.py (new) — one helper for writing secrets to disk, replacing copy-pasted mkstemp blocks; 2 of 5 call sites migrated here.
  • Deleted: FileUploadWidget.tsx (+test), password-widget.less, File.enum.ts, PasswordWidget.enum.ts. These deletions are the standardisation.

Schema contract fixes (the 5 source edits): dropped .der/.p12 from validateSSLClientConfig — both are binary and are rejected at decode, so offering them was a lie; added the missing accept lists to Snowflake privateKey and SAP SuccessFactors; annotated three sslCertValues fields; added .json to VertexAI.

Backward compatibility. No schema shape changes — only accept and uiFieldType annotations, which are UI-only hints. Secret masking is unchanged: the API returns *********, the chip stands for the stored secret, and removing it clears the field, matching the allowClear semantics #32945 gave plain passwords.

Rollout note. Collate carries the same annotations in a companion PR and must merge after this one — Collate CI resolves the submodule with --remote against OSS main, so its annotations are inert until this lands.

Tests:

Use cases covered

  • Attaching a credential with the file picker and by dragging onto the zone produce identical state
  • A non-UTF-8 (binary) file is rejected with a message and commits no value
  • Files over 1 MiB, wrong extensions, and unreadable files are each rejected distinctly
  • An already-saved secret shows as a stored-credential chip; removing it clears it
  • fileOrInput fields still accept pasted content, but never alongside a chip
  • A 255-character filename truncates with an ellipsis without overflowing the field

Unit tests

  • I added unit tests for the new/changed logic.
  • credential-file-input.test.tsx (new) — 29 tests
  • CredentialFileField.utils.test.ts (new) — including a contract suite that walks every generated connection schema and fails if an annotated field is not a format: password string, if a path-shaped field is annotated, if accept contains a binary extension, or if accept appears without the marker. This is what stops the annotation set rotting as connectors are added.
  • test_secure_tempfile.py (new, 222 lines)
  • Updated: PasswordWidget.test.tsx, FormBuilderV1Widgets.test.tsx, ConnectionConfigForm.schema-render.test.tsx, test_nats.py

Backend integration tests

  • Not applicable (no backend API changes).

Ingestion integration tests

  • Not applicable — the ingestion change is an internal helper refactor, covered by the new unit tests above.

Playwright (UI) tests

  • I added Playwright E2E tests.
  • playwright/e2e/Flow/CredentialFileUpload.spec.ts (new) — picker, drag-and-drop, non-UTF-8 rejection
  • playwright/e2e/Flow/ServiceForm.spec.ts — the assertion moved from the textarea value to the credential-file-name chip; the payload assertion is unchanged, so it still pins the submitted value

Manual testing performed

  1. Ran the full suites: OSS UI jest 16,943 passed; core-components vitest 123 passed; ingestion pytest 344 passed.
  2. Ran the new E2E against a live local stack (server on :8585, UI dev server) — 3/3 credential specs pass.
  3. Every failing test across all suites was reproduced on a pristine worktree of the exact main commit this branch merged, confirming none is caused by this PR. ServiceForm fails 7 on baseline main and 6 here — this branch fails one fewer.
  4. Verified the 255-character-filename chip truncates cleanly and stays inside the field bounds.

UI screen recording / screenshots:

Screen-Recording.47.mp4

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: no migration needed — the edits are UI-only accept and uiFieldType annotations; no stored shape changes.
  • For UI changes: I attached a screen recording and/or screenshots above.
  • I have added tests (unit / integration / Playwright as applicable).

…and-drop

Connector credential fields marked `uiFieldType: file | fileOrInput` in their
JSON Schema now render one shared picker with drag-and-drop, instead of two
divergent implementations that each read the file themselves.

Component library
- Add `CredentialFileInput`, composed from the existing `FileUploadDropZone`
  and `PasswordInput`. It reads the file in the browser and submits its text
  as the field value, so the secret keeps the masking and secrets-manager
  handling every other password field gets — nothing is uploaded anywhere.
- Decode strictly with `TextDecoder('utf-8', { fatal: true })` plus a NUL scan.
  `File.text()` decodes leniently, so a DER or PKCS#12 payload used to come
  back as replacement characters, get saved as the secret, and only fail much
  later at connection time.
- Reject wrong-extension, oversized, binary and unreadable files with a
  `role="alert"` message, leaving the stored value untouched.
- Read through `FileReader` rather than `Blob.arrayBuffer()`: jsdom implements
  the former and not the latter, so the path stays reachable from unit tests.
- Polyfill `DataTransfer` in the vitest setup. jsdom ships none, and
  `filesToFileList` in `file-upload.tsx` builds every `FileList` through it, so
  no drop-zone consumer was testable before.

Both form stacks
- `CorePasswordWidget` (FormBuilderV1) and `PasswordWidget` (legacy RJSF) both
  delegate to the new component through one shared schema-to-props mapping.
- `file` and `fileOrInput` now behave differently. `CorePasswordWidget` treated
  them identically — both rendered an editable textarea — and the second
  branch's `isInputTypeFileOrInput` conditionals were unreachable dead code.
- Blank the readback mask in credential-file fields. The API returns `*********`
  in place of the stored secret; rendering it invited the user to edit a value
  that is not the credential. `formData` is left untouched, so an unmodified
  field round-trips the mask and the backend keeps the existing secret.
- Delete `FileUploadWidget` and its antd `Upload` + `Radio.Group` wrapper, which
  duplicated the file-reading behaviour and displayed the secret content as the
  file name.

Known gap: the schemas still advertise `.der` and `.p12` in `accept`, which the
new decoder rejects. Removing them is the schema half of this work and lands
next; see #31677.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 9, 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.

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 72%
72.42% (99037/136751) 56.95% (58444/102617) 58.22% (19519/33521)

…ations

Both failures were mine, and both were tests pinned to behaviour this branch
deliberately changed.

**NATS unit tests.** `test_failed_temp_certificate_write_removes_file` and
`test_zero_byte_certificate_write_reports_cleanup_failure` patched
`nats.connection.tempfile.mkstemp` and `nats.connection.os.write`, neither of
which the module imports any more — the write moved to
`metadata.utils.secure_tempfile`. The zero-byte case tested a failure mode that
no longer exists: the hand-rolled loop raised "Could not write" on a short
write, where `handle.write` now handles partial writes itself. Replaced with the
contract that still belongs to this module — a path joins the cleanup list only
once it exists — plus a positive test that the certificate is written and
tracked. Removing the partial file is the helper's job and is covered in
tests/unit/utils/test_secure_tempfile.py.

**ServiceForm Playwright spec.** "Verify SSL cert upload with long filename"
asserted the uploaded content appeared in the paste box; an attached credential
is now represented by its chip instead, so that locator no longer resolves. The
assertion moves to the chip, which is where the long file name now lands and so
is the better subject for a test about name overflow. The valuable half — that
the submitted `caCertificate` equals the file content — is untouched, and it is
the end-to-end proof that an uploaded file and pasted text submit the same
value.

Verified locally this time: 91 NATS tests pass, 137 of 138 ssl_manager tests
pass (the one failure is a missing `cassandra` driver in this environment), and
`yarn lint:playwright` exits 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review (106 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 757 → 757
- 1 added, 1 removed, 18 changed spec-list

New source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableAliases/TableAliases.component.tsx

Removed source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/FileUploadWidget.tsx

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/playwright/constant/config.ts
  openmetadata-ui/src/main/resources/ui/playwright/constant/sidebar.ts
  openmetadata-ui/src/main/resources/ui/playwright/e2e/fixtures/pages.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/fixtures/base.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/tag/ClassificationClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/team/TeamClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/admin.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
  … and 8 more

What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit:

python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit  # or a separate commit

sweta1308 and others added 4 commits September 10, 2026 11:19
An edit form showed "Saved credential" as a file name, under a file icon, with
a remove button — which reads as "you attached a file called Saved credential".
Nothing was attached: the API returns a mask, so there is no name and no size
to show.

A credential the form cannot read now gets its own row — a key icon, "Saved
credential", and a line saying it is hidden for security and must be removed
before a new one can be uploaded or pasted. The file chip is reserved for a file
actually attached in this session, which is the only case with a real name.

The same row covers a value restored into an upload-only field, where there is
likewise no provenance to show.

Reported from a real edit form; the file-chip case is unchanged, so the
ServiceForm E2E assertion on the long file name still holds.
…ential-file-upload

# Conflicts:
#	openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.test.tsx
#	openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.tsx
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 757 → 757
- 0 added, 0 removed, 13 changed spec-list

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/playwright/constant/config.ts
  openmetadata-ui/src/main/resources/ui/playwright/constant/service.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/service/DashboardServiceClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/service/DatabaseServiceClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/service/MessagingServiceClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/fixtures/base.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/service.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/serviceIngestion.ts
  … and 3 more

What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit:

python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit  # or a separate commit

sweta1308 and others added 4 commits September 10, 2026 18:00
The field took part in RJSF validation but nothing proved it: no test submitted
a required credential field empty, so a silent failure at save would have gone
unnoticed.

Covers the whole chain — the widget forwards `rawErrors` as `isInvalid` plus
the hint, the component renders that hint, and a file rejection replaces the
form error while it is showing rather than stacking two messages.

Also records what an audit of the issue's contract turned up: the CSV export of
a service carries no connection config at all (columns are name, displayName,
description, owner, tags, glossaryTerms, tiers, certification, domains,
extension), so an uploaded credential is structurally absent from it rather
than merely masked. Verified against a running instance with a service whose
private key was a known sentinel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 758 → 758
- 0 added, 0 removed, 6 changed spec-list

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/fixtures/base.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/tier.ts

What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit:

python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit  # or a separate commit

sweta1308 and others added 5 commits September 11, 2026 13:00
`src/enums/*.enum.ts` is where this repo puts enums (30 files), and it is
where the two files this PR deletes -- File.enum.ts and
PasswordWidget.enum.ts -- lived. Folding their replacement enum into a
utils module quietly broke the convention the rest of the PR is cleaning
up, and left CredentialFileField.utils.ts holding something other than
the pure functions DEVELOPER_HANDBOOK asks a *.utils.ts to hold. None of
the four sibling src/utils/*.utils.ts files export an enum.

No behaviour change: the enum moves, the three call sites import it from
its new home.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tants, utils

The component file held its own constants, types and file-reading helpers
alongside the component. Split them into siblings so the .tsx defines only
components, following the `<name>.types.ts` pattern the package already uses
for form-field, tree-select, page-header and page-layout, each with its own
barrel line. The public API is unchanged, so no consumer import moves.

This surfaced a bug in the component's own test. It imported
DEFAULT_CREDENTIAL_FILE_MAX_SIZE from './credential-file-input', which after
the split no longer exports it -- so the constant was undefined, the fake file
size became NaN, and "rejects a file over the size limit" was passing an
oversized file straight through instead of rejecting it. `tsc --noEmit` stayed
green because the test file is not in its program; only running the tests
caught it. The import now points at the constants module.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Splitting the component into siblings and barrelling each with `export *`
widened this package's public surface from 6 exports to 14, publishing
internals that were never part of the contract: the file-reading helpers, and
`CREDENTIAL_ROW_CLASS`, which is a raw Tailwind class string.

`DEFAULT_LABELS` and `DEFAULT_VALIDATION_MESSAGES` are also names a sibling
(cover-image-upload-field) already uses locally, so putting them in the barrel
invites a collision the moment either side exports one.

Export only what was public before the split: the component, the types, and the
size cap. Nothing outside the component folder used the rest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 3 resolved / 4 findings

Standardizes credential file upload across form stacks with a unified drag-and-drop component, file picker, and validation. Resolves the fileOrInput dual-affordance issue, adds clear-to-remove semantics for saved credentials, and fixes an fd leak in secure tempfile creation. Consider correcting the Portuguese strings in the Persian locale file (pr-pr.json) — several newly added keys carry Portuguese text instead of Persian translations.

💡 Quality: pr-pr.json (Persian) locale contains Portuguese strings

📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3159 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3476 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3478-3479 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3481-3483 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3485-3486 📄 openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3488

Several newly added keys in the Persian locale file pr-pr.json carry Portuguese text instead of Persian: "workspace": "Espaço de trabalho", "custom-properties-settings-description": "Criar e gerir propriedades personalizadas com Personas", "custom-property-description-help": "Descreva a sua propriedade personalizada...", custom-property-display-name-help, custom-property-entity-reference-config-help, custom-property-enum-config-help, custom-property-format-config-help, custom-property-multi-select-help, custom-property-name-help, and custom-property-type-help. Persian users will see untranslated Portuguese for these labels. Replace these values with the correct Persian translations.

✅ 3 resolved
Quality: fileOrInput shows chip and prefilled textarea at once

📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/application/credential-file-input/credential-file-input.tsx:291-293 📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/application/credential-file-input/credential-file-input.tsx:345-359
In CredentialFileInput, selectedFile (the file chip) and the manual-input PasswordInput render independently: when allowManualInput is true and a file is uploaded, fileMeta is set (showing the chip) while the textarea is simultaneously shown with value={value ?? ''} — the uploaded file's content. The user sees the same credential twice (chip + prefilled masked textarea separated by an 'or' divider), and silently editing the textarea drops the chip. Consider hiding the manual-input textarea while a file chip is displayed, or hiding the chip in manual mode.

Edge Case: No affordance to clear a saved credential in file mode

📄 openmetadata-ui-core-components/src/main/resources/ui/src/components/application/credential-file-input/credential-file-input.tsx:280-293 📄 openmetadata-ui/src/main/resources/ui/src/components/common/FormBuilderV1/widgets/CorePasswordWidget.tsx:72-86 📄 openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/PasswordWidget.tsx:36-50
For uiFieldType: file with an existing server-stored secret, both widgets blank the readback mask (value becomes undefined), so CredentialFileInput renders the empty drop zone with no chip and no remove button. The user can upload a replacement but has no way to clear the credential to empty, since formData is intentionally left untouched. If clearing a stored credential is a supported action, expose a remove control for the masked/saved state; otherwise this is acceptable but worth confirming.

Edge Case: fchmod failure leaks the open file descriptor

📄 ingestion/src/metadata/utils/secure_tempfile.py:61-72
In write_secret_temp_file, os.fchmod runs before the descriptor is wrapped by os.fdopen. If fchmod raises, control jumps to the except block which removes the path and re-raises, but the raw file_descriptor from mkstemp is never closed, leaking it. The original NATS implementation closed the fd in a finally block. Wrap the fdopen earlier or close the descriptor in the error path (e.g. os.close(file_descriptor) if the fdopen was never reached). fchmod rarely fails on a freshly created temp file, so impact is low.

🤖 Prompt for agents
Code Review: Standardizes credential file upload across form stacks with a unified drag-and-drop component, file picker, and validation. Resolves the fileOrInput dual-affordance issue, adds clear-to-remove semantics for saved credentials, and fixes an fd leak in secure tempfile creation. Consider correcting the Portuguese strings in the Persian locale file (`pr-pr.json`) — several newly added keys carry Portuguese text instead of Persian translations.

1. 💡 Quality: pr-pr.json (Persian) locale contains Portuguese strings
   Files: openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3159, openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3476, openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3478-3479, openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3481-3483, openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3485-3486, openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json:3488

   Several newly added keys in the Persian locale file `pr-pr.json` carry Portuguese text instead of Persian: `"workspace": "Espaço de trabalho"`, `"custom-properties-settings-description": "Criar e gerir propriedades personalizadas com Personas"`, `"custom-property-description-help": "Descreva a sua propriedade personalizada..."`, `custom-property-display-name-help`, `custom-property-entity-reference-config-help`, `custom-property-enum-config-help`, `custom-property-format-config-help`, `custom-property-multi-select-help`, `custom-property-name-help`, and `custom-property-type-help`. Persian users will see untranslated Portuguese for these labels. Replace these values with the correct Persian translations.

Review coverage

Rules No rules evaluated

Functional validation Not enabled · Set up

Auto-approval Not enabled · Set up

Options

Display: compact → Counting what did not apply, without listing it.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add UI support for uploading file-based connector credentials

4 participants