Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
layout: default
nav_order: 5
title: Configuration
---

## Configuration

### ConnectionInfo

All clients require a `ConnectionInfo` object:

```csharp
var conn = new ConnectionInfo
{
FmsUri = "https://your-server.com",
Database = "YourDatabase",
Username = "admin",
Password = "password"
};
```

#### REST API Version

Set `RestTargetVersion` to target a specific Data API version:

```csharp
conn.RestTargetVersion = RestTargetVersion.v1; // default
conn.RestTargetVersion = RestTargetVersion.v2;
conn.RestTargetVersion = RestTargetVersion.vLatest;
```

### Creating a Client Directly

```csharp
var client = new FileMakerRestClient(new HttpClient(), conn);
```

### Using Dependency Injection

#### Standard (Scoped) Lifetime

Register `IFileMakerApiClient` with `AddHttpClient` for managed `HttpClient` lifetime:

```csharp
services.AddSingleton<ConnectionInfo>(new ConnectionInfo
{
FmsUri = "https://your-server.com",
Database = "YourDatabase",
Username = "admin",
Password = "password"
});

services.AddHttpClient<IFileMakerApiClient, FileMakerRestClient>();
```

This creates a new `FileMakerRestClient` per scope (typically per HTTP request in ASP.NET Core).

#### Singleton Lifetime

For better performance when making many Data API calls per request, register as a singleton:

```csharp
services.AddHttpClient(); // register IHttpClientFactory

services.AddSingleton<ConnectionInfo>(new ConnectionInfo
{
FmsUri = "https://your-server.com",
Database = "YourDatabase",
Username = "admin",
Password = "password"
});

services.AddSingleton<IFileMakerApiClient, FileMakerRestClient>(sp =>
{
var hcf = sp.GetRequiredService<IHttpClientFactory>();
var ci = sp.GetRequiredService<ConnectionInfo>();
return new FileMakerRestClient(hcf.CreateClient(), ci);
});
```

The singleton approach reuses the FileMaker Data API token across requests, reducing authentication overhead.

### FileMaker Cloud Authentication

For FileMaker Cloud (Claris Connect), use `FileMakerCloudAuthTokenProvider`:

```csharp
var conn = new ConnectionInfo
{
FmsUri = "https://yourhost.account.filemaker-cloud.com",
Database = "YourDatabase",
Username = "user@domain.com",
Password = "password"
};

var client = new FileMakerRestClient(
new HttpClient(),
new FileMakerCloudAuthTokenProvider(conn));
```

`FileMakerCloudAuthTokenProvider` handles authentication via AWS Cognito. The default Cognito settings are pre-configured for FileMaker Cloud:

| Property | Default |
|---|---|
| `CognitoUserPoolID` | `us-west-2_NqkuZcXQY` |
| `CognitoClientID` | `4l9rvl4mv5es1eep1qe97cautn` |
| `RegionEndpoint` | `us-west-2` |

Override these on `ConnectionInfo` if your FileMaker Cloud instance uses different values.

For a full description of using the FileMaker Data API with FileMaker Cloud, see [this discussion](https://github.com/fuzzzerd/fmdata/issues/217#issuecomment-1203202293).
84 changes: 84 additions & 0 deletions docs/containers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
layout: default
parent: Guide
nav_order: 7
title: Container Data
---

## Container Data

Container fields in FileMaker store files (images, PDFs, etc.). FMData handles downloading and uploading container data separately from regular field data.

### Model Setup

Add a `byte[]` property with `[ContainerDataFor]` referencing the container field's C# property name:

```csharp
[DataContract(Name = "Documents")]
public class Document
{
[DataMember]
public string Title { get; set; }

[DataMember]
public string Attachment { get; set; } // container field

[ContainerDataFor("Attachment")] // references the C# property name
public byte[] AttachmentData { get; set; }
}
```

### Downloading Container Data

After finding records, use `ProcessContainers` to download all container data for a collection:

```csharp
var results = await client.FindAsync(new Document { Title = "Report" });
await client.ProcessContainers(results);
// each result's AttachmentData now contains the file bytes
```

For a single record:

```csharp
await client.ProcessContainer(singleDocument);
```

#### Auto-load on Find

Set `LoadContainerData` on a find request to automatically download container data with the results:

```csharp
var req = client.GenerateFindRequest<Document>();
req.Layout = "Documents";
req.AddQuery(new Document { Title = "Report" }, omit: false);
req.LoadContainerData = true;

var results = await client.SendAsync(req);
// container data is already loaded
```

### Uploading Container Data

Use `UpdateContainerAsync` to upload data to a container field:

```csharp
var fileBytes = File.ReadAllBytes("report.pdf");

await client.UpdateContainerAsync(
"Documents", // layout
recordId, // FileMaker record ID
"Attachment", // container field name
"report.pdf", // file name
fileBytes); // file content
```

For repeating container fields, specify the repetition number:

```csharp
await client.UpdateContainerAsync(
"Documents", recordId, "Attachment", "report.pdf",
repetition: 2, content: fileBytes);
```

> **Note:** Creating a record with container data requires two calls — one to create the record, then a second to upload the container data.
80 changes: 80 additions & 0 deletions docs/creating-records.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
layout: default
parent: Guide
nav_order: 3
title: Creating Records
---

## Creating Records

### Basic Create

Pass a model instance to `CreateAsync`. The layout is determined by the `[DataContract(Name = "...")]` attribute:

```csharp
var invoice = new Invoice
{
InvoiceNumber = "INV-001",
Amount = 150.00m,
Status = "Open"
};

var response = await client.CreateAsync(invoice);
```

### Layout Override

Specify a layout explicitly:

```csharp
var response = await client.CreateAsync("AlternateLayout", invoice);
```

### Null and Default Value Control

By default, properties with `null` or default values are excluded from the request. To include them:

```csharp
var response = await client.CreateAsync(invoice,
includeNullValues: true,
includeDefaultValues: true);
```

### Create with Scripts

Run FileMaker scripts as part of the create operation. Scripts execute in this order: pre-request, pre-sort, then post-request (after the record is created).

```csharp
var response = await client.CreateAsync(invoice,
script: "AfterCreate", scriptParameter: "param1",
preRequestScript: "BeforeCreate", preRequestScriptParam: "param2",
preSortScript: "SortSetup", preSortScriptParameter: "param3");
```

### Reading Script Results

The `ICreateResponse` contains an `ActionResponse` with script results:

```csharp
var response = await client.CreateAsync(invoice,
script: "AfterCreate", scriptParameter: "param");

var scriptResult = response.Response.ScriptResult;
var scriptError = response.Response.ScriptError; // 0 = no error

var preReqResult = response.Response.ScriptResultPreRequest;
var preSortResult = response.Response.ScriptResultPreSort;
```

### Advanced: SendAsync with ICreateRequest

For full control, build a request manually:

```csharp
var req = client.GenerateCreateRequest(invoice);
req.Layout = "Invoices";
req.Script = "AfterCreate";
req.ScriptParameter = "param";

var response = await client.SendAsync(req);
```
52 changes: 52 additions & 0 deletions docs/deleting-records.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
layout: default
parent: Guide
nav_order: 5
title: Deleting Records
---

## Deleting Records

### Delete by Model Type

Delete using the layout from the model's `[DataContract]` attribute:

```csharp
var response = await client.DeleteAsync<Invoice>(recordId);
```

### Delete by Layout and Record ID

Specify the layout explicitly:

```csharp
var response = await client.DeleteAsync(recordId, "Invoices");
```

### Delete with Scripts

Use `SendAsync` with an `IDeleteRequest` to run scripts alongside a delete:

```csharp
var req = client.GenerateDeleteRequest();
req.Layout = "Invoices";
req.RecordId = recordId;
req.Script = "AfterDelete";
req.ScriptParameter = "param";
req.PreRequestScript = "BeforeDelete";
req.PreRequestScriptParameter = "preParam";

var response = await client.SendAsync(req);
```

### Reading Script Results

The `IDeleteResponse` contains an `ActionResponse` with script results:

```csharp
var scriptResult = response.Response.ScriptResult;
var scriptError = response.Response.ScriptError; // 0 = no error

var preReqResult = response.Response.ScriptResultPreRequest;
var preSortResult = response.Response.ScriptResultPreSort;
```
Loading
Loading