From aae9db4de45b3c09fb923700c3e9e438f5988ebd Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Tue, 5 Dec 2023 19:06:18 -0500 Subject: [PATCH] fix: use latest api interfaces --- .github/workflows/main.yaml | 4 +++- .openapi-generator/FILES | 11 +++++++---- api_open_fga.go | 22 +++++++++++++++------ api_open_fga_test.go | 20 +++++++++---------- client/client_test.go | 10 +++++----- docs/AuthorizationModel.md | 9 ++------- docs/RelationshipCondition.md | 9 +++++++-- go.mod | 2 +- model_authorization_model.go | 35 ++++++++++++--------------------- model_relationship_condition.go | 35 +++++++++++++++++++++------------ 10 files changed, 86 insertions(+), 71 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 6800c0a..d171b5a 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -38,7 +38,9 @@ jobs: needs: [test] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.5.4 + with: + fetch-depth: 0 - uses: Roang-zero1/github-create-release-action@57eb9bdce7a964e48788b9e78b5ac766cb684803 with: diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index cd7e220..8906a07 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -2,6 +2,7 @@ .github/CODEOWNERS .github/ISSUE_TEMPLATE/bug_report.md .github/ISSUE_TEMPLATE/feature_request.md +.github/dependabot.yaml .github/workflows/main.yaml .github/workflows/semgrep.yaml .gitignore @@ -66,6 +67,7 @@ docs/Store.md docs/Tuple.md docs/TupleChange.md docs/TupleKey.md +docs/TupleKeyWithoutCondition.md docs/TupleOperation.md docs/TupleToUserset.md docs/TypeDefinition.md @@ -81,8 +83,8 @@ docs/WriteAssertionsRequest.md docs/WriteAuthorizationModelRequest.md docs/WriteAuthorizationModelResponse.md docs/WriteRequest.md -docs/WriteRequestTupleKey.md -docs/WriteRequestTupleKeys.md +docs/WriteRequestDeletes.md +docs/WriteRequestWrites.md git_push.sh go.mod go.sum @@ -135,6 +137,7 @@ model_store.go model_tuple.go model_tuple_change.go model_tuple_key.go +model_tuple_key_without_condition.go model_tuple_operation.go model_tuple_to_userset.go model_type_definition.go @@ -150,8 +153,8 @@ model_write_assertions_request.go model_write_authorization_model_request.go model_write_authorization_model_response.go model_write_request.go -model_write_request_tuple_key.go -model_write_request_tuple_keys.go +model_write_request_deletes.go +model_write_request_writes.go oauth2/LICENSE oauth2/ORIGINAL_AUTHORS oauth2/ORIGINAL_CONTRIBUTORS diff --git a/api_open_fga.go b/api_open_fga.go index c7f71df..589caad 100644 --- a/api_open_fga.go +++ b/api_open_fga.go @@ -34,8 +34,9 @@ type OpenFgaApi interface { /* * Check Check whether a user is authorized to access an object * The Check API queries to check if the user has a certain relationship with an object in a certain store. - A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. + A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. + You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will return whether the relationship exists in the field `allowed`. ## Example @@ -186,7 +187,8 @@ type OpenFgaApi interface { * ListObjects List all objects of the given type that the user has a relation with * The ListObjects API returns a list of all the objects of the given type that the user has a relation with. To achieve this, both the store tuples and the authorization model are used. An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. - You may also specify `contextual_tuples` that will be treated as regular tuples. + You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. + You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related objects in an array in the "objects" field of the response and they will be strings in the object format `:` (e.g. "document:roadmap"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. @@ -451,6 +453,8 @@ type OpenFgaApi interface { * ReadChanges Return a list of all the tuple changes * The ReadChanges API will return a paginated list of tuple changes (additions and deletions) that occurred in a given store, sorted by ascending time. The response will include a continuation token that is used to get the next set of changes. If there are no changes after the provided continuation token, the same token will be returned in order for it to be used when new changes are recorded. If the store never had any tuples added or removed, this token will be empty. You can use the `type` parameter to only get the list of tuple changes that affect objects of that type. + When reading a write tuple change, if it was conditioned, the condition will be returned. + When reading a delete tuple change, the condition will NOT be returned regardless of whether it was originally conditioned or not. * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @return ApiReadChangesRequest @@ -466,7 +470,8 @@ type OpenFgaApi interface { /* * Write Add or delete tuples from the store * The Write API will update the tuples for a certain store. Tuples and type definitions allow OpenFGA to determine whether a relationship exists between an object and an user. - In the body, `writes` adds new tuples while `deletes` removes existing tuples. The API is not idempotent: if, later on, you try to add the same tuple, or if you try to delete a non-existing tuple, it will throw an error. + In the body, `writes` adds new tuples and `deletes` removes existing tuples. When deleting a tuple, any `condition` specified with it is ignored. + The API is not idempotent: if, later on, you try to add the same tuple key (even if the `condition` is different), or if you try to delete a non-existing tuple, it will throw an error. An `authorization_model_id` may be specified in the body. If it is, it will be used to assert that each written tuple (not deleted) is valid for the model specified. If it is not specified, the latest authorization model ID will be used. ## Example ### Adding relationships @@ -606,8 +611,9 @@ func (r ApiCheckRequest) Execute() (CheckResponse, *_nethttp.Response, error) { - Check Check whether a user is authorized to access an object - The Check API queries to check if the user has a certain relationship with an object in a certain store. -A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. +A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. +You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will return whether the relationship exists in the field `allowed`. ## Example @@ -1996,7 +2002,8 @@ func (r ApiListObjectsRequest) Execute() (ListObjectsResponse, *_nethttp.Respons - The ListObjects API returns a list of all the objects of the given type that the user has a relation with. To achieve this, both the store tuples and the authorization model are used. An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. -You may also specify `contextual_tuples` that will be treated as regular tuples. +You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. +You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related objects in an array in the "objects" field of the response and they will be strings in the object format `:` (e.g. "document:roadmap"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. @@ -3807,6 +3814,8 @@ func (r ApiReadChangesRequest) Execute() (ReadChangesResponse, *_nethttp.Respons - The ReadChanges API will return a paginated list of tuple changes (additions and deletions) that occurred in a given store, sorted by ascending time. The response will include a continuation token that is used to get the next set of changes. If there are no changes after the provided continuation token, the same token will be returned in order for it to be used when new changes are recorded. If the store never had any tuples added or removed, this token will be empty. You can use the `type` parameter to only get the list of tuple changes that affect objects of that type. +When reading a write tuple change, if it was conditioned, the condition will be returned. +When reading a delete tuple change, the condition will NOT be returned regardless of whether it was originally conditioned or not. - @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return ApiReadChangesRequest @@ -4081,7 +4090,8 @@ func (r ApiWriteRequest) Execute() (map[string]interface{}, *_nethttp.Response, - Write Add or delete tuples from the store - The Write API will update the tuples for a certain store. Tuples and type definitions allow OpenFGA to determine whether a relationship exists between an object and an user. -In the body, `writes` adds new tuples while `deletes` removes existing tuples. The API is not idempotent: if, later on, you try to add the same tuple, or if you try to delete a non-existing tuple, it will throw an error. +In the body, `writes` adds new tuples and `deletes` removes existing tuples. When deleting a tuple, any `condition` specified with it is ignored. +The API is not idempotent: if, later on, you try to add the same tuple key (even if the `condition` is different), or if you try to delete a non-existing tuple, it will throw an error. An `authorization_model_id` may be specified in the body. If it is, it will be used to assert that each written tuple (not deleted) is valid for the model specified. If it is not specified, the latest authorization model ID will be used. ## Example ### Adding relationships diff --git a/api_open_fga_test.go b/api_open_fga_test.go index b81df49..3f3fe91 100644 --- a/api_open_fga_test.go +++ b/api_open_fga_test.go @@ -119,11 +119,11 @@ func TestOpenFgaApiConfiguration(t *testing.T) { func(req *http.Request) (*http.Response, error) { resp, err := httpmock.NewJsonResponse(200, ReadAuthorizationModelsResponse{AuthorizationModels: []AuthorizationModel{ { - Id: PtrString("01GXSA8YR785C4FYS3C0RTG7B1"), + Id: "01GXSA8YR785C4FYS3C0RTG7B1", TypeDefinitions: []TypeDefinition{}, }, { - Id: PtrString("01GXSBM5PVYHCJNRNKXMB4QZTW"), + Id: "01GXSBM5PVYHCJNRNKXMB4QZTW", TypeDefinitions: []TypeDefinition{}, }, }}) @@ -251,11 +251,11 @@ func TestOpenFgaApiConfiguration(t *testing.T) { func(req *http.Request) (*http.Response, error) { resp, err := httpmock.NewJsonResponse(200, ReadAuthorizationModelsResponse{AuthorizationModels: []AuthorizationModel{ { - Id: PtrString("01GXSA8YR785C4FYS3C0RTG7B1"), + Id: "01GXSA8YR785C4FYS3C0RTG7B1", TypeDefinitions: []TypeDefinition{}, }, { - Id: PtrString("01GXSBM5PVYHCJNRNKXMB4QZTW"), + Id: "01GXSBM5PVYHCJNRNKXMB4QZTW", TypeDefinitions: []TypeDefinition{}, }, }}) @@ -315,11 +315,11 @@ func TestOpenFgaApiConfiguration(t *testing.T) { func(req *http.Request) (*http.Response, error) { resp, err := httpmock.NewJsonResponse(200, ReadAuthorizationModelsResponse{AuthorizationModels: []AuthorizationModel{ { - Id: PtrString("01GXSA8YR785C4FYS3C0RTG7B1"), + Id: "01GXSA8YR785C4FYS3C0RTG7B1", TypeDefinitions: []TypeDefinition{}, }, { - Id: PtrString("01GXSBM5PVYHCJNRNKXMB4QZTW"), + Id: "01GXSBM5PVYHCJNRNKXMB4QZTW", TypeDefinitions: []TypeDefinition{}, }, }}) @@ -451,8 +451,8 @@ func TestOpenFgaApi(t *testing.T) { t.Fatalf("%v", err) } - if *(got.AuthorizationModels[0].Id) != *(expectedResponse.AuthorizationModels[0].Id) { - t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, *(got.AuthorizationModels[0].Id), *(expectedResponse.AuthorizationModels[0].Id)) + if got.AuthorizationModels[0].Id != expectedResponse.AuthorizationModels[0].Id { + t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, got.AuthorizationModels[0].Id, expectedResponse.AuthorizationModels[0].Id) } }) @@ -528,7 +528,7 @@ func TestOpenFgaApi(t *testing.T) { if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil { t.Fatalf("%v", err) } - modelId := *(*expectedResponse.AuthorizationModel).Id + modelId := expectedResponse.AuthorizationModel.Id httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -555,7 +555,7 @@ func TestOpenFgaApi(t *testing.T) { t.Fatalf("%v", err) } - if *(*got.AuthorizationModel).Id != modelId { + if got.AuthorizationModel.Id != modelId { t.Fatalf("OpenFga%v().Execute() = %v, want %v", test.Name, string(responseJson), test.JsonResponse) } }) diff --git a/client/client_test.go b/client/client_test.go index c13141f..45d3054 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -364,8 +364,8 @@ func TestOpenFgaClient(t *testing.T) { t.Fatalf("%v", err) } - if *(got.AuthorizationModels[0].Id) != *(expectedResponse.AuthorizationModels[0].Id) { - t.Fatalf("OpenFgaClient.%v() = %v, want %v", test.Name, *(got.AuthorizationModels[0].Id), *(expectedResponse.AuthorizationModels[0].Id)) + if got.AuthorizationModels[0].Id != expectedResponse.AuthorizationModels[0].Id { + t.Fatalf("OpenFgaClient.%v() = %v, want %v", test.Name, got.AuthorizationModels[0].Id, expectedResponse.AuthorizationModels[0].Id) } // ReadAuthorizationModels without options should work _, err = fgaClient.ReadAuthorizationModels(context.Background()).Execute() @@ -470,7 +470,7 @@ func TestOpenFgaClient(t *testing.T) { if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil { t.Fatalf("%v", err) } - modelId := *(expectedResponse.AuthorizationModel).Id + modelId := expectedResponse.AuthorizationModel.Id httpmock.Activate() defer httpmock.DeactivateAndReset() @@ -496,7 +496,7 @@ func TestOpenFgaClient(t *testing.T) { t.Fatalf("%v", err) } - if *(got.AuthorizationModel).Id != modelId { + if got.AuthorizationModel.Id != modelId { t.Fatalf("OpenFgaClient.%v() = %v, want %v", test.Name, string(responseJson), test.JsonResponse) } // ReadAuthorizationModel without options should not work @@ -528,7 +528,7 @@ func TestOpenFgaClient(t *testing.T) { if err := json.Unmarshal([]byte(test.JsonResponse), &expectedResponse); err != nil { t.Fatalf("%v", err) } - modelId := *(expectedResponse.AuthorizationModels[0].Id) + modelId := expectedResponse.AuthorizationModels[0].Id httpmock.Activate() defer httpmock.DeactivateAndReset() diff --git a/docs/AuthorizationModel.md b/docs/AuthorizationModel.md index 5e78b2b..c3f2687 100644 --- a/docs/AuthorizationModel.md +++ b/docs/AuthorizationModel.md @@ -4,7 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**Id** | Pointer to **string** | | [optional] +**Id** | **string** | | **SchemaVersion** | **string** | | **TypeDefinitions** | [**[]TypeDefinition**](TypeDefinition.md) | | **Conditions** | Pointer to [**map[string]Condition**](Condition.md) | | [optional] @@ -13,7 +13,7 @@ Name | Type | Description | Notes ### NewAuthorizationModel -`func NewAuthorizationModel(schemaVersion string, typeDefinitions []TypeDefinition, ) *AuthorizationModel` +`func NewAuthorizationModel(id string, schemaVersion string, typeDefinitions []TypeDefinition, ) *AuthorizationModel` NewAuthorizationModel instantiates a new AuthorizationModel object This constructor will assign default values to properties that have it defined, @@ -47,11 +47,6 @@ and a boolean to check if the value has been set. SetId sets Id field to given value. -### HasId - -`func (o *AuthorizationModel) HasId() bool` - -HasId returns a boolean if a field has been set. ### GetSchemaVersion diff --git a/docs/RelationshipCondition.md b/docs/RelationshipCondition.md index 43e3cef..3ca44cf 100644 --- a/docs/RelationshipCondition.md +++ b/docs/RelationshipCondition.md @@ -5,13 +5,13 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **Name** | **string** | A reference (by name) of the relationship condition defined in the authorization model. | -**Context** | **map[string]interface{}** | Additional context/data to persist along with the condition. The keys must match the parameters defined by the condition, and the value types must match the parameter type definitions. | +**Context** | Pointer to **map[string]interface{}** | Additional context/data to persist along with the condition. The keys must match the parameters defined by the condition, and the value types must match the parameter type definitions. | [optional] ## Methods ### NewRelationshipCondition -`func NewRelationshipCondition(name string, context map[string]interface{}, ) *RelationshipCondition` +`func NewRelationshipCondition(name string, ) *RelationshipCondition` NewRelationshipCondition instantiates a new RelationshipCondition object This constructor will assign default values to properties that have it defined, @@ -65,6 +65,11 @@ and a boolean to check if the value has been set. SetContext sets Context field to given value. +### HasContext + +`func (o *RelationshipCondition) HasContext() bool` + +HasContext returns a boolean if a field has been set. [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/go.mod b/go.mod index 5242eb3..051fda9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/openfga/go-sdk -go 1.21.1 +go 1.21.4 require ( github.com/jarcoal/httpmock v1.3.1 diff --git a/model_authorization_model.go b/model_authorization_model.go index 58af688..07c61c5 100644 --- a/model_authorization_model.go +++ b/model_authorization_model.go @@ -18,7 +18,7 @@ import ( // AuthorizationModel struct for AuthorizationModel type AuthorizationModel struct { - Id *string `json:"id,omitempty"yaml:"id,omitempty"` + Id string `json:"id"yaml:"id"` SchemaVersion string `json:"schema_version"yaml:"schema_version"` TypeDefinitions []TypeDefinition `json:"type_definitions"yaml:"type_definitions"` Conditions *map[string]Condition `json:"conditions,omitempty"yaml:"conditions,omitempty"` @@ -28,8 +28,9 @@ type AuthorizationModel struct { // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewAuthorizationModel(schemaVersion string, typeDefinitions []TypeDefinition) *AuthorizationModel { +func NewAuthorizationModel(id string, schemaVersion string, typeDefinitions []TypeDefinition) *AuthorizationModel { this := AuthorizationModel{} + this.Id = id this.SchemaVersion = schemaVersion this.TypeDefinitions = typeDefinitions return &this @@ -43,36 +44,28 @@ func NewAuthorizationModelWithDefaults() *AuthorizationModel { return &this } -// GetId returns the Id field value if set, zero value otherwise. +// GetId returns the Id field value func (o *AuthorizationModel) GetId() string { - if o == nil || o.Id == nil { + if o == nil { var ret string return ret } - return *o.Id + + return o.Id } -// GetIdOk returns a tuple with the Id field value if set, nil otherwise +// GetIdOk returns a tuple with the Id field value // and a boolean to check if the value has been set. func (o *AuthorizationModel) GetIdOk() (*string, bool) { - if o == nil || o.Id == nil { + if o == nil { return nil, false } - return o.Id, true -} - -// HasId returns a boolean if a field has been set. -func (o *AuthorizationModel) HasId() bool { - if o != nil && o.Id != nil { - return true - } - - return false + return &o.Id, true } -// SetId gets a reference to the given string and assigns it to the Id field. +// SetId sets field value func (o *AuthorizationModel) SetId(v string) { - o.Id = &v + o.Id = v } // GetSchemaVersion returns the SchemaVersion field value @@ -157,9 +150,7 @@ func (o *AuthorizationModel) SetConditions(v map[string]Condition) { func (o AuthorizationModel) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} - if o.Id != nil { - toSerialize["id"] = o.Id - } + toSerialize["id"] = o.Id toSerialize["schema_version"] = o.SchemaVersion toSerialize["type_definitions"] = o.TypeDefinitions if o.Conditions != nil { diff --git a/model_relationship_condition.go b/model_relationship_condition.go index 36565ad..37f2b7b 100644 --- a/model_relationship_condition.go +++ b/model_relationship_condition.go @@ -21,17 +21,16 @@ type RelationshipCondition struct { // A reference (by name) of the relationship condition defined in the authorization model. Name string `json:"name"yaml:"name"` // Additional context/data to persist along with the condition. The keys must match the parameters defined by the condition, and the value types must match the parameter type definitions. - Context map[string]interface{} `json:"context"yaml:"context"` + Context *map[string]interface{} `json:"context,omitempty"yaml:"context,omitempty"` } // NewRelationshipCondition instantiates a new RelationshipCondition object // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewRelationshipCondition(name string, context map[string]interface{}) *RelationshipCondition { +func NewRelationshipCondition(name string) *RelationshipCondition { this := RelationshipCondition{} this.Name = name - this.Context = context return &this } @@ -67,34 +66,44 @@ func (o *RelationshipCondition) SetName(v string) { o.Name = v } -// GetContext returns the Context field value +// GetContext returns the Context field value if set, zero value otherwise. func (o *RelationshipCondition) GetContext() map[string]interface{} { - if o == nil { + if o == nil || o.Context == nil { var ret map[string]interface{} return ret } - - return o.Context + return *o.Context } -// GetContextOk returns a tuple with the Context field value +// GetContextOk returns a tuple with the Context field value if set, nil otherwise // and a boolean to check if the value has been set. func (o *RelationshipCondition) GetContextOk() (*map[string]interface{}, bool) { - if o == nil { + if o == nil || o.Context == nil { return nil, false } - return &o.Context, true + return o.Context, true +} + +// HasContext returns a boolean if a field has been set. +func (o *RelationshipCondition) HasContext() bool { + if o != nil && o.Context != nil { + return true + } + + return false } -// SetContext sets field value +// SetContext gets a reference to the given map[string]interface{} and assigns it to the Context field. func (o *RelationshipCondition) SetContext(v map[string]interface{}) { - o.Context = v + o.Context = &v } func (o RelationshipCondition) MarshalJSON() ([]byte, error) { toSerialize := map[string]interface{}{} toSerialize["name"] = o.Name - toSerialize["context"] = o.Context + if o.Context != nil { + toSerialize["context"] = o.Context + } return json.Marshal(toSerialize) }