Skip to content

Commit 09b1853

Browse files
authored
Warrant token support for listWarrants (#246)
* Add request options for list warrants to pass a warrant token * Add CreateWarrantOptions and DeleteWarrantOptions * Rename authorized method to lowercase * Add constants for FGA
1 parent 318b330 commit 09b1853

File tree

9 files changed

+239
-15
lines changed

9 files changed

+239
-15
lines changed

src/main/kotlin/com/workos/fga/FgaApi.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,20 @@ import com.workos.fga.types.CheckRequestOptions
1414
import com.workos.fga.types.CreateResourceOptions
1515
import com.workos.fga.types.ListResourcesOptions
1616
import com.workos.fga.types.ListWarrantsOptions
17+
import com.workos.fga.types.ListWarrantsRequestOptions
1718
import com.workos.fga.types.QueryOptions
1819
import com.workos.fga.types.QueryRequestOptions
1920
import com.workos.fga.types.UpdateResourceOptions
2021
import com.workos.fga.types.WriteWarrantOptions
2122

23+
const val CHECK_RESULT_AUTHORIZED = "authorized"
24+
const val CHECK_RESULT_NOT_AUTHORIZED = "not_authorized"
25+
const val CHECK_OP_ALL_OF = "all_of"
26+
const val CHECK_OP_ANY_OF = "any_of"
27+
const val CHECK_OP_BATCH = "batch"
28+
const val WARRANT_OP_CREATE = "create"
29+
const val WARRANT_OP_DELETE = "delete"
30+
2231
class FgaApi(private val workos: WorkOS) {
2332
/** Get an existing resource. */
2433
fun getResource(resourceType: String, resourceId: String): Resource {
@@ -61,14 +70,21 @@ class FgaApi(private val workos: WorkOS) {
6170
}
6271

6372
/** Get a list of all existing warrants matching the criteria specified */
64-
fun listWarrants(options: ListWarrantsOptions? = null): Warrants {
73+
fun listWarrants(options: ListWarrantsOptions? = null, requestOptions: ListWarrantsRequestOptions? = null): Warrants {
6574
val params: Map<String, String> =
6675
RequestConfig.toMap(options ?: ListWarrantsOptions()) as Map<String, String>
6776

77+
val config = RequestConfig.builder()
78+
.params(params)
79+
80+
if (requestOptions != null) {
81+
config.headers(mapOf("Warrant-Token" to requestOptions.warrantToken))
82+
}
83+
6884
return workos.get(
6985
"/fga/v1/warrants",
7086
Warrants::class.java,
71-
RequestConfig.builder().params(params).build()
87+
config.build()
7288
)
7389
}
7490

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.workos.fga.builders
2+
3+
import com.workos.fga.models.Subject
4+
import com.workos.fga.types.CreateWarrantOptions
5+
6+
/**
7+
* Builder for options when performing a warrant create.
8+
*
9+
* @param resourceType The type of the resource. Must be one of your system's existing resource types.
10+
* @param resourceId The unique ID of the resource.
11+
* @param relation The relation for this resource to subject association. The relation must be valid per the resource type definition.
12+
* @param subject (see [Subject]) The resource with the specified `relation` to the resource provided by resourceType and resourceId.
13+
* @param policy A boolean expression that must evaluate to true for this warrant to apply. The expression can reference variables provided in the context attribute of access check requests.
14+
*/
15+
class CreateWarrantOptionsBuilder @JvmOverloads constructor(
16+
private var resourceType: String,
17+
private var resourceId: String,
18+
private var relation: String,
19+
private var subject: Subject,
20+
private var policy: String? = null,
21+
) {
22+
fun resourceType(value: String) = apply { resourceType = value }
23+
24+
fun resourceId(value: String) = apply { resourceId = value }
25+
26+
fun relation(value: String) = apply { relation = value }
27+
28+
fun subject(value: Subject) = apply { subject = value }
29+
30+
fun policy(value: String) = apply { policy = value }
31+
32+
fun build(): CreateWarrantOptions {
33+
return CreateWarrantOptions(
34+
resourceType = this.resourceType,
35+
resourceId = this.resourceId,
36+
relation = this.relation,
37+
subject = this.subject,
38+
policy = this.policy,
39+
)
40+
}
41+
42+
companion object {
43+
@JvmStatic
44+
fun create(resourceType: String, resourceId: String, relation: String, subject: Subject): CreateWarrantOptionsBuilder {
45+
return CreateWarrantOptionsBuilder(resourceType, resourceId, relation, subject)
46+
}
47+
}
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.workos.fga.builders
2+
3+
import com.workos.fga.models.Subject
4+
import com.workos.fga.types.DeleteWarrantOptions
5+
6+
/**
7+
* Builder for options when performing a warrant delete.
8+
*
9+
* @param resourceType The type of the resource. Must be one of your system's existing resource types.
10+
* @param resourceId The unique ID of the resource.
11+
* @param relation The relation for this resource to subject association. The relation must be valid per the resource type definition.
12+
* @param subject (see [Subject]) The resource with the specified `relation` to the resource provided by resourceType and resourceId.
13+
* @param policy A boolean expression that must evaluate to true for this warrant to apply. The expression can reference variables provided in the context attribute of access check requests.
14+
*/
15+
class DeleteWarrantOptionsBuilder @JvmOverloads constructor(
16+
private var resourceType: String,
17+
private var resourceId: String,
18+
private var relation: String,
19+
private var subject: Subject,
20+
private var policy: String? = null,
21+
) {
22+
fun resourceType(value: String) = apply { resourceType = value }
23+
24+
fun resourceId(value: String) = apply { resourceId = value }
25+
26+
fun relation(value: String) = apply { relation = value }
27+
28+
fun subject(value: Subject) = apply { subject = value }
29+
30+
fun policy(value: String) = apply { policy = value }
31+
32+
fun build(): DeleteWarrantOptions {
33+
return DeleteWarrantOptions(
34+
resourceType = this.resourceType,
35+
resourceId = this.resourceId,
36+
relation = this.relation,
37+
subject = this.subject,
38+
policy = this.policy,
39+
)
40+
}
41+
42+
companion object {
43+
@JvmStatic
44+
fun create(resourceType: String, resourceId: String, relation: String, subject: Subject): DeleteWarrantOptionsBuilder {
45+
return DeleteWarrantOptionsBuilder(resourceType, resourceId, relation, subject)
46+
}
47+
}
48+
}

src/main/kotlin/com/workos/fga/models/CheckResponse.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.workos.fga.models
33
import com.fasterxml.jackson.annotation.JsonCreator
44
import com.fasterxml.jackson.annotation.JsonInclude
55
import com.fasterxml.jackson.annotation.JsonProperty
6+
import com.workos.fga.CHECK_RESULT_AUTHORIZED
67
import com.workos.fga.types.WarrantCheckOptions
78

89
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -16,8 +17,8 @@ data class CheckResponse @JsonCreator constructor(
1617
@JsonProperty("debug_info")
1718
val debugInfo: DebugInfo? = null
1819
) {
19-
fun Authorized(): Boolean {
20-
return this.result == "authorized"
20+
fun authorized(): Boolean {
21+
return this.result == CHECK_RESULT_AUTHORIZED
2122
}
2223
}
2324

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.workos.fga.types
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
import com.workos.fga.WARRANT_OP_CREATE
6+
import com.workos.fga.models.Subject
7+
8+
@JsonInclude(JsonInclude.Include.NON_NULL)
9+
class CreateWarrantOptions @JvmOverloads constructor(
10+
/**
11+
* The type of the resource.
12+
*/
13+
@JsonProperty("resource_type")
14+
resourceType: String,
15+
16+
/**
17+
* The unique ID of the resource.
18+
*/
19+
@JsonProperty("resource_id")
20+
resourceId: String,
21+
22+
/**
23+
* The relation for this resource to subject association. The relation must be valid per the resource type definition.
24+
*/
25+
@JsonProperty("relation")
26+
relation: String,
27+
28+
/**
29+
* The resource with the specified `relation` to the resource provided by resourceType and resourceId.
30+
*/
31+
@JsonProperty("subject")
32+
subject: Subject,
33+
34+
/**
35+
* A boolean expression that must evaluate to true for this warrant to apply. The expression can reference variables provided in the context attribute of access check requests.
36+
*/
37+
@JsonProperty("policy")
38+
policy: String? = null,
39+
) : WriteWarrantOptions(WARRANT_OP_CREATE, resourceType, resourceId, relation, subject, policy)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.workos.fga.types
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
import com.workos.fga.WARRANT_OP_DELETE
6+
import com.workos.fga.models.Subject
7+
8+
@JsonInclude(JsonInclude.Include.NON_NULL)
9+
class DeleteWarrantOptions @JvmOverloads constructor(
10+
/**
11+
* The type of the resource.
12+
*/
13+
@JsonProperty("resource_type")
14+
resourceType: String,
15+
16+
/**
17+
* The unique ID of the resource.
18+
*/
19+
@JsonProperty("resource_id")
20+
resourceId: String,
21+
22+
/**
23+
* The relation for this resource to subject association. The relation must be valid per the resource type definition.
24+
*/
25+
@JsonProperty("relation")
26+
relation: String,
27+
28+
/**
29+
* The resource with the specified `relation` to the resource provided by resourceType and resourceId.
30+
*/
31+
@JsonProperty("subject")
32+
subject: Subject,
33+
34+
/**
35+
* A boolean expression that must evaluate to true for this warrant to apply. The expression can reference variables provided in the context attribute of access check requests.
36+
*/
37+
@JsonProperty("policy")
38+
policy: String? = null,
39+
) : WriteWarrantOptions(WARRANT_OP_DELETE, resourceType, resourceId, relation, subject, policy)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.workos.fga.types
2+
3+
class ListWarrantsRequestOptions constructor(
4+
/**
5+
* A valid token string from a previous write operation or latest. This is used to specify the desired consistency for this read operation.
6+
*/
7+
val warrantToken: String? = null,
8+
)

src/main/kotlin/com/workos/fga/types/WriteWarrantOptions.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
55
import com.workos.fga.models.Subject
66

77
@JsonInclude(JsonInclude.Include.NON_NULL)
8-
class WriteWarrantOptions @JvmOverloads constructor(
8+
open class WriteWarrantOptions @JvmOverloads constructor(
99
/**
1010
* Operation to perform for the given warrant (create/delete).
1111
*/
@@ -43,6 +43,7 @@ class WriteWarrantOptions @JvmOverloads constructor(
4343
val policy: String? = null,
4444
) {
4545
init {
46+
require(op.isNotBlank()) { "Op is required" }
4647
require(resourceType.isNotBlank()) { "Resource type is required" }
4748
require(resourceId.isNotBlank()) { "Resource id is required" }
4849
require(relation.isNotBlank()) { "Relation is required" }

src/test/kotlin/com/workos/test/fga/FgaApiTest.kt

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import com.workos.common.models.Order
77
import com.workos.fga.builders.CheckBatchOptionsBuilder
88
import com.workos.fga.builders.CheckOptionsBuilder
99
import com.workos.fga.builders.CreateResourceOptionsBuilder
10+
import com.workos.fga.builders.CreateWarrantOptionsBuilder
11+
import com.workos.fga.builders.DeleteWarrantOptionsBuilder
1012
import com.workos.fga.builders.ListResourcesOptionsBuilder
1113
import com.workos.fga.builders.ListWarrantsOptionsBuilder
1214
import com.workos.fga.builders.QueryOptionsBuilder
@@ -593,6 +595,16 @@ class FgaApiTest : TestBase() {
593595
"resource_id": "tony-stark"
594596
}
595597
},
598+
{
599+
"op": "delete",
600+
"resource_type": "tenant",
601+
"resource_id": "avengers",
602+
"relation": "member",
603+
"subject": {
604+
"resource_type": "user",
605+
"resource_id": "tony-stark"
606+
}
607+
},
596608
{
597609
"op": "create",
598610
"resource_type": "role",
@@ -602,13 +614,25 @@ class FgaApiTest : TestBase() {
602614
"resource_type": "user",
603615
"resource_id": "tony-stark"
604616
}
617+
},
618+
{
619+
"op": "create",
620+
"resource_type": "tenant",
621+
"resource_id": "stark-industries",
622+
"relation": "member",
623+
"subject": {
624+
"resource_type": "user",
625+
"resource_id": "tony-stark"
626+
}
605627
}
606628
]"""
607629
)
608630

609631
val options = listOf(
610632
WriteWarrantOptionsBuilder("delete", "role", "editor", "member", Subject("user", "tony-stark")).build(),
611-
WriteWarrantOptionsBuilder("create", "role", "admin", "member", Subject("user", "tony-stark")).build()
633+
DeleteWarrantOptionsBuilder("tenant", "avengers", "member", Subject("user", "tony-stark")).build(),
634+
WriteWarrantOptionsBuilder("create", "role", "admin", "member", Subject("user", "tony-stark")).build(),
635+
CreateWarrantOptionsBuilder("tenant", "stark-industries", "member", Subject("user", "tony-stark")).build()
612636
)
613637

614638
val warrantResponse = workos.fga.batchWriteWarrants(options)
@@ -657,7 +681,7 @@ class FgaApiTest : TestBase() {
657681

658682
val checkResponse = workos.fga.check(options)
659683

660-
assertEquals(true, checkResponse.Authorized())
684+
assertEquals(true, checkResponse.authorized())
661685
assertEquals(false, checkResponse.isImplicit)
662686
}
663687

@@ -704,7 +728,7 @@ class FgaApiTest : TestBase() {
704728

705729
val checkResponse = workos.fga.check(options, requestOptions)
706730

707-
assertEquals(true, checkResponse.Authorized())
731+
assertEquals(true, checkResponse.authorized())
708732
assertEquals(false, checkResponse.isImplicit)
709733
}
710734

@@ -801,7 +825,7 @@ class FgaApiTest : TestBase() {
801825

802826
val checkResponse = workos.fga.check(options)
803827

804-
assertEquals(true, checkResponse.Authorized())
828+
assertEquals(true, checkResponse.authorized())
805829
assertEquals(true, checkResponse.isImplicit)
806830
assertNotNull(checkResponse.debugInfo)
807831
assertNotNull(checkResponse.debugInfo?.processingTime)
@@ -898,9 +922,9 @@ class FgaApiTest : TestBase() {
898922

899923
val checkResponses = workos.fga.checkBatch(options)
900924

901-
assertEquals(true, checkResponses[0].Authorized())
925+
assertEquals(true, checkResponses[0].authorized())
902926
assertEquals(false, checkResponses[0].isImplicit)
903-
assertEquals(true, checkResponses[1].Authorized())
927+
assertEquals(true, checkResponses[1].authorized())
904928
assertEquals(true, checkResponses[1].isImplicit)
905929
}
906930

@@ -965,9 +989,9 @@ class FgaApiTest : TestBase() {
965989

966990
val checkResponses = workos.fga.checkBatch(options, requestOptions)
967991

968-
assertEquals(true, checkResponses[0].Authorized())
992+
assertEquals(true, checkResponses[0].authorized())
969993
assertEquals(false, checkResponses[0].isImplicit)
970-
assertEquals(true, checkResponses[1].Authorized())
994+
assertEquals(true, checkResponses[1].authorized())
971995
assertEquals(true, checkResponses[1].isImplicit)
972996
}
973997

@@ -1098,7 +1122,7 @@ class FgaApiTest : TestBase() {
10981122

10991123
val checkResponses = workos.fga.checkBatch(options)
11001124

1101-
assertEquals(true, checkResponses[0].Authorized())
1125+
assertEquals(true, checkResponses[0].authorized())
11021126
assertEquals(true, checkResponses[0].isImplicit)
11031127
assertNotNull(checkResponses[0].debugInfo)
11041128
assertNotNull(checkResponses[0].debugInfo?.processingTime)
@@ -1137,7 +1161,7 @@ class FgaApiTest : TestBase() {
11371161
assertEquals("matched", checkResponses[0].debugInfo?.decisionTree?.children?.get(0)?.children?.get(0)?.decision)
11381162
assertNotNull(checkResponses[0].debugInfo?.decisionTree?.children?.get(0)?.children?.get(0)?.processingTime)
11391163
assertNull(checkResponses[0].debugInfo?.decisionTree?.children?.get(0)?.children?.get(0)?.children)
1140-
assertEquals(true, checkResponses[1].Authorized())
1164+
assertEquals(true, checkResponses[1].authorized())
11411165
assertEquals(false, checkResponses[1].isImplicit)
11421166
assertNotNull(checkResponses[1].debugInfo)
11431167
assertEquals(8006583, checkResponses[1].debugInfo?.processingTime)

0 commit comments

Comments
 (0)