Skip to content

Commit

Permalink
Fix validation of creating gRPC stubs
Browse files Browse the repository at this point in the history
Previously, validation of was performed only if the creating stubs
contained request predicate. And it performed only for request scheme.
Now mockingbird validates both request and response schemes regardless
if the creating stubs contains request predicate or not.
  • Loading branch information
ashashev authored and danslapman committed Oct 21, 2023
1 parent 798a59e commit 33efe27
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,9 @@ final class AdminApiHandler(
)
)
requestSchema <- protobufSchemaResolver.parseDefinitionFrom(requestSchemaBytes)
rootFields <- GrpcStub.getRootFields(body.requestClass, requestSchema)
_ <- ZIO.foreachParDiscard(body.requestPredicates.definition.keys)(
GrpcStub.validateOptics(_, body.requestClass, requestSchema)
GrpcStub.validateOptics(_, requestSchema, rootFields)
)
candidates0 <- grpcStubDAO.findChunk(
prop[GrpcStub](_.methodName) === body.methodName,
Expand All @@ -444,6 +445,7 @@ final class AdminApiHandler(
)
)
responseSchema <- protobufSchemaResolver.parseDefinitionFrom(responseSchemaBytes)
_ <- GrpcStub.getRootFields(body.responseClass, responseSchema)
now <- ZIO.clockWith(_.instant)
stub = body
.into[GrpcStub]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ object GrpcExractor {
}

def convertMessageToJson(bytes: Array[Byte], className: String): Task[Json] =
ZIO.fromEither {
val message = parseFrom(bytes, className)
val jsonString = JsonFormat.printer().preservingProtoFieldNames().print(message)
parse(jsonString)
}
for {
message <- ZIO.attempt(parseFrom(bytes, className))
jsonString <- ZIO.attempt(JsonFormat.printer().preservingProtoFieldNames().print(message))
js <- ZIO.fromEither(parse(jsonString))
} yield js
}

implicit class FromDynamicSchema(private val dynamicSchema: DynamicSchema) extends AnyVal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,28 @@ class GrpcStubResolverImpl(stubDAO: GrpcStubDAO[Task], stateDAO: PersistentState
(stub, pairs.find(_._2.id == stub.id).map(_._1).get, states.headOption)
}

private def parseJson(stub: GrpcStub, bytes: Array[Byte]): UIO[Option[(Json, SID[GrpcStub])]] =
private def parseJson(stub: GrpcStub, bytes: Array[Byte]): URIO[WLD, Option[(Json, SID[GrpcStub])]] =
ZIO
.blocking(
stub.requestSchema.convertMessageToJson(bytes, stub.requestClass).map(json => (json, stub.id).some)
)
.catchSome { case _: InvalidProtocolBufferException | ParsingFailure(_, _) =>
ZIO.none
.catchSome { case e @ (_: InvalidProtocolBufferException | ParsingFailure(_, _)) =>
log.infoCause(
"Ошибка разбора gRPC запроса {} для заглушки {}",
e,
stub.requestClass,
stub.id
) *>
ZIO.none
}
.tapError(e =>
log.errorCause(
"Ошибка разбора gRPC запроса {} для заглушки {}",
e,
stub.requestClass,
stub.id
)
)
.orDie

private def findStates(id: SID[GrpcStub], spec: StateSpec): RIO[WLD, Vector[PersistentState]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,26 @@ case class GrpcStub(
object GrpcStub {
private val indexRegex = "\\[([\\d]+)\\]".r

def getRootFields(className: String, definition: GrpcProtoDefinition): IO[ValidationError, List[GrpcField]] = {
val name = definition.`package`.map(p => className.stripPrefix(p ++ ".")).getOrElse(className)
for {
rootMessage <- ZIO.getOrFailWith(ValidationError(Vector(s"Root message '$className' not found")))(
definition.schemas.find(_.name == name)
)
rootFields <- rootMessage match {
case GrpcMessageSchema(_, fields, oneofs, _) =>
ZIO.succeed(fields ++ oneofs.map(_.flatMap(_.options)).getOrElse(List.empty))
case GrpcEnumSchema(_, _) =>
ZIO.fail(ValidationError(Vector(s"Enum cannot be a root message, but '$className' is")))
}
} yield rootFields
}

def validateOptics(
optic: JsonOptic,
className: String,
definition: GrpcProtoDefinition
definition: GrpcProtoDefinition,
rootFields: List[GrpcField]
): IO[ValidationError, Unit] = for {
rootMessage <- ZIO.getOrFailWith(ValidationError(Vector("Root message not found")))(
definition.schemas.find(_.name == className)
)
rootFields <- rootMessage match {
case GrpcMessageSchema(_, fields, oneofs, _) =>
ZIO.succeed(fields ++ oneofs.map(_.flatMap(_.options)).getOrElse(List.empty))
case GrpcEnumSchema(_, _) => ZIO.fail(ValidationError(Vector("Enum cannot be a root message")))
}
fields <- Ref.make(rootFields)
opticFields = optic.path.split("\\.").map {
case indexRegex(x) => Left(x.toInt)
Expand Down

0 comments on commit 33efe27

Please sign in to comment.