//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.
//

=== Client Library

The Java client library simplifies the interaction with the <<core>> by hiding the underlying HTTP
communication details and providing native methods and payload objects.

The library is available as a Maven artifact:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.client.idrepo</groupId>
  <artifactId>syncope-client-idrepo-lib</artifactId>
  <version>{docVersion}</version>
</dependency>
----

ifeval::["{snapshotOrRelease}" == "snapshot"]

[WARNING]
====
Do not forget to add the following repository to your `pom.xml`:

[source,xml]
----
<repository>
  <id>ASF</id>
  <url>https://repository.apache.org/content/repositories/snapshots/</url>
  <snapshots>
    <enabled>true</enabled>
  </snapshots>
</repository>
----
====

endif::[]

[discrete]
===== Initialization

First you need to build an instance of `SyncopeClientFactoryBean` by providing the deployment base URL, as follows:

[source,java]
----
SyncopeClientFactoryBean clientFactory = new SyncopeClientFactoryBean().
              setAddress("http://localhost:9080/syncope/rest/");
----

You might also select a specific <<domains,domain>> - other than `Master`, choose to exchange XML payloads - rather
than JSON (default), to select 
https://en.wikipedia.org/wiki/HTTP_compression[HTTP compression^] or to set the
https://cxf.apache.org/javadoc/latest/org/apache/cxf/configuration/jsse/TLSClientParameters.html[TLS client configuration^]
(more options in the
https://syncope.apache.org/apidocs/4.0/org/apache/syncope/client/lib/SyncopeClientFactoryBean.html[Javadoc^]):

[source,java]
----
TLSClientParameters tlsClientParameters = ...;
SyncopeClientFactoryBean clientFactory = new SyncopeClientFactoryBean().
              setAddress("http://localhost:9080/syncope/rest/").
              setDomain("Two").
              setContentType(SyncopeClientFactoryBean.ContentType.XML).
              setUseCompression(true).
              setTlsClientParameters(tlsClientParameters);
----

At this point an instance of `SyncopeClient` can be obtained by passing the login credentials via:

[source,java]
----
SyncopeClient client = clientFactory.create("admin", "password");
----

Or you can combine into a single statement as:

[source,java]
----
SyncopeClient client = new SyncopeClientFactoryBean().
              setAddress("http://localhost:9080/syncope/rest/").
              create("admin", "password");
----

[discrete]
===== Examples

Select one of the
https://syncope.apache.org/apidocs/4.0/org/apache/syncope/common/rest/api/service/package-summary.html[RESTful services^]
and invoke one of the available methods:

[source,java]
----
LoggerService loggerService = client.getService(LoggerService.class);

LoggerTO loggerTO = loggerService.read(LoggerType.LOG, "org.apache.syncope.core.connid");
loggerTO.setLevel(LoggerLevel.DEBUG);

loggerService.update(LoggerType.LOG, loggerTO);
----

[TIP]
Advanced REST features are also available from `SyncopeClient` instances: check
https://syncope.apache.org/apidocs/4.0/org/apache/syncope/client/lib/SyncopeClient.html[the javadoc^]
for more information.

.Search for Users, Groups or Any Objects
====
All search operations return
https://syncope.apache.org/apidocs/4.0/org/apache/syncope/common/lib/to/PagedResult.html[paged result handlers^]
which can be exploited both for getting the actual results and for extrapolating pagination coordinates.

[source,java]
----
UserService userService = client.getService(UserService.class);

int count = userService.search(new AnyQuery.Builder().page(0).size(0).build()).getTotalCount(); // <1>

PagedResult<UserTO> matchingUsers = userService.search(
    new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
    fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").
    equalTo("ros*ini").query()).build()); // <2>

PagedResult<UserTO> matchingUsers = userService.search(
    new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
    fiql(SyncopeClient.getUserSearchConditionBuilder().isNull("loginDate").query()).
    build()); // <3>

PagedResult<UserTO> matchingUsers = userService.search(
    new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
    fiql(SyncopeClient.getUserSearchConditionBuilder().inRoles("Other").query()).
    build()); // <4>

AnyObjectService anyObjectService = client.getService(AnyObjectService.class);

PagedResult<AnyObjectTO> matchingAnyObjects = anyObjectService.search(
    new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
    fiql(SyncopeClient.getAnyObjectSearchConditionBuilder("PRINTER").query()).
    build()); // <5>

GroupService groupService = client.getService(GroupService.class);

PagedResult<GroupTO> matchingGroups = groupService.search(
    new AnyQuery.Builder().realm("/even/two").page(3).size(150).
    fiql(SyncopeClient.getGroupSearchConditionBuilder().
        is("name").equalTo("palo*").query()).
    build()); // <6>
----
<1> get the total number of users available in the given deployment (and <<domains,domain>>)
<2> get users in the root realm with username matching the provided wildcard expression
<3> get users in the root realm with no values for `loginDate`, i.e. that have never authenticated to the
given deployment
<4> get users in the root realm with <<roles,role>> `Other` assigned
<5> get all any objects in the root realm with <<anytype,type>> `PRINTER`
<6> get all groups having name starting with prefix 'palo' - third page of the result,
where each page contains 150 items
====

.Delete several users at once
====
[source,java]
----
BatchRequest batchRequest = client.batch(); // <1>

UserService batchUserService = batchRequest.getService(UserService.class);

final int pageSize = 100;
final int count = userService.search(
        new AnyQuery.Builder().page(0).size(0).build()).getTotalCount(); // <2>
for (int page = 1; page <= (count / pageSize) + 1; page++) {
    for (UserTO user : userService.search(
            new AnyQuery.Builder().page(page).size(pageSize).build()).getResult()) {  // <3>  

        batchUserService.delete(user.getKey()); // <4>
    }       
}       

BatchResponse batchResponse = batchRequest.commit();  // <5>  
List<BatchResponseItem> batchResponseItems = batchResponse.getItems(); // <6>
----
<1> begin the batch request
<2> get the total number of users available in the given deployment (and <<domains,domain>>)
<3> loop through all users available, using paginated search
<4> add each user's deletion to the batch request
<5> send the batch request for processing
<6> examine the batch results
====

.Self-read own profile information
====
[source,java]
----
Triple<Map<String, Set<String>>, List<String>, UserTO> self = client.self();
UserTO userTO = self.getRight(); // <1>
Map<String, Set<String>> realm2entitlements = self.getLeft(); // <2>
List<String> delegations = self.getMiddle(); // <3>
----
<1> https://syncope.apache.org/apidocs/4.0/org/apache/syncope/common/lib/to/UserTO.html[UserTO^] of the requesting user
<2> for each <<realms,realm>>, the owned <<entitlements,entitlements>>
<3> <<delegation,delegations>> assigned to the requesting user
====

.Change user status
====
[source,java]
----
String key = ...; // <1>
StatusR statusR = new StatusR();
statusR.setKey(key);
statusR.setType(StatusRType.SUSPEND); // <2>
UserTO userTO = userService.status(statusR).
  readEntity(new GenericType<ProvisioningResult<UserTO>>() {
  }).getEntity(); // <3>
----
<1> assume the key of the user to be suspended is known in advance
<2> `ACTIVATE`, `SUSPEND`, `REACTIVATE` values are accepted, and honoured depending on the actual status of the user
being updated
<3> request for user update and read back the updated entity
====
