text:
package kafka.server

import kafka.network.RequestChannel
import kafka.raft.RaftManager
import kafka.server.QuotaFactory.QuotaManagers
import kafka.test.MockController
import kafka.utils.{MockTime, NotNothing}
import org.apache.kafka.clients.admin.AlterConfigOp
import org.apache.kafka.common.Uuid.ZERO_UUID
import org.apache.kafka.common.acl.AclOperation
import org.apache.kafka.common.config.{ConfigResource, TopicConfig}
import org.apache.kafka.common.errors._
import org.apache.kafka.common.memory.MemoryPool
import org.apache.kafka.common.message.AlterConfigsRequestData.{AlterConfigsResource => OldAlterConfigsResource, AlterConfigsResourceCollection => OldAlterConfigsResourceCollection, AlterableConfig => OldAlterableConfig, AlterableConfigCollection => OldAlterableConfigCollection}
import org.apache.kafka.common.message.AlterConfigsResponseData.{AlterConfigsResourceResponse => OldAlterConfigsResourceResponse}
import org.apache.kafka.common.message.ApiMessageType.ListenerType
import org.apache.kafka.common.message.CreatePartitionsRequestData.CreatePartitionsTopic
import org.apache.kafka.common.message.CreatePartitionsResponseData.CreatePartitionsTopicResult
import org.apache.kafka.common.message.CreateTopicsRequestData.{CreatableTopic, CreatableTopicCollection}
import org.apache.kafka.common.message.CreateTopicsResponseData.CreatableTopicResult
import org.apache.kafka.common.message.DeleteTopicsRequestData.DeleteTopicState
import org.apache.kafka.common.message.DeleteTopicsResponseData.DeletableTopicResult
import org.apache.kafka.common.message.IncrementalAlterConfigsRequestData.{AlterConfigsResource, AlterConfigsResourceCollection, AlterableConfig, AlterableConfigCollection}
import org.apache.kafka.common.message.IncrementalAlterConfigsResponseData.AlterConfigsResourceResponse
import org.apache.kafka.common.message._
import org.apache.kafka.common.network.{ClientInformation, ListenerName}
import org.apache.kafka.common.protocol.Errors._
import org.apache.kafka.common.protocol.{ApiKeys, ApiMessage, Errors}
import org.apache.kafka.common.requests._
import org.apache.kafka.common.resource.{PatternType, Resource, ResourcePattern, ResourceType}
import org.apache.kafka.common.security.auth.{KafkaPrincipal, SecurityProtocol}
import org.apache.kafka.common.{ElectionType, Uuid}
import org.apache.kafka.controller.ControllerRequestContextUtil.ANONYMOUS_CONTEXT
import org.apache.kafka.controller.{Controller, ControllerRequestContext}
import org.apache.kafka.server.authorizer.{Action, AuthorizableRequestContext, AuthorizationResult, Authorizer}
import org.apache.kafka.server.common.ApiMessageAndVersion
import org.junit.jupiter.api.Assertions._
import org.junit.jupiter.api.{AfterEach, Test}
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.ArgumentMatchers._
import org.mockito.Mockito._
import org.mockito.{ArgumentCaptor, ArgumentMatchers}

import java.net.InetAddress
import java.util
import java.util.Collections.singletonList
import java.util.concurrent.{CompletableFuture, ExecutionException, TimeUnit}
import java.util.{Collections, Properties}
import scala.annotation.nowarn
import scala.jdk.CollectionConverters._
import scala.reflect.ClassTag

class ControllerApisTest {
  private val nodeId = 1
  private val brokerRack = "Rack1"
  private val clientID = "Client1"
  private val requestChannelMetrics: RequestChannel.Metrics = mock(classOf[RequestChannel.Metrics])
  private val requestChannel: RequestChannel = mock(classOf[RequestChannel])
  private val time = new MockTime
  private val clientQuotaManager: ClientQuotaManager = mock(classOf[ClientQuotaManager])
  private val clientRequestQuotaManager: ClientRequestQuotaManager = mock(classOf[ClientRequestQuotaManager])
  private val clientControllerQuotaManager: ControllerMutationQuotaManager = mock(classOf[ControllerMutationQuotaManager])
  private val replicaQuotaManager: ReplicationQuotaManager = mock(classOf[ReplicationQuotaManager])
  private val raftManager: RaftManager[ApiMessageAndVersion] = mock(classOf[RaftManager[ApiMessageAndVersion]])

  private val quotas = QuotaManagers(
    clientQuotaManager,
    clientQuotaManager,
    clientRequestQuotaManager,
    clientControllerQuotaManager,
    replicaQuotaManager,
    replicaQuotaManager,
    replicaQuotaManager,
    None)

  private def createControllerApis(authorizer: Option[Authorizer],
                                   controller: Controller,
                                   props: Properties = new Properties()): ControllerApis = {
    props.put(KafkaConfig.NodeIdProp, nodeId: java.lang.Integer)
    props.put(KafkaConfig.ProcessRolesProp, "controller")
    props.put(KafkaConfig.ControllerListenerNamesProp, "PLAINTEXT")
    props.put(KafkaConfig.QuorumVotersProp, s"$nodeId@localhost:9092")
    new ControllerApis(
      requestChannel,
      authorizer,
      quotas,
      time,
      controller,
      raftManager,
      new KafkaConfig(props),
      MetaProperties("JgxuGe9URy-E-ceaL04lEw", nodeId = nodeId),
      Seq.empty,
      new SimpleApiVersionManager(ListenerType.CONTROLLER)
    )
  }

  /**
   * Build a RequestChannel.Request from the AbstractRequest
   *
   * @param request - AbstractRequest
   * @param listenerName - Default listener for the RequestChannel
   * @tparam T - Type of AbstractRequest
   * @return
   */
  private def buildRequest[T <: AbstractRequest](
    request: AbstractRequest,
    listenerName: ListenerName = ListenerName.forSecurityProtocol(SecurityProtocol.PLAINTEXT)
  ): RequestChannel.Request = {
    val buffer = request.serializeWithHeader(new RequestHeader(request.apiKey, request.version, clientID, 0))

    // read the header from the buffer first so that the body can be read next from the Request constructor
    val header = RequestHeader.parse(buffer)
    val context = new RequestContext(header, "1", InetAddress.getLocalHost, KafkaPrincipal.ANONYMOUS,
      listenerName, SecurityProtocol.PLAINTEXT, ClientInformation.EMPTY, false)
    new RequestChannel.Request(processor = 1, context = context, startTimeNanos = 0, MemoryPool.NONE, buffer,
      requestChannelMetrics)
  }

  def createDenyAllAuthorizer(): Authorizer = {
    val authorizer = mock(classOf[Authorizer])
    when(authorizer.authorize(
      any(classOf[AuthorizableRequestContext]),
      any(classOf[java.util.List[Action]])
    )).thenReturn(
      singletonList(AuthorizationResult.DENIED)
    )
    authorizer
  }

