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

com.profitbricks.sdk.model.ModelBase.groovy Maven / Gradle / Ivy

/*
   Copyright 2018 Profitbricks GmbH

   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 com.profitbricks.sdk.model

import com.profitbricks.sdk.annotation.*
import groovy.transform.EqualsAndHashCode
import groovy.util.logging.Log4j2

import java.lang.annotation.Annotation

import static com.profitbricks.sdk.Common.*

/**
 * base class for all model entities
 * all operations are blocking until a proper request status is received
 *
 * @author fudge 
 */
@Log4j2
@EqualsAndHashCode
abstract class ModelBase {
    def id

    /**
     * the resource part of the API URL path
     * @return the resource path for this REST resource
     */
    abstract String getResource()

    /**
     * provides the request body for POST requests
     * @return the request body
     */
    protected Map getCreateBody() { bodyFrom propertyNames([Creatable]) }

    /**
     * provides the request body for PUT requests
     * @return the request body
     */
    protected Map getUpdateBody() { bodyFrom propertyNames([Updatable]) }

    /**
     * provides the 'list all' REST call
     * @return a list of resource IDs
     */
    final List getAll(Map options = [:]) {
        API.get(requestFor(resource, options))?.data?.items?.collect{it.id}
    }

    /**
     * provides the creation REST call
     * @return the response JSON object
     */
    def create(final Map options = [:]) {
        from waitFor(API.post(requestFor(resource, options) + [body: createBody]), options)?.data
    }

    /**
     * provides the 'get resource' REST call
     * @return the response JSON object
     */
    def read(final id = id, final Map options = [:]) {
        from API.get(requestFor("${resource}/${id ?: ''}", options))?.data
    }

    /**
     * provides the 'update resource' REST call, this is confined to only use PUT (full update)
     * instead of PATCH (partial update)
     * @return the response JSON object
     */
    boolean update(final Map options = [:]) {
        waitFor(API.put(requestFor("${resource}/${id}", options) + [body: updateBody]), options)?.status == 202
    }

    /**
     * provides the 'delete resource' REST call
     * @return the response JSON object
     */
    boolean delete(final Map options = [:]) {
        waitFor(API.delete(requestFor("${resource}/$id", options)), options)?.status == 202
    }

    /**
     * provides generic request body creation from a list of field names
     * @param propNames list of property names to take into account
     * @return a properly filled request body
     */
    protected final Map bodyFrom(final List propNames) {
        final props = metaClass.properties.findAll{ propNames.contains(it.name) && this."${it.name}" != null }.collectEntries{ [(it.name): this."${it.name}"] }

        final rtn = [properties: [:]]
        // we have to spice up keywords with underscores
        props.each { k, v -> rtn.properties."${k.toString().replaceAll(/_/, '')}" = v }
        if (log.isDebugEnabled()) log.debug "body: ${rtn}"
        return rtn
    }

    /**
     * assembles a list of property names annotated by at least one of the given
     * annotation list
     *
     * @param annotations only fields annotated with the given classes are considered
     * @return a list of property names of the current entity
     */
    final List propertyNames(final List> annotations) {
        this.class.declaredFields.findAll {
            annotations.find { a ->
                a in it.declaredAnnotations*.annotationType() }
        }.collect{it.name}
    }

    /**
     * unmarshalls an entity from a given JSON representation
     *
     * @param data a JSON object
     * @return the unmarshalled entity
     */
    def from(final data) {
        def e = null

        if (data) {
            e = this.class.newInstance(id: data.id)
            if (!e) throw new InvalidObjectException("cannot construct ${this.class.name}")

            (e.propertyNames([Creatable, Updatable, Readable]))?.each {
                def val = data.properties?."${it.replaceAll(/_/, '')}"
                if (val != null && !(val =~ /(?i)null/)) // NB: PUT seems to be non RFC-like for some entities in the PBC API
                    e."$it" = val
            }
        }
        return e
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy