Skip to content

Commit 7a834a5

Browse files
committed
feat: implement UpdateRecord functionality
- Added UpdateRecord method in client.go to enable updating existing records in collections. - Introduced comprehensive test cases for UpdateRecord in client_test.go, covering success, validation errors, and various edge cases. - Updated README.md with usage examples for updating records, including handling expanded relations and specific fields. - Added a new example file demonstrating different scenarios for updating records. - Created GitHub Actions workflows for automated testing and manual testing with Go version options.
1 parent 20a2dc7 commit 7a834a5

File tree

6 files changed

+739
-2
lines changed

6 files changed

+739
-2
lines changed

.github/workflows/manual-test.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Manual Test
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
go_version:
7+
description: 'Go version to test against'
8+
required: false
9+
default: '1.24.x'
10+
type: string
11+
12+
jobs:
13+
manual-test:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Go
20+
uses: actions/setup-go@v5
21+
with:
22+
go-version: ${{ inputs.go_version || '1.22.x' }}
23+
24+
- name: Cache Go modules
25+
uses: actions/cache@v4
26+
with:
27+
path: |
28+
~/.cache/go-build
29+
~/go/pkg/mod
30+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
31+
restore-keys: |
32+
${{ runner.os }}-go-
33+
34+
- name: Download dependencies
35+
run: go mod download
36+
37+
- name: Verify dependencies
38+
run: go mod verify
39+
40+
- name: Run tests
41+
run: go test -v ./...
42+
43+
- name: Run tests with coverage
44+
run: go test -coverprofile=coverage.out -covermode=atomic ./...
45+
46+
- name: Run tests with race detector
47+
run: go test -race ./...
48+
49+
- name: Check formatting
50+
run: |
51+
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
52+
echo "The following files are not formatted:"
53+
gofmt -s -l .
54+
exit 1
55+
fi
56+
57+
- name: Vet
58+
run: go vet ./...

.github/workflows/test.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
go-version: [1.24.x, 1.25.x]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Go
21+
uses: actions/setup-go@v5
22+
with:
23+
go-version: ${{ matrix.go-version }}
24+
25+
- name: Cache Go modules
26+
uses: actions/cache@v4
27+
with:
28+
path: |
29+
~/.cache/go-build
30+
~/go/pkg/mod
31+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
32+
restore-keys: |
33+
${{ runner.os }}-go-
34+
35+
- name: Download dependencies
36+
run: go mod download
37+
38+
- name: Verify dependencies
39+
run: go mod verify
40+
41+
- name: Run tests
42+
run: go test -v ./...
43+
44+
- name: Run tests with coverage
45+
run: go test -coverprofile=coverage.out -covermode=atomic ./...
46+
47+
- name: Upload coverage to Codecov
48+
uses: codecov/codecov-action@v4
49+
with:
50+
file: ./coverage.out
51+
flags: unittests
52+
name: codecov-umbrella
53+
fail_ci_if_error: false
54+
55+
- name: Run tests with race detector
56+
run: go test -race ./...
57+
58+
- name: Check formatting
59+
run: |
60+
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
61+
echo "The following files are not formatted:"
62+
gofmt -s -l .
63+
exit 1
64+
fi
65+
66+
- name: Vet
67+
run: go vet ./...

