-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.cursorrules
More file actions
137 lines (105 loc) · 4.92 KB
/
.cursorrules
File metadata and controls
137 lines (105 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# Cursor Rules for astroapi-go
## Project
Pure Go HTTP client SDK for Astrology API v3. Module: `github.com/astro-api/astroapi-go`. Go 1.23. No external dependencies except `testify` for tests.
## Architecture
- `client.go` — root `AstrologyClient` with sub-clients for each API category
- `categories/base.go` — `BaseCategoryClient` with shared HTTP methods (Get/Post/Put/Delete/MakeRequest)
- `categories/<name>/` — each category has exactly 4 files: `<name>.go`, `params.go`, `responses.go`, `<name>_test.go`
- `shared/shared.go` — common types: `BirthData`, `Subject`, `DateTimeLocation`, `DateRange`, `AstrologyOptions`, `ReportOptions`
- `option/option.go` — functional options (`RequestOption = func(*RequestConfig)`)
- `errors/error.go` — `AstrologyError` with `StatusCode`, `Message`, `Code`, `Body`
- `internal/transport/` — `AuthTransport` and `RetryTransport` (both `http.RoundTripper`)
- `internal/validator/` — struct tag validation (`validate:"required"`)
- `internal/apijson/` — `Field[T]` generic optional/nullable JSON fields
## Code Conventions
### Category client structure (MUST follow this pattern exactly):
```go
// Package <name> provides the Client for accessing <description>.
package <name>
import (
"context"
"github.com/astro-api/astroapi-go/categories"
"github.com/astro-api/astroapi-go/option"
)
const apiPrefix = "api/v3/<name>"
type Client struct {
*categories.BaseCategoryClient
}
func NewClient(base *categories.BaseCategoryClient) *Client {
return &Client{BaseCategoryClient: base}
}
func (c *Client) GetSomething(ctx context.Context, params SomeParams, opts ...option.RequestOption) (*SomeResponse, error) {
var out SomeResponse
if err := c.Post(ctx, c.BuildURL(apiPrefix, "endpoint"), params, &out, opts...); err != nil {
return nil, err
}
return &out, nil
}
```
### Params (in params.go):
```go
package <name>
import "github.com/astro-api/astroapi-go/shared"
type SomeParams struct {
Subject shared.Subject `json:"subject" validate:"required"`
Options *shared.AstrologyOptions `json:"options,omitempty"`
}
```
### Responses (in responses.go):
```go
package <name>
type SomeResponse map[string]any
```
### Tests (in <name>_test.go):
```go
package <name>_test // black-box testing
// Unit test — mock HTTP server
func TestClient_GetSomething(t *testing.T) {
client, cleanup := testutil.NewClient(t, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v3/<name>/endpoint", r.URL.Path)
assert.Equal(t, http.MethodPost, r.Method)
testutil.JSON(w, testutil.DataEnvelope(map[string]any{"key": "value"}))
})
defer cleanup()
result, err := client.<Category>.GetSomething(ctx, params)
require.NoError(t, err)
assert.NotNil(t, result)
}
// Integration test — real API, skipped without ASTROLOGY_API_KEY
func TestClient_Integration_AllEndpoints(t *testing.T) {
client := testutil.NewIntegrationClient(t)
t.Run("GetSomething", func(t *testing.T) {
result, err := client.<Category>.GetSomething(ctx, params)
require.NoError(t, err)
assert.NotNil(t, result)
})
}
```
## Rules
- All methods take `ctx context.Context` as first param and `opts ...option.RequestOption` as last
- Use `c.Post()` / `c.Get()` from embedded `BaseCategoryClient`, never call `MakeRequest` directly from category code
- URL building: `c.BuildURL(apiPrefix, "segment1", "segment2")` — never concatenate strings manually
- Params with required fields use `validate:"required"` struct tag
- Optional fields use `omitempty` in JSON tag and pointer types for struct fields
- Response types are `map[string]any` aliases unless there's a strong reason for typed structs
- Mock-only tests guard with `if testutil.IsIntegration() { t.Skip("...") }`
- Use `testutil.DefaultSubject()` and `testutil.DefaultDateTimeLocation()` for test fixtures
- Use `testutil.DataEnvelope()` or `testutil.ResultEnvelope()` to wrap mock responses
- After creating a new category, wire it into `AstrologyClient` in `client.go`
- Import errors package as: `astroerrors "github.com/astro-api/astroapi-go/errors"`
- Import root package as: `astroapi "github.com/astro-api/astroapi-go"`
## Versioning
Git tags following semver: `v{MAJOR}.{MINOR}.{PATCH}`
- PATCH (v0.1.0 → v0.1.1): bug fix, docs
- MINOR (v0.1.1 → v0.2.0): new category, new method (backwards-compatible)
- MAJOR (v0.2.0 → v1.0.0): breaking API change (removed/renamed types, changed signatures)
Release: `git tag v0.1.0 && git push origin v0.1.0` — CI creates GitHub Release + notifies Go Module Proxy.
Starting from v2+, module path must include major suffix (`github.com/astro-api/astroapi-go/v2`) and all internal imports must be updated.
## Commands
```bash
make build # go build ./...
make test # go test -race -count=1 -timeout 120s ./...
make lint # golangci-lint run ./...
make fmt # gofmt -w -s .
make vet # go vet ./...
```