diff --git a/README.md b/README.md
index 206d7f4..79d177a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
-# Lattice SDK Go library
+# Lattice SDK Go Library

-The Lattice SDK Go library provides convenient access to the Lattice API from Go.
+
+The Lattice SDK Go library provides convenient access to the Lattice SDK APIs from Go.
## Documentation
@@ -26,6 +27,10 @@ go get github.com/anduril/lattice-sdk-go/v2
For support with this library please reach out to your Anduril representative.
+## Reference
+
+A full reference for this library is available [here](https://github.com/anduril/lattice-sdk-go/blob/HEAD/./reference.md).
+
## Usage
Instantiate and use the client with the following:
@@ -37,7 +42,7 @@ import (
client "github.com/anduril/lattice-sdk-go/v2/client"
option "github.com/anduril/lattice-sdk-go/v2/option"
context "context"
- Lattice "github.com/anduril/lattice-sdk-go/v2"
+ v2 "github.com/anduril/lattice-sdk-go/v2"
)
func do() {
@@ -48,7 +53,7 @@ func do() {
)
client.Entities.LongPollEntityEvents(
context.TODO(),
- &Lattice.EntityEventRequest{
+ &v2.EntityEventRequest{
SessionToken: "sessionToken",
},
)
diff --git a/client/client.go b/client/client.go
index a799ecb..51fcc87 100644
--- a/client/client.go
+++ b/client/client.go
@@ -9,7 +9,6 @@ import (
objects "github.com/anduril/lattice-sdk-go/v2/objects"
option "github.com/anduril/lattice-sdk-go/v2/option"
tasks "github.com/anduril/lattice-sdk-go/v2/tasks"
- http "net/http"
)
type Client struct {
@@ -17,17 +16,18 @@ type Client struct {
Tasks *tasks.Client
Objects *objects.Client
+ options *core.RequestOptions
baseURL string
caller *internal.Caller
- header http.Header
}
func NewClient(opts ...option.RequestOption) *Client {
options := core.NewRequestOptions(opts...)
return &Client{
- Entities: entities.NewClient(opts...),
- Tasks: tasks.NewClient(opts...),
- Objects: objects.NewClient(opts...),
+ Entities: entities.NewClient(options),
+ Tasks: tasks.NewClient(options),
+ Objects: objects.NewClient(options),
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -35,6 +35,5 @@ func NewClient(opts ...option.RequestOption) *Client {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
diff --git a/client/client_test.go b/client/client_test.go
index 458c288..51dc022 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -40,6 +40,6 @@ func TestNewClient(t *testing.T) {
option.WithHTTPHeader(header),
)
assert.Empty(t, c.baseURL)
- assert.Equal(t, "test", c.header.Get("X-API-Tenancy"))
+ assert.Equal(t, "test", c.options.HTTPHeader.Get("X-API-Tenancy"))
})
}
diff --git a/core/request_option.go b/core/request_option.go
index 09ceed2..2e6e32a 100644
--- a/core/request_option.go
+++ b/core/request_option.go
@@ -56,8 +56,8 @@ func (r *RequestOptions) cloneHeader() http.Header {
headers := r.HTTPHeader.Clone()
headers.Set("X-Fern-Language", "Go")
headers.Set("X-Fern-SDK-Name", "github.com/anduril/lattice-sdk-go/v2")
- headers.Set("X-Fern-SDK-Version", "v2.2.0")
- headers.Set("User-Agent", "github.com/anduril/lattice-sdk-go/2.2.0")
+ headers.Set("X-Fern-SDK-Version", "v2.3.0")
+ headers.Set("User-Agent", "github.com/anduril/lattice-sdk-go/2.3.0")
return headers
}
diff --git a/entities/client.go b/entities/client.go
index f19b9f7..82a15bf 100644
--- a/entities/client.go
+++ b/entities/client.go
@@ -14,15 +14,15 @@ import (
type Client struct {
WithRawResponse *RawClient
+ options *core.RequestOptions
baseURL string
caller *internal.Caller
- header http.Header
}
-func NewClient(opts ...option.RequestOption) *Client {
- options := core.NewRequestOptions(opts...)
+func NewClient(options *core.RequestOptions) *Client {
return &Client{
WithRawResponse: NewRawClient(options),
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -30,7 +30,6 @@ func NewClient(opts ...option.RequestOption) *Client {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
@@ -163,7 +162,7 @@ func (c *Client) StreamEntities(
)
endpointURL := baseURL + "/api/v1/entities/stream"
headers := internal.MergeHeaders(
- c.header.Clone(),
+ c.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Accept", "text/event-stream")
diff --git a/entities/raw_client.go b/entities/raw_client.go
index e409d2c..7bdbcb9 100644
--- a/entities/raw_client.go
+++ b/entities/raw_client.go
@@ -14,11 +14,12 @@ import (
type RawClient struct {
baseURL string
caller *internal.Caller
- header http.Header
+ options *core.RequestOptions
}
func NewRawClient(options *core.RequestOptions) *RawClient {
return &RawClient{
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -26,7 +27,6 @@ func NewRawClient(options *core.RequestOptions) *RawClient {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
@@ -43,7 +43,7 @@ func (r *RawClient) PublishEntity(
)
endpointURL := baseURL + "/api/v1/entities"
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
@@ -101,7 +101,7 @@ func (r *RawClient) GetEntity(
entityID,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
@@ -167,7 +167,7 @@ func (r *RawClient) OverrideEntity(
fieldPath,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Content-Type", "application/json")
@@ -234,7 +234,7 @@ func (r *RawClient) RemoveEntityOverride(
fieldPath,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
@@ -292,7 +292,7 @@ func (r *RawClient) LongPollEntityEvents(
)
endpointURL := baseURL + "/api/v1/entities/events"
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Content-Type", "application/json")
diff --git a/internal/query.go b/internal/query.go
index 24ec496..786318b 100644
--- a/internal/query.go
+++ b/internal/query.go
@@ -24,31 +24,81 @@ type QueryEncoder interface {
EncodeQueryValues(key string, v *url.Values) error
}
-// QueryValues encodes url.Values from request objects.
-//
-// Note: This type is inspired by Google's query encoding library, but
-// supports far less customization and is tailored to fit this SDK's use case.
-//
-// Ref: https://github.com/google/go-querystring
-func QueryValues(v interface{}) (url.Values, error) {
+// prepareValue handles common validation and unwrapping logic for both functions
+func prepareValue(v interface{}) (reflect.Value, url.Values, error) {
values := make(url.Values)
val := reflect.ValueOf(v)
for val.Kind() == reflect.Ptr {
if val.IsNil() {
- return values, nil
+ return reflect.Value{}, values, nil
}
val = val.Elem()
}
if v == nil {
- return values, nil
+ return reflect.Value{}, values, nil
}
if val.Kind() != reflect.Struct {
- return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind())
+ return reflect.Value{}, nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind())
}
err := reflectValue(values, val, "")
+ if err != nil {
+ return reflect.Value{}, nil, err
+ }
+
+ return val, values, nil
+}
+
+// QueryValues encodes url.Values from request objects.
+//
+// Note: This type is inspired by Google's query encoding library, but
+// supports far less customization and is tailored to fit this SDK's use case.
+//
+// Ref: https://github.com/google/go-querystring
+func QueryValues(v interface{}) (url.Values, error) {
+ _, values, err := prepareValue(v)
+ return values, err
+}
+
+// QueryValuesWithDefaults encodes url.Values from request objects
+// and default values, merging the defaults into the request.
+// It's expected that the values of defaults are wire names.
+func QueryValuesWithDefaults(v interface{}, defaults map[string]interface{}) (url.Values, error) {
+ val, values, err := prepareValue(v)
+ if err != nil {
+ return values, err
+ }
+
+ // apply defaults to zero-value fields directly on the original struct
+ valType := val.Type()
+ for i := 0; i < val.NumField(); i++ {
+ field := val.Field(i)
+ fieldType := valType.Field(i)
+ fieldName := fieldType.Name
+
+ if fieldType.PkgPath != "" && !fieldType.Anonymous {
+ // Skip unexported fields.
+ continue
+ }
+
+ // check if field is zero value and we have a default for it
+ if field.CanSet() && field.IsZero() {
+ tag := fieldType.Tag.Get("url")
+ if tag == "" || tag == "-" {
+ continue
+ }
+ wireName, _ := parseTag(tag)
+ if wireName == "" {
+ wireName = fieldName
+ }
+ if defaultVal, exists := defaults[wireName]; exists {
+ values.Set(wireName, valueString(reflect.ValueOf(defaultVal), tagOptions{}, reflect.StructField{}))
+ }
+ }
+ }
+
return values, err
}
diff --git a/internal/query_test.go b/internal/query_test.go
index 75f66f8..60d4be9 100644
--- a/internal/query_test.go
+++ b/internal/query_test.go
@@ -237,3 +237,138 @@ func TestQueryValues(t *testing.T) {
assert.Equal(t, "metadata%5Binner%5D=one&metadata%5Binner%5D=two&metadata%5Binner%5D=three", values.Encode())
})
}
+
+func TestQueryValuesWithDefaults(t *testing.T) {
+ t.Run("apply defaults to zero values", func(t *testing.T) {
+ type example struct {
+ Name string `json:"name" url:"name"`
+ Age int `json:"age" url:"age"`
+ Enabled bool `json:"enabled" url:"enabled"`
+ }
+
+ defaults := map[string]interface{}{
+ "name": "default-name",
+ "age": 25,
+ "enabled": true,
+ }
+
+ values, err := QueryValuesWithDefaults(&example{}, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode())
+ })
+
+ t.Run("preserve non-zero values over defaults", func(t *testing.T) {
+ type example struct {
+ Name string `json:"name" url:"name"`
+ Age int `json:"age" url:"age"`
+ Enabled bool `json:"enabled" url:"enabled"`
+ }
+
+ defaults := map[string]interface{}{
+ "name": "default-name",
+ "age": 25,
+ "enabled": true,
+ }
+
+ values, err := QueryValuesWithDefaults(&example{
+ Name: "actual-name",
+ Age: 30,
+ // Enabled remains false (zero value), should get default
+ }, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "age=30&enabled=true&name=actual-name", values.Encode())
+ })
+
+ t.Run("ignore defaults for fields not in struct", func(t *testing.T) {
+ type example struct {
+ Name string `json:"name" url:"name"`
+ Age int `json:"age" url:"age"`
+ }
+
+ defaults := map[string]interface{}{
+ "name": "default-name",
+ "age": 25,
+ "nonexistent": "should-be-ignored",
+ }
+
+ values, err := QueryValuesWithDefaults(&example{}, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "age=25&name=default-name", values.Encode())
+ })
+
+ t.Run("type conversion for compatible defaults", func(t *testing.T) {
+ type example struct {
+ Count int64 `json:"count" url:"count"`
+ Rate float64 `json:"rate" url:"rate"`
+ Message string `json:"message" url:"message"`
+ }
+
+ defaults := map[string]interface{}{
+ "count": int(100), // int -> int64 conversion
+ "rate": float32(2.5), // float32 -> float64 conversion
+ "message": "hello", // string -> string (no conversion needed)
+ }
+
+ values, err := QueryValuesWithDefaults(&example{}, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "count=100&message=hello&rate=2.5", values.Encode())
+ })
+
+ t.Run("mixed with pointer fields and omitempty", func(t *testing.T) {
+ type example struct {
+ Required string `json:"required" url:"required"`
+ Optional *string `json:"optional,omitempty" url:"optional,omitempty"`
+ Count int `json:"count,omitempty" url:"count,omitempty"`
+ }
+
+ defaultOptional := "default-optional"
+ defaults := map[string]interface{}{
+ "required": "default-required",
+ "optional": &defaultOptional, // pointer type
+ "count": 42,
+ }
+
+ values, err := QueryValuesWithDefaults(&example{
+ Required: "custom-required", // should override default
+ // Optional is nil, should get default
+ // Count is 0, should get default
+ }, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "count=42&optional=default-optional&required=custom-required", values.Encode())
+ })
+
+ t.Run("override non-zero defaults with explicit zero values", func(t *testing.T) {
+ type example struct {
+ Name *string `json:"name" url:"name"`
+ Age *int `json:"age" url:"age"`
+ Enabled *bool `json:"enabled" url:"enabled"`
+ }
+
+ defaults := map[string]interface{}{
+ "name": "default-name",
+ "age": 25,
+ "enabled": true,
+ }
+
+ // first, test that a properly empty request is overridden:
+ {
+ values, err := QueryValuesWithDefaults(&example{}, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode())
+ }
+
+ // second, test that a request that contains zeros is not overridden:
+ var (
+ name = ""
+ age = 0
+ enabled = false
+ )
+ values, err := QueryValuesWithDefaults(&example{
+ Name: &name, // explicit empty string should override default
+ Age: &age, // explicit zero should override default
+ Enabled: &enabled, // explicit false should override default
+ }, defaults)
+ require.NoError(t, err)
+ assert.Equal(t, "age=0&enabled=false&name=", values.Encode())
+})
+}
diff --git a/internal/retrier.go b/internal/retrier.go
index 3418f00..bb8e954 100644
--- a/internal/retrier.go
+++ b/internal/retrier.go
@@ -4,6 +4,7 @@ import (
"crypto/rand"
"math/big"
"net/http"
+ "strconv"
"time"
)
@@ -105,7 +106,7 @@ func (r *Retrier) run(
if r.shouldRetry(response) {
defer response.Body.Close()
- delay, err := r.retryDelay(retryAttempt)
+ delay, err := r.retryDelay(response, retryAttempt)
if err != nil {
return nil, err
}
@@ -117,7 +118,7 @@ func (r *Retrier) run(
request,
errorDecoder,
maxRetryAttempts,
- retryAttempt+1,
+ retryAttempt + 1,
decodeError(response, errorDecoder),
)
}
@@ -133,17 +134,66 @@ func (r *Retrier) shouldRetry(response *http.Response) bool {
response.StatusCode >= http.StatusInternalServerError
}
-// retryDelay calculates the delay time in milliseconds based on the retry attempt.
-func (r *Retrier) retryDelay(retryAttempt uint) (time.Duration, error) {
- // Apply exponential backoff.
- delay := minRetryDelay + minRetryDelay*time.Duration(retryAttempt*retryAttempt)
+// retryDelay calculates the delay time based on response headers,
+// falling back to exponential backoff if no headers are present.
+func (r *Retrier) retryDelay(response *http.Response, retryAttempt uint) (time.Duration, error) {
+ // Check for Retry-After header first (RFC 7231)
+ if retryAfter := response.Header.Get("Retry-After"); retryAfter != "" {
+ // Parse as number of seconds...
+ if seconds, err := strconv.Atoi(retryAfter); err == nil {
+ delay := time.Duration(seconds) * time.Second
+ if delay > maxRetryDelay {
+ delay = maxRetryDelay
+ }
+ return r.addJitter(delay)
+ }
+
+ // ...or as an HTTP date; both are valid
+ if retryTime, err := time.Parse(time.RFC1123, retryAfter); err == nil {
+ delay := time.Until(retryTime)
+ if delay < 0 {
+ delay = 0
+ }
+ if delay > maxRetryDelay {
+ delay = maxRetryDelay
+ }
+ return r.addJitter(delay)
+ }
+ }
+
+ // Then check for industry-standard X-RateLimit-Reset header
+ if rateLimitReset := response.Header.Get("X-RateLimit-Reset"); rateLimitReset != "" {
+ if resetTimestamp, err := strconv.ParseInt(rateLimitReset, 10, 64); err == nil {
+ // Assume Unix timestamp in seconds
+ resetTime := time.Unix(resetTimestamp, 0)
+ delay := time.Until(resetTime)
+ if delay > 0 {
+ if delay > maxRetryDelay {
+ delay = maxRetryDelay
+ }
+ return r.addJitter(delay)
+ }
+ }
+ }
+
+ // Fall back to exponential backoff
+ return r.exponentialBackoffDelay(retryAttempt)
+}
- // Do not allow the number to exceed maxRetryDelay.
+// exponentialBackoffDelay calculates the delay time in milliseconds based on the retry attempt.
+func (r *Retrier) exponentialBackoffDelay(retryAttempt uint) (time.Duration, error) {
+ // Apply exponential backoff.
+ delay := minRetryDelay + minRetryDelay * time.Duration(retryAttempt * retryAttempt)
if delay > maxRetryDelay {
delay = maxRetryDelay
}
- // Apply some jitter by randomizing the value in the range of 75%-100%.
+ return r.addJitter(delay)
+}
+
+// addJitter applies jitter to the given delay by randomizing the value
+// in the range of 75%-100%.
+func (r *Retrier) addJitter(delay time.Duration) (time.Duration, error) {
max := big.NewInt(int64(delay / 4))
jitter, err := rand.Int(rand.Reader, max)
if err != nil {
@@ -151,8 +201,6 @@ func (r *Retrier) retryDelay(retryAttempt uint) (time.Duration, error) {
}
delay -= time.Duration(jitter.Int64())
-
- // Never sleep less than the base sleep seconds.
if delay < minRetryDelay {
delay = minRetryDelay
}
diff --git a/internal/retrier_test.go b/internal/retrier_test.go
index 743d145..0a9e28f 100644
--- a/internal/retrier_test.go
+++ b/internal/retrier_test.go
@@ -3,6 +3,7 @@ package internal
import (
"context"
"encoding/json"
+ "fmt"
"io"
"net/http"
"net/http/httptest"
@@ -147,8 +148,8 @@ func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server {
// Ensure that the duration between retries increases exponentially,
// and that it is within the minimum and maximum retry delay values.
actualDuration := timestamps[index].Sub(timestamps[index-1])
- expectedDurationMin := expectedRetryDurations[index-1] * 75 / 100
- expectedDurationMax := expectedRetryDurations[index-1] * 125 / 100
+ expectedDurationMin := expectedRetryDurations[index-1] * 50 / 100
+ expectedDurationMax := expectedRetryDurations[index-1] * 150 / 100
assert.True(
t,
actualDuration >= expectedDurationMin && actualDuration <= expectedDurationMax,
@@ -182,6 +183,7 @@ func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server {
require.LessOrEqual(t, index, len(tc.giveStatusCodes))
statusCode := tc.giveStatusCodes[index]
+
w.WriteHeader(statusCode)
if tc.giveResponse != nil && statusCode == http.StatusOK {
@@ -209,3 +211,100 @@ var expectedRetryDurations = []time.Duration{
5000 * time.Millisecond,
5000 * time.Millisecond,
}
+
+func TestRetryDelayTiming(t *testing.T) {
+ tests := []struct {
+ name string
+ headerName string
+ headerValueFunc func() string
+ expectedMinMs int64
+ expectedMaxMs int64
+ }{
+ {
+ name: "retry-after with seconds value",
+ headerName: "retry-after",
+ headerValueFunc: func() string {
+ return "1"
+ },
+ expectedMinMs: 700, // 70% of 1000ms after jitter and execution overhead
+ expectedMaxMs: 1100, // 1000ms + some tolerance
+ },
+ {
+ name: "retry-after with HTTP date",
+ headerName: "retry-after",
+ headerValueFunc: func() string {
+ return time.Now().Add(3 * time.Second).Format(time.RFC1123)
+ },
+ expectedMinMs: 1500, // 50% of 3000ms after jitter and execution overhead
+ expectedMaxMs: 3100, // 3000ms + some tolerance
+ },
+ {
+ name: "x-ratelimit-reset with future timestamp",
+ headerName: "x-ratelimit-reset",
+ headerValueFunc: func() string {
+ return fmt.Sprintf("%d", time.Now().Add(3 * time.Second).Unix())
+ },
+ expectedMinMs: 1500, // 50% of 3000ms after jitter and execution overhead
+ expectedMaxMs: 3100, // 3000ms + some tolerance
+ },
+ {
+ name: "retry-after exceeding max delay gets capped",
+ headerName: "retry-after",
+ headerValueFunc: func() string {
+ return "10"
+ },
+ expectedMinMs: 2500, // 50% of 5000ms (maxRetryDelay) after jitter and execution overhead
+ expectedMaxMs: 5100, // maxRetryDelay + some tolerance
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ var timestamps []time.Time
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ timestamps = append(timestamps, time.Now())
+ if len(timestamps) == 1 {
+ // First request - return retryable error with header
+ w.Header().Set(tt.headerName, tt.headerValueFunc())
+ w.WriteHeader(http.StatusTooManyRequests)
+ } else {
+ // Second request - return success
+ w.WriteHeader(http.StatusOK)
+ response := &Response{Id: "success"}
+ bytes, _ := json.Marshal(response)
+ w.Write(bytes)
+ }
+ }))
+ defer server.Close()
+
+ caller := NewCaller(&CallerParams{
+ Client: server.Client(),
+ })
+
+ var response *Response
+ _, err := caller.Call(
+ context.Background(),
+ &CallParams{
+ URL: server.URL,
+ Method: http.MethodGet,
+ Request: &Request{},
+ Response: &response,
+ MaxAttempts: 2,
+ ResponseIsOptional: true,
+ },
+ )
+
+ require.NoError(t, err)
+ require.Len(t, timestamps, 2, "Expected exactly 2 requests")
+
+ actualDelayMs := timestamps[1].Sub(timestamps[0]).Milliseconds()
+
+ assert.GreaterOrEqual(t, actualDelayMs, tt.expectedMinMs,
+ "Actual delay %dms should be >= expected min %dms", actualDelayMs, tt.expectedMinMs)
+ assert.LessOrEqual(t, actualDelayMs, tt.expectedMaxMs,
+ "Actual delay %dms should be <= expected max %dms", actualDelayMs, tt.expectedMaxMs)
+ })
+ }
+}
diff --git a/objects.go b/objects.go
index 97b39d2..3712e18 100644
--- a/objects.go
+++ b/objects.go
@@ -12,6 +12,8 @@ import (
type GetObjectRequest struct {
// If set, Lattice will compress the response using the specified compression method. If the header is not defined, or the compression method is set to `identity`, no compression will be applied to the response.
AcceptEncoding *GetObjectRequestAcceptEncoding `json:"-" url:"-"`
+ // Indicates a client's preference for the priority of the response. The value is a structured header as defined in RFC 9218. If you do not set the header, Lattice uses the default priority set for the environment. Incremental delivery directives are not supported and will be ignored.
+ Priority *string `json:"-" url:"-"`
}
type ListObjectsRequest struct {
diff --git a/objects/client.go b/objects/client.go
index 6f491ea..c6881eb 100644
--- a/objects/client.go
+++ b/objects/client.go
@@ -15,15 +15,15 @@ import (
type Client struct {
WithRawResponse *RawClient
+ options *core.RequestOptions
baseURL string
caller *internal.Caller
- header http.Header
}
-func NewClient(opts ...option.RequestOption) *Client {
- options := core.NewRequestOptions(opts...)
+func NewClient(options *core.RequestOptions) *Client {
return &Client{
WithRawResponse: NewRawClient(options),
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -31,7 +31,6 @@ func NewClient(opts ...option.RequestOption) *Client {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
@@ -53,7 +52,7 @@ func (c *Client) ListObjects(
return nil, err
}
headers := internal.MergeHeaders(
- c.header.Clone(),
+ c.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
diff --git a/objects/raw_client.go b/objects/raw_client.go
index 0ef7cb4..fee4b24 100644
--- a/objects/raw_client.go
+++ b/objects/raw_client.go
@@ -16,11 +16,12 @@ import (
type RawClient struct {
baseURL string
caller *internal.Caller
- header http.Header
+ options *core.RequestOptions
}
func NewRawClient(options *core.RequestOptions) *RawClient {
return &RawClient{
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -28,7 +29,6 @@ func NewRawClient(options *core.RequestOptions) *RawClient {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
@@ -50,12 +50,15 @@ func (r *RawClient) GetObject(
objectPath,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
if request.AcceptEncoding != nil {
headers.Add("Accept-Encoding", string(*request.AcceptEncoding))
}
+ if request.Priority != nil {
+ headers.Add("Priority", *request.Priority)
+ }
errorCodes := internal.ErrorCodes{
400: func(apiError *core.APIError) error {
@@ -122,7 +125,7 @@ func (r *RawClient) UploadObject(
objectPath,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
@@ -195,7 +198,7 @@ func (r *RawClient) DeleteObject(
objectPath,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
@@ -260,7 +263,7 @@ func (r *RawClient) GetObjectMetadata(
objectPath,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
diff --git a/reference.md b/reference.md
new file mode 100644
index 0000000..978c888
--- /dev/null
+++ b/reference.md
@@ -0,0 +1,1115 @@
+# Reference
+## Entities
+client.Entities.PublishEntity(request) -> *v2.Entity
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Publish an entity for ingest into the Entities API. Entities created with this method are "owned" by the originator: other sources,
+such as the UI, may not edit or delete these entities. The server validates entities at API call time and
+returns an error if the entity is invalid.
+
+An entity ID must be provided when calling this endpoint. If the entity referenced by the entity ID does not exist
+then it will be created. Otherwise the entity will be updated. An entity will only be updated if its
+provenance.sourceUpdateTime is greater than the provenance.sourceUpdateTime of the existing entity.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Entities.PublishEntity(
+ context.TODO(),
+ &v2.Entity{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**request:** `*v2.Entity`
+
+
+
+
+
+
+
+
+
+
+
+client.Entities.GetEntity(EntityID) -> *v2.Entity
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Entities.GetEntity(
+ context.TODO(),
+ "entityId",
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**entityID:** `string` — ID of the entity to return
+
+
+
+
+
+
+
+
+
+
+
+client.Entities.OverrideEntity(EntityID, FieldPath, request) -> *v2.Entity
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Only fields marked with overridable can be overridden. Please refer to our documentation to see the comprehensive
+list of fields that can be overridden. The entity in the request body should only have a value set on the field
+specified in the field path parameter. Field paths are rooted in the base entity object and must be represented
+using lower_snake_case. Do not include "entity" in the field path.
+
+Note that overrides are applied in an eventually consistent manner. If multiple overrides are created
+concurrently for the same field path, the last writer wins.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Entities.OverrideEntity(
+ context.TODO(),
+ "entityId",
+ "mil_view.disposition",
+ &v2.EntityOverride{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**entityID:** `string` — The unique ID of the entity to override
+
+
+
+
+
+-
+
+**fieldPath:** `string` — fieldPath to override
+
+
+
+
+
+-
+
+**entity:** `*v2.Entity`
+
+The entity containing the overridden fields. The service will extract the overridable fields from
+the object and ignore all other fields.
+
+
+
+
+
+-
+
+**provenance:** `*v2.Provenance` — Additional information about the source of the override.
+
+
+
+
+
+
+
+
+
+
+
+client.Entities.RemoveEntityOverride(EntityID, FieldPath) -> *v2.Entity
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+This operation clears the override value from the specified field path on the entity.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Entities.RemoveEntityOverride(
+ context.TODO(),
+ "entityId",
+ "mil_view.disposition",
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**entityID:** `string` — The unique ID of the entity to undo an override from.
+
+
+
+
+
+-
+
+**fieldPath:** `string` — The fieldPath to clear overrides from.
+
+
+
+
+
+
+
+
+
+
+
+client.Entities.LongPollEntityEvents(request) -> *v2.EntityEventResponse
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+This is a long polling API that will first return all pre-existing data and then return all new data as
+it becomes available. If you want to start a new polling session then open a request with an empty
+'sessionToken' in the request body. The server will return a new session token in the response.
+If you want to retrieve the next batch of results from an existing polling session then send the session
+token you received from the server in the request body. If no new data is available then the server will
+hold the connection open for up to 5 minutes. After the 5 minute timeout period, the server will close the
+connection with no results and you may resume polling with the same session token. If your session falls behind
+more than 3x the total number of entities in the environment, the server will terminate your session.
+In this case you must start a new session by sending a request with an empty session token.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Entities.LongPollEntityEvents(
+ context.TODO(),
+ &v2.EntityEventRequest{
+ SessionToken: "sessionToken",
+ },
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**sessionToken:** `string` — Long-poll session identifier. Leave empty to start a new polling session.
+
+
+
+
+
+-
+
+**batchSize:** `*int` — Maximum size of response batch. Defaults to 100. Must be between 1 and 2000 (inclusive).
+
+
+
+
+
+
+
+
+
+
+
+client.Entities.StreamEntities(request) -> v2.StreamEntitiesResponse
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Establishes a persistent connection to stream entity events as they occur.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Entities.StreamEntities(
+ context.TODO(),
+ &v2.EntityStreamRequest{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**heartbeatIntervalMs:** `*int` — at what interval to send heartbeat events, defaults to 30s.
+
+
+
+
+
+-
+
+**preExistingOnly:** `*bool` — only stream pre-existing entities in the environment and then close the connection, defaults to false.
+
+
+
+
+
+-
+
+**componentsToInclude:** `[]string` — list of components to include, leave empty to include all components.
+
+
+
+
+
+
+
+
+
+
+
+## Tasks
+client.Tasks.CreateTask(request) -> *v2.Task
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Submit a request to create a task and schedule it for delivery. Tasks, once delivered, will
+be asynchronously updated by their destined agent.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Tasks.CreateTask(
+ context.TODO(),
+ &v2.TaskCreation{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**taskID:** `*string`
+
+If non-empty, will set the requested Task ID, otherwise will generate a new random
+GUID. Will reject if supplied Task ID does not match [A-Za-z0-9_-.]{5,36}.
+
+
+
+
+
+-
+
+**displayName:** `*string` — Human readable display name for this Task, should be short (<100 chars).
+
+
+
+
+
+-
+
+**description:** `*string` — Longer, free form human readable description of this Task.
+
+
+
+
+
+-
+
+**specification:** `*v2.GoogleProtobufAny` — Full set of task parameters.
+
+
+
+
+
+-
+
+**author:** `*v2.Principal`
+
+
+
+
+
+-
+
+**relations:** `*v2.Relations`
+
+Any relationships associated with this Task, such as a parent Task or an assignee
+this Task is designated to for execution.
+
+
+
+
+
+-
+
+**isExecutedElsewhere:** `*bool`
+
+If set, then the service will not trigger execution of this task on an agent. Useful
+for when ingesting tasks from an external system that is triggering execution of tasks
+on agents.
+
+
+
+
+
+-
+
+**initialEntities:** `[]*v2.TaskEntity`
+
+Indicates an initial set of entities that can be used to execute an entity aware
+task. For example, an entity Objective, an entity Keep In Zone, etc.
+
+
+
+
+
+
+
+
+
+
+
+client.Tasks.GetTask(TaskID) -> *v2.Task
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Tasks.GetTask(
+ context.TODO(),
+ "taskId",
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**taskID:** `string` — ID of task to return
+
+
+
+
+
+
+
+
+
+
+
+client.Tasks.UpdateTaskStatus(TaskID, request) -> *v2.Task
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Update the status of a task.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Tasks.UpdateTaskStatus(
+ context.TODO(),
+ "taskId",
+ &v2.TaskStatusUpdate{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**taskID:** `string` — ID of task to update status of
+
+
+
+
+
+-
+
+**statusVersion:** `*int`
+
+The status version of the task to update. This version number increments to indicate the task's
+current stage in its status lifecycle. Specifically, whenever a task's status updates, the status
+version increments by one. Any status updates received with a lower status version number than what
+is known are considered stale and ignored.
+
+
+
+
+
+-
+
+**newStatus:** `*v2.TaskStatus` — The new status of the task.
+
+
+
+
+
+-
+
+**author:** `*v2.Principal`
+
+
+
+
+
+
+
+
+
+
+
+client.Tasks.QueryTasks(request) -> *v2.TaskQueryResults
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Query for tasks by a specified search criteria.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Tasks.QueryTasks(
+ context.TODO(),
+ &v2.TaskQuery{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**pageToken:** `*string` — If set, returns results starting from the given pageToken.
+
+
+
+
+
+-
+
+**parentTaskID:** `*string`
+
+If present matches Tasks with this parent Task ID.
+Note: this is mutually exclusive with all other query parameters, i.e., either provide parent Task ID, or
+any of the remaining parameters, but not both.
+
+
+
+
+
+-
+
+**statusFilter:** `*v2.TaskQueryStatusFilter`
+
+
+
+
+
+-
+
+**updateTimeRange:** `*v2.TaskQueryUpdateTimeRange` — If provided, only provides Tasks updated within the time range.
+
+
+
+
+
+
+
+
+
+
+
+client.Tasks.ListenAsAgent(request) -> *v2.AgentRequest
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+This is a long polling API that will block until a new task is ready for delivery. If no new task is
+available then the server will hold on to your request for up to 5 minutes, after that 5 minute timeout
+period you will be expected to reinitiate a new request.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Tasks.ListenAsAgent(
+ context.TODO(),
+ &v2.AgentListener{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**agentSelector:** `*v2.EntityIDsSelector` — Selector criteria to determine which Agent Tasks the agent receives
+
+
+
+
+
+
+
+
+
+
+
+## Objects
+client.Objects.ListObjects() -> *v2.ListResponse
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Lists objects in your environment. You can define a prefix to list a subset of your objects. If you do not set a prefix, Lattice returns all available objects. By default this endpoint will list local objects only.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Objects.ListObjects(
+ context.TODO(),
+ &v2.ListObjectsRequest{
+ Prefix: v2.String(
+ "prefix",
+ ),
+ SinceTimestamp: v2.Time(
+ v2.MustParseDateTime(
+ "2024-01-15T09:30:00Z",
+ ),
+ ),
+ PageToken: v2.String(
+ "pageToken",
+ ),
+ AllObjectsInMesh: v2.Bool(
+ true,
+ ),
+ },
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**prefix:** `*string` — Filters the objects based on the specified prefix path. If no path is specified, all objects are returned.
+
+
+
+
+
+-
+
+**sinceTimestamp:** `*time.Time` — Sets the age for the oldest objects to query across the environment.
+
+
+
+
+
+-
+
+**pageToken:** `*string` — Base64 and URL-encoded cursor returned by the service to continue paging.
+
+
+
+
+
+-
+
+**allObjectsInMesh:** `*bool` — Lists objects across all environment nodes in a Lattice Mesh.
+
+
+
+
+
+
+
+
+
+
+
+client.Objects.GetObject(ObjectPath) -> string
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Fetches an object from your environment using the objectPath path parameter.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Objects.GetObject(
+ context.TODO(),
+ "objectPath",
+ &v2.GetObjectRequest{},
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**objectPath:** `string` — The path of the object to fetch.
+
+
+
+
+
+-
+
+**acceptEncoding:** `*v2.GetObjectRequestAcceptEncoding` — If set, Lattice will compress the response using the specified compression method. If the header is not defined, or the compression method is set to `identity`, no compression will be applied to the response.
+
+
+
+
+
+-
+
+**priority:** `*string` — Indicates a client's preference for the priority of the response. The value is a structured header as defined in RFC 9218. If you do not set the header, Lattice uses the default priority set for the environment. Incremental delivery directives are not supported and will be ignored.
+
+
+
+
+
+
+
+
+
+
+
+client.Objects.DeleteObject(ObjectPath) -> error
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Deletes an object from your environment given the objectPath path parameter.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Objects.DeleteObject(
+ context.TODO(),
+ "objectPath",
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**objectPath:** `string` — The path of the object to delete.
+
+
+
+
+
+
+
+
+
+
+
+client.Objects.GetObjectMetadata(ObjectPath) -> error
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Returns metadata for a specified object path. Use this to fetch metadata such as object size (size_bytes), its expiry time (expiry_time), or its latest update timestamp (last_updated_at).
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```go
+client.Objects.GetObjectMetadata(
+ context.TODO(),
+ "objectPath",
+ )
+}
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**objectPath:** `string` — The path of the object to query.
+
+
+
+
+
+
+
+
+
+
diff --git a/tasks/client.go b/tasks/client.go
index 10ce67b..7ea29f0 100644
--- a/tasks/client.go
+++ b/tasks/client.go
@@ -8,21 +8,20 @@ import (
core "github.com/anduril/lattice-sdk-go/v2/core"
internal "github.com/anduril/lattice-sdk-go/v2/internal"
option "github.com/anduril/lattice-sdk-go/v2/option"
- http "net/http"
)
type Client struct {
WithRawResponse *RawClient
+ options *core.RequestOptions
baseURL string
caller *internal.Caller
- header http.Header
}
-func NewClient(opts ...option.RequestOption) *Client {
- options := core.NewRequestOptions(opts...)
+func NewClient(options *core.RequestOptions) *Client {
return &Client{
WithRawResponse: NewRawClient(options),
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -30,7 +29,6 @@ func NewClient(opts ...option.RequestOption) *Client {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
diff --git a/tasks/raw_client.go b/tasks/raw_client.go
index 75d15f8..31d754a 100644
--- a/tasks/raw_client.go
+++ b/tasks/raw_client.go
@@ -14,11 +14,12 @@ import (
type RawClient struct {
baseURL string
caller *internal.Caller
- header http.Header
+ options *core.RequestOptions
}
func NewRawClient(options *core.RequestOptions) *RawClient {
return &RawClient{
+ options: options,
baseURL: options.BaseURL,
caller: internal.NewCaller(
&internal.CallerParams{
@@ -26,7 +27,6 @@ func NewRawClient(options *core.RequestOptions) *RawClient {
MaxAttempts: options.MaxAttempts,
},
),
- header: options.ToHeader(),
}
}
@@ -43,7 +43,7 @@ func (r *RawClient) CreateTask(
)
endpointURL := baseURL + "/api/v1/tasks"
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Content-Type", "application/json")
@@ -102,7 +102,7 @@ func (r *RawClient) GetTask(
taskID,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
errorCodes := internal.ErrorCodes{
@@ -165,7 +165,7 @@ func (r *RawClient) UpdateTaskStatus(
taskID,
)
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Content-Type", "application/json")
@@ -225,7 +225,7 @@ func (r *RawClient) QueryTasks(
)
endpointURL := baseURL + "/api/v1/tasks/query"
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Content-Type", "application/json")
@@ -285,7 +285,7 @@ func (r *RawClient) ListenAsAgent(
)
endpointURL := baseURL + "/api/v1/agent/listen"
headers := internal.MergeHeaders(
- r.header.Clone(),
+ r.options.ToHeader(),
options.ToHeader(),
)
headers.Add("Content-Type", "application/json")
diff --git a/types.go b/types.go
index a020796..9bc678f 100644
--- a/types.go
+++ b/types.go
@@ -2123,6 +2123,8 @@ type Entity struct {
Supplies *Supplies `json:"supplies,omitempty" url:"supplies,omitempty"`
// Orbit information for space objects.
Orbit *Orbit `json:"orbit,omitempty" url:"orbit,omitempty"`
+ // Symbology/iconography for the entity respecting an existing standard.
+ Symbology *Symbology `json:"symbology,omitempty" url:"symbology,omitempty"`
extraProperties map[string]interface{}
rawJSON json.RawMessage
@@ -2387,6 +2389,13 @@ func (e *Entity) GetOrbit() *Orbit {
return e.Orbit
}
+func (e *Entity) GetSymbology() *Symbology {
+ if e == nil {
+ return nil
+ }
+ return e.Symbology
+}
+
func (e *Entity) GetExtraProperties() map[string]interface{} {
return e.extraProperties
}
@@ -4977,6 +4986,52 @@ func (m *MergedFrom) String() string {
return fmt.Sprintf("%#v", m)
}
+type MilStd2525C struct {
+ Sidc *string `json:"sidc,omitempty" url:"sidc,omitempty"`
+
+ extraProperties map[string]interface{}
+ rawJSON json.RawMessage
+}
+
+func (m *MilStd2525C) GetSidc() *string {
+ if m == nil {
+ return nil
+ }
+ return m.Sidc
+}
+
+func (m *MilStd2525C) GetExtraProperties() map[string]interface{} {
+ return m.extraProperties
+}
+
+func (m *MilStd2525C) UnmarshalJSON(data []byte) error {
+ type unmarshaler MilStd2525C
+ var value unmarshaler
+ if err := json.Unmarshal(data, &value); err != nil {
+ return err
+ }
+ *m = MilStd2525C(value)
+ extraProperties, err := internal.ExtractExtraProperties(data, *m)
+ if err != nil {
+ return err
+ }
+ m.extraProperties = extraProperties
+ m.rawJSON = json.RawMessage(data)
+ return nil
+}
+
+func (m *MilStd2525C) String() string {
+ if len(m.rawJSON) > 0 {
+ if value, err := internal.StringifyJSON(m.rawJSON); err == nil {
+ return value
+ }
+ }
+ if value, err := internal.StringifyJSON(m); err == nil {
+ return value
+ }
+ return fmt.Sprintf("%#v", m)
+}
+
// Provides the disposition, environment, and nationality of an Entity.
type MilView struct {
Disposition *MilViewDisposition `json:"disposition,omitempty" url:"disposition,omitempty"`
@@ -8588,6 +8643,53 @@ func (s *Supplies) String() string {
return fmt.Sprintf("%#v", s)
}
+// Symbology associated with an entity.
+type Symbology struct {
+ MilStd2525C *MilStd2525C `json:"milStd2525C,omitempty" url:"milStd2525C,omitempty"`
+
+ extraProperties map[string]interface{}
+ rawJSON json.RawMessage
+}
+
+func (s *Symbology) GetMilStd2525C() *MilStd2525C {
+ if s == nil {
+ return nil
+ }
+ return s.MilStd2525C
+}
+
+func (s *Symbology) GetExtraProperties() map[string]interface{} {
+ return s.extraProperties
+}
+
+func (s *Symbology) UnmarshalJSON(data []byte) error {
+ type unmarshaler Symbology
+ var value unmarshaler
+ if err := json.Unmarshal(data, &value); err != nil {
+ return err
+ }
+ *s = Symbology(value)
+ extraProperties, err := internal.ExtractExtraProperties(data, *s)
+ if err != nil {
+ return err
+ }
+ s.extraProperties = extraProperties
+ s.rawJSON = json.RawMessage(data)
+ return nil
+}
+
+func (s *Symbology) String() string {
+ if len(s.rawJSON) > 0 {
+ if value, err := internal.StringifyJSON(s.rawJSON); err == nil {
+ return value
+ }
+ }
+ if value, err := internal.StringifyJSON(s); err == nil {
+ return value
+ }
+ return fmt.Sprintf("%#v", s)
+}
+
// symmetric 2d matrix only representing the upper right triangle, useful for
//
// covariance matrices