Skip to content

Commit

Permalink
Merge pull request #771 from Permify/docs
Browse files Browse the repository at this point in the history
docs: upgrade the api services
  • Loading branch information
tolgaOzen authored Oct 25, 2023
2 parents 12c15c3 + e1fc99d commit 5bb7545
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 36 deletions.
15 changes: 10 additions & 5 deletions docs/docs/api-overview/data/delete-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ You can delete any stored relation tuples or attributes with following API

**Path:** POST /v1/tenants/{tenant_id}/data/delete

| Required | Argument | Type | Default | Description |
[![View in Swagger](http://jessemillar.github.io/view-in-swagger-button/button.svg)](https://permify.github.io/permify-swagger/#/Data/data.delete)

| Required | Argument | Type | Description |
|----------|----------|---------|---------|-------------------------------------------------------------------------------------------|
| [x] | tenant_id | string | - | identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant `t1` for this field.
| [x] | entity | object | - | contains entity type and id of the entity. Example: repository:1”.
| [x] | relation | string | - | relation of the given entity |
| [ ] | subject | object | - | the user or user set. It contains type and id of the subject. ||
| [x] | tenant_id | string | identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant `t1` for this field.
| [x] | tuples_filter | object |filter to delete relational tuples. Contains **entity**, **relation** and **subject**.
| [x] | attribute_filter | object | filter to delete attributes. Contains **entity** and **attributes**.
| [x] | entity | object | contains entity type and id of the entity. Example: repository:1”.
| [x] | relation | string | relation of the given entity |
| [x] | attribute | string array | attributes to be deleted |
| [ ] | subject | object | the user or user set. It contains type and id of the subject. ||

<Tabs>
<TabItem value="go" label="Go">
Expand Down
105 changes: 105 additions & 0 deletions docs/docs/api-overview/data/read-attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Read Attributes

Read API allows for directly querying the stored graph data to display and filter stored attributes.

## Request

**Path:** POST /v1/tenants/{tenant_id}/data/attributes/read

[![View in Swagger](http://jessemillar.github.io/view-in-swagger-button/button.svg)](https://permify.github.io/permify-swagger/#/Data/data.attributes.read)

| Required | Argument | Type | Description |
|----------|----------|---------|---------|-------------------------------------------------------------------------------------------|
| [x] | tenant_id | string | identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant `t1` for this field.
| [ ] | snap_token | string | the snap token to avoid stale cache, see more details on [Snap Tokens](../../reference/snap-tokens) |
| [x] | entity | object | contains entity type and id of the entity. Example: repository:1”.
| [x] | attributes | string array | attributes of the given entity |


<Tabs>
<TabItem value="go" label="Go">

```go
rr, err: = client.Data.Relationship.Read(context.Background(), & v1.Data.RelationshipReadRequest {
TenantId: "t1",
Metadata: &v1.Data.RelationshipReadRequestMetadata {
SnapToken: ""
},
Filter: &v1.TupleFilter {
Entity: &v1.EntityFilter {
Type: "organization",
Ids: []string {"1"} ,
},
Relation: "member",
Subject: &v1.SubjectFilter {
Type: "",
Id: []string {""},
Relation: ""
}}
})
```

</TabItem>

<TabItem value="node" label="Node">

```javascript
client.data.relationship.read({
tenantId: "t1",
metadata: {
snap_token: "",
},
filter: {
entity: {
type: "organization",
ids: [
"1"
]
},
relation: "member",
subject: {
type: "",
ids: [],
relation: ""
}
}
}).then((response) => {
// handle response
})
```

</TabItem>
<TabItem value="curl" label="cURL">

```curl
curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/relationships/read' \
--header 'Content-Type: application/json' \
--data-raw '{
metadata: {
snap_token: "",
},
filter: {
entity: {
type: "organization",
ids: [
"1"
]
},
relation: "member",
subject: {
type: "",
ids: [],
relation: ""
}
}
}'
```
</TabItem>
</Tabs>

## Need any help ?

Our team is happy to help you get started with Permify. If you'd like to learn more about using Permify in your app or have any questions about this example, [schedule a call with one of our Permify engineer](https://meetings-eu1.hubspot.com/ege-aytin/call-with-an-expert).
2 changes: 2 additions & 0 deletions docs/docs/api-overview/data/read-relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Read API allows for directly querying the stored graph data to display and filte

**Path:** POST /v1/tenants/{tenant_id/data/relationships/read

[![View in Swagger](http://jessemillar.github.io/view-in-swagger-button/button.svg)](https://permify.github.io/permify-swagger/#/Data/data.relationships.read)

| Required | Argument | Type | Default | Description |
|----------|----------|---------|---------|-------------------------------------------------------------------------------------------|
| [x] | tenant_id | string | - | identifier of the tenant, if you are not using multi-tenancy (have only one tenant) use pre-inserted tenant `t1` for this field.
Expand Down
162 changes: 131 additions & 31 deletions docs/docs/api-overview/data/write-data.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Write Data
# Write Authorization Data

In Permify, relations between your entities, objects and users stored as [relational tuples] in a [preferred database]. Since relations and authorization data's are live instances these relational tuples can be created with an simple API call in runtime.
In Permify, relations between your entities, objects and users stored as [relational tuples] in a [preferred database]. Since relations and authorization data's are live instances these relational tuples can be created with an simple API call in runtime.

When using Permify, the application client should update preferred database about the changes happening in entities or resources that are related to the authorization structure. If we consider a document system; when some user joins a group that has edit access on some documents, the application side needs to write relational tuples to keep [preferred database] up-to-date. Besides, each relational tuple should be created according to its authorization model, Permify Schema.

Expand All @@ -14,21 +14,34 @@ Another example: when one a company executive grant admin role to user (lets say
[relational tuples]: ../../getting-started/sync-data
[preferred database]: ../../getting-started/sync-data#where-relational-tuples-used

## Request
## Write Request

So if user:3 has been granted an admin role in organization:1, relational tuple `organization:1#admin@user:3` must be created by using **/v1/data/write** endpoint.
:::info
You can use the **/v1/tenants/{tenant_id}/data/write** endpoint for both creating **relation tuples** and for creating **attribute data**.
:::

**Path:** POST /v1/tenants/{tenant_id}/data/write

| Required | Argument | Type | Default | Description |
|----------|-------------------|--------|---------|-------------|
| [x] | tenant_id | string | - | identifier of the tenant, if you are not using multi-tenancy (have only one tenant in your system) use pre-inserted tenant **t1** for this field.
| [x] | tuples | array | - | Can contain multiple relation tuple object|
| [x] | entity | object | - | Type and id of the entity. Example: "organization:1”|
| [x] | relation | string | - | Custom relation name. Eg. admin, manager, viewer etc.|
| [x] | subject | string | - | User or user set who wants to take the action. |
| [ ] | schema_version | string | 8 | Version of the schema |
[![View in Swagger](http://jessemillar.github.io/view-in-swagger-button/button.svg)](https://permify.github.io/permify-swagger/#/Data/data.write)

#### Glossary for parameters & payload objects:

| Required | Argument | Type | Default | Description |
| -------- | -------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| [x] | tenant_id | string | - | identifier of the tenant, if you are not using multi-tenancy (have only one tenant in your system) use pre-inserted tenant **t1** for this parameter. |
| [ ] | schema_version | string | 8 | Version of the schema. |
| [x] | tuples | array | - | Array of objects that are used to define relationships. Each object contains **entity**, **relation**, and **subject** arguments.|
| [x] | attributes | array | - | Array of objects that are used to define relationships. Each object contains **entity**, **attribute**, and **value** arguments. |
| [x] | entity | object | - | Type and id of the entity. Example: "organization:1” |
| [x] | subject | string | - | User or user set who wants to take the action. |
| [x] | relation | string | - | Custom relation name. Eg. admin, manager, viewer etc. |
| [x] | attribute | string | - | Custom attribute name. |
| [x] | value | object | - | Represents value and type of the attribute data. |


### Creating Relational Tuple

Let's create an example relation tuple. If user:3 has been granted an admin role in organization:1, relational tuple `organization:1#admin@user:3` should be created as follows:

<Tabs>
<TabItem value="go" label="Go">
Expand Down Expand Up @@ -60,25 +73,29 @@ rr, err: = client.Data.Write(context.Background(), & v1.DataWriteRequest {
<TabItem value="node" label="Node">

```javascript
client.data.write({
client.data
.write({
tenantId: "t1",
metadata: {
schemaVersion: ""
schemaVersion: "",
},
tuples: [{
tuples: [
{
entity: {
type: "organization",
id: "1"
type: "organization",
id: "1",
},
relation: "admin",
subject: {
type: "user",
id: "3"
}
}]
}).then((response) => {
type: "user",
id: "3",
},
},
],
})
.then((response) => {
// handle response
})
});
```

</TabItem>
Expand Down Expand Up @@ -107,29 +124,111 @@ curl --location --request POST 'localhost:3476/v1/tenants/{tenant_id}/data/write
]
}'
```

</TabItem>
</Tabs>

### Creating Attribute Data

You can use `attributes` argument to create attribute/attributes, similarly the `tuples`.

Let's conitnue with an example: Assume user:1 has been granted an admin role in organization:1 and organization:1 is a private (boolean) organization:

:::warning **value** field
**value** field is mandatory on attribute data creation.

Here are the available attribute value types:

- **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**
:::

<Tabs>
<TabItem value="go" label="Go">

```go

```

</TabItem>

<TabItem value="node" label="Node">

```javascript

```

</TabItem>
<TabItem value="curl" label="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": "organization",
"id": "1"
},
"relation": "admin",
"subject": {
"type": "user",
"id": "1"
}
}
],
"attributes": [
{
"entity": {
"type": "organization",
"id": "1"
},
"attribute": "private",
"value": {
"@type": "type.googleapis.com/base.v1.BooleanValue",
"value": "true"
}
}
]
}
}'
```

</TabItem>
</Tabs>

## Response

```json
{
"snap_token": "FxHhb4CrLBc="
"snap_token": "FxHhb4CrLBc="
}
```

You can store that snap token alongside with the resource in your relational database, then use it used in endpoints to get fresh results from the API's. For example it can be used in access control check with sending via `snap_token` field to ensure getting check result as fresh as previous request.

See more details on what is [Snap Tokens](../../reference/snap-tokens) and how its avoiding stale cache.

## Suggested Workflow
## 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 function
To give more concurrent example of this action, let's take a look at below createDocument function

```go
func CreateDocuments(db *gorm.DB) error {
Expand All @@ -138,7 +237,7 @@ func CreateDocuments(db *gorm.DB) error {
defer func() {
if r := recover(); r != nil {
tx.Rollback()
// if transaction fails, then delete malformed relation tuple
// if transaction fails, then delete malformed relation tuple
permify.DeleteData(...)
}
}()
Expand All @@ -149,24 +248,25 @@ func CreateDocuments(db *gorm.DB) error {

if err := tx.Create(docs).Error; err != nil {
tx.Rollback()
// if transaction fails, then delete malformed relation tuple
// if transaction fails, then delete malformed relation tuple
permify.DeleteData(...)
return err
}

// if transaction successful, write relation tuple to Permify
// if transaction successful, write relation tuple to Permify
permify.WriteData(...)

return tx.Commit().Error
}
```

The key point to take way from above approach is if the transaction fails for any reason, the relation will also be deleted from Permify to provide maximum consistency.

### 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,
For example, you can model a simple project management authorization in Permify as follows,

```perm
entity user {}
Expand All @@ -193,4 +293,4 @@ This **team member** relation won't need to be stored in the application databas

## Need any help ?

Our team is happy to help you get started with Permify. If you'd like to learn more about using Permify in your app or have any questions about this example, [schedule a call with one of our Permify engineer](https://meetings-eu1.hubspot.com/ege-aytin/call-with-an-expert).
Our team is happy to help you get started with Permify. If you'd like to learn more about using Permify in your app or have any questions about this example, [schedule a call with one of our Permify engineer](https://meetings-eu1.hubspot.com/ege-aytin/call-with-an-expert).
Loading

0 comments on commit 5bb7545

Please sign in to comment.