From 2c2b48848524ad6b61d7409d90662647e8471504 Mon Sep 17 00:00:00 2001 From: Maria Ines Parnisari Date: Tue, 13 May 2025 16:49:18 -0400 Subject: [PATCH] add code generator --- package.json | 4 +- src/components/CodeGenerator.tsx | 181 ++++++++++++++++++++++++++++++ src/components/FullPlayground.tsx | 36 ++++++ yarn.lock | 161 ++++++++++++++++++++++++++ 4 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 src/components/CodeGenerator.tsx diff --git a/package.json b/package.json index feac038..89e6ae5 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-joyride": "^2.5.3", "react-reflex": "^4.0.9", "react-responsive-carousel": "^3.2.23", + "react-syntax-highlighter": "^15.6.1", "sjcl": "^1.0.8", "string-to-color": "^2.2.2", "string.prototype.replaceall": "^1.0.6", @@ -122,5 +123,6 @@ "last 1 firefox version", "last 1 safari version" ] - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/components/CodeGenerator.tsx b/src/components/CodeGenerator.tsx new file mode 100644 index 0000000..1663d47 --- /dev/null +++ b/src/components/CodeGenerator.tsx @@ -0,0 +1,181 @@ +import {AlertDialog} from "@/playground-ui/AlertDialog.tsx"; +import { useMemo, useState } from "react"; + +import SyntaxHighlighter from 'react-syntax-highlighter'; + +import React from "react"; +import Grid from '@material-ui/core/Grid'; +import Button from "@material-ui/core/Button"; + +function generateRelUpdateJs(yaml: string): string { + let result = "" + for (const tuple of yaml.split("\n")) { + const match = tuple.match(/([^#]+)#([^@]+)@(.+)/); + if (!match) { + continue + } + const [obj, rel, sub] = [match[1], match[2], match[3]] + const [subType, subId] = sub.split(":"); + const [objType, objId] = obj.split(":"); + result += `v1.RelationshipUpdate.create({ + relationship: v1.Relationship.create({ + resource: v1.ObjectReference.create({ + objectType: '${objType}', + objectId: '${objId}', + }), + relation: '${rel}', + subject: v1.SubjectReference.create({ + object: v1.ObjectReference.create({ + objectType: '${subType}', + objectId: '${subId}', + }), + }), + }), + operation: v1.RelationshipUpdate_Operation.CREATE, + }),` + } + return result + +} + +function LangGenerator(props: {schema: string, relationshipsYaml: string, lang: string}) { + if (props.lang == "Go") { + return `package main + +import ( + "context" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "log" + + "github.com/authzed/authzed-go/proto/authzed/api/v1" + "github.com/authzed/authzed-go/v1" + "github.com/authzed/grpcutil" +) + +func main() { + client, err := authzed.NewClient( + "localhost:50051", + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpcutil.WithInsecureBearerToken("your_token_here"), + ) + if err != nil { + log.Fatalf("unable to initialize client: %s", err) + } + + // Write the schema. + resp, err := client.WriteSchema(context.Background(), &v1.WriteSchemaRequest{ + Schema: ` + "`" + props.schema + "`" + `, + }) + if err != nil { + log.Fatalf("failed to write schema: %s", err) + } + log.Println("Written at: " + resp.WrittenAt.String()) + + // Write relationships. + // TODO +} + + `} else if (props.lang == "JS") { + return `import { v1 } from '@authzed/authzed-node'; +// set up it on localhost like this: +const client = v1.NewClient('your_token_here', 'localhost:50051', v1.ClientSecurity.INSECURE_LOCALHOST_ALLOWED); +// const client = v1.NewClient('your_token_here'); +const { promises: promiseClient } = client; // access client.promises after instantiating client + +const writeRequest = v1.WriteSchemaRequest.create({ + schema: ` + "`" + props.schema + "`" + `, +}); + +// Write the schema. +await new Promise((resolve, reject) => { + client.writeSchema(writeRequest, function (err, response) { + if (err) reject(err); + resolve(response); + }); +}); + +// Write relationships. +const writeRelationshipRequest = v1.WriteRelationshipsRequest.create({ + updates: [` +generateRelUpdateJs(props.relationshipsYaml) + ` + ], +}); + +await new Promise((resolve, reject) => { + client.writeRelationships(writeRelationshipRequest, function (err, response) { + if (err) reject(err); + resolve(response); + }); +}); + +` + } else { + return "" + } +} + +export function GenerateContent({ schema, relationshipsYaml, langPicked, setLangPicked }) { + const generatedCode = useMemo( + () => LangGenerator({ lang: langPicked, schema, relationshipsYaml }), + [langPicked, schema, relationshipsYaml] + ); + + const syntaxLang = langPicked === "Go" ? "go" : "javascript"; + + return ( + + + + + + + + {generatedCode} + + + + ); +} + +export function CodeGenerator(props: { schema: string, relationshipsYaml: string, onClose: () => void }) { + const [showAlert] = useState(true); + const [langPicked, setLangPicked] = useState("Go"); + + // Always up-to-date code based on current language + const generatedCode = useMemo( + () => LangGenerator({ lang: langPicked, schema: props.schema, relationshipsYaml: props.relationshipsYaml }), + [langPicked, props.schema, props.relationshipsYaml] + ); + + const handleClose = () => { + navigator.clipboard.writeText(generatedCode) + props.onClose() + }; + + return ( + + } + buttonTitle="Copy" + /> + ); +} diff --git a/src/components/FullPlayground.tsx b/src/components/FullPlayground.tsx index e2f824f..5743914 100644 --- a/src/components/FullPlayground.tsx +++ b/src/components/FullPlayground.tsx @@ -87,6 +87,7 @@ import { TerminalPanel, TerminalSummary } from "./panels/terminal"; import { ValidationPanel, ValidationSummary } from "./panels/validation"; import { VisualizerPanel, VisualizerSummary } from "./panels/visualizer"; import { WatchesPanel, WatchesSummary } from "./panels/watches"; +import {CodeGenerator} from "@/components/CodeGenerator.tsx"; const TOOLBAR_BREAKPOINT = 1550; // pixels @@ -547,6 +548,40 @@ export function ThemedAppView(props: { datastore: DataStore }) { } }; + function CodeGeneratorButton () { + const [showCodeGenerator, setShowCodeGenerator] = useState(false); + const [codeGenProps, setCodeGenProps] = useState({ schema: "", relationshipsYaml: "" }); + + const handleOpenCodeGenerator = () => { + const schema = datastore.getSingletonByKind(DataStoreItemKind.SCHEMA).editableContents!; + const relationshipsYaml = datastore.getSingletonByKind(DataStoreItemKind.RELATIONSHIPS).editableContents!; + if (schema && relationshipsYaml) { + setCodeGenProps({ schema, relationshipsYaml }); + setShowCodeGenerator(true); + } + }; + + return ( + <> + + {showCodeGenerator && ( + setShowCodeGenerator(false)} + /> + )} + + ); + } + const datastoreUpdated = () => { if (sharingState.status !== SharingStatus.NOT_RUN) { setSharingState({ @@ -772,6 +807,7 @@ export function ThemedAppView(props: { datastore: DataStore }) { ) : ( )} +