diff --git a/otoroshi/app/api/api.scala b/otoroshi/app/api/api.scala index 28be74d2a..4eaada5d6 100644 --- a/otoroshi/app/api/api.scala +++ b/otoroshi/app/api/api.scala @@ -879,7 +879,27 @@ class OtoroshiResources(env: Env) { stateOne = id => env.proxyState.api(id), stateUpdate = seq => env.proxyState.updateApis(seq) ) - ) + ), +// ////// +// Resource( +// "Flow", +// "flows", +// "flow", +// "flows.otoroshi.io", +// ResourceVersion("v1", true, false, true), +// GenericResourceAccessApiWithState[Flow]( +// Flow.format, +// classOf[Flow], +// env.datastores.flowDataStore.key, +// env.datastores.flowDataStore.extractId, +// json => json.select("id").asString, +// () => "id", +// (_v, _p) => env.datastores.flowDataStore.template(env).json, +// stateAll = () => env.proxyState.allFlows(), +// stateOne = id => env.proxyState.flow(id), +// stateUpdate = seq => env.proxyState.updateFlows(seq) +// ) +// ) ) ++ env.adminExtensions.resources() } diff --git a/otoroshi/app/next/controllers/frontends.scala b/otoroshi/app/next/controllers/frontends.scala index 47ca7136d..4b2c2d230 100644 --- a/otoroshi/app/next/controllers/frontends.scala +++ b/otoroshi/app/next/controllers/frontends.scala @@ -20,6 +20,7 @@ class NgFrontendsController(val ApiAction: ApiAction, val cc: ControllerComponen lazy val logger = Logger("otoroshi-frontends-api") + // TODO - ??? def form() = ApiAction { env.openApiSchema.asForms.get("otoroshi.next.models.NgFrontend") match { case Some(value) => @@ -42,4 +43,8 @@ class NgFrontendsController(val ApiAction: ApiAction, val cc: ControllerComponen case _ => NotFound(Json.obj("error" -> "Schema and flow not found")) } } + + def template() = ApiAction { + Ok(NgFrontend.empty.json) + } } diff --git a/otoroshi/app/next/models/Api.scala b/otoroshi/app/next/models/Api.scala index e760a290f..33e2c3320 100644 --- a/otoroshi/app/next/models/Api.scala +++ b/otoroshi/app/next/models/Api.scala @@ -10,7 +10,7 @@ import otoroshi.next.plugins.NgApikeyCallsConfig import otoroshi.security.IdGenerator import otoroshi.storage.{BasicStore, RedisLike, RedisLikeStore} import otoroshi.utils.syntax.implicits.{BetterJsReadable, BetterJsValue} -import play.api.libs.json.{Format, JsArray, JsError, JsResult, JsString, JsSuccess, JsValue, Json} +import play.api.libs.json.{Format, JsArray, JsBoolean, JsError, JsNull, JsNumber, JsObject, JsResult, JsString, JsSuccess, JsValue, Json} import scala.util.{Failure, Success, Try} @@ -65,16 +65,18 @@ case object ApiRemoved extends ApiState { // } //} -case class ApiRoute(frontend: NgFrontend, flows: String, backend: String) +case class ApiRoute(id: String, name: Option[String], frontend: NgFrontend, flowRef: String, backend: ApiBackend) object ApiRoute { val _fmt = new Format[ApiRoute] { override def reads(json: JsValue): JsResult[ApiRoute] = Try { ApiRoute( - frontend = NgFrontend.readFrom((json \ "frontend")), - flows = (json \ "flows").as[String], - backend = (json \ "backend").as[String] + id = json.select("id").as[String], + name = json.select("name").asOpt[String], + frontend = NgFrontend.readFrom(json \ "frontend"), + flowRef = (json \ "flow_ref").as[String], + backend = (json \ "backend").as[ApiBackend](ApiBackend._fmt) ) } match { case Failure(ex) => @@ -84,7 +86,11 @@ object ApiRoute { } override def writes(o: ApiRoute): JsValue = Json.obj( - + "id" -> o.id, + "name" -> o.name, + "frontend" -> o.frontend.json, + "backend" -> ApiBackend._fmt.writes(o.backend), + "flow_ref" -> o.flowRef ) } } @@ -260,9 +266,7 @@ object ApiDocumentation { .getOrElse(Seq.empty) ) } match { - case Failure(ex) => - ex.printStackTrace() - JsError(ex.getMessage) + case Failure(ex) => JsError(ex.getMessage) case Success(value) => JsSuccess(value) } @@ -474,17 +478,22 @@ object ApiConsumerStatus { } } -case class ApiBackend(id: String, name: String, backend: NgBackend) +sealed trait ApiBackend object ApiBackend { - val _fmt: Format[ApiBackend] = new Format[ApiBackend] { + case class ApiBackendRef(ref: String) extends ApiBackend + case class ApiBackendInline(id: String, name: String, backend: NgBackend) extends ApiBackend + val _fmt: Format[ApiBackend] = new Format[ApiBackend] { override def reads(json: JsValue): JsResult[ApiBackend] = Try { - ApiBackend( - id = json.select("id").as[String], - name = json.select("name").as[String], - backend = json.select("backend").as(NgBackend.fmt) - ) + json.select("ref").asOpt[String] match { + case Some(ref) => ApiBackendRef(ref) + case None => ApiBackendInline( + id = json.select("id").as[String], + name = json.select("name").as[String], + backend = json.select("backend").as(NgBackend.fmt) + ) + } } match { case Failure(ex) => ex.printStackTrace() @@ -492,11 +501,16 @@ object ApiBackend { case Success(value) => JsSuccess(value) } - override def writes(o: ApiBackend): JsValue = Json.obj( - "id" -> o.id, - "name" -> o.name, - "backend" -> NgBackend.fmt.writes(o.backend) - ) + override def writes(o: ApiBackend): JsValue = { + o match { + case ApiBackendRef(ref) => Json.obj("ref" -> ref) + case ApiBackendInline(id, name, backend) => Json.obj( + "id" -> id, + "name" -> name, + "backend" -> NgBackend.fmt.writes(backend) + ) + } + } } } @@ -628,7 +642,8 @@ object Api { .asOpt[Seq[JsValue]] .map(_.flatMap(v => ApiBackendClient._fmt.reads(v).asOpt)) .getOrElse(Seq.empty), - documentation = ApiDocumentation._fmt.reads((json \ "documentation").as[JsValue]).asOpt, + documentation = (json \ "documentation") + .asOpt[ApiDocumentation](ApiDocumentation._fmt.reads), consumers = (json \ "consumers") .asOpt[Seq[JsValue]] .map(_.flatMap(v => ApiConsumer._fmt.reads(v).asOpt)) diff --git a/otoroshi/conf/routes b/otoroshi/conf/routes index a36460e6f..5b2b2337a 100644 --- a/otoroshi/conf/routes +++ b/otoroshi/conf/routes @@ -555,7 +555,9 @@ GET /api/backends otoroshi.next.controllers.adminap POST /api/backends otoroshi.next.controllers.adminapi.NgBackendsController.createAction() ## Frontends +## TODO - remove this endpoint GET /api/frontends/_form otoroshi.next.controllers.adminapi.NgFrontendsController.form() +GET /api/frontends/_template otoroshi.next.controllers.adminapi.NgFrontendsController.template() ## Plugins GET /api/plugins/categories otoroshi.next.controllers.NgPluginsController.categories() diff --git a/otoroshi/javascript/src/components/Drafts/DraftEditor.js b/otoroshi/javascript/src/components/Drafts/DraftEditor.js index 62399520e..0a41b2cf1 100644 --- a/otoroshi/javascript/src/components/Drafts/DraftEditor.js +++ b/otoroshi/javascript/src/components/Drafts/DraftEditor.js @@ -15,7 +15,12 @@ import { PillButton } from '../PillButton'; import JsonViewCompare from './Compare'; import { Button } from '../Button'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + queries: { + retry: false, + refetchOnWindowFocus: false + }, +}); function findDraftByEntityId(id) { return nextClient.forEntityNext(nextClient.ENTITIES.DRAFTS).findById(id); diff --git a/otoroshi/javascript/src/components/nginputs/inputs.js b/otoroshi/javascript/src/components/nginputs/inputs.js index 9cffd65f9..79c9b697a 100644 --- a/otoroshi/javascript/src/components/nginputs/inputs.js +++ b/otoroshi/javascript/src/components/nginputs/inputs.js @@ -143,9 +143,8 @@ export class NgDotsRenderer extends Component { return (