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

io.stargate.sdk.doc.DocumentClient Maven / Gradle / Ivy

/*
 * Copyright DataStax, 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 io.stargate.sdk.doc;

import io.stargate.sdk.api.odm.RecordMapper;
import io.stargate.sdk.http.ServiceHttp;
import io.stargate.sdk.http.LoadBalancedHttpClient;
import io.stargate.sdk.http.domain.ApiResponseHttp;
import io.stargate.sdk.utils.Assert;
import io.stargate.sdk.utils.JsonUtils;

import java.net.HttpURLConnection;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

/**
 * Part of the Document API in stargate wrapper for methods at the document level.
 *
 * @author Cedrick LUNVEN (@clunven)
 */
public class DocumentClient {
    
    /** Get Topology of the nodes. */
    private final LoadBalancedHttpClient stargateHttpClient;
    
    /** Namespace. */
    private CollectionClient collectionClient;
    
    /** Unique document identifer. */
    private String docId;
    
    /**
     * Full constructor.
     * 
     * @param stargateHttpClient stargateHttpClient
     * @param collectionClient CollectionClient
     * @param docId String
     */
    public DocumentClient(LoadBalancedHttpClient stargateHttpClient, CollectionClient collectionClient, String docId) {
        this.collectionClient   = collectionClient;
        this.docId              = docId;
        this.stargateHttpClient = stargateHttpClient;
    }
    
    /**
     * Leverage find() to check existence without eventual formatting issues. 
     * https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/getDocById
     * 
     * @return boolean
     */
    public boolean exist() {
        return HttpURLConnection.HTTP_OK == stargateHttpClient.GET(documentResource).getCode();
    }
    
    /**
     * Replace a document. https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/replaceDoc
     * 
     * @param  
     *       working class
     * @param doc 
     *       object to be updated
     * @return
     *       the unique document identifier
     */
    public  String upsert(DOC doc) {
        Assert.notNull(doc, "document");
        ApiResponseHttp res = stargateHttpClient.PUT(documentResource, JsonUtils.marshall(doc));
        return marshallDocumentId(res.getBody());
    }
    
    /**
     * Replace a document providing only the Json.
     *
     * @param json
     *      jon file.
     * @return
     *     the unique document identifier
     */
    public String upsert(String json) {
        Assert.hasLength(json, "Json document should not be null");
        ApiResponseHttp res = stargateHttpClient.PUT(documentResource, json);
        return marshallDocumentId(res.getBody());
    }
  
    /**
     * Update part of a document. https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/updatePartOfDoc
     * 
     * @param  working class
     * @param doc working class
     * @return DOC
     */
    public  String update(DOC doc) {
        Assert.notNull(doc, "document");
        ApiResponseHttp res = stargateHttpClient.PATCH(documentResource, JsonUtils.marshall(doc));
        return marshallDocumentId(res.getBody());
    }
    
    /**
     * Update a document providing only the Json.
     *
     * @param json
     *      jon file.
     * @return
     *     the unique document identifier
     */
    public String update(String json) {
        Assert.hasLength(json, "Json document should not be null");
        ApiResponseHttp res = stargateHttpClient.PATCH(documentResource, json);
        return marshallDocumentId(res.getBody());
    }
    
    /**
     * Get a document by id.
     *
     * @return
     *      the json payload if document exists.
     */
    public Optional find() {
        ApiResponseHttp res = stargateHttpClient.GET(documentResource, "?raw=true");
        if (HttpURLConnection.HTTP_OK == res.getCode()) {
            return Optional.of(res.getBody());
         }
         return Optional.empty();
    }
    
    /**
     * Get a document by id.
     *
     * @param 
     *      nea
     * @param docm 
     *      document mapper
     * @return a document if exist     
     */
    public  Optional find(RecordMapper docm) {
        Assert.notNull(docm, "documentMapper");
        Optional f = find();
        if (f.isPresent()) {
            return f.map(docm::map);
        }
        return Optional.empty();
    }
    
    /**
     * Get a document by {document-id}. https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/getDocById
     *
     * @param  working class
     * @param clazz working class
     * @return a document if exist
     */
    public  Optional find(Class clazz) {
        Assert.notNull(clazz, "className");
        Optional f = find();
        if (f.isPresent()) {
            return f.map(b -> marshallDocument(b, clazz));
        }
        return Optional.empty();
    }

    /**
     * Delete a document. https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/deleteDoc
     */
    public void delete() {
        if (!exist()) {
            throw new IllegalArgumentException("Cannot delete " + docId + ", it does not exists");
        }
        stargateHttpClient.DELETE(documentResource);
    }
    
    /**
     * Add '/' if needed.
     * 
     * @param path
     *      current path
     * @return
     *      path with '/'
     */
    private String formatPath(String path) {
        Assert.hasLength(path, "hasLength");
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        return path;
    }
    
    /**
     * Get a sub document by {document-path}.
     * https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/GetSubDocByPath
     *
     * @param  working class
     * @param path subpath in the doc
     * @param className  working class
     * @return SUBDOC
     */
    public  Optional findSubDocument(String path, Class className) {
        Assert.notNull(className, "expectedClass");
        Assert.hasLength(path, "path");
        ApiResponseHttp res = stargateHttpClient.GET(documentResource, formatPath(path) + "?raw=true");
        if (HttpURLConnection.HTTP_OK == res.getCode()) {
           return Optional.of(marshallDocument(res.getBody(), className));
        }
        if (HttpURLConnection.HTTP_NOT_FOUND == res.getCode()) {
            return Optional.empty();
        }
        return Optional.empty();
    }
    
    /**
     * Retrieve a sub document with no marshalling
     * 
     * @param path
     *      path of the document
     * @return
     *      value as a String
     */
    public Optional findSubDocument(String path) {
        Assert.hasLength(path, "path");
        ApiResponseHttp res = stargateHttpClient.GET(documentResource, formatPath(path) + "?raw=true");
        if (HttpURLConnection.HTTP_OK == res.getCode()) {
           return Optional.of(res.getBody());
        }
        if (HttpURLConnection.HTTP_NOT_FOUND == res.getCode()) {
            return Optional.empty();
        }
        return Optional.empty();
    }
    
    /**
     * Execute a function on a document.
     * 
     * @param path
     *      current document sub path
     * @param function
     *      function executed.
     * @param value
     *      value for attribute to update
     * @return
     *      attribute updated
     */
    @SuppressWarnings("unchecked")
    public String executefunction(String path, String function, Object value) {
        if (value instanceof String) { 
            value = "\"" + value + "\"";
        }
        ApiResponseHttp res = stargateHttpClient.POST(documentResource, 
                "{ \"operation\":\"" + function + "\",\"value\":" + JsonUtils.marshall(value) + "}", 
                formatPath(path) + "/function");
        // Parse result
        Map doc = JsonUtils.unmarshallBean(res.getBody(), Map.class);
        if (doc.containsKey("data")) {
            return JsonUtils.marshall(doc.get("data"));
        }
        return res.getBody();
    }
    
    /**
     * Replace a subpart of the document.
     * https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/replaceSubDoc
     * 
     * @param  working class
     * @param path subpath in the doc
     * @param newValue object for the new value
     */
    public  void replaceSubDocument(String path, SUBDOC newValue) {
        Assert.notNull(newValue, "newValue");
        stargateHttpClient.PUT(documentResource, JsonUtils.marshall(newValue), formatPath(path));
    }
    
    /**
     * Replace a subpart of the document.
     * 
     * @param path
     *      sub path
     * @param newValue
     *      new value for the path
     */
    public void replaceSubDocument(String path, String newValue) {
        Assert.hasLength(path, "path");
        Assert.hasLength(newValue, "newValue");
        stargateHttpClient.PUT(documentResource, newValue, formatPath(path));
    }
    
    /**
     * Update part of a sub document
     * https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/updatePartOfSubDoc
     * 
     * @param  working class
     * @param path subpath in the doc
     * @param newValue object for the new value
     */
    public  void updateSubDocument(String path, SUBDOC newValue) {
        Assert.notNull(newValue, "newValue");
        stargateHttpClient.PATCH(documentResource, JsonUtils.marshall(newValue), formatPath(path));
    }
    
    /**
     * Update a subpart of the document.
     * 
     * @param path
     *      sub path
     * @param newValue
     *      new value for the path
     */
    public void updateSubDocument(String path, String newValue) {
        Assert.hasLength(path, "path");
        Assert.hasLength(newValue, "newValue");
        stargateHttpClient.PATCH(documentResource, newValue, formatPath(path));
    }
    
    /**
     * Delete a sub document.
     * https://docs.datastax.com/en/astra/docs/_attachments/docv2.html#operation/deleteSubDoc
     * 
     * @param path sub document path
     */
    public void deleteSubDocument(String path) {
        stargateHttpClient.DELETE(documentResource, formatPath(path) + "?raw=true");
    }
    
    /**
     * marshallDocumentId
     * 
     * @param body String
     * @return String
     */
    private String marshallDocumentId(String body) {
        try {
            return (String) JsonUtils.unmarshallBean(body, Map.class).get(CollectionClient.DOCUMENT_ID);
        } catch (Exception e) {
            throw new RuntimeException("Cannot marshall document after 'upsert'", e);
        }
    }
    
    // ---------------------------------
    // ----       Resources         ----
    // ---------------------------------    
    
    /**
     * marshallDocument
     *
     * @param body String
     * @param clazz DOC
     * @return DOC
     */
    @SuppressWarnings("unchecked")
    private  SUBDOC marshallDocument(String body,  Class clazz) {
        try {
            if (clazz.equals(String.class)) {
               return (SUBDOC) body;
            }
            return JsonUtils.unmarshallBean(body, clazz);
        } catch (Exception e) {
            throw new RuntimeException("Cannot marshal output '" + body + "' into class '"+ clazz +"'", e);
        }
    }
    
    /** 
     * /v2/schemas/namespaces/{namespace}/collections/{collection}/{docId} 
     */
    public Function documentResource =
            (node) -> collectionClient.collectionResource.apply(node) +  "/" + docId;
       
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy