Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
mslabek committed Oct 24, 2024
1 parent f02e9df commit a1ec130
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import pl.touk.nussknacker.http.enricher.HttpEnricher.{ApiKeyConfig, Body, BodyT
import pl.touk.nussknacker.http.enricher.HttpEnricherParameters.BodyParam
import sttp.client3.basicRequest
import sttp.client3.circe._
import sttp.model.{Header, Method, QueryParams, Uri}
import sttp.model.{Header, Headers, Method, QueryParams, Uri}

import java.net.URL
import scala.collection.immutable
Expand All @@ -35,23 +35,25 @@ class HttpEnricher(
componentUseCase: ComponentUseCase
): Future[AnyRef] = {
val url = {
val urlParam = HttpEnricherParameters.UrlParam.extractor(context, params)
val queryParamsFromParam: QueryParams = HttpEnricherParameters.QueryParamsParam.extractor(context, params) match {
case null => QueryParams()
case jMap => QueryParams.fromMap(jMap.asScala.toMap)
}
val queryParamsApiKeys = securityConfig.collect { case q: ApiKeyInQuery => q.name -> q.value }.toMap
val allQueryParams = queryParamsFromParam.param(queryParamsApiKeys)
buildURL(rootUrl, urlParam, allQueryParams).fold(ex => throw ex, identity)
val urlParam = HttpEnricherParameters.UrlParam.extractor(context, params)
val queryParamsFromParam = HttpEnricherParameters.QueryParamsParam.extractor(context, params)
val queryParamsApiKeys = securityConfig.collect { case q: ApiKeyInQuery => q.name -> q.value }.toList
val allQueryParamsGrouped =
(queryParamsFromParam ++ queryParamsApiKeys)
val finalQueryParams = QueryParams.fromSeq(allQueryParamsGrouped)
buildURL(rootUrl, urlParam, finalQueryParams).fold(ex => throw ex, identity)
}

val headers: List[Header] = HttpEnricherParameters.HeadersParam.extractor(context, params) match {
case null => List.empty
case jMap =>
jMap.asScala.toMap.map { case (k, v) =>
Header(k, v)
}.toList
}
// TODO: merging cookies?
val headersFromParam: List[(String, String)] = HttpEnricherParameters.HeadersParam.extractor(context, params)
val headersApiKeys = securityConfig.collect { case q: ApiKeyInHeader => q.name -> q.value }.toList
val headers = (headersFromParam ++ headersApiKeys)
.groupBy(_._1)
.map { case (k, v) =>
k -> v.map(_._2).mkString(",")
}
.map(a => Header.apply(a._1, a._2))
.toList

val body = BodyParam.extractor(context, params, bodyType)

Expand Down Expand Up @@ -88,7 +90,6 @@ class HttpEnricher(
}
val requestWithSecurityApplied = securityConfig.foldLeft(requestWithAppliedBody) { (request, securityToApply) =>
securityToApply match {
case ApiKeyInHeader(name, value) => request.header(name, value)
case ApiKeyInCookie(name, value) => request.cookie(name, value)
case _ => request
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package pl.touk.nussknacker.http.enricher

import pl.touk.nussknacker.engine.api._
import pl.touk.nussknacker.engine.api.context.ProcessCompilationError.CustomNodeError
import pl.touk.nussknacker.engine.api.context.transformation.{
DefinedEagerParameter,
DefinedLazyParameter,
Expand All @@ -16,14 +15,13 @@ import pl.touk.nussknacker.engine.api.typed.typing
import pl.touk.nussknacker.engine.api.typed.typing.TypingResult
import pl.touk.nussknacker.http.HttpEnricherConfig
import pl.touk.nussknacker.http.client.HttpClientProvider
import pl.touk.nussknacker.http.enricher.HttpEnricher.{BodyType, HttpMethod, buildURL}
import pl.touk.nussknacker.http.enricher.HttpEnricher.{BodyType, HttpMethod}
import pl.touk.nussknacker.http.enricher.HttpEnricherFactory.{
BodyParamExtractor,
BodyTypeParamExtractor,
TransformationState
}
import pl.touk.nussknacker.http.enricher.HttpEnricherParameters._
import sttp.model.QueryParams

class HttpEnricherFactory(val config: HttpEnricherConfig)
extends EagerService
Expand Down Expand Up @@ -67,28 +65,20 @@ class HttpEnricherFactory(val config: HttpEnricherConfig)
implicit nodeId: NodeId
): ContextTransformationDefinition = {
case TransformationStep(
(UrlParam.name, DefinedLazyParameter(lazyUrlParam)) ::
(QueryParamsParam.name, _) ::
(MethodParam.name, DefinedEagerParameter(httpMethod: String, _)) ::
(HeadersParam.name, _) ::
(UrlParam.name, DefinedLazyParameter(urlParamTypingResult)) ::
(QueryParamsParam.name, DefinedLazyParameter(queryParamsParamTypingResult)) ::
(MethodParam.name, DefinedEagerParameter(methodParamValue: String, _)) ::
(HeadersParam.name, DefinedLazyParameter(headersParamTypingResult)) ::
(BodyTypeParam.name, _) ::
parametersTail,
Some(TransformationState.BodyTypeDeclared(bodyType))
) =>
val outName = OutputVariableNameDependency.extract(dependencies)

val method = HttpMethod.values
.find(_.name == httpMethod)
.find(_.name == methodParamValue)
.getOrElse(throw new IllegalStateException("Invalid body type parameter value."))

val compileTimeUrlValidationErrorOpt = lazyUrlParam.valueOpt.flatMap {
case url: String =>
buildURL(config.rootUrl, url, QueryParams()).swap.toOption.map(ex =>
CustomNodeError(s"Invalid URL: ${ex.cause.getMessage}", Some(UrlParam.name))
)
case _ => None
}

val requestBodyTypingResult = bodyType match {
case BodyType.None => typing.TypedNull
case nonEmptyBodyType =>
Expand All @@ -101,14 +91,23 @@ class HttpEnricherFactory(val config: HttpEnricherConfig)
}
}

val compileTimeUrlValidation = UrlParam.validate(urlParamTypingResult, config.rootUrl)
val queryParamErrors = QueryParamsParam.validate(queryParamsParamTypingResult)

// TODO http: add validation for String | List[String] in headers and query params

val errors = compileTimeUrlValidation ++ queryParamErrors

val outputTypingResult = HttpEnricherOutput.typingResult(requestBodyTypingResult)

FinalResults.forValidation(
context,
compileTimeUrlValidationErrorOpt.toList,
errors,
Some(TransformationState.FinalState(bodyType, method))
)(ctx =>
ctx.withVariable(
outName,
HttpEnricherOutput.typingResult(requestBodyTypingResult),
outputTypingResult,
Some(ParameterName(OutputVar.CustomNodeFieldName))
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import pl.touk.nussknacker.http.enricher.MapExtensions.MapToHashMapExtension
import sttp.client3.Response
import sttp.model.MediaType

// TODO decision: can we leak headers / url / body in scenario? would it be enough to filter out configured securities?
private[enricher] object HttpEnricherOutput {

// TODO: fill out request typing result with values determined at validation
// TODO: add typing results with values if evaulable
def typingResult(requestBodyTypingResult: TypingResult): TypedObjectTypingResult = Typed.record(
List(
"request" -> Typed.record(
Expand All @@ -26,13 +25,14 @@ private[enricher] object HttpEnricherOutput {
List(
"statusCode" -> Typed[Int],
"statusText" -> Typed[String],
"headers" -> Typed.typedClass[java.util.Map[String, String]],
"headers" -> Typed.genericTypeClass[Map[_, _]](Typed[String] :: Typed[String] :: Nil),
"body" -> Unknown
)
),
)
)

// TODO: filter out configured seucurities
def buildOutput(response: Response[Either[String, String]], requestBody: Option[Body]): java.util.Map[String, _] =
Map(
"request" -> Map(
Expand Down Expand Up @@ -69,19 +69,19 @@ private[enricher] object HttpEnricherOutput {
case Right(value) => value
}
contentType match {
case s if s == MediaType.ApplicationJson.toString() =>
case s if s.toLowerCase.contains(MediaType.ApplicationJson.toString()) =>
io.circe.parser.parse(body) match {
case Right(json) => JsonUtils.jsonToAny(json)
case Left(err) =>
throw NonTransientException(
input = body,
message = s"Could not parse json: ${err.message}",
cause = err.underlying
) // TODO decision: if we cant parse - throw exception or return null?
)
}
case s if s == MediaType.TextPlain.toString() => body
case s if s.toLowerCase.contains(MediaType.TextPlain.toString()) => body
/*
TODO decision: if we cant parse body, do we:
TODO decision: if we get an unsupported body type:
1. treat it as text/plain - pass it as string without parsing
2. throw exception
3. return null
Expand Down
Loading

0 comments on commit a1ec130

Please sign in to comment.