  @Test
  def testUnauthorizedFetch(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleFetch(buildRequest(new FetchRequest(new FetchRequestData(), 12))))
  }

  @Test
  def testFetchSentToKRaft(): Unit = {
    when(
      raftManager.handleRequest(
        any(classOf[RequestHeader]),
        any(classOf[ApiMessage]),
        any(classOf[Long])
      )
    ).thenReturn(
      new CompletableFuture[ApiMessage]()
    )

    createControllerApis(None, new MockController.Builder().build())
      .handleFetch(buildRequest(new FetchRequest(new FetchRequestData(), 12)))

    verify(raftManager).handleRequest(
      ArgumentMatchers.any(),
      ArgumentMatchers.any(),
      ArgumentMatchers.any()
    )
  }

  @Test
  def testFetchLocalTimeComputedCorrectly(): Unit = {
    val localTimeDurationMs = 5
    val initialTimeNanos = time.nanoseconds()
    val initialTimeMs = time.milliseconds()

    when(
      raftManager.handleRequest(
        any(classOf[RequestHeader]),
        any(classOf[ApiMessage]),
        any(classOf[Long])
      )
    ).thenAnswer { _ =>
      time.sleep(localTimeDurationMs)
      new CompletableFuture[ApiMessage]()
    }

    // Local time should be updated when `ControllerApis.handle` returns
    val fetchRequestData = new FetchRequestData()
    val request = buildRequest(new FetchRequest(fetchRequestData, ApiKeys.FETCH.latestVersion))
    createControllerApis(None, new MockController.Builder().build())
      .handle(request, RequestLocal.NoCaching)

    verify(raftManager).handleRequest(
      ArgumentMatchers.eq(request.header),
      ArgumentMatchers.eq(fetchRequestData),
      ArgumentMatchers.eq(initialTimeMs)
    )

    assertEquals(localTimeDurationMs, TimeUnit.MILLISECONDS.convert(
      request.apiLocalCompleteTimeNanos - initialTimeNanos,
      TimeUnit.NANOSECONDS
    ))
  }

  @Test
  def testUnauthorizedFetchSnapshot(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleFetchSnapshot(buildRequest(new FetchSnapshotRequest(new FetchSnapshotRequestData(), 0))))
  }

  @Test
  def testFetchSnapshotSentToKRaft(): Unit = {
    when(
      raftManager.handleRequest(
        any(classOf[RequestHeader]),
        any(classOf[ApiMessage]),
        any(classOf[Long])
      )
    ).thenReturn(
      new CompletableFuture[ApiMessage]()
    )

    createControllerApis(None, new MockController.Builder().build())
      .handleFetchSnapshot(buildRequest(new FetchSnapshotRequest(new FetchSnapshotRequestData(), 0)))

    verify(raftManager).handleRequest(
      ArgumentMatchers.any(),
      ArgumentMatchers.any(),
      ArgumentMatchers.any()
    )
  }

  @Test
  def testUnauthorizedVote(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleVote(buildRequest(new VoteRequest.Builder(new VoteRequestData()).build(0))))
  }

  @Test
  def testHandleLegacyAlterConfigsErrors(): Unit = {
    val requestData = new AlterConfigsRequestData().setResources(
      new OldAlterConfigsResourceCollection(util.Arrays.asList(
        new OldAlterConfigsResource().
          setResourceName("1").
          setResourceType(ConfigResource.Type.BROKER.id()).
          setConfigs(new OldAlterableConfigCollection(util.Arrays.asList(new OldAlterableConfig().
            setName(KafkaConfig.LogCleanerBackoffMsProp).
            setValue("100000")).iterator())),
        new OldAlterConfigsResource().
          setResourceName("2").
          setResourceType(ConfigResource.Type.BROKER.id()).
          setConfigs(new OldAlterableConfigCollection(util.Arrays.asList(new OldAlterableConfig().
            setName(KafkaConfig.LogCleanerBackoffMsProp).
            setValue("100000")).iterator())),
        new OldAlterConfigsResource().
          setResourceName("2").
          setResourceType(ConfigResource.Type.BROKER.id()).
          setConfigs(new OldAlterableConfigCollection(util.Arrays.asList(new OldAlterableConfig().
            setName(KafkaConfig.LogCleanerBackoffMsProp).
            setValue("100000")).iterator())),
        new OldAlterConfigsResource().
          setResourceName("baz").
          setResourceType(123.toByte).
          setConfigs(new OldAlterableConfigCollection(util.Arrays.asList(new OldAlterableConfig().
            setName("foo").
            setValue("bar")).iterator())),
        ).iterator()))
    val request = buildRequest(new AlterConfigsRequest(requestData, 0))
    createControllerApis(Some(createDenyAllAuthorizer()),
      new MockController.Builder().build()).handleLegacyAlterConfigs(request)
    val capturedResponse: ArgumentCaptor[AbstractResponse] =
      ArgumentCaptor.forClass(classOf[AbstractResponse])
    verify(requestChannel).sendResponse(
      ArgumentMatchers.eq(request),
      capturedResponse.capture(),
      ArgumentMatchers.eq(None))
    assertNotNull(capturedResponse.getValue)
    val response = capturedResponse.getValue.asInstanceOf[AlterConfigsResponse]
    assertEquals(Set(
      new OldAlterConfigsResourceResponse().
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate resource.").
        setResourceName("2").
        setResourceType(ConfigResource.Type.BROKER.id()),
      new OldAlterConfigsResourceResponse().
        setErrorCode(UNSUPPORTED_VERSION.code()).
        setErrorMessage("Unknown resource type 123.").
        setResourceName("baz").
        setResourceType(123.toByte),
      new OldAlterConfigsResourceResponse().
        setErrorCode(CLUSTER_AUTHORIZATION_FAILED.code()).
        setErrorMessage("Cluster authorization failed.").
        setResourceName("1").
        setResourceType(ConfigResource.Type.BROKER.id())),
      response.data().responses().asScala.toSet)
  }

  @Test
  def testUnauthorizedBeginQuorumEpoch(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleBeginQuorumEpoch(buildRequest(new BeginQuorumEpochRequest.Builder(
          new BeginQuorumEpochRequestData()).build(0))))
  }

  @Test
  def testUnauthorizedEndQuorumEpoch(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleEndQuorumEpoch(buildRequest(new EndQuorumEpochRequest.Builder(
          new EndQuorumEpochRequestData()).build(0))))
  }

  @Test
  def testUnauthorizedDescribeQuorum(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleDescribeQuorum(buildRequest(new DescribeQuorumRequest.Builder(
          new DescribeQuorumRequestData()).build(0))))
  }

  @Test
  def testUnauthorizedHandleAlterPartitionRequest(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleAlterPartitionRequest(buildRequest(new AlterPartitionRequest.Builder(
          new AlterPartitionRequestData(), false).build(0))))
  }

  @Test
  def testUnauthorizedHandleBrokerHeartBeatRequest(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleBrokerHeartBeatRequest(buildRequest(new BrokerHeartbeatRequest.Builder(
          new BrokerHeartbeatRequestData()).build(0))))
  }

  @Test
  def testUnauthorizedHandleUnregisterBroker(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleUnregisterBroker(buildRequest(new UnregisterBrokerRequest.Builder(
          new UnregisterBrokerRequestData()).build(0))))
  }

  @Test
  def testClose(): Unit = {
    val apis = createControllerApis(Some(createDenyAllAuthorizer()), mock(classOf[Controller]))
    apis.close()
    assertTrue(apis.isClosed)
  }

  @Test
  def testUnauthorizedBrokerRegistration(): Unit = {
    val brokerRegistrationRequest = new BrokerRegistrationRequest.Builder(
      new BrokerRegistrationRequestData()
        .setBrokerId(nodeId)
        .setRack(brokerRack)
    ).build()

    val request = buildRequest(brokerRegistrationRequest)
    val capturedResponse: ArgumentCaptor[AbstractResponse] = ArgumentCaptor.forClass(classOf[AbstractResponse])

    createControllerApis(Some(createDenyAllAuthorizer()), mock(classOf[Controller])).handle(request,
      RequestLocal.withThreadConfinedCaching)
    verify(requestChannel).sendResponse(
      ArgumentMatchers.eq(request),
      capturedResponse.capture(),
      ArgumentMatchers.eq(None))

    assertNotNull(capturedResponse.getValue)

    val brokerRegistrationResponse = capturedResponse.getValue.asInstanceOf[BrokerRegistrationResponse]
    assertEquals(Map(CLUSTER_AUTHORIZATION_FAILED -> 1),
      brokerRegistrationResponse.errorCounts().asScala)
  }

  @Test
  def testUnauthorizedHandleAlterClientQuotas(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleAlterClientQuotas(buildRequest(new AlterClientQuotasRequest(
          new AlterClientQuotasRequestData(), 0))))
  }

  @Test
  def testUnauthorizedHandleIncrementalAlterConfigs(): Unit = {
    val requestData = new IncrementalAlterConfigsRequestData().setResources(
      new AlterConfigsResourceCollection(
        util.Arrays.asList(new AlterConfigsResource().
          setResourceName("1").
          setResourceType(ConfigResource.Type.BROKER.id()).
          setConfigs(new AlterableConfigCollection(util.Arrays.asList(new AlterableConfig().
            setName(KafkaConfig.LogCleanerBackoffMsProp).
            setValue("100000").
            setConfigOperation(AlterConfigOp.OpType.SET.id())).iterator())),
        new AlterConfigsResource().
          setResourceName("foo").
          setResourceType(ConfigResource.Type.TOPIC.id()).
          setConfigs(new AlterableConfigCollection(util.Arrays.asList(new AlterableConfig().
            setName(TopicConfig.FLUSH_MS_CONFIG).
            setValue("1000").
            setConfigOperation(AlterConfigOp.OpType.SET.id())).iterator())),
        ).iterator()))
    val request = buildRequest(new IncrementalAlterConfigsRequest.Builder(requestData).build(0))
    createControllerApis(Some(createDenyAllAuthorizer()),
      new MockController.Builder().build()).handleIncrementalAlterConfigs(request)
    val capturedResponse: ArgumentCaptor[AbstractResponse] =
      ArgumentCaptor.forClass(classOf[AbstractResponse])
    verify(requestChannel).sendResponse(
      ArgumentMatchers.eq(request),
      capturedResponse.capture(),
      ArgumentMatchers.eq(None))
    assertNotNull(capturedResponse.getValue)
    val response = capturedResponse.getValue.asInstanceOf[IncrementalAlterConfigsResponse]
    assertEquals(Set(new AlterConfigsResourceResponse().
        setErrorCode(CLUSTER_AUTHORIZATION_FAILED.code()).
        setErrorMessage(CLUSTER_AUTHORIZATION_FAILED.message()).
        setResourceName("1").
        setResourceType(ConfigResource.Type.BROKER.id()),
      new AlterConfigsResourceResponse().
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code()).
        setErrorMessage(TOPIC_AUTHORIZATION_FAILED.message()).
        setResourceName("foo").
        setResourceType(ConfigResource.Type.TOPIC.id())),
      response.data().responses().asScala.toSet)
  }

  @Test
  def testInvalidIncrementalAlterConfigsResources(): Unit = {
    val requestData = new IncrementalAlterConfigsRequestData().setResources(
      new AlterConfigsResourceCollection(util.Arrays.asList(
        new AlterConfigsResource().
          setResourceName("1").
          setResourceType(ConfigResource.Type.BROKER_LOGGER.id()).
          setConfigs(new AlterableConfigCollection(util.Arrays.asList(new AlterableConfig().
            setName("kafka.server.KafkaConfig").
            setValue("TRACE").
            setConfigOperation(AlterConfigOp.OpType.SET.id())).iterator())),
        new AlterConfigsResource().
          setResourceName("3").
          setResourceType(ConfigResource.Type.BROKER.id()).
          setConfigs(new AlterableConfigCollection(util.Arrays.asList(new AlterableConfig().
            setName(KafkaConfig.LogCleanerBackoffMsProp).
            setValue("100000").
            setConfigOperation(AlterConfigOp.OpType.SET.id())).iterator())),
        new AlterConfigsResource().
          setResourceName("3").
          setResourceType(ConfigResource.Type.BROKER.id()).
          setConfigs(new AlterableConfigCollection(util.Arrays.asList(new AlterableConfig().
            setName(KafkaConfig.LogCleanerBackoffMsProp).
            setValue("100000").
            setConfigOperation(AlterConfigOp.OpType.SET.id())).iterator())),
        new AlterConfigsResource().
          setResourceName("foo").
          setResourceType(124.toByte).
          setConfigs(new AlterableConfigCollection(util.Arrays.asList(new AlterableConfig().
            setName("foo").
            setValue("bar").
            setConfigOperation(AlterConfigOp.OpType.SET.id())).iterator())),
        ).iterator()))
    val request = buildRequest(new IncrementalAlterConfigsRequest.Builder(requestData).build(0))
    createControllerApis(Some(createDenyAllAuthorizer()),
      new MockController.Builder().build()).handleIncrementalAlterConfigs(request)
    val capturedResponse: ArgumentCaptor[AbstractResponse] =
      ArgumentCaptor.forClass(classOf[AbstractResponse])
    verify(requestChannel).sendResponse(
      ArgumentMatchers.eq(request),
      capturedResponse.capture(),
      ArgumentMatchers.eq(None))
    assertNotNull(capturedResponse.getValue)
    val response = capturedResponse.getValue.asInstanceOf[IncrementalAlterConfigsResponse]
    assertEquals(Set(
      new AlterConfigsResourceResponse().
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Unexpected resource type BROKER_LOGGER.").
        setResourceName("1").
        setResourceType(ConfigResource.Type.BROKER_LOGGER.id()),
      new AlterConfigsResourceResponse().
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate resource.").
        setResourceName("3").
        setResourceType(ConfigResource.Type.BROKER.id()),
      new AlterConfigsResourceResponse().
        setErrorCode(UNSUPPORTED_VERSION.code()).
        setErrorMessage("Unknown resource type 124.").
        setResourceName("foo").
        setResourceType(124.toByte)),
      response.data().responses().asScala.toSet)
  }

  @Test
  def testUnauthorizedHandleAlterPartitionReassignments(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
        handleAlterPartitionReassignments(buildRequest(new AlterPartitionReassignmentsRequest.Builder(
            new AlterPartitionReassignmentsRequestData()).build())))
  }

  @Test
  def testUnauthorizedHandleAllocateProducerIds(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
      handleAllocateProducerIdsRequest(buildRequest(new AllocateProducerIdsRequest.Builder(
        new AllocateProducerIdsRequestData()).build())))
  }

  @Test
  def testUnauthorizedHandleListPartitionReassignments(): Unit = {
    assertThrows(classOf[ClusterAuthorizationException], () => createControllerApis(
      Some(createDenyAllAuthorizer()), new MockController.Builder().build()).
      handleListPartitionReassignments(buildRequest(new ListPartitionReassignmentsRequest.Builder(
        new ListPartitionReassignmentsRequestData()).build())))
  }

  @Test
  def testCreateTopics(): Unit = {
    val controller = new MockController.Builder().build()
    val controllerApis = createControllerApis(None, controller)
    val request = new CreateTopicsRequestData().setTopics(new CreatableTopicCollection(
      util.Arrays.asList(new CreatableTopic().setName("foo").setNumPartitions(1).setReplicationFactor(3),
        new CreatableTopic().setName("foo").setNumPartitions(2).setReplicationFactor(3),
        new CreatableTopic().setName("bar").setNumPartitions(2).setReplicationFactor(3),
        new CreatableTopic().setName("bar").setNumPartitions(2).setReplicationFactor(3),
        new CreatableTopic().setName("bar").setNumPartitions(2).setReplicationFactor(3),
        new CreatableTopic().setName("baz").setNumPartitions(2).setReplicationFactor(3),
        new CreatableTopic().setName("indescribable").setNumPartitions(2).setReplicationFactor(3),
        new CreatableTopic().setName("quux").setNumPartitions(2).setReplicationFactor(3),
    ).iterator()))
    val expectedResponse = Set(new CreatableTopicResult().setName("foo").
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic name."),
      new CreatableTopicResult().setName("bar").
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic name."),
      new CreatableTopicResult().setName("baz").
        setErrorCode(NONE.code()).
        setTopicId(new Uuid(0L, 1L)).
        setNumPartitions(2).
        setReplicationFactor(3).
        setTopicConfigErrorCode(NONE.code()),
      new CreatableTopicResult().setName("indescribable").
        setErrorCode(NONE.code()).
        setTopicId(new Uuid(0L, 2L)).
        setTopicConfigErrorCode(TOPIC_AUTHORIZATION_FAILED.code()),
      new CreatableTopicResult().setName("quux").
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code()))
    assertEquals(expectedResponse, controllerApis.createTopics(ANONYMOUS_CONTEXT, request,
      false,
      _ => Set("baz", "indescribable"),
      _ => Set("baz")).get().topics().asScala.toSet)
  }

  @Test
  def testDeleteTopicsByName(): Unit = {
    val fooId = Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q")
    val controller = new MockController.Builder().newInitialTopic("foo", fooId).build()
    val controllerApis = createControllerApis(None, controller)
    val request = new DeleteTopicsRequestData().setTopicNames(
      util.Arrays.asList("foo", "bar", "quux", "quux"))
    val expectedResponse = Set(new DeletableTopicResult().setName("quux").
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic name."),
      new DeletableTopicResult().setName("bar").
        setErrorCode(UNKNOWN_TOPIC_OR_PARTITION.code()).
        setErrorMessage("This server does not host this topic-partition."),
      new DeletableTopicResult().setName("foo").setTopicId(fooId))
    assertEquals(expectedResponse, controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
      ApiKeys.DELETE_TOPICS.latestVersion().toInt,
      true,
      _ => Set.empty,
      _ => Set.empty).get().asScala.toSet)
  }

  @Test
  def testDeleteTopicsById(): Unit = {
    val fooId = Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q")
    val barId = Uuid.fromString("VlFu5c51ToiNx64wtwkhQw")
    val quuxId = Uuid.fromString("ObXkLhL_S5W62FAE67U3MQ")
    val controller = new MockController.Builder().newInitialTopic("foo", fooId).build()
    val controllerApis = createControllerApis(None, controller)
    val request = new DeleteTopicsRequestData()
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(fooId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(barId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(quuxId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(quuxId))
    val response = Set(new DeletableTopicResult().setName(null).setTopicId(quuxId).
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic id."),
      new DeletableTopicResult().setName(null).setTopicId(barId).
        setErrorCode(UNKNOWN_TOPIC_ID.code()).
        setErrorMessage("This server does not host this topic ID."),
      new DeletableTopicResult().setName("foo").setTopicId(fooId))
    assertEquals(response, controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
      ApiKeys.DELETE_TOPICS.latestVersion().toInt,
      true,
      _ => Set.empty,
      _ => Set.empty).get().asScala.toSet)
  }

  @Test
  def testInvalidDeleteTopicsRequest(): Unit = {
    val fooId = Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q")
    val barId = Uuid.fromString("VlFu5c51ToiNx64wtwkhQw")
    val bazId = Uuid.fromString("YOS4oQ3UT9eSAZahN1ysSA")
    val controller = new MockController.Builder().
      newInitialTopic("foo", fooId).
      newInitialTopic("bar", barId).build()
    val controllerApis = createControllerApis(None, controller)
    val request = new DeleteTopicsRequestData()
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName("foo").setTopicId(fooId))
    request.topics().add(new DeleteTopicState().setName("bar").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(barId))
    request.topics().add(new DeleteTopicState().setName("quux").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName("quux").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName("quux").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(bazId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(bazId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(bazId))
    val response = Set(new DeletableTopicResult().setName(null).setTopicId(ZERO_UUID).
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Neither topic name nor id were specified."),
      new DeletableTopicResult().setName("foo").setTopicId(fooId).
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("You may not specify both topic name and topic id."),
      new DeletableTopicResult().setName("bar").setTopicId(barId).
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("The provided topic name maps to an ID that was already supplied."),
      new DeletableTopicResult().setName("quux").setTopicId(ZERO_UUID).
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic name."),
      new DeletableTopicResult().setName(null).setTopicId(bazId).
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic id."))
    assertEquals(response, controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
      ApiKeys.DELETE_TOPICS.latestVersion().toInt,
      false,
      names => names.toSet,
      names => names.toSet).get().asScala.toSet)
  }

  @Test
  def testNotAuthorizedToDeleteWithTopicExisting(): Unit = {
    val fooId = Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q")
    val barId = Uuid.fromString("VlFu5c51ToiNx64wtwkhQw")
    val bazId = Uuid.fromString("hr4TVh3YQiu3p16Awkka6w")
    val quuxId = Uuid.fromString("5URoQzW_RJiERVZXJgUVLg")
    val controller = new MockController.Builder().
      newInitialTopic("foo", fooId).
      newInitialTopic("bar", barId).
      newInitialTopic("baz", bazId).
      newInitialTopic("quux", quuxId).build()
    val controllerApis = createControllerApis(None, controller)
    val request = new DeleteTopicsRequestData()
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(fooId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(barId))
    request.topics().add(new DeleteTopicState().setName("baz").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName("quux").setTopicId(ZERO_UUID))
    val response = Set(new DeletableTopicResult().setName(null).setTopicId(barId).
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code).
        setErrorMessage(TOPIC_AUTHORIZATION_FAILED.message),
      new DeletableTopicResult().setName("quux").setTopicId(ZERO_UUID).
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code).
        setErrorMessage(TOPIC_AUTHORIZATION_FAILED.message),
      new DeletableTopicResult().setName("baz").setTopicId(ZERO_UUID).
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code).
        setErrorMessage(TOPIC_AUTHORIZATION_FAILED.message),
      new DeletableTopicResult().setName("foo").setTopicId(fooId).
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code).
        setErrorMessage(TOPIC_AUTHORIZATION_FAILED.message))
    assertEquals(response, controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
      ApiKeys.DELETE_TOPICS.latestVersion().toInt,
      false,
      _ => Set("foo", "baz"),
      _ => Set.empty).get().asScala.toSet)
  }

  @Test
  def testNotAuthorizedToDeleteWithTopicNotExisting(): Unit = {
    val barId = Uuid.fromString("VlFu5c51ToiNx64wtwkhQw")
    val controller = new MockController.Builder().build()
    val controllerApis = createControllerApis(None, controller)
    val request = new DeleteTopicsRequestData()
    request.topics().add(new DeleteTopicState().setName("foo").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName("bar").setTopicId(ZERO_UUID))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(barId))
    val expectedResponse = Set(new DeletableTopicResult().setName("foo").
        setErrorCode(UNKNOWN_TOPIC_OR_PARTITION.code).
        setErrorMessage(UNKNOWN_TOPIC_OR_PARTITION.message),
      new DeletableTopicResult().setName("bar").
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code).
        setErrorMessage(TOPIC_AUTHORIZATION_FAILED.message),
      new DeletableTopicResult().setName(null).setTopicId(barId).
        setErrorCode(UNKNOWN_TOPIC_ID.code).
        setErrorMessage(UNKNOWN_TOPIC_ID.message))
    assertEquals(expectedResponse, controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
      ApiKeys.DELETE_TOPICS.latestVersion().toInt,
      false,
      _ => Set("foo"),
      _ => Set.empty).get().asScala.toSet)
  }

  @Test
  def testNotControllerErrorPreventsDeletingTopics(): Unit = {
    val fooId = Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q")
    val barId = Uuid.fromString("VlFu5c51ToiNx64wtwkhQw")
    val controller = new MockController.Builder().
      newInitialTopic("foo", fooId).build()
    controller.setActive(false)
    val controllerApis = createControllerApis(None, controller)
    val request = new DeleteTopicsRequestData()
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(fooId))
    request.topics().add(new DeleteTopicState().setName(null).setTopicId(barId))
    assertEquals(classOf[NotControllerException], assertThrows(
      classOf[ExecutionException], () => controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
        ApiKeys.DELETE_TOPICS.latestVersion().toInt,
        false,
        _ => Set("foo", "bar"),
        _ => Set("foo", "bar")).get()).getCause.getClass)
  }

  @Test
  def testDeleteTopicsDisabled(): Unit = {
    val fooId = Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q")
    val controller = new MockController.Builder().
      newInitialTopic("foo", fooId).build()
    val props = new Properties()
    props.put(KafkaConfig.DeleteTopicEnableProp, "false")
    val controllerApis = createControllerApis(None, controller, props)
    val request = new DeleteTopicsRequestData()
    request.topics().add(new DeleteTopicState().setName("foo").setTopicId(ZERO_UUID))
    assertThrows(classOf[TopicDeletionDisabledException],
      () => controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
        ApiKeys.DELETE_TOPICS.latestVersion().toInt,
        false,
        _ => Set("foo", "bar"),
        _ => Set("foo", "bar")))
    assertThrows(classOf[InvalidRequestException],
      () => controllerApis.deleteTopics(ANONYMOUS_CONTEXT, request,
        1,
        false,
        _ => Set("foo", "bar"),
        _ => Set("foo", "bar")))
  }

  @ParameterizedTest
  @ValueSource(booleans = Array(true, false))
  def testCreatePartitionsRequest(validateOnly: Boolean): Unit = {
    val controller = mock(classOf[Controller])
    val controllerApis = createControllerApis(None, controller)
    val request = new CreatePartitionsRequestData()
    request.topics().add(new CreatePartitionsTopic().setName("foo").setAssignments(null).setCount(5))
    request.topics().add(new CreatePartitionsTopic().setName("bar").setAssignments(null).setCount(5))
    request.topics().add(new CreatePartitionsTopic().setName("bar").setAssignments(null).setCount(5))
    request.topics().add(new CreatePartitionsTopic().setName("bar").setAssignments(null).setCount(5))
    request.topics().add(new CreatePartitionsTopic().setName("baz").setAssignments(null).setCount(5))
    request.setValidateOnly(validateOnly)

    // Check if the controller is called correctly with the 'validateOnly' field set appropriately.
    when(controller.createPartitions(
      any(),
      ArgumentMatchers.eq(
        Collections.singletonList(
          new CreatePartitionsTopic().setName("foo").setAssignments(null).setCount(5))),
      ArgumentMatchers.eq(validateOnly))).thenReturn(CompletableFuture
      .completedFuture(Collections.singletonList(
        new CreatePartitionsTopicResult().setName("foo").
          setErrorCode(NONE.code()).
          setErrorMessage(null)
      )))
    assertEquals(Set(new CreatePartitionsTopicResult().setName("foo").
      setErrorCode(NONE.code()).
      setErrorMessage(null),
      new CreatePartitionsTopicResult().setName("bar").
        setErrorCode(INVALID_REQUEST.code()).
        setErrorMessage("Duplicate topic name."),
      new CreatePartitionsTopicResult().setName("baz").
        setErrorCode(TOPIC_AUTHORIZATION_FAILED.code()).
        setErrorMessage(null)),
      controllerApis.createPartitions(ANONYMOUS_CONTEXT, request,
        _ => Set("foo", "bar")).get().asScala.toSet)
  }

  @Test
  def testCreatePartitionsAuthorization(): Unit = {
    val controller = new MockController.Builder()
      .newInitialTopic("foo", Uuid.fromString("vZKYST0pSA2HO5x_6hoO2Q"))
      .build()
    val authorizer = mock(classOf[Authorizer])
    val controllerApis = createControllerApis(Some(authorizer), controller)

    val requestData = new CreatePartitionsRequestData()
    requestData.topics().add(new CreatePartitionsTopic().setName("foo").setAssignments(null).setCount(2))
    requestData.topics().add(new CreatePartitionsTopic().setName("bar").setAssignments(null).setCount(10))
    val request = new CreatePartitionsRequest.Builder(requestData).build()

    val fooResource = new ResourcePattern(ResourceType.TOPIC, "foo", PatternType.LITERAL)
    val fooAction = new Action(AclOperation.ALTER, fooResource, 1, true, true)

    val barResource = new ResourcePattern(ResourceType.TOPIC, "bar", PatternType.LITERAL)
    val barAction = new Action(AclOperation.ALTER, barResource, 1, true, true)

    when(authorizer.authorize(
      any[RequestContext],
      any[util.List[Action]]
    )).thenAnswer { invocation =>
      val actions = invocation.getArgument[util.List[Action]](1).asScala
      val results = actions.map { action =>
        if (action == fooAction) AuthorizationResult.ALLOWED
        else if (action == barAction) AuthorizationResult.DENIED
        else throw new AssertionError(s"Unexpected action $action")
      }
      new util.ArrayList[AuthorizationResult](results.asJava)
    }

    val response = handleRequest[CreatePartitionsResponse](request, controllerApis)
    val results = response.data.results.asScala
    assertEquals(Some(Errors.NONE), results.find(_.name == "foo").map(result => Errors.forCode(result.errorCode)))
    assertEquals(Some(Errors.TOPIC_AUTHORIZATION_FAILED), results.find(_.name == "bar").map(result => Errors.forCode(result.errorCode)))
  }

  @Test
  def testElectLeadersAuthorization(): Unit = {
    val authorizer = mock(classOf[Authorizer])
    val controller = mock(classOf[Controller])
    val controllerApis = createControllerApis(Some(authorizer), controller)

    val request = new ElectLeadersRequest.Builder(
      ElectionType.PREFERRED,
      null,
      30000
    ).build()

    val resource = new ResourcePattern(ResourceType.CLUSTER, Resource.CLUSTER_NAME, PatternType.LITERAL)
    val actions = singletonList(new Action(AclOperation.ALTER, resource, 1, true, true))

    when(authorizer.authorize(
      any[RequestContext],
      ArgumentMatchers.eq(actions)
    )).thenReturn(singletonList(AuthorizationResult.DENIED))

    val response = handleRequest[ElectLeadersResponse](request, controllerApis)
    assertEquals(Errors.CLUSTER_AUTHORIZATION_FAILED, Errors.forCode(response.data.errorCode))
  }

  @Test
  def testElectLeadersHandledByController(): Unit = {
    val controller = mock(classOf[Controller])
    val controllerApis = createControllerApis(None, controller)

    val request = new ElectLeadersRequest.Builder(
      ElectionType.PREFERRED,
      null,
      30000
    ).build()

    val responseData = new ElectLeadersResponseData()
        .setErrorCode(Errors.NOT_CONTROLLER.code)

    when(controller.electLeaders(any[ControllerRequestContext],
      ArgumentMatchers.eq(request.data)
    )).thenReturn(CompletableFuture.completedFuture(responseData))

    val response = handleRequest[ElectLeadersResponse](request, controllerApis)
    assertEquals(Errors.NOT_CONTROLLER, Errors.forCode(response.data.errorCode))
  }

  private def handleRequest[T <: AbstractResponse](
    request: AbstractRequest,
    controllerApis: ControllerApis
  )(
    implicit classTag: ClassTag[T],
    @nowarn("cat=unused") nn: NotNothing[T]
  ): T = {
    val req = buildRequest(request)

    controllerApis.handle(req, RequestLocal.NoCaching)

    val capturedResponse: ArgumentCaptor[AbstractResponse] =
      ArgumentCaptor.forClass(classOf[AbstractResponse])
    verify(requestChannel).sendResponse(
      ArgumentMatchers.eq(req),
      capturedResponse.capture(),
      ArgumentMatchers.eq(None)
    )

    capturedResponse.getValue match {
      case response: T => response
      case response =>
        throw new ClassCastException(s"Expected response with type ${classTag.runtimeClass}, " +
          s"but found ${response.getClass}")
    }
  }

  @AfterEach
  def tearDown(): Unit = {
    quotas.shutdown()
  }
}


text:
@interface TGBroadcastListsController : TGViewController

- (void)createBroadcast:(NSString *)listName userIds:(NSArray *)userIds;

@end

redpajama_set_name: RedPajamaGithub


text:
package core.stringbuilder;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class InsertTest {

	@Test
	public void insertStringAtIndex() {
		StringBuilder builder = new StringBuilder("StringBuilder");
		builder.insert(13, "Mutable");
		builder.insert(13, "Is");

		assertEquals(builder.toString(), "StringBuilderIsMutable");
	}
}


text:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <title>Module: ZipUtils::DryRun</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <link rel="stylesheet" href="../.././rdoc-style.css" type="text/css" media="screen" />

  Module
ZipUtils::DryRun

        In:

lib/more/facets/ziputils.rb

Dry-run verions of ZipUtils.

This is the same as passing the :dryrun flag to ZipUtils. Which is also equivalent to passing
:noop and :verbose together.

Methods

compress

tar_bzip2

tar_gzip

untar_bzip2

untar_gzip

unzip

zip

Public Instance methods

    compress(format_extension, folder, file=nil, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 263
  def compress(format_extension, folder, file=nil, options={})
    options[:dryrun] = true
    ZipUtils.tar_gzip(format_extension, folder, file, options)
  end

    tar_bzip2(folder, file=nil, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 278
  def tar_bzip2(folder, file=nil, options={})
    options[:dryrun] = true
    ZipUtils.untar_bzip2(folder, file, options)
  end

    tar_gzip(folder, file=nil, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 268
  def tar_gzip(folder, file=nil, options={})
    options[:dryrun] = true
    ZipUtils.tar_gzip(folder, file, options)
  end

    untar_bzip2(file, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 283
  def untar_bzip2(file, options={})
    options[:dryrun] = true
    ZipUtils.untar_bzip2(file, options)
  end

    untar_gzip(file, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 273
  def untar_gzip(file, options={})
    options[:dryrun] = true
    ZipUtils.untar_gzip(file, options)
  end

    unzip(file, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 293
  def unzip(file, options={})
    options[:dryrun] = true
    ZipUtils.unzip(file, options)
  end

    zip(folder, file=nil, options={})

[ show source ]

# File lib/more/facets/ziputils.rb, line 288
  def zip(folder, file=nil, options={})
    options[:dryrun] = true
    ZipUtils.unzip(folder, file, options)
  end


text:
This page lists the changes that were done in each version of Wikimate.

Since v0.10.0 this project adheres to [Semantic Versioning](http://semver.org/)
and [Keep a Changelog](http://keepachangelog.com/).

## Upcoming version

### Changed

- Applied code formatting rules from [PSR-12](https://www.php-fig.org/psr/psr-12/) ([#142])
- Resolved static analysis warnings reported by [PHPStan](https://phpstan.org/) &
  [PHPMD](https://phpmd.org/) ([#143])
- Clarified error response for edits denied by a CAPTCHA ([#145])
- Activated default markdownlint rules ([#146])

## Version 1.0.0 - 2021-09-05

### Added

- New exception class `WikimateException` for API communication errors ([#136])
- Usage documentation about maximum lag and retries ([#134])
- New GitHub Action to enforce updates to `CHANGELOG.md` ([#131])
- New `CONTRIBUTING.md` file with contribution guidelines ([#135])
- New GitHub Action to check markdown files ([#138])

### Changed

- Centralized API communication checks in `WikiPage::request()` ([#136])
- Centralized debug logging of API requests/responses in `WikiPage::request()` ([#139])
- Rewrote installation and invocation instructions using Composer ([#140])
- Added additional context to `README.md` ([#127])
- Added semi-linear merge recommendation to `GOVERNANCE.md` ([#130])

_The following two entries are backwards incompatible API changes
and may require changes in applications that invoke these methods:_

- Error return values for `WikiPage::getSection()` changed from `false` to `null` ([#129])
- `Wikimate::login()` error code `'login'` is now `'auth'`, also used by `logout()` ([#132])

### Fixed

- Fixed one error return value in `WikiPage::setText()` ([#129])
- Fixed exception type/message for `$keyNames` parameter to `WikiPage::getAllSections()` ([#133])

### Removed

- Method `Wikimate::debugCurlConfig()`, deprecated since v0.10.0 ([#128])
- File `globals.php`, replaced by expanded Composer instructions ([#140])

## Version 0.15.0 - 2021-08-26

### Added

- New methods `WikiFile::revert()` and `Wikimate::filerevert()` ([#123])
- New method `Wikimate::logout()` ([#124])
- Added post-release update steps to `GOVERNANCE.md` ([#125])

### Changed

- Updated `Wikimate::token()` to remember CSRF token and reduce API calls ([#122])

### Fixed

- Fixed format of user agent string ([#121])

## Version 0.14.0 - 2021-08-24

### Added

- Support for the maxlag parameter (with retries) in API requests ([#112])
- Support for getting/setting user agent for API requests ([#107])
- Added missing PHPDoc comments for properties, constants, and more ([#109])

### Changed

- Changed API requests from deprecated PHP format to JSON format ([#111])
- Grouped sections and added table of contents in `USAGE.md` ([#108])

### Fixed

- Removed null returns from destructors & fixed PHPDoc comments ([#114])
- Fixed sections object initialization warning in PHP 7.4+ ([#118])

## Version 0.13.0 - 2021-07-05

### Added

- Added more debug logging of MediaWiki requests and responses ([#101], [#106])
- New `GOVERNANCE.md` file to explicitly codify the project management principles
  and provide guidelines for maintenance tasks ([#83], [#105])

### Changed

- Modernized token handling for login and data-modifying actions.
  Requires MediaWiki v1.27 or newer. ([#100], [#106])

### Fixed

- Prevented PHP notice in `WikiFile::getInfo()` for moved or deleted file ([#85])
- Fixed capitalization of a built-in PHP class in a comment ([#106])

## Version 0.12.0 - 2017-02-03

### Added

- New class WikiFile to retrieve properties of a file, and download and upload its contents.
  All properties pertain to the current revision of the file, or a specific older revision.
  ([#69], [#71], [#78], [#80])
- WikiFile also provides the file history
  and the ability to delete a file or an older revision of it ([#76])

## Version 0.11.0 - 2016-11-16

### Added

- Support for a section name (in addition to an index)
  in `WikiPage::setText()` and `WikiPage::setSection()` ([#45])
- Support for optional domain at authentication ([#28])

### Changed

- Updated `WikiPage::getSection()` to include subsections by default;
  disabling the new `$includeSubsections` option reverts to the old behavior
  of returning only the text until the first subsection ([#55])
- Improved section processing in `WikiPage::getText()` ([#33], [#37], [#50])
- Ensured that MediaWiki API error responses appear directly in `WikiPage::$error`
  rather than a nested 'error' array.
  This may require changes in your application's error handling ([#63])
- Restructured and improved documentation ([#32], [#34], [#47], [#49], [#61])

### Fixed

- Ensured use of Wikimate user agent by _Requests_ library ([#64])
- Corrected handling an invalid page title ([#57])
- Fixed returning an empty section without header in `WikiPage::getSection()` ([#52])
- Prevented PHP Notices in several methods ([#43], [#67])
- Corrected handling an unknown section parameter in `WikiPage::getSection()` ([#41])
- Fixed passing the return value in `WikiPage::setSection()` ([#30])
- Corrected call to `Wikimate::debugRequestsConfig()` ([#30])

## Version 0.10.0 - 2014-06-24

### Changed

- Switched to using the _Requests_ library instead of Curl ([#25])

## Version 0.9 - 2014-06-13

- Bumped version for stable release

## Version 0.5 - 2011-09-09

- Removed the use of constants in favour of constructor arguments
- Added checks that throw an exception if can't write to wikimate_cookie.txt
- Throws exception if curl library not loaded
- Throws exception if can't login

## Version 0.4 - 2011-01-15

- Added `WikiPage::newSection()` and `WikiPage::setSection()` (shortcuts to `WikiPage::setText()`)
- Added the ability to get individual sections of the article with `WikiPage::getSection()`
- Added the ability to get all sections in an array with `WikiPage::getAllSections()`
- Added the ability to get an array showing section offsets and lengths in the page wikicode
  with `WikiPage::getSectionOffsets()`
- Added the ability to see how many sections are on a page with `WikiPage::getNumSections()`

## Version 0.3 - 2010-12-26

- Initial commit

[#25]: https://github.com/hamstar/Wikimate/pull/25
[#28]: https://github.com/hamstar/Wikimate/pull/28
[#30]: https://github.com/hamstar/Wikimate/pull/30
[#32]: https://github.com/hamstar/Wikimate/pull/32
[#33]: https://github.com/hamstar/Wikimate/pull/33
[#34]: https://github.com/hamstar/Wikimate/pull/34
[#37]: https://github.com/hamstar/Wikimate/pull/37
[#41]: https://github.com/hamstar/Wikimate/pull/41
[#43]: https://github.com/hamstar/Wikimate/pull/43
[#45]: https://github.com/hamstar/Wikimate/pull/45
[#47]: https://github.com/hamstar/Wikimate/pull/47
[#49]: https://github.com/hamstar/Wikimate/pull/49
[#50]: https://github.com/hamstar/Wikimate/pull/50
[#52]: https://github.com/hamstar/Wikimate/pull/52
[#55]: https://github.com/hamstar/Wikimate/pull/55
[#57]: https://github.com/hamstar/Wikimate/pull/57
[#61]: https://github.com/hamstar/Wikimate/pull/61
[#63]: https://github.com/hamstar/Wikimate/pull/63
[#64]: https://github.com/hamstar/Wikimate/pull/64
[#67]: https://github.com/hamstar/Wikimate/pull/67
[#69]: https://github.com/hamstar/Wikimate/pull/69
[#71]: https://github.com/hamstar/Wikimate/pull/71
[#76]: https://github.com/hamstar/Wikimate/pull/76
[#78]: https://github.com/hamstar/Wikimate/pull/78
[#80]: https://github.com/hamstar/Wikimate/pull/80
[#83]: https://github.com/hamstar/Wikimate/pull/83
[#85]: https://github.com/hamstar/Wikimate/pull/85
[#100]: https://github.com/hamstar/Wikimate/pull/100
[#101]: https://github.com/hamstar/Wikimate/pull/101
[#105]: https://github.com/hamstar/Wikimate/pull/105
[#106]: https://github.com/hamstar/Wikimate/pull/106
[#107]: https://github.com/hamstar/Wikimate/pull/107
[#108]: https://github.com/hamstar/Wikimate/pull/108
[#109]: https://github.com/hamstar/Wikimate/pull/109
[#111]: https://github.com/hamstar/Wikimate/pull/111
[#112]: https://github.com/hamstar/Wikimate/pull/112
[#114]: https://github.com/hamstar/Wikimate/pull/114
[#118]: https://github.com/hamstar/Wikimate/pull/118
[#121]: https://github.com/hamstar/Wikimate/pull/121
[#122]: https://github.com/hamstar/Wikimate/pull/122
[#123]: https://github.com/hamstar/Wikimate/pull/123
[#124]: https://github.com/hamstar/Wikimate/pull/124
[#125]: https://github.com/hamstar/Wikimate/pull/125
[#127]: https://github.com/hamstar/Wikimate/pull/127
[#128]: https://github.com/hamstar/Wikimate/pull/128
[#129]: https://github.com/hamstar/Wikimate/pull/129
[#130]: https://github.com/hamstar/Wikimate/pull/130
[#131]: https://github.com/hamstar/Wikimate/pull/131
[#132]: https://github.com/hamstar/Wikimate/pull/132
[#133]: https://github.com/hamstar/Wikimate/pull/133
[#134]: https://github.com/hamstar/Wikimate/pull/134
[#135]: https://github.com/hamstar/Wikimate/pull/135
[#136]: https://github.com/hamstar/Wikimate/pull/136
[#138]: https://github.com/hamstar/Wikimate/pull/138
[#139]: https://github.com/hamstar/Wikimate/pull/139
[#140]: https://github.com/hamstar/Wikimate/pull/140
[#142]: https://github.com/hamstar/Wikimate/pull/142
[#143]: https://github.com/hamstar/Wikimate/pull/143
[#145]: https://github.com/hamstar/Wikimate/pull/145
[#146]: https://github.com/hamstar/Wikimate/pull/146


text:
#ifndef InspectorTimelineAgent_h
#define InspectorTimelineAgent_h

#if ENABLE(INSPECTOR)

#include "InspectorBaseAgent.h"
#include "InspectorFrontend.h"
#include "InspectorValues.h"
#include "LayoutRect.h"
#include "PlatformInstrumentation.h"
#include "ScriptGCEvent.h"
#include "ScriptGCEventListener.h"
#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
#include <wtf/WeakPtr.h>

namespace WebCore {
class Event;
class FloatQuad;
class Frame;
class InspectorClient;
class InspectorFrontend;
class InspectorMemoryAgent;
class InspectorPageAgent;
class InspectorState;
class InstrumentingAgents;
class IntRect;
class KURL;
class Page;
class RenderObject;
class ResourceRequest;
class ResourceResponse;
class TimelineTraceEventProcessor;

typedef String ErrorString;

namespace TimelineRecordType {
extern const char DecodeImage[];
extern const char Rasterize[];
};

class TimelineTimeConverter {
public:
    TimelineTimeConverter()
        : m_startOffset(0)
    {
    }
    double fromMonotonicallyIncreasingTime(double time) const  { return (time - m_startOffset) * 1000.0; }
    void reset();

private:
    double m_startOffset;
};

class InspectorTimelineAgent
    : public InspectorBaseAgent<InspectorTimelineAgent>,
      public ScriptGCEventListener,
      public InspectorBackendDispatcher::TimelineCommandHandler,
      public PlatformInstrumentationClient {
    WTF_MAKE_NONCOPYABLE(InspectorTimelineAgent);
public:
    enum InspectorType { PageInspector, WorkerInspector };

    static PassOwnPtr<InspectorTimelineAgent> create(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorMemoryAgent* memoryAgent, InspectorCompositeState* state, InspectorType type, InspectorClient* client)
    {
        return adoptPtr(new InspectorTimelineAgent(instrumentingAgents, pageAgent, memoryAgent, state, type, client));
    }

    ~InspectorTimelineAgent();

    virtual void setFrontend(InspectorFrontend*);
    virtual void clearFrontend();
    virtual void restore();

    virtual void start(ErrorString*, const int* maxCallStackDepth, const bool* includeDomCounters, const bool* includeNativeMemoryStatistics);
    virtual void stop(ErrorString*);
    virtual void canMonitorMainThread(ErrorString*, bool*);
    virtual void supportsFrameInstrumentation(ErrorString*, bool*);

    int id() const { return m_id; }

    void didCommitLoad();

    // Methods called from WebCore.
    void willCallFunction(const String& scriptName, int scriptLine, Frame*);
    void didCallFunction();

    void willDispatchEvent(const Event&, Frame*);
    void didDispatchEvent();

    void didBeginFrame();
    void didCancelFrame();

    void didInvalidateLayout(Frame*);
    void willLayout(Frame*);
    void didLayout(RenderObject*);

    void didScheduleStyleRecalculation(Frame*);
    void willRecalculateStyle(Frame*);
    void didRecalculateStyle();

    void willPaint(Frame*);
    void didPaint(RenderObject*, const LayoutRect&);

    void willScroll(Frame*);
    void didScroll();

    void willComposite();
    void didComposite();

    void willWriteHTML(unsigned startLine, Frame*);
    void didWriteHTML(unsigned endLine);

    void didInstallTimer(int timerId, int timeout, bool singleShot, Frame*);
    void didRemoveTimer(int timerId, Frame*);
    void willFireTimer(int timerId, Frame*);
    void didFireTimer();

    void willDispatchXHRReadyStateChangeEvent(const String&, int, Frame*);
    void didDispatchXHRReadyStateChangeEvent();
    void willDispatchXHRLoadEvent(const String&, Frame*);
    void didDispatchXHRLoadEvent();

    void willEvaluateScript(const String&, int, Frame*);
    void didEvaluateScript();

    void didTimeStamp(Frame*, const String&);
    void didMarkDOMContentEvent(Frame*);
    void didMarkLoadEvent(Frame*);

    void time(Frame*, const String&);
    void timeEnd(Frame*, const String&);

    void didScheduleResourceRequest(const String& url, Frame*);
    void willSendResourceRequest(unsigned long, const ResourceRequest&, Frame*);
    void willReceiveResourceResponse(unsigned long, const ResourceResponse&, Frame*);
    void didReceiveResourceResponse();
    void didFinishLoadingResource(unsigned long, bool didFail, double finishTime, Frame*);
    void willReceiveResourceData(unsigned long identifier, Frame*, int length);
    void didReceiveResourceData();

    void didRequestAnimationFrame(int callbackId, Frame*);
    void didCancelAnimationFrame(int callbackId, Frame*);
    void willFireAnimationFrame(int callbackId, Frame*);
    void didFireAnimationFrame();

    void willProcessTask();
    void didProcessTask();

#if ENABLE(WEB_SOCKETS)
    void didCreateWebSocket(unsigned long identifier, const KURL&, const String& protocol, Frame*);
    void willSendWebSocketHandshakeRequest(unsigned long identifier, Frame*);
    void didReceiveWebSocketHandshakeResponse(unsigned long identifier, Frame*);
    void didDestroyWebSocket(unsigned long identifier, Frame*);
#endif

    // ScriptGCEventListener methods.
    virtual void didGC(double, double, size_t);

    // PlatformInstrumentationClient methods.
    virtual void willDecodeImage(const String& imageType) OVERRIDE;
    virtual void didDecodeImage() OVERRIDE;
    virtual void willResizeImage(bool shouldCache) OVERRIDE;
    virtual void didResizeImage() OVERRIDE;

private:
    friend class TimelineRecordStack;
    friend class TimelineTraceEventProcessor;

    struct TimelineRecordEntry {
        TimelineRecordEntry(PassRefPtr<InspectorObject> record, PassRefPtr<InspectorObject> data, PassRefPtr<InspectorArray> children, const String& type, size_t usedHeapSizeAtStart)
            : record(record), data(data), children(children), type(type), usedHeapSizeAtStart(usedHeapSizeAtStart)
        {
        }
        RefPtr<InspectorObject> record;
        RefPtr<InspectorObject> data;
        RefPtr<InspectorArray> children;
        String type;
        size_t usedHeapSizeAtStart;
    };

    InspectorTimelineAgent(InstrumentingAgents*, InspectorPageAgent*, InspectorMemoryAgent*, InspectorCompositeState*, InspectorType, InspectorClient*);

    void sendEvent(PassRefPtr<InspectorObject>);
    void appendRecord(PassRefPtr<InspectorObject> data, const String& type, bool captureCallStack, Frame*);
    void pushCurrentRecord(PassRefPtr<InspectorObject>, const String& type, bool captureCallStack, Frame*, bool hasLowLevelDetails = false);

    void setDOMCounters(TypeBuilder::Timeline::TimelineEvent* record);
    void setNativeHeapStatistics(TypeBuilder::Timeline::TimelineEvent* record);
    void setFrameIdentifier(InspectorObject* record, Frame*);
    void pushGCEventRecords();

    void didCompleteCurrentRecord(const String& type);

    void setHeapSizeStatistics(InspectorObject* record);
    void commitFrameRecord();

    void addRecordToTimeline(PassRefPtr<InspectorObject>, const String& type);
    void innerAddRecordToTimeline(PassRefPtr<InspectorObject>, const String& type);
    void clearRecordStack();

    void localToPageQuad(const RenderObject& renderer, const LayoutRect&, FloatQuad*);
    const TimelineTimeConverter& timeConverter() const { return m_timeConverter; }
    double timestamp();
    Page* page();

    InspectorPageAgent* m_pageAgent;
    InspectorMemoryAgent* m_memoryAgent;
    TimelineTimeConverter m_timeConverter;

    InspectorFrontend::Timeline* m_frontend;
    double m_timestampOffset;

    Vector<TimelineRecordEntry> m_recordStack;

    int m_id;
    struct GCEvent {
        GCEvent(double startTime, double endTime, size_t collectedBytes)
            : startTime(startTime), endTime(endTime), collectedBytes(collectedBytes)
        {
        }
        double startTime;
        double endTime;
        size_t collectedBytes;
    };
    typedef Vector<GCEvent> GCEvents;
    GCEvents m_gcEvents;
    int m_maxCallStackDepth;
    unsigned m_platformInstrumentationClientInstalledAtStackDepth;
    RefPtr<InspectorObject> m_pendingFrameRecord;
    InspectorType m_inspectorType;
    InspectorClient* m_client;
    WeakPtrFactory<InspectorTimelineAgent> m_weakFactory;
    RefPtr<TimelineTraceEventProcessor> m_traceEventProcessor;
};

} // namespace WebCore

#endif // !ENABLE(INSPECTOR)
#endif // !defined(InspectorTimelineAgent_h)


text:
The `ws` project implements the [WebSocket standard](https://tools.ietf.org/html/rfc6455) in Java. It provides a WebSocket client API that enables developers to build Java applications that communicate over WebSocket with an RFC-6455 endpoint, such as KAAZING Gateway.

Developers can include the Java WebSocket Client using the following maven dependency to develop and run web applications:

```xml
<dependency>
    <groupId>org.kaazing</groupId>
    <artifactId>gateway.client.java</artifactId>
    <version>[5.1.0.0,5.2.0.0)</version>
</dependency>

```

# Building This Project

## Minimum Requirements for Building the Projects in This Repo

* Java SE Development Kit (JDK) 7 or higher
* maven 3.0.5 or higher

## Steps for Building this Project

0. Clone the repo: ```git clone https://github.com/kaazing/java.client.git```
0. Go to the cloned directory: ```cd java.client```
0. Build the project: ```mvn clean install```

# Running the WebSocket demo

## From the command-line
0. Change directory: ```cd ws/demo/target```
0. Run the demo application: ```java -cp . -jar gateway.client.java.demo-develop-SNAPSHOT.jar```

## From within Eclipse
0. Import the project in Eclipse
0. Under `gateway.client.java.demo` project, right-click on `WebSocketFrame.java` or `WebSocketApplet.java` in
src/main/java/org.kaazing.net.ws.demo and run!

# Using KAAZING Gateway or any RFC-6455 Endpoint

You can use an RFC-6455 endpoint, such as KAAZING Gateway, to connect to a back-end service. To learn how to administer the Gateway, its configuration files, and security, see the documentation on [developer.kaazing.com](http://developer.kaazing.com/documentation/5.0/index.html).

# Learning How to Develop Client Applications

To learn how to develop client applications with this project, see the documentation on [developer.kaazing.com](http://developer.kaazing.com/documentation/5.0/index.html).

# View a Running Demo

To view demos of clients built with this project, see [kaazing.org](http://kaazing.org/)


text:
<?php

if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

require __DIR__.'/../autoload.php';

$config = include __DIR__.'/../app/config.php';
$app = new Application($config);

$request = Request::createFromGlobals();

$response = $app->handle($request);

$app->terminate($response);