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

org.bsc.confluence.rest.RESTConfluenceService Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.bsc.confluence.rest;

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.bsc.confluence.ConfluenceService;
import org.bsc.confluence.rest.model.Attachment;
import org.bsc.confluence.rest.model.Blogpost;
import org.bsc.confluence.rest.model.Page;
import org.bsc.ssl.SSLCertificateInfo;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static java.util.concurrent.CompletableFuture.*;

/**
 * @author bosrrentino
 * @see "https://docs.atlassian.com/confluence/REST/latest/"
 */
public class RESTConfluenceService extends AbstractRESTConfluenceService implements ConfluenceService {

    public enum ContentType {page, blogpost}

    final Credentials credentials;

    final java.net.URL endpoint;

    static {
        Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);
    }

    /**
     * @param url
     * @param credentials
     * @param sslInfo
     */
    public RESTConfluenceService(String url, Credentials credentials, SSLCertificateInfo sslInfo) {
        if (credentials == null) {
            throw new IllegalArgumentException("credentials argument is null!");
        }
        if (url == null) {
            throw new IllegalArgumentException("url argument is null!");
        }
        if (sslInfo == null) {
            throw new IllegalArgumentException("sslInfo argument is null!");
        }

        try {
            this.endpoint = new java.net.URL(url);

        } catch (MalformedURLException ex) {
            throw new IllegalArgumentException("url argument is not valid!", ex);
        }

        this.credentials = credentials;

        client.connectTimeout(ConfluenceService.getConnectTimeout(TimeUnit.SECONDS), TimeUnit.SECONDS);
        client.writeTimeout(ConfluenceService.getWriteTimeout(TimeUnit.SECONDS), TimeUnit.SECONDS);
        client.readTimeout(ConfluenceService.getReadTimeout(TimeUnit.SECONDS), TimeUnit.SECONDS);

        if (!sslInfo.isIgnore() && "https".equals(this.endpoint.getProtocol())) {

            client.hostnameVerifier(sslInfo.getHostnameVerifier())
                    .sslSocketFactory(sslInfo.getSSLSocketFactory(), sslInfo.getTrustManager())
            ;
        }

        // Use interceptor which transparently adds credentials and HTTP headers for each request
        client.addInterceptor(chain -> {
            Request.Builder requestBuilder = chain.request().newBuilder();
            if (credentials.username != null) {
                final String credential =
                    okhttp3.Credentials.basic(credentials.username, credentials.password);
                requestBuilder.header("Authorization", credential);
            }
            if (!credentials.httpHeaders.isEmpty()) {
                credentials.httpHeaders.entrySet().forEach(entry ->
                    requestBuilder.header(entry.getKey(), entry.getValue()));
            }
            return chain.proceed(requestBuilder.build());
        });

    }

    @SuppressWarnings("unchecked")
    private  CompletableFuture cast(CompletableFuture s) {
        return (CompletableFuture) s;
    }

    public final JsonObjectBuilder jsonForCreatingContent(ContentType type, final String spaceKey, final String title, Storage content) {
        return Json.createObjectBuilder()
                .add("type", type.name())
                .add("title", title)
                .add("space", Json.createObjectBuilder().add("key", spaceKey))
                .add("body", Json.createObjectBuilder()
                        .add("storage", Json.createObjectBuilder()
                                .add("representation", content.rapresentation.toString())
                                .add("value", content.value)))

                ;
    }

    public final JsonObjectBuilder jsonForCreatingContent(ContentType type, final String spaceKey, final long parentPageId, final String title, Storage content) {
        return jsonForCreatingContent(type, spaceKey, title, content)
                .add("ancestors", Json.createArrayBuilder()
                        .add(Json.createObjectBuilder().add("id", parentPageId)))
                ;
    }

    public final JsonObjectBuilder jsonAddBody(JsonObjectBuilder builder, Storage storage) {
        return builder
                .add("body", Json.createObjectBuilder()
                        .add("storage", Json.createObjectBuilder()
                                .add("representation", storage.rapresentation.toString())
                                .add("value", storage.value)))
                ;
    }

    /**
     * @param spaceKey
     * @param title
     * @return
     */
    public final CompletableFuture createPageByTitle(String spaceKey, String title, Storage content) {
        final JsonObjectBuilder input = jsonForCreatingContent(ContentType.page, spaceKey, title, content);

        return createPage(input.build())
                .thenApply(data -> data.map(Page::new).get());
    }

    /**
     * @return
     */
    @Override
    protected HttpUrl.Builder urlBuilder() {

        int port = endpoint.getPort();
        port = (port > -1) ? port : endpoint.getDefaultPort();

        String path = endpoint.getPath();

        path = (path.startsWith("/")) ? path.substring(1) : path;

        return new HttpUrl.Builder()
                .scheme(endpoint.getProtocol())
                .host(endpoint.getHost())
                .port(port)
                .addPathSegments(path)
                //.addPathSegments(ConfluenceService.Protocol.REST.path())
                ;
    }

    @Override
    public Credentials getCredentials() {
        return credentials;
    }

    @Override
    public Model.Page newPage(Model.ID id, String space, String title, int version) {
        return new Page(Json.createObjectBuilder()
                        .add( "version", Json.createObjectBuilder().add("number", version).build())
                        .add( "id", id.toString())
                        .add( "title", title )
                        .add( "space", Json.createObjectBuilder().add( "key", space))
                        .build());
    }

    @Override
    public CompletableFuture> getPageByTitle(Model.ID parentPageId, String title) {

        return childrenPages(String.valueOf(parentPageId))
                .thenApply(children ->
                        children.stream()
                                .map(Page::new)
                                .filter(page -> page.getTitle().equals(title))
                                .findFirst());
    }

    /**
     * @param pageId
     * @return
     * @throws Exception
     */
    @Override
    public CompletableFuture> getPage(Model.ID pageId) {
        return findPageById(pageId.toString())
                .thenApply(page -> page.map(Page::new));
    }

    @Override
    public CompletableFuture> getPage(String spaceKey, String pageTitle) {
        return findPage(spaceKey, pageTitle)
                .thenApply(page -> page.map(Page::new));
    }


    @Override
    public CompletableFuture> getDescendents(Model.ID pageId) {
        return descendantPages(pageId.getValue())
                .thenApply(descendant ->
                        descendant.stream()
                                .map((page) -> new Page(page))
                                .collect(Collectors.toList()));
    }


    @Override
    public CompletableFuture createPage(Model.Page parentPage, String title, Storage content) {

        final String spaceKey = parentPage.getSpace();
        final JsonObjectBuilder input = jsonForCreatingContent(ContentType.page, spaceKey, parentPage.getId().getValue(), title, content);

        return createPage(input.build())
                .thenApply(page -> page.map(Page::new).get());
    }


    @Override
    public CompletableFuture storePage(Model.Page page, Storage content) {

        int previousVersion = page.getVersion();

        final JsonObjectBuilder builder = Json.createObjectBuilder()
                .add("version", Json.createObjectBuilder().add("number", ++previousVersion))
                .add("id", page.getId().getValue())
                .add("type", "page")
                .add("title", page.getTitle())
                .add("space", Json.createObjectBuilder().add("key", page.getSpace()))
                .add("body", Json.createObjectBuilder()
                        .add("storage", Json.createObjectBuilder()
                                .add("representation", content.rapresentation.toString())
                                .add("value", content.value)));

        final JsonObject input =  builder.build();

        final CompletableFuture updatePage =
                updatePage(page.getId().toString(), input)
                        .thenApply(p -> p.map(Page::new).get());

        return supplyAsync(() -> updatePage.join());
    }

    @Override
    public CompletableFuture storePage(Model.Page page) {
        return completedFuture(page);
    }

    @Override
    public CompletableFuture removePage(Model.Page parentPage, String title) {

        return childrenPages(parentPage.getId().toString())
                .thenCompose(children ->
                        children.stream()
                                .map(page -> new Page(page))
                                .filter(page -> page.getTitle().equals(title))
                                .map(page -> deletePageById(page.getId().toString()))
                                .findFirst()
                                .orElse(completedFuture(false)));

    }


    @Override
    public CompletableFuture removePage(Model.ID pageId) {
        return deletePageById(pageId.toString());
    }

    @Override
    public CompletableFuture addLabelsByName(Model.ID id, String[] labels) {
        return runAsync(() -> addLabels(id.toString(), labels));
    }

    ////////////////////////////////////////////////////////////////////////////////
    // ATTACHMENT
    ///////////////////////////////////////////////////////////////////////////////

    private Attachment cast(Model.Attachment attachment) {
        if (attachment == null) {
            throw new IllegalArgumentException("attachment argument is null!");
        }
        if (!(attachment instanceof Attachment)) {
            throw new IllegalArgumentException("page argument is not right type!");
        }
        return (Attachment) attachment;

    }

    @Override
    public Model.Attachment newAttachment() {
        return new Attachment();
    }

    @Override
    public CompletableFuture> getAttachment(Model.ID pageId, String name, String version) {
        return getAttachment(pageId.toString(), name)
                .thenApply(attachments ->
                        attachments.stream()
                                .findFirst()
                                .map(result -> (Model.Attachment) new Attachment(result)));
    }

    @Override
    public CompletableFuture addAttachment(Model.Page page, Model.Attachment attachment, InputStream source) {
        final CompletableFuture addAttchment =
                addAttachment(page.getId().toString(), cast(attachment), source)
                        .thenApply(attachments ->
                                attachments.stream()
                                        .findFirst()
                                        .map(result -> (Model.Attachment) new Attachment(result))
                                        .get());

        return supplyAsync(() -> addAttchment.join());

    }

    ////////////////////////////////////////////////////////////////////////////////
    // BLOG POST
    ///////////////////////////////////////////////////////////////////////////////

    @Override
    public Model.Blogpost createBlogpost(String space, String title, Storage content, int version) {
        final Blogpost result = new Blogpost();

        result.setSpace(space);
        result.setTitle(title);
        result.setContent(content);
        result.setVersion(version);
        return result;
    }

    @Override
    public CompletableFuture addBlogpost(Model.Blogpost blogpost) {

        final Blogpost restBlogpost = Blogpost.class.cast(blogpost);

        final JsonObjectBuilder builder =
                jsonForCreatingContent(ContentType.blogpost,
                        restBlogpost.getSpace(),
                        restBlogpost.getTitle(),
                        restBlogpost.getContent());

        return cast(createPage(builder.build()).thenApply(page -> page.map(Blogpost::new).orElse(null)));
    }

    @Override
    public void close() throws IOException {
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy