You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There is no SDK primitive to go from a raw Connector Namespace trigger callback (an HTTP body / Stream / string) to a typed TriggerCallbackPayload<T>. Consumers must hand-write ~100+ lines of boilerplate (bounded body read, UTF-8 decode, JsonDocument parse, manual body-field type discrimination, base64 decode for binary triggers, and finally JsonSerializer.Deserialize<...>), and two subtle correctness traps are easy to hit.
This was surfaced by a real customer report on the Connectors for Functions channel (OneDrive for Business "When a file is created" trigger) and reproduced end-to-end against a live Connector Namespace.
Background / Evidence
Connector Namespace delivers two distinct trigger callback shapes for the same connector:
Reproduced end-to-end (real files uploaded to OneDrive, real Connector Gateway callbacks observed in App Insights):
Binary .txt trigger → {"body":"<base64>"} → deserializing into OneDriveForBusinessOnNewFilesTriggerPayloadthrowsJsonException: Expected StartObject or Null for TriggerCallbackBody<BlobMetadata>, got String. — a cryptic message that does not tell the developer they bound a binary-content trigger to a metadata payload type.
.json file via binary trigger → {"body":{...file content...}} → deserializes to a single all-nullBlobMetadata (silent data loss).
A correctly-shaped metadata envelope whose item fields are camelCase also deserializes to all-null, because the deserialization path does not set PropertyNameCaseInsensitive.
Gaps
No single entry point from a trigger callback body to the typed payload. Every consumer reimplements the same boilerplate (see the OneDriveTriggerCallback sample in Connectors-NET-Samples).
Case sensitivity trap. Deserializing without PropertyNameCaseInsensitive = true yields silently all-null items when the wire uses camelCase. ConnectorJsonSerializer.Options already sets this, but there is nothing that steers trigger-payload consumers to it.
Unactionable error for binary triggers.TriggerCallbackBodyConverter<T> throws "Expected StartObject or Null... got String." when a binary-content (string) body is bound to a metadata payload type. The message should tell the developer this is a binary-content trigger and how to read it.
Proposed change (this repo)
Add a framework-agnostic SDK helper (operates on string/Stream, no Functions Worker dependency) — e.g. ConnectorTriggerPayload:
ConnectorTriggerPayload.DefaultSerializerOptions — case-insensitive options reused for all trigger payloads (closes gap Adding Microsoft SECURITY.MD #2).
Read<TPayload>(string json) / ReadAsync<TPayload>(Stream body, CancellationToken) — read a metadata trigger callback into its typed TriggerCallbackPayload<T> subclass with the correct options (closes gap This repo is missing a license file #1).
TryReadBinaryContent(string json, out byte[] content) / ReadBinaryContentAsync(Stream, CancellationToken) — decode a binary-content trigger callback ({"body":"<base64>"}) into bytes (closes gap This repo is missing a license file #1 for the binary variant).
Improve TriggerCallbackBodyConverter<T> to throw an actionable error when it encounters a string body (closes gap This repo is missing important files #3): explain that the callback is a binary-content trigger (e.g. OnNewFileV2) and direct the caller to TryReadBinaryContent.
This reduces a OneDrive trigger handler from ~100 lines of plumbing to roughly:
The [ConnectorTrigger] POCO binding in the Functions connector extension should consume these SDK primitives (read OperationName, discriminate binary vs metadata, use case-insensitive options). That lives in the Functions connector extension repo, not here.
An LSP code action to generate the minimal handler body from [ConnectorTriggerMetadata].
Acceptance criteria
New helper with string + Stream overloads, case-insensitive by default.
Binary-content read helper for {"body":"<base64>"}.
TriggerCallbackBodyConverter<T> emits an actionable message for string bodies.
Unit tests covering: metadata (PascalCase + camelCase), binary base64, empty/null bodies, and the actionable error.
Summary
There is no SDK primitive to go from a raw Connector Namespace trigger callback (an HTTP body /
Stream/string) to a typedTriggerCallbackPayload<T>. Consumers must hand-write ~100+ lines of boilerplate (bounded body read, UTF-8 decode,JsonDocumentparse, manualbody-field type discrimination, base64 decode for binary triggers, and finallyJsonSerializer.Deserialize<...>), and two subtle correctness traps are easy to hit.This was surfaced by a real customer report on the Connectors for Functions channel (OneDrive for Business "When a file is created" trigger) and reproduced end-to-end against a live Connector Namespace.
Background / Evidence
Connector Namespace delivers two distinct trigger callback shapes for the same connector:
bodyshapeOnNewFilesV2{"body":{"value":[{...BlobMetadata...}]}}(object)OnNewFileV2{"body":"<base64>"}(string)Reproduced end-to-end (real files uploaded to OneDrive, real Connector Gateway callbacks observed in App Insights):
.txttrigger →{"body":"<base64>"}→ deserializing intoOneDriveForBusinessOnNewFilesTriggerPayloadthrowsJsonException: Expected StartObject or Null for TriggerCallbackBody<BlobMetadata>, got String.— a cryptic message that does not tell the developer they bound a binary-content trigger to a metadata payload type..jsonfile via binary trigger →{"body":{...file content...}}→ deserializes to a single all-nullBlobMetadata(silent data loss).null, because the deserialization path does not setPropertyNameCaseInsensitive.Gaps
OneDriveTriggerCallbacksample inConnectors-NET-Samples).PropertyNameCaseInsensitive = trueyields silently all-nullitems when the wire uses camelCase.ConnectorJsonSerializer.Optionsalready sets this, but there is nothing that steers trigger-payload consumers to it.TriggerCallbackBodyConverter<T>throws"Expected StartObject or Null... got String."when a binary-content (string) body is bound to a metadata payload type. The message should tell the developer this is a binary-content trigger and how to read it.Proposed change (this repo)
Add a framework-agnostic SDK helper (operates on
string/Stream, no Functions Worker dependency) — e.g.ConnectorTriggerPayload:ConnectorTriggerPayload.DefaultSerializerOptions— case-insensitive options reused for all trigger payloads (closes gap Adding Microsoft SECURITY.MD #2).Read<TPayload>(string json)/ReadAsync<TPayload>(Stream body, CancellationToken)— read a metadata trigger callback into its typedTriggerCallbackPayload<T>subclass with the correct options (closes gap This repo is missing a license file #1).TryReadBinaryContent(string json, out byte[] content)/ReadBinaryContentAsync(Stream, CancellationToken)— decode a binary-content trigger callback ({"body":"<base64>"}) into bytes (closes gap This repo is missing a license file #1 for the binary variant).TriggerCallbackBodyConverter<T>to throw an actionable error when it encounters a string body (closes gap This repo is missing important files #3): explain that the callback is a binary-content trigger (e.g.OnNewFileV2) and direct the caller toTryReadBinaryContent.This reduces a OneDrive trigger handler from ~100 lines of plumbing to roughly:
Out of scope (tracked separately)
[ConnectorTrigger]POCO binding in the Functions connector extension should consume these SDK primitives (readOperationName, discriminate binary vs metadata, use case-insensitive options). That lives in the Functions connector extension repo, not here.[ConnectorTriggerMetadata].Acceptance criteria
string+Streamoverloads, case-insensitive by default.{"body":"<base64>"}.TriggerCallbackBodyConverter<T>emits an actionable message for string bodies.nullbodies, and the actionable error.