README.md

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ A simple Go client for [PocketBase](https://pocketbase.io/) that handles the com
1111

1212
- User and superuser authentication
1313
- Create new records in collections
14+
- Update existing records in collections
1415
- Fetch records from collections (with automatic pagination)
1516
- Query single records by ID
1617
- User impersonation for superusers
@@ -240,6 +241,38 @@ createdRecord, err := client.CreateRecord(ctx, "posts", recordData,
240241
)
241242
```
242243

244+
#### Update an existing record
245+
246+
```go
247+
// Update a record by providing only the fields you want to change
248+
updateData := pocketbase.Record{
249+
"title": "Updated Post Title",
250+
"content": "This post has been updated with new content",
251+
"status": "published",
252+
"tags": []string{"golang", "tutorial", "updated"},
253+
}
254+
255+
updatedRecord, err := client.UpdateRecord(ctx, "posts", "RECORD_ID_HERE", updateData)
256+
if err != nil {
257+
if apiErr, ok := err.(*pocketbase.APIError); ok {
258+
if apiErr.IsBadRequest() {
259+
fmt.Println("Validation error:", apiErr.Message)
260+
}
261+
}
262+
log.Fatal(err)
263+
}
264+
fmt.Printf("Updated record: %s\n", updatedRecord["title"])
265+
```
266+
267+
You can also expand relations and select specific fields when updating:
268+
269+
```go
270+
updatedRecord, err := client.UpdateRecord(ctx, "posts", "RECORD_ID_HERE", updateData,
271+
pocketbase.WithExpand("author", "category"),
272+
pocketbase.WithFields("id", "title", "content", "author"),
273+
)
274+
```
275+
243276
### Records and errors
244277

245278
Records are returned as `map[string]any`, so you can access any field:
@@ -339,7 +372,9 @@ if err != nil {
339372

340373
## Testing
341374

342-
Run the tests:
375+
### Local Testing
376+
377+
Run the tests locally:
343378

344379
```bash
345380
go test ./...
@@ -355,13 +390,31 @@ The tests use `httptest.Server` to mock PocketBase responses and cover:
355390
- Query options
356391
- Thread safety
357392

393+
### Continuous Integration
394+
395+
This project uses GitHub Actions for continuous integration:
396+
397+
#### Automated Testing
398+
- **Triggers**: Runs on every push to `main` branch and every pull request targeting `main`
399+
- **Go versions**: Tests against Go 1.21.x and 1.22.x
400+
- **Coverage**: Generates and reports test coverage to Codecov
401+
- **Quality checks**: Includes formatting validation and `go vet`
402+
403+
#### Manual Testing
404+
- **Trigger**: Can be manually triggered via GitHub Actions interface
405+
- **Custom Go version**: Allows specifying a custom Go version for testing
406+
- **Purpose**: Useful for testing other branches or specific Go versions
407+
408+
The CI pipeline ensures code quality and compatibility across supported Go versions.
409+
358410
## Examples
359411

360412
The [examples](examples/) directory contains well-documented code examples that demonstrate different features:
361413

362414
- `common.go` - Shared utilities and client setup
363415
- `auth_example.go` - User authentication
364416
- `create_record_example.go` - **Creating new records** in collections
417+
- `update_record_example.go` - **Updating existing records** in collections
365418
- `fetch_all_example.go` - Fetching all records from collections
366419
- `fetch_options_example.go` - Filtering, sorting, and expanding records
367420
- `fetch_single_example.go` - Fetching individual records
@@ -390,7 +443,7 @@ The examples show real-world usage patterns including proper error handling, con
390443

391444
This covers the basic read and write operations. Future versions might add:
392445

393-
- Updating and deleting records
446+
- Deleting records
394447
- File uploads
395448
- Real-time subscriptions
396449
- Admin API

client.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,52 @@ func (c *Client) CreateRecord(ctx context.Context, collection string, record Rec
346346
return createdRecord, nil
347347
}
348348

349+
// UpdateRecord updates an existing record in the specified collection.
350+
// The record parameter should contain only the fields that need to be updated.
351+
// Fields like 'id', 'created', and 'updated' are automatically handled by PocketBase.
352+
//
353+
// Example:
354+
//
355+
// // Update a post's title and status
356+
// updateData := map[string]any{
357+
// "title": "Updated Post Title",
358+
// "status": "published",
359+
// }
360+
//
361+
// updatedRecord, err := client.UpdateRecord(ctx, "posts", "RECORD_ID_HERE", updateData)
362+
// if err != nil {
363+
// return err
364+
// }
365+
// fmt.Printf("Updated record: %s", updatedRecord["title"])
366+
func (c *Client) UpdateRecord(ctx context.Context, collection, recordID string, record Record, opts ...QueryOption) (Record, error) {
367+
options := &QueryOptions{}
368+
for _, opt := range opts {
369+
opt(options)
370+
}
371+
372+
endpoint := fmt.Sprintf("/api/collections/%s/records/%s", collection, recordID)
373+
374+
// Build query parameters
375+
params := url.Values{}
376+
if len(options.Expand) > 0 {
377+
params.Set("expand", strings.Join(options.Expand, ","))
378+
}
379+
if len(options.Fields) > 0 {
380+
params.Set("fields", strings.Join(options.Fields, ","))
381+
}
382+
if len(params) > 0 {
383+
endpoint += "?" + params.Encode()
384+
}
385+
386+
var updatedRecord Record
387+
err := c.doRequest(ctx, "PATCH", endpoint, record, &updatedRecord)
388+
if err != nil {
389+
return nil, err
390+
}
391+
392+
return updatedRecord, nil
393+
}
394+
349395
// doRequest is a helper method that handles HTTP requests to the PocketBase API.
350396
// It manages request construction, authentication headers, JSON encoding/decoding,
351397
// and error handling.

0 commit comments

Comments
 (0)