Skip to content

Commit

Permalink
Creates python api tpl and show it in automations page
Browse files Browse the repository at this point in the history
  • Loading branch information
ap0k4 committed Sep 21, 2024
1 parent ded53bd commit 6bf96d9
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 38 deletions.
23 changes: 13 additions & 10 deletions packages/protolib/src/bundles/apis/adminPages.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { APIModel } from '.'
import { YStack, Text, Stack, XStack, Accordion, Spacer, Square, ScrollView, useToastController, Spinner, Paragraph, SizableText } from "@my/ui";
import { ToyBrick, Eye, ChevronDown, UploadCloud, CheckCircle, Package, AlertTriangle} from '@tamagui/lucide-icons'
import { ToyBrick, Eye, ChevronDown, UploadCloud, CheckCircle, Package, AlertTriangle } from '@tamagui/lucide-icons'
import { z, getPendingResult, API } from 'protobase'
import { usePageParams } from '../../next'
import { usePrompt } from '../../context/PromptAtom'
Expand All @@ -9,7 +9,7 @@ import { DataTable2 } from '../../components/DataTable2'
import { DataView, DataViewActionButton } from '../../components/DataView'
import { AlertDialog } from '../../components/AlertDialog'
import { AdminPage } from '../../components/AdminPage'
import {useWorkspaceEnv} from '../../lib/useWorkspaceEnv'
import { useWorkspaceEnv } from '../../lib/useWorkspaceEnv'
import { useEffect, useState } from 'react'
import Center from '../../components/Center'
import { Tinted } from '../../components/Tinted';
Expand Down Expand Up @@ -164,7 +164,7 @@ export default {
const [publishOpen, setPublishOpen] = useState(false)
const [publishState, setPublishState] = useState<"confirm" | "publishing" | "published" | "error">("confirm")
const [packageId, setPackageId] = useState("")
const {messages, setMessages} = useSubscription(notificationsTopicPrefix+'#')
const { messages, setMessages } = useSubscription(notificationsTopicPrefix + '#')
const [data, setData] = useState(defaultData)
const [error, setError] = useState<any>('')
const toast = useToastController()
Expand All @@ -187,10 +187,10 @@ export default {
}, [publishOpen])

const processMessages = async (messages) => {
if(packageId) {
if (packageId) {
//search messages for messages directed at packageId
const done = messages.find(m => m.topic.startsWith(notificationsTopicPrefix+packageId+'/done'))
if(done) {
const done = messages.find(m => m.topic.startsWith(notificationsTopicPrefix + packageId + '/done'))
if (done) {
await API.get('/adminapi/v1/services/api/restart')
setPublishState("published")
setPackageId("")
Expand Down Expand Up @@ -337,17 +337,17 @@ export default {
width={"600px"}
integratedChat
onAccept={async () => {
if(publishState == "confirm") {
if (publishState == "confirm") {
setPublishState("publishing")
const result = await API.get('/adminapi/v1/services/api/package')
const currentPackageId = result?.data?.packageId
if(!currentPackageId) {
if (!currentPackageId) {
setPublishState("error")
return true
}
setPackageId(currentPackageId)
return true
}
}
setPublishOpen(false)
setPublishState("confirm")
}}
Expand Down Expand Up @@ -397,7 +397,10 @@ export default {
openMode={env === 'dev' ? 'edit' : 'view'}
hideAdd={env !== 'dev'}
disableItemSelection={env !== 'dev'}
onSelectItem={(item) => replace('editFile', '/packages/app/bundles/custom/apis/' + item.data.name + '.ts')}
onSelectItem={(item) => {
replace('editFile', item.data.filePath);
}}

rowIcon={ToyBrick}
sourceUrl={sourceUrl}
initialItems={initialItems}
Expand Down
71 changes: 43 additions & 28 deletions packages/protolib/src/bundles/apis/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,28 @@ const indexFile = (root) => APIDir(root) + "index.ts"
const indexFilePath = "/packages/app/bundles/custom/apis/index.ts"

const getAPI = (apiPath, req) => {
const sourceFile = getSourceFile(APIDir(getRoot(req)) + apiPath)
const arg = getDefinition(sourceFile, '"type"')
const obj = getDefinition(sourceFile, '"object"')
const apiType = apiPath.endsWith('.py') ? 'python' : 'typescript'
let type = apiType
let object = "None"
const filePath = APIDir(getRoot(req)) + apiPath
if (apiType === 'typescript') {
const sourceFile = getSourceFile(filePath)
const arg = getDefinition(sourceFile, '"type"')
const obj = getDefinition(sourceFile, '"object"')
type = arg ? arg.getText().replace(/^['"]+|['"]+$/g, '') : type
object = obj ? obj.getText().replace(/^['"]+|['"]+$/g, '') : object
}
return {
name: apiPath.replace(/\.[^/.]+$/, ""), //remove extension
type: arg ? arg.getText().replace(/^['"]+|['"]+$/g, '') : "Unknown",
object: obj ? obj.getText().replace(/^['"]+|['"]+$/g, '') : "None"
type,
object,
filePath: APIDir("") + apiPath
}
}

const deleteAPI = (req, value) => {
const api = getAPI(fspath.basename(value.name) + '.ts', req)
const extension = value.template === 'python-api' ? '.py' : '.ts'
const api = getAPI(fspath.basename(value.name) + extension, req)
removeFileWithImports(getRoot(req), value, '"apis"', indexFilePath, req, fs);
if (api.type === "AutoAPI") {
const objectPath = fspath.join(getRoot(), ObjectModel.getDefaultSchemaFilePath(api.object))
Expand All @@ -45,7 +55,7 @@ async function checkFileExists(filePath) {
const getDB = (path, req, session) => {
const db = {
async *iterator() {
const files = (await fs.readdir(APIDir(getRoot(req)))).filter(f => f != 'index.ts' && !fsSync.lstatSync(fspath.join(APIDir(getRoot(req)), f)).isDirectory() && f.endsWith('.ts'))
const files = (await fs.readdir(APIDir(getRoot(req)))).filter(f => f != 'index.ts' && !fsSync.lstatSync(fspath.join(APIDir(getRoot(req)), f)).isDirectory() && (f.endsWith('.ts') || f.endsWith('.py')))
const apis = await Promise.all(files.map(async f => getAPI(f, req)));

for (const api of apis) {
Expand All @@ -65,8 +75,9 @@ const getDB = (path, req, session) => {
let ObjectSourceFile

const template = fspath.basename(value.template ?? 'empty')
const extension = value.template === 'python-api' ? '.py' : '.ts'

const filePath = getRoot(req) + 'packages/app/bundles/custom/apis/' + fspath.basename(value.name) + '.ts';
const filePath = getRoot(req) + 'packages/app/bundles/custom/apis/' + fspath.basename(value.name) + extension;
exists = await checkFileExists(filePath);

if (template.startsWith("automatic-crud")) {
Expand All @@ -80,26 +91,28 @@ const getDB = (path, req, session) => {
}
}

if(template == "automatic-crud-google-sheet") {
const regex = /\/d\/([a-zA-Z0-9-_]+)/;
const match = value.param.match(regex);
const id = match ? match[1] : null;
value.param = id
if (template == "automatic-crud-google-sheet") {
const regex = /\/d\/([a-zA-Z0-9-_]+)/;
const match = value.param.match(regex);
const id = match ? match[1] : null;
value.param = id
}

const computedName = value.name
const codeName = computedName.replace(/\s/g, "")
const codeNameLowerCase = codeName.toLowerCase()
const result = await API.post('/adminapi/v1/templates/file?token=' + getServiceToken(), {
name: value.name + '.ts',
name: value.name + extension,
data: {
options: { template: `/packages/protolib/src/bundles/apis/templates/${template}.tpl`, variables: {
codeName: codeName,
name: computedName,
codeNameLowerCase: codeNameLowerCase,
object: value.object,
param: value.param,
}},
options: {
template: `/packages/protolib/src/bundles/apis/templates/${template}.tpl`, variables: {
codeName: codeName,
name: computedName,
codeNameLowerCase: codeNameLowerCase,
object: value.object,
param: value.param,
}
},
path: '/packages/app/bundles/custom/apis'
}
})
Expand All @@ -114,15 +127,17 @@ const getDB = (path, req, session) => {
await addFeature(ObjectSourceFile, '"AutoAPI"', "true")
}
//link in index.ts
const sourceFile = getSourceFile(indexFile(getRoot(req)))
addImportToSourceFile(sourceFile, codeName + 'Api', ImportType.DEFAULT, './' + codeName)
if (extension == '.ts') {
const sourceFile = getSourceFile(indexFile(getRoot(req)))
addImportToSourceFile(sourceFile, codeName + 'Api', ImportType.DEFAULT, './' + codeName)

const arg = getDefinition(sourceFile, '"apis"')
if (!arg) {
throw "No link definition schema marker found for file: " + path
const arg = getDefinition(sourceFile, '"apis"')
if (!arg) {
throw "No link definition schema marker found for file: " + path
}
addObjectLiteralProperty(arg, codeName, codeName + 'Api')
sourceFile.saveSync();
}
addObjectLiteralProperty(arg, codeName, codeName + 'Api')
sourceFile.saveSync();
},

async get(key) {
Expand Down
8 changes: 8 additions & 0 deletions packages/protolib/src/bundles/apis/templates/python-api.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from flask import Blueprint

#blueprint with same name as the file
{{codeNameLowerCase}}_bp = Blueprint('{{codeNameLowerCase}}_bp', __name__)

@test_bp.route("/pyapi/v1/{{codeNameLowerCase}}")
def {{codeNameLowerCase}}_run():
return "ok"
6 changes: 6 additions & 0 deletions packages/protolib/src/bundles/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export const apiTemplates = {
return
}
},
"python-api": {
id: "python-api",
name: "Python API",
description: 'Create python automations that react to events and perform actions (when ..., do ...)',
icon: PencilRuler
},
"automatic-crud-storage": {
id: "automatic-crud-storage",
name: "Object storage (custom database)",
Expand Down

0 comments on commit 6bf96d9

Please sign in to comment.