Skip to content

Commit

Permalink
refacto designer for APIS
Browse files Browse the repository at this point in the history
  • Loading branch information
Zwiterrion committed Jan 10, 2025
1 parent 8067f5e commit 436b989
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 78 deletions.
73 changes: 49 additions & 24 deletions otoroshi/app/storage/stores/KvGlobalConfigDataStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,39 +268,64 @@ class KvGlobalConfigDataStore(redisCli: RedisLike, _env: Env)
val extensions = (exportSource \ "extensions").asOpt[JsObject].getOrElse(Json.obj())

for {
_ <- redisCli
first <- redisCli
.keys(s"${env.storageRoot}:*")
.flatMap(keys => if (keys.nonEmpty) redisCli.del(keys: _*) else FastFuture.successful(0L))
_ <- config.save()
_ <-
second <- config.save()
admins <-
Future.sequence(
admins.value.map(v => env.datastores.webAuthnAdminDataStore.registerUser(WebAuthnOtoroshiAdmin.reads(v).get))
)
_ <- Future.sequence(
simpleAdmins <- Future.sequence(
simpleAdmins.value.map(v =>
env.datastores.simpleAdminDataStore.registerUser(SimpleOtoroshiAdmin.reads(v).get)
)
)
_ <- Future.sequence(serviceGroups.value.map(ServiceGroup.fromJsons).map(_.save()))
_ <- Future.sequence(apiKeys.value.map(ApiKey.fromJsons).map(_.save()))
_ <- Future.sequence(serviceDescriptors.value.map(ServiceDescriptor.fromJsons).map(_.save()))
_ <- Future.sequence(errorTemplates.value.map(ErrorTemplate.fromJsons).map(_.save()))
_ <- Future.sequence(jwtVerifiers.value.map(GlobalJwtVerifier.fromJsons).map(_.save()))
_ <- Future.sequence(authConfigs.value.map(AuthModuleConfig.fromJsons).map(_.save()))
_ <- Future.sequence(certificates.value.map(Cert.fromJsons).map(_.save()))
_ <- Future.sequence(clientValidators.value.map(ClientCertificateValidator.fromJsons).map(_.save()))
_ <- Future.sequence(scripts.value.map(Script.fromJsons).map(_.save()))
_ <- Future.sequence(tcpServices.value.map(TcpService.fromJsons).map(_.save()))
_ <- Future.sequence(dataExporters.value.map(DataExporterConfig.fromJsons).map(_.save()))
_ <- Future.sequence(tenants.value.map(Tenant.fromJsons).map(_.save()))
_ <- Future.sequence(teams.value.map(Team.fromJsons).map(_.save()))
_ <- Future.sequence(routes.value.map(NgRoute.fromJsons).map(_.save()))
_ <- Future.sequence(routeCompositions.value.map(NgRouteComposition.fromJsons).map(_.save()))
_ <- Future.sequence(backends.value.map(StoredNgBackend.fromJsons).map(_.save()))
_ <- Future.sequence(wasmPlugins.value.map(WasmPlugin.fromJsons).map(_.save()))
_ <- Future.sequence(drafts.value.map(Draft.fromJsons).map(_.save()))
_ <- env.adminExtensions.importAllEntities(extensions)
} yield ()
serviceGroups <- Future.sequence(serviceGroups.value.map(ServiceGroup.fromJsons).map(_.save()))
apiKeys <- Future.sequence(apiKeys.value.map(ApiKey.fromJsons).map(_.save()))
serviceDescriptors <- Future.sequence(serviceDescriptors.value.map(ServiceDescriptor.fromJsons).map(_.save()))
errorTemplates <- Future.sequence(errorTemplates.value.map(ErrorTemplate.fromJsons).map(_.save()))
jwtVerifiers <- Future.sequence(jwtVerifiers.value.map(GlobalJwtVerifier.fromJsons).map(_.save()))
authConfigs <- Future.sequence(authConfigs.value.map(AuthModuleConfig.fromJsons).map(_.save()))
certificates <- Future.sequence(certificates.value.map(Cert.fromJsons).map(_.save()))
clientValidators <- Future.sequence(clientValidators.value.map(ClientCertificateValidator.fromJsons).map(_.save()))
scripts <- Future.sequence(scripts.value.map(Script.fromJsons).map(_.save()))
tcpServices <- Future.sequence(tcpServices.value.map(TcpService.fromJsons).map(_.save()))
dataExporters <- Future.sequence(dataExporters.value.map(DataExporterConfig.fromJsons).map(_.save()))
tenants <- Future.sequence(tenants.value.map(Tenant.fromJsons).map(_.save()))
teams <- Future.sequence(teams.value.map(Team.fromJsons).map(_.save()))
routes <- Future.sequence(routes.value.map(NgRoute.fromJsons).map(_.save()))
routeCompositions <- Future.sequence(routeCompositions.value.map(NgRouteComposition.fromJsons).map(_.save()))
backends <- Future.sequence(backends.value.map(StoredNgBackend.fromJsons).map(_.save()))
wasmPlugins <- Future.sequence(wasmPlugins.value.map(WasmPlugin.fromJsons).map(_.save()))
drafts <- Future.sequence(drafts.value.map(Draft.fromJsons).map(_.save()))
alls <- env.adminExtensions.importAllEntities(extensions)
} yield {
println("first", first)
println("second", second)
println("admins", admins)
println("simpleAdmins", simpleAdmins)
println("serviceGroups", serviceGroups)
println("apiKeys", apiKeys)
println("serviceDescriptors", serviceDescriptors)
println("errorTemplates", errorTemplates)
println("jwtVerifiers", jwtVerifiers)
println("authConfigs", authConfigs)
println("certificates", certificates)
println("clientValidators", clientValidators)
println("scripts", scripts)
println("tcpServices", tcpServices)
println("dataExporters", dataExporters)
println("tenants", tenants)
println("teams", teams)
println("routes", routes)
println("routeCompositions", routeCompositions)
println("backends", backends)
println("wasmPlugins", wasmPlugins)
println("drafts", drafts)
println("alls", alls)
()
}
}

override def fullExport()(implicit ec: ExecutionContext, env: Env): Future[JsValue] = {
Expand Down
3 changes: 2 additions & 1 deletion otoroshi/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ reStart / javaOptions ++= Seq(
"-Dotoroshi.next.state-sync-interval=1000",
// "-Dotoroshi.next.experimental.netty-server.native.driver=IOUring",
// "-Dotoroshi.storage=ext:foo",
"-Dotoroshi.storage=file"
"-Dotoroshi.storage=file",
// "-DOTOROSHI_IMPORT_FROM=/Users/zwitterion/Downloads/dev-otoroshi-2025-01-10-15-31-04.ndjson"
//"-Dotoroshi.storage=postgresql",
// "-Dotoroshi.storage=redis",
// "-Dotoroshi.storage=lettuce",
Expand Down
2 changes: 1 addition & 1 deletion otoroshi/javascript/src/apps/BackOfficeApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ class BackOfficeAppContainer extends Component {
/>
<Route
path={[
'/apis/:routeId',
'/apis/:apiId',
'/apis',
]}
component={(props) => (
Expand Down
11 changes: 7 additions & 4 deletions otoroshi/javascript/src/components/inputs/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,10 @@ class TableComponent extends Component {
this.state.showAddForm || this.state.showEditForm
? this.props.fetchItems()
: this.props.fetchItems({
...paginationState,
pageSize: this.state.rowsPerPage,
page: page + 1,
})
...paginationState,
pageSize: this.state.rowsPerPage,
page: page + 1,
})
).then((rawItems) => {
if (Array.isArray(rawItems)) {
const sortedItems = [...rawItems];
Expand Down Expand Up @@ -910,6 +910,9 @@ class TableComponent extends Component {
<ReactTable
ref={this.tableRef}
className="fulltable -striped -highlight"
style={{
scrollbarWidth: 'none'
}}
manual
page={this.state.page}
pages={this.state.pages}
Expand Down
118 changes: 93 additions & 25 deletions otoroshi/javascript/src/pages/ApiEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Uptime } from '../../components/Status';
import { Table } from '../../components/inputs';
import { v4 as uuid } from 'uuid';
import Designer from '../RouteDesigner/Designer';
import Loader from '../../components/Loader';

// Mock Data for NgTarget
const ngTargetMock = createNgTarget({
Expand Down Expand Up @@ -151,41 +152,106 @@ export default function ApiEditor(props) {

useEffect(() => {
props.setSidebarContent(<Sidebar api={apiMock} />);
props.setTitle("Flows")
return () => props.setSidebarContent(null)
}, [])

const api = apiMock

return <div className='editor p-3'>
return <div className='editor'>
<Switch>
<Route exact path='/apis/:apiId/flows' component={componentProps => <Flows {...props} {...componentProps} api={api} />} />
<Route exact path='/apis/:apiId/flows/:flowId/edit' component={componentProps => <FlowDesigner {...props} {...componentProps} api={api} />} />
<Route path='/apis/:apiId' component={Dashboard} />
<Route exact path='/apis/:apiId/flows/:flowId' component={componentProps => <FlowDesigner {...props} {...componentProps} api={api} />} />
<Route path='/apis/:apiId' component={componentProps => <Dashboard {...props} {...componentProps} />} />
<Route path='/apis' component={componentProps => <Apis {...props} {...componentProps} />} />
</Switch>
</div>
}

function Apis(props) {
const ref = useRef()
const params = useParams()
const history = useHistory()

useEffect(() => {
props.setTitle("Apis")
}, [])

const [fields, setFields] = useState({
id: false,
name: true,
})
const columns = [
{
title: 'Id',
content: item => item.id
},
{
title: 'Name',
content: item => item.name
}
];

const fetchItems = (paginationState) => Promise.resolve([apiMock])

const fetchTemplate = () => Promise.resolve({
id: uuid(),
name: 'My new apis'
})

return <Table
ref={ref}
parentProps={{ params }}
navigateTo={(item) => history.push(`/apis/${item.id}`)}
navigateOnEdit={(item) => history.push(`/apis/${item.id}`)}
selfUrl="flows"
defaultTitle="Flow"
itemName="Flow"
formSchema={null}
formFlow={null}
columns={columns}
fields={fields}
deleteItem={(item) => console.log('delete item', item)}
defaultSort="name"
defaultSortDesc="true"
fetchItems={fetchItems}
fetchTemplate={fetchTemplate}
showActions={true}
showLink={false}
extractKey={(item) => item.id}
rowNavigation={true}
hideAddItemAction={true}
itemUrl={(i) => `/bo/dashboard/apis/${i.id}`}
rawEditUrl={true}
displayTrash={(item) => item.id === props.globalEnv.adminApiId} />
}

function FlowDesigner({ api, ...props }) {
const history = useHistory()
const params = useParams()

return <div>
<Designer
{...props}
toggleTesterButton={(va) => { }}
tab=""
history={history}
value={api.flows.find(flow => flow.id === params.flowId)}
setValue={(v) => {
// this.setState({ value: v }, this.setTitle);
}}
setSaveButton={(n) => {
console.log('setSaveButton')
// this.setState({ saveButton: n, saveTypeButton: 'routes' })
}}
setMenu={(n) => this.setState({ menu: n, menuRefreshed: Date.now() })} />
</div>
const loading = false;

useEffect(() => {
props.setTitle("Flow designer")
})

const value = api.flows.find(flow => flow.id === params.flowId)

return <Loader loading={loading}>
<div className='designer'>
<Designer
history={history}
value={value}
setValue={(v) => {
// this.setState({ value: v }, this.setTitle);
}}
setSaveButton={(n) => {
console.log('setSaveButton')
// this.setState({ saveButton: n, saveTypeButton: 'routes' })
}}
setMenu={(n) => this.setState({ menu: n, menuRefreshed: Date.now() })} />
</div>
</Loader>
}

function Flows({ api, ...props }) {
Expand Down Expand Up @@ -216,14 +282,12 @@ function Flows({ api, ...props }) {
plugins: []
})

console.log(params)

return <div>
<Table
ref={ref}
parentProps={{ params }}
navigateTo={(item) => history.push(`/apis/${params.apiId}/flows/${item.id}/edit`)}
navigateOnEdit={(item) => history.push(`/apis/${params.apiId}/flows/${item.id}/edit`)}
navigateTo={(item) => history.push(`/apis/${params.apiId}/flows/${item.id}`)}
navigateOnEdit={(item) => history.push(`/apis/${params.apiId}/flows/${item.id}`)}
selfUrl="flows"
defaultTitle="Flow"
itemName="Flow"
Expand Down Expand Up @@ -271,9 +335,13 @@ function Flows({ api, ...props }) {
</div>
}

function Dashboard() {
function Dashboard(props) {
const api = apiMock

useEffect(() => {
props.setTitle("Dashboard")
}, [])

return <div className='d-flex flex-column gap-3'>
<div className='d-flex gap-3'>
<div className='d-flex flex-column flex-grow gap-3'>
Expand Down
7 changes: 7 additions & 0 deletions otoroshi/javascript/src/pages/FeaturesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ export const graph = (env) => {
icon: () => 'fa-pencil-ruler',
link: '/drafts',
},
{
title: 'Apis',
description: 'All apis',
img: 'apis',
icon: () => 'fa-brush',
link: '/apis',
},
],
},
{
Expand Down
10 changes: 1 addition & 9 deletions otoroshi/javascript/src/pages/RouteDesigner/Designer.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ const Modal = ({ question, onOk, onCancel }) => (
);

export default forwardRef(
({ value, setSaveButton, setTestingButton, setMenu, history, setValue, ...props }, ref) => {
({ value, setSaveButton, history, setValue, ...props }, ref) => {
const { routeId } = props;
const location = useLocation();

Expand All @@ -270,23 +270,17 @@ export default forwardRef(
useEffect(() => {
if (location?.state?.showTryIt || window.location.search.includes('showTryIt')) {
childRef.current.toggleTryIt();
props.toggleTesterButton(true);
} else if (location?.state?.plugin) childRef.current.selectPlugin(location?.state?.plugin);
}, [location.state]);

return (
<Designer
ref={childRef}
toggleTesterButton={props.toggleTesterButton}
history={history}
routeId={routeId}
location={location}
value={value}
setValue={setValue}
setSaveButton={setSaveButton}
setTestingButton={setTestingButton}
setMenu={setMenu}
pathname={location.pathname}
/>
);
}
Expand Down Expand Up @@ -1545,8 +1539,6 @@ class Designer extends React.Component {
hide={(e) => {
e.stopPropagation();

if (this.props.toggleTesterButton) this.props.toggleTesterButton(false);

this.setState({
selectedNode: backendCallNodes.find((node) => {
return node.id.includes(
Expand Down
Loading

0 comments on commit 436b989

Please sign in to comment.