All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kalix.springsdk.impl.DescriptorValidationCommon.scala Maven / Gradle / Ivy

/*
 * Copyright 2021 Lightbend Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kalix.springsdk.impl

import java.lang.reflect.Method

import kalix.javasdk.valueentity
import kalix.springsdk.annotations.Subscribe.ValueEntity
import kalix.springsdk.impl.ComponentDescriptorFactory.hasHandleDeletes
import kalix.springsdk.impl.ComponentDescriptorFactory.hasValueEntitySubscription

object DescriptorValidationCommon {

  def validateHandleDeletesMethodArity(method: Method): Unit = {
    method.getParameterTypes.toList match {
      case params if params.nonEmpty =>
        throw InvalidComponentException(
          s"Method [${method.getName}] annotated with '@Subscribe.ValueEntity' and handleDeletes=true must not have parameters. Found ${params.size} method parameters.")
      case _ => // happy days, dev did good with the signature
    }
  }

  def validateHandleDeletesTrueOnMethodLevel(component: Class[_]) = {
    val incorrectDeleteHandlers = component.getMethods
      .filter(hasValueEntitySubscription)
      .filter(_.getParameterTypes.isEmpty) //maybe a delete handler
      .filterNot(hasHandleDeletes)
      .map(_.getName)

    if (incorrectDeleteHandlers.nonEmpty) {
      throw InvalidComponentException(
        s"Methods: '${incorrectDeleteHandlers.mkString(", ")}' look like delete handlers but with handleDeletes flag eq false. " +
        s"Change flag to true, or fix method signature to accept one parameter.")
    }
  }

  def validateIfHandleDeletesMethodsMatchesSubscriptions(component: Class[_]) = {
    val handleDeletesValueEntityClasses = component.getMethods
      .filter(hasHandleDeletes)
      .map(method => (method.getName, method.getAnnotation(classOf[ValueEntity]).value()))
      .toMap

    val valueEntitySubscriptionMethods = component.getMethods
      .filterNot(hasHandleDeletes)
      .filter(hasValueEntitySubscription)
      .map(method => (method.getName, method.getAnnotation(classOf[ValueEntity]).value()))
      .toMap

    validateDuplicatedClasses(handleDeletesValueEntityClasses)
    validateDuplicatedClasses(valueEntitySubscriptionMethods)

    val deletesVEClasses = handleDeletesValueEntityClasses.values.map(_.getName).toSet
    val subscriptionVEClasses = valueEntitySubscriptionMethods.values.map(_.getName).toSet

    val diff = deletesVEClasses.diff(subscriptionVEClasses)

    if (diff.nonEmpty) {
      throw InvalidComponentException(
        s"Some methods annotated with handleDeletes=true don't have matching subscription methods. " +
        s"Add subscription annotated with '@Subscribe.ValueEntity' for types: ${diff.mkString(", ")}")
    }
  }

  def validateDuplicatedClasses(methodWithValueEntityClass: Map[String, Class[_ <: valueentity.ValueEntity[_]]]) = {
    methodWithValueEntityClass.groupBy(_._2.getName).map {
      case (className, groupedMethods) if groupedMethods.size > 1 =>
        throw new InvalidComponentException(
          s"Duplicated subscription to the same ValueEntity: $className from methods: ${groupedMethods.keys.mkString(", ")}")
      case _ => //ok
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy