Skip to content

Commit ccd5666

Browse files
committed
[spicedb] Extend codegen
1 parent e28a756 commit ccd5666

File tree

5 files changed

+222
-174
lines changed

5 files changed

+222
-174
lines changed

components/spicedb/BUILD.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@ scripts:
5959
description: "Generate definition in typescript."
6060
deps: []
6161
script: |
62-
(cd codegen && go run .)
62+
(cd codegen && go run . ts)
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
10+
"github.com/spf13/cobra"
11+
12+
"github.com/authzed/spicedb/pkg/namespace"
13+
"github.com/authzed/spicedb/pkg/schemadsl/compiler"
14+
15+
corev1 "github.com/authzed/spicedb/pkg/proto/core/v1"
16+
implv1 "github.com/authzed/spicedb/pkg/proto/impl/v1"
17+
)
18+
19+
// codegenTypescript represents the codegenTypescript command
20+
var codegenTypescript = &cobra.Command{
21+
Use: "codegen ts",
22+
Aliases: []string{"ts"},
23+
Short: "Enables the debug log level for a component",
24+
Run: func(cmd *cobra.Command, args []string) {
25+
schema := GetCompiledSchema()
26+
fmt.Print(GenerateDefinition(schema))
27+
},
28+
}
29+
30+
func GenerateDefinition(schema *compiler.CompiledSchema) string {
31+
32+
resource := "export type ResourceType ="
33+
resourceTypes := "export const AllResourceTypes: ResourceType[] = ["
34+
relation := "export type Relation ="
35+
permission := "export type Permission ="
36+
other := ""
37+
fluentApi := ""
38+
39+
// list definitions
40+
for _, def := range schema.ObjectDefinitions {
41+
// make sure the first character is upper case
42+
simpleName := firstUpper(def.Name)
43+
resourceTypeName := simpleName + "ResourceType"
44+
resource += "\n | " + resourceTypeName + ""
45+
resourceTypes += "\n \"" + def.Name + "\","
46+
other += "\nexport type " + resourceTypeName + " = \"" + def.Name + "\";\n"
47+
fluentApi += "\n" + generateFluentAPI(def)
48+
// check if relations exists
49+
hasRelations := false
50+
for _, rel := range def.Relation {
51+
if namespace.GetRelationKind(rel) == implv1.RelationMetadata_RELATION {
52+
hasRelations = true
53+
break
54+
}
55+
}
56+
if hasRelations {
57+
relation += "\n | " + simpleName + "Relation"
58+
other += "\nexport type " + simpleName + "Relation ="
59+
for _, rel := range def.Relation {
60+
if namespace.GetRelationKind(rel) == implv1.RelationMetadata_RELATION {
61+
other += "\n | \"" + rel.Name + "\""
62+
}
63+
}
64+
other += ";\n"
65+
}
66+
// check if permissions exists
67+
hasPermissions := false
68+
for _, rel := range def.Relation {
69+
if namespace.GetRelationKind(rel) == implv1.RelationMetadata_PERMISSION {
70+
hasPermissions = true
71+
break
72+
}
73+
}
74+
if hasPermissions {
75+
permission += "\n | " + simpleName + "Permission"
76+
// permissions
77+
other += "\nexport type " + simpleName + "Permission ="
78+
for _, rel := range def.Relation {
79+
if namespace.GetRelationKind(rel) == implv1.RelationMetadata_PERMISSION {
80+
other += "\n | \"" + rel.Name + "\""
81+
}
82+
}
83+
other += ";\n"
84+
}
85+
}
86+
87+
return `
88+
/**
89+
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
90+
* Licensed under the GNU Affero General Public License (AGPL).
91+
* See License.AGPL.txt in the project root for license information.
92+
*/
93+
94+
// This file is generated by the spicedb/codegen/codegen.go. Do not edit manually.
95+
96+
import { v1 } from "@authzed/authzed-node";
97+
98+
export const InstallationID = "1";
99+
100+
` + resource + `;
101+
102+
` + resourceTypes + `
103+
];
104+
105+
` + relation + `;
106+
107+
` + permission + `;
108+
109+
` + other + `
110+
111+
export const rel = {
112+
` + fluentApi + `
113+
};
114+
`
115+
}
116+
117+
func generateFluentAPI(def *corev1.NamespaceDefinition) string {
118+
ifElse := func(cond bool, ifTrue string, ifFalse string) string {
119+
if cond {
120+
return ifTrue
121+
}
122+
return ifFalse
123+
}
124+
125+
relations := ""
126+
for _, rel := range def.Relation {
127+
if namespace.GetRelationKind(rel) != implv1.RelationMetadata_RELATION {
128+
continue
129+
}
130+
131+
objects := ``
132+
for _, t := range rel.TypeInformation.AllowedDirectRelations {
133+
if t.GetPublicWildcard() != nil {
134+
objects += `get any` + firstUpper(t.Namespace) + `() {
135+
return {
136+
...result2,
137+
subject: {
138+
object: {
139+
objectType: "` + t.Namespace + `",
140+
objectId: "*",
141+
},
142+
143+
},
144+
} as v1.Relationship;
145+
},`
146+
} else {
147+
hasRelation := t.GetRelation() != "..."
148+
name := ifElse(hasRelation, t.Namespace+"_"+t.GetRelation(), t.Namespace)
149+
objects += `
150+
` + ifElse(t.Namespace == "installation",
151+
"get "+name+"()",
152+
name+"(objectId: string)") + ` {
153+
return {
154+
...result2,
155+
subject: {
156+
object: {
157+
objectType: "` + t.Namespace + `",
158+
objectId: ` + ifElse(t.Namespace == "installation", `InstallationID`, "objectId") + `,
159+
},
160+
` + ifElse(hasRelation, `optionalRelation: "`+t.GetRelation()+`",`, "") + `
161+
},
162+
} as v1.Relationship;
163+
},`
164+
}
165+
}
166+
167+
relations += `
168+
get ` + rel.Name + `() {
169+
const result2 = {
170+
...result,
171+
relation: "` + rel.Name + `",
172+
};
173+
return {
174+
` + objects + `
175+
};
176+
},
177+
178+
`
179+
}
180+
181+
return `
182+
` + ifElse(def.Name == "installation",
183+
`get `+def.Name+`() {`,
184+
``+def.Name+`(id: string) {`) + `
185+
const result: Partial<v1.Relationship> = {
186+
resource: {
187+
objectType: "` + def.Name + `",
188+
objectId: ` + ifElse(def.Name == "installation", `InstallationID`, "id") + `
189+
},
190+
};
191+
return {
192+
` + relations + `
193+
};
194+
},
195+
`
196+
}
197+
198+
func init() {
199+
rootCmd.AddCommand(codegenTypescript)
200+
}

0 commit comments

Comments
 (0)