// Convert the wrapped attribute value into Any proto message
value, err := anypb.New(&v1.BooleanValue{
Data: true,
})
if err != nil {
// Handle error
}
cr, err := client.Data.Write(context.Background(), &v1.DataWriteRequest{
TenantId: "t1",
Metadata: &v1.DataWriteRequestMetadata{
SchemaVersion: "",
},
Tuples: []*v1.Tuple{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Relation: "editor",
Subject: &v1.Subject{
Type: "user",
Id: "1",
Relation: "",
},
},
},
Attributes: []*v1.Attribute{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Attribute: "is_private",
Value: value,
},
},
})const booleanValue = BooleanValue.fromJSON({ data: true });
const value = Any.fromJSON({
typeUrl: 'type.googleapis.com/base.v1.BooleanValue',
value: BooleanValue.encode(booleanValue).finish()
});
client.data.write({
tenantId: "t1",
metadata: {
schemaVersion: ""
},
tuples: [{
entity: {
type: "document",
id: "1"
},
relation: "editor",
subject: {
type: "user",
id: "1"
}
}],
attributes: [{
entity: {
type: "document",
id: "1"
},
attribute: "is_private",
value: value,
}]
}).then((response) => {
// handle response
})curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/write' \
--header 'Content-Type: application/json' \
--data-raw '{
"metadata": {
"schema_version": ""
},
"tuples": [
{
"entity": {
"type": "document",
"id": "1"
},
"relation": "editor",
"subject": {
"type": "user",
"id": "1"
}
}
],
"attributes": [
{
"entity": {
"type": "document",
"id": "1"
},
"attribute": "is_private",
"value": {
"@type": "type.googleapis.com/base.v1.BooleanValue",
"data": true
}
}
]
}'{
"snap_token": "<string>"
}{
"code": 123,
"message": "<string>",
"details": [
{
"@type": "<string>"
}
]
}Write Authorization Data
// Convert the wrapped attribute value into Any proto message
value, err := anypb.New(&v1.BooleanValue{
Data: true,
})
if err != nil {
// Handle error
}
cr, err := client.Data.Write(context.Background(), &v1.DataWriteRequest{
TenantId: "t1",
Metadata: &v1.DataWriteRequestMetadata{
SchemaVersion: "",
},
Tuples: []*v1.Tuple{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Relation: "editor",
Subject: &v1.Subject{
Type: "user",
Id: "1",
Relation: "",
},
},
},
Attributes: []*v1.Attribute{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Attribute: "is_private",
Value: value,
},
},
})const booleanValue = BooleanValue.fromJSON({ data: true });
const value = Any.fromJSON({
typeUrl: 'type.googleapis.com/base.v1.BooleanValue',
value: BooleanValue.encode(booleanValue).finish()
});
client.data.write({
tenantId: "t1",
metadata: {
schemaVersion: ""
},
tuples: [{
entity: {
type: "document",
id: "1"
},
relation: "editor",
subject: {
type: "user",
id: "1"
}
}],
attributes: [{
entity: {
type: "document",
id: "1"
},
attribute: "is_private",
value: value,
}]
}).then((response) => {
// handle response
})curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/write' \
--header 'Content-Type: application/json' \
--data-raw '{
"metadata": {
"schema_version": ""
},
"tuples": [
{
"entity": {
"type": "document",
"id": "1"
},
"relation": "editor",
"subject": {
"type": "user",
"id": "1"
}
}
],
"attributes": [
{
"entity": {
"type": "document",
"id": "1"
},
"attribute": "is_private",
"value": {
"@type": "type.googleapis.com/base.v1.BooleanValue",
"data": true
}
}
]
}'{
"snap_token": "<string>"
}{
"code": 123,
"message": "<string>",
"details": [
{
"@type": "<string>"
}
]
}/v1/tenants/{tenant_id}/data/write endpoint for both creating relation tuples and for creating attribute data. POST /v1/tenants/{tenant_id}/data/write
Content
- Example Relationship Creation
- Example Attributes Creation
- Creating Attributes and Relationship In Single Request
- Suggested Workflow
- Parameters & Properties
Example Relationship Creation
Let’s create an example relation tuple. If user:3 has been granted an admin role in organization:1, relational tupleorganization:1#admin@user:3 should be created as follows:
- Go
- Node
- Python
- cURL
rr, err: = client.Data.Write(context.Background(), & v1.DataWriteRequest {
TenantId: "t1",
Metadata: &v1.DataWriteRequestMetadata {
SchemaVersion: ""
},
Tuples: [] * v1.Tuple {
{
Entity: & v1.Entity {
Type: "organization",
Id: "1",
},
Relation: "admin",
Subject: & v1.Subject {
Type: "user",
Id: "3",
},
}
},
})
client.data
.write({
tenantId: "t1",
metadata: {
schemaVersion: "",
},
tuples: [
{
entity: {
type: "organization",
id: "1",
},
relation: "admin",
subject: {
type: "user",
id: "3",
},
},
],
})
.then((response) => {
// handle response
});
with permify.ApiClient(configuration) as api_client:
api_instance = permify.DataApi(api_client)
body = permify.DataWriteRequest(
tenant_id='t1',
metadata={"schemaVersion": ""},
tuples=[{
"entity": {
"type": "organization",
"id": "1",
},
"relation": "admin",
"subject": {
"type": "user",
"id": "3",
},
}]
)
curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/write' \
--header 'Content-Type: application/json' \
--data-raw '{
"metadata": {
"schema_version": ""
},
"tuples": [
{
"entity": {
"type": "organization",
"id": "1"
},
"relation": "admin",
"subject":{
"type": "user",
"id": "3",
"relation": ""
}
}
]
}'
Example Attribute Creation
You can useattributes argument to create attribute/attributes with a single API call, similarly creating a relational tuple.
Let’s say document:1 is a private (boolean) document, that only specific users have view access - document:1$is_private|boolean:true.
entity $ attribute | value- Go
- Node
- Python
- cURL
// Convert the wrapped attribute value into Any proto message
value, err := anypb.New(&v1.BooleanValue{
Data: true,
})
if err != nil {
// Handle error
}
cr, err := client.Data.Write(context.Background(), &v1.DataWriteRequest{
TenantId: "t1",,
Metadata: &v1.DataWriteRequestMetadata{
SchemaVersion: "",
},
Attributes: []*v1.Attribute{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Attribute: "is_private",
Value: value,
},
},
})
const booleanValue = BooleanValue.fromJSON({ data: true });
const value = Any.fromJSON({
typeUrl: 'type.googleapis.com/base.v1.BooleanValue',
value: BooleanValue.encode(booleanValue).finish()
});
client.data.write({
tenantId: "t1",
metadata: {
schemaVersion: ""
},
attributes: [{
entity: {
type: "document",
id: "1"
},
attribute: "is_private",
value: value,
}]
}).then((response) => {
// handle response
})
boolean_value = BooleanValue.from_json({"data": True})
value = Any.from_json({
"typeUrl": 'type.googleapis.com/base.v1.BooleanValue',
"value": BooleanValue.encode(boolean_value).finish()
})
with permify.ApiClient(configuration) as api_client:
api_instance = permify.DataApi(api_client)
tenant_id = 't1'
body = permify.DataWriteRequest(
tenant_id=tenant_id,
metadata={"schemaVersion": ""},
attributes=[{
"entity": {
"type": "document",
"id": "1"
},
"attribute": "is_private",
"value": value,
}]
)
curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/write' \
--header 'Content-Type: application/json' \
--data-raw '{
{
"metadata": {
"schema_version": ""
},
"attributes": [
{
"entity": {
"type": "document",
"id": "1"
},
"attribute": "is_private",
"value": {
"@type": "type.googleapis.com/base.v1.BooleanValue",
"data": true
}
}
]
}
}'
- type.googleapis.com/base.v1.StringValue
- type.googleapis.com/base.v1.BooleanValue
- type.googleapis.com/base.v1.IntegerValue
- type.googleapis.com/base.v1.DoubleValue
- type.googleapis.com/base.v1.StringArrayValue
- type.googleapis.com/base.v1.BooleanArrayValue
- type.googleapis.com/base.v1.IntegerArrayValue
- type.googleapis.com/base.v1.DoubleArrayValue
Creating Attributes and Relationship In Single Request
Assume we want to both create relational tuple and attribute within in single request. Specifically we want to create following tuples,document:1#editor@user:1document:1$is_private|boolean:true
- Go
- Node
- Python
// Convert the wrapped attribute value into Any proto message
value, err := anypb.New(&v1.BooleanValue{
Data: true,
})
if err != nil {
// Handle error
}
cr, err := client.Data.Write(context.Background(), &v1.DataWriteRequest{
TenantId: "t1",,
Metadata: &v1.DataWriteRequestMetadata{
SchemaVersion: "",
},
Tuples: []*v1.Attribute{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Relation: "editor",
Subject: &v1.Subject{
Type: "user",
Id: "1",
Relation: "",
},
},
},
Attributes: []*v1.Attribute{
{
Entity: &v1.Entity{
Type: "document",
Id: "1",
},
Attribute: "is_private",
Value: value,
},
},
})
const booleanValue = BooleanValue.fromJSON({ data: true });
const value = Any.fromJSON({
typeUrl: 'type.googleapis.com/base.v1.BooleanValue',
value: BooleanValue.encode(booleanValue).finish()
});
client.data.write({
tenantId: "t1",
metadata: {
schemaVersion: ""
},
tuples: [{
entity: {
type: "document",
id: "1"
},
relation: "editor",
subject: {
type: "user",
id: "1"
}
}],
attributes: [{
entity: {
type: "document",
id: "1"
},
attribute: "is_private",
value: value,
}]
}).then((response) => {
// handle response
})
boolean_value = BooleanValue.from_json({"data": True})
value = Any.from_json({
"typeUrl": 'type.googleapis.com/base.v1.BooleanValue',
"value": BooleanValue.encode(boolean_value).finish()
})
with permify.ApiClient(configuration) as api_client:
api_instance = permify.DataApi(api_client)
tenant_id = 't1'
body = permify.DataWriteRequest(
tenant_id=tenant_id,
metadata={"schemaVersion": ""},
tuples=[{
"entity": {
"type": "document",
"id": "1"
},
"relation": "editor",
"subject": {
"type": "user",
"id": "1"
},
}],
attributes=[{
"entity": {
"type": "document",
"id": "1"
},
"attribute": "is_private",
"value": value,
}]
)
</Tab>
<Tab title="cURL">
```curl
curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/write' \
--header 'Content-Type: application/json' \
--data-raw '{
{
"metadata": {
"schema_version": ""
},
"tuples": [
{
"entity": {
"type": "document",
"id": "1"
},
"relation": "editor",
"subject": {
"type": "user",
"id": "1"
}
}
],
"attributes": [
{
"entity": {
"type": "document",
"id": "1"
},
"attribute": "is_private",
"value": {
"@type": "type.googleapis.com/base.v1.BooleanValue",
"data": true
}
}
]
}
}'
Suggested Workflow
The most of the data that should written in Permify also needs to be write or engage with applications database as well. So where and how to write relationships into both applications database and Permify ?Two Phase Commit Approach
In a standard relational based databases, the suggested place to write relationships to Permify is sending the write request in database transaction of the client action: such as storing the owner of the document when an user creates a document. To give more concurrent example of this action, let’s take a look at below createDocument functionfunc CreateDocuments(db *gorm.DB) error {
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
// if transaction fails, then delete malformed relation tuple
permify.DeleteData(...)
}
}()
if err := tx.Error; err != nil {
return err
}
if err := tx.Create(docs).Error; err != nil {
tx.Rollback()
// if transaction fails, then delete malformed relation tuple
permify.DeleteData(...)
return err
}
// if transaction successful, write relation tuple to Permify
permify.WriteData(...)
return tx.Commit().Error
}
Data That Not Stored In Application Database
Although ownership generally stored in application databases, there are some data that not needed to be stored in your actual database. Such as defining organizational roles, group members, project editors etc. For example, you can model a simple project management authorization in Permify as follows,entity user {}
entity team {
relation owner @user
relation member @user
}
entity project {
relation team @team
relation owner @user
action view = team.member or team.owner or project.owner
action edit = project.owner or team.owner
action delete = project.owner or team.owner
}
WriteData can be performed in any logical place in your stack.
Parameters & Properties
Path Parameters
Identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant t1 for this field. Required, and must match the pattern \“[a-zA-Z0-9-,]+\“, max 64 bytes.
Body
DataWriteRequest defines the structure of a request for writing data. It contains the necessary information such as tenant_id, metadata, tuples and attributes for the write operation.
DataWriteRequestMetadata defines the structure of metadata for a write request. It includes the schema version of the data to be written.
Show child attributes
Show child attributes
tuples contains the list of tuples (entity-relation-entity triples) that need to be written.
Show child attributes
Show child attributes
attributes contains the list of attributes (entity-attribute-value triples) that need to be written.
Show child attributes
Show child attributes
Response
A successful response.
DataWriteResponse defines the structure of the response after writing data. It contains the snap_token generated after the write operation.
The snap token to avoid stale cache, see more details on Snap Tokens.