com.cedarsoftware.controller.NCubeController.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of n-cube Show documentation
Show all versions of n-cube Show documentation
Multi-dimensional Rule Engine
package com.cedarsoftware.controller
import com.cedarsoftware.ncube.Action
import com.cedarsoftware.ncube.ApplicationID
import com.cedarsoftware.ncube.Axis
import com.cedarsoftware.ncube.AxisType
import com.cedarsoftware.ncube.AxisValueType
import com.cedarsoftware.ncube.CellInfo
import com.cedarsoftware.ncube.Column
import com.cedarsoftware.ncube.Delta
import com.cedarsoftware.ncube.NCube
import com.cedarsoftware.ncube.NCubeConstants
import com.cedarsoftware.ncube.NCubeInfoDto
import com.cedarsoftware.ncube.NCubeManager
import com.cedarsoftware.ncube.NCubeMutableClient
import com.cedarsoftware.ncube.NCubeRuntimeClient
import com.cedarsoftware.ncube.NCubeTest
import com.cedarsoftware.ncube.ReferenceAxisLoader
import com.cedarsoftware.ncube.ReleaseStatus
import com.cedarsoftware.ncube.util.VersionComparator
import com.cedarsoftware.servlet.JsonCommandServlet
import com.cedarsoftware.util.ArrayUtilities
import com.cedarsoftware.util.CompactCILinkedMap
import com.cedarsoftware.util.InetAddressUtilities
import com.cedarsoftware.util.ThreadAwarePrintStream
import com.cedarsoftware.util.ThreadAwarePrintStreamErr
import com.cedarsoftware.util.io.JsonObject
import com.cedarsoftware.util.io.JsonReader
import com.cedarsoftware.util.io.JsonWriter
import com.google.common.util.concurrent.AtomicDouble
import groovy.transform.CompileStatic
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.actuate.info.InfoEndpoint
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.bind.annotation.RestController
import javax.management.MBeanServer
import javax.management.ObjectName
import javax.servlet.http.HttpServletRequest
import java.lang.management.ManagementFactory
import java.util.regex.Pattern
import static com.cedarsoftware.ncube.NCubeAppContext.testServer
import static com.cedarsoftware.ncube.ReferenceAxisLoader.*
import static com.cedarsoftware.util.Converter.convertToDate
import static com.cedarsoftware.util.Converter.convertToLong
import static com.cedarsoftware.util.Converter.convertToString
import static com.cedarsoftware.util.StringUtilities.isEmpty
/**
* NCubeController API.
*
* @author John DeRegnaucourt ([email protected])
*
* Copyright (c) Cedar Software LLC
*
* 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.
*/
@CompileStatic
@RestController
class NCubeController implements NCubeConstants
{
@Autowired
private InfoEndpoint infoEndpoint
@Autowired(required = false)
NCubeManager ncubeManager // optional (on runtime-server will be null, on combined-server and storage-server will be valid)
@Value('${server.tomcat.max-connections:1000}') int tomcatMaxConnections
@Value('${server.tomcat.max-threads:200}') int tomcatMaxThreads
private static final Pattern IS_NUMBER_REGEX = ~/^[+\-]?([1-9]\d{0,2}(\d*|(,\d{3})*)(\.\d*|\.?)|(0\.?|0?\.\d+))$/
private static final Pattern NO_QUOTES_REGEX = ~/"/
private NCubeMutableClient mutableClient
private static String servletHostname = null
private static String inetHostname = null
private static AtomicDouble processLoadPeak = new AtomicDouble(0.0d)
private static AtomicDouble systemLoadPeak = new AtomicDouble(0.0d)
private static final Map NO_CELL = [type:null, value:null]
private static final String EXECUTE_ERROR = 'User code cannot be executed on this server. Attempted method: '
private final boolean allowExecute
NCubeController(NCubeMutableClient mutableClient, boolean allowExecute)
{
System.out = new ThreadAwarePrintStream(System.out)
System.err = new ThreadAwarePrintStreamErr(System.err)
this.mutableClient = mutableClient
this.allowExecute = allowExecute
}
protected String getUserForDatabase()
{
String user = null
String principal = SecurityContextHolder.context?.authentication?.principal
if (!isEmpty(principal))
{
user = principal.split('@').first()
}
if (isEmpty(user))
{
HttpServletRequest request = JsonCommandServlet.servletRequest.get()
Enumeration e = request.headerNames
while (e.hasMoreElements())
{
String headerName = (String) e.nextElement()
if ('smuser'.equalsIgnoreCase(headerName))
{
user = request.getHeader(headerName)
break
}
}
}
if (isEmpty(user))
{
user = System.getProperty('user.name')
}
if (ncubeManager)
{
ncubeManager.userId = user
}
return user
}
// ============================================= Begin API =========================================================
@SuppressWarnings("GroovyUnusedDeclaration")
String getUserId()
{
return mutableClient.userId
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean assertPermissions(ApplicationID appId, String resource, String actionName)
{
appId = addTenant(appId)
Action action = actionName == null ? null : Action.valueOf(actionName.toUpperCase())
return mutableClient.assertPermissions(appId, resource, action)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map checkMultiplePermissions(ApplicationID appId, String resource, Object[] actions)
{
if (ArrayUtilities.isEmpty(actions))
{
throw new IllegalArgumentException('Must send at least action to check permissions')
}
appId = addTenant(appId)
for (int i = 0; i < actions.length; i++)
{
actions[i] = Action.valueOf((actions[i] as String).toUpperCase())
}
return mutableClient.checkMultiplePermissions(appId, resource, actions)
}
Boolean checkPermissions(ApplicationID appId, String resource, String actionName)
{
appId = addTenant(appId)
Action action = actionName == null ? null : Action.valueOf(actionName.toUpperCase())
return mutableClient.checkPermissions(appId, resource, action)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean isSysAdmin()
{
return mutableClient.isSysAdmin()
}
Boolean isAppAdmin(ApplicationID appId)
{
appId = addTenant(appId)
return mutableClient.isAppAdmin(appId)
}
@SuppressWarnings("GroovyUnusedDeclaration")
String getAppLockedBy(ApplicationID appId)
{
appId = addTenant(appId)
return mutableClient.getAppLockedBy(appId)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean isAppLocked(ApplicationID appId)
{
appId = addTenant(appId)
String lockedBy = mutableClient.getAppLockedBy(appId)
return lockedBy != null
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean lockApp(ApplicationID appId, boolean shouldLock)
{
appId = addTenant(appId)
return mutableClient.lockApp(appId, shouldLock)
}
Integer moveBranch(ApplicationID appId, String newSnapVer)
{
appId = addTenant(appId)
return mutableClient.moveBranch(appId, newSnapVer)
}
Integer releaseVersion(ApplicationID appId, String newSnapVer = null)
{
appId = addTenant(appId)
int rowCount = mutableClient.releaseVersion(appId, newSnapVer)
return rowCount
}
Object[] search(ApplicationID appId, String cubeNamePattern = null, String content = null, Map options = [(SEARCH_ACTIVE_RECORDS_ONLY):true])
{
appId = addTenant(appId)
if (options)
{
options.remove(SEARCH_CLOSURE)
options.remove(SEARCH_OUTPUT)
}
List cubeInfos = mutableClient.search(appId, cubeNamePattern, content, options)
return cubeInfos as Object[]
}
@SuppressWarnings("GroovyUnusedDeclaration")
Integer getSearchCount(ApplicationID appId, String cubeNamePattern = null, String content = null, Map options = [(SEARCH_ACTIVE_RECORDS_ONLY):true])
{
return search(appId, cubeNamePattern, content, options).length
}
Boolean restoreCubes(ApplicationID appId, Object[] cubeNames)
{
appId = addTenant(appId)
return mutableClient.restoreCubes(appId, cubeNames)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Object[] getRevisionHistory(ApplicationID appId, String cubeName, boolean ignoreVersion = false)
{
appId = addTenant(appId)
List cubeInfos = mutableClient.getRevisionHistory(appId, cubeName, ignoreVersion)
return cubeInfos.toArray()
}
@SuppressWarnings("GroovyUnusedDeclaration")
Object[] getCellAnnotation(ApplicationID appId, String cubeName, Object[] ids, boolean ignoreVersion = false)
{
appId = addTenant(appId)
List cubeInfos = mutableClient.getCellAnnotation(appId, cubeName, getCoordinate(ids), ignoreVersion)
return cubeInfos.toArray()
}
@SuppressWarnings("GroovyUnusedDeclaration")
String getHtml(ApplicationID appId, String cubeName)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
// The Strings below are hints to n-cube to tell it which axis to place on top
String html = toHtmlWithColumnHints(ncube)
return html
}
private static String toHtmlWithColumnHints(NCube ncube)
{
ncube.toHtml('trait', 'traits', 'businessDivisionCode', 'bu', 'month', 'months', 'col', 'column', 'cols', 'columns', 'attribute', 'attributes')
}
@SuppressWarnings("GroovyUnusedDeclaration")
String getJson(ApplicationID appId, String cubeName)
{
return getJson(appId, cubeName, [mode:"json-index"])
}
NCubeInfoDto loadCubeRecord(ApplicationID appId, String cubeName, Map options)
{
appId = addTenant(appId)
NCubeInfoDto record = mutableClient.loadCubeRecord(appId, cubeName, options)
return record
}
@SuppressWarnings("GroovyUnusedDeclaration")
NCubeInfoDto loadCubeRecordById(long id, Map options = null)
{
NCubeInfoDto record = mutableClient.loadCubeRecordById(id, options)
return record
}
String getJson(ApplicationID appId, String cubeName, Map options)
{
appId = addTenant(appId)
String json = mutableClient.getJson(appId, cubeName, options)
return json
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getVisualizerGraph(ApplicationID appId, Map options)
{
verifyAllowExecute('getVisualizerGraph')
appId = addTenant(appId)
Map graph = runtimeClient.getVisualizerGraph(appId, options)
return graph
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getVisualizerScopeChange(ApplicationID appId, Map options)
{
verifyAllowExecute('getVisualizerScopeChange')
appId = addTenant(appId)
Map graph = runtimeClient.getVisualizerScopeChange(appId, options)
return graph
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getVisualizerNodeDetails(ApplicationID appId, Map options)
{
verifyAllowExecute('getVisualizerNodeDetails')
appId = addTenant(appId)
Map graph = runtimeClient.getVisualizerNodeDetails(appId, options)
return graph
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean updateCubeMetaProperties(ApplicationID appId, String cubeName, Map newMetaProperties)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
ncube.clearMetaProperties()
ncube.addMetaProperties(newMetaProperties)
mutableClient.updateCube(ncube)
return true
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getCubeMetaProperties(ApplicationID appId, String cubeName)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
return valuesToCellInfo(ncube.metaProperties)
}
@SuppressWarnings("GroovyUnusedDeclaration")
void updateAxisMetaProperties(ApplicationID appId, String cubeName, String axisName, Map newMetaProperties)
{
appId = addTenant(appId)
mutableClient.updateAxisMetaProperties(appId, cubeName, axisName, newMetaProperties)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getAxisMetaProperties(ApplicationID appId, String cubeName, String axisName)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, null)
NCube ncube = getCubeInternal(appId, cubeName)
Axis axis = ncube.getAxis(axisName)
return valuesToCellInfo(axis.metaProperties)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean updateColumnMetaProperties(ApplicationID appId, String cubeName, String axisName, long colId, Map newMetaProperties)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
NCube ncube = getCubeInternal(appId, cubeName)
Axis axis = ncube.getAxis(axisName)
Column column = axis.getColumnById(colId)
column.clearMetaProperties()
column.addMetaProperties(newMetaProperties)
ncube.clearSha1()
mutableClient.updateCube(ncube)
return true
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getColumnMetaProperties(ApplicationID appId, String cubeName, String axisName, long colId)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, null)
NCube ncube = getCubeInternal(appId, cubeName)
Axis axis = ncube.getAxis(axisName)
Column col = axis.getColumnById(colId)
return valuesToCellInfo(col.metaProperties)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map mapReduce(ApplicationID appId, String cubeName, String colAxisName, String where = 'true', Map options = [:])
{
verifyAllowExecute('mapReduce')
appId = addTenant(appId)
Map result = runtimeClient.mapReduce(appId, cubeName, colAxisName, where, options)
return result
}
private static Map valuesToCellInfo(Map metaProps)
{
Map map = new CompactCILinkedMap<>()
for (item in metaProps.entrySet())
{
if (item.value instanceof CellInfo)
{
CellInfo cellInfo = (CellInfo) item.value
cellInfo.collapseToUiSupportedTypes() // byte/short/int => long, float => double
map.put(item.key, cellInfo)
}
else
{
CellInfo cellInfo = new CellInfo(item.value)
cellInfo.collapseToUiSupportedTypes() // byte/short/int => long, float => double
map.put(item.key, cellInfo)
}
}
return map
}
// TODO: Filter APP names by Access Control List data
Object[] getAppNames()
{
// TODO: Snag tenant based on authentication
String tenantName = tenant
ApplicationID.validateTenant(tenantName)
Object[] appNames = mutableClient.appNames
if (appNames.length == 0)
{
ApplicationID defaultAppId = new ApplicationID(tenantName, ApplicationID.DEFAULT_APP, '1.0.0', ReleaseStatus.SNAPSHOT.name(), 'DEFAULT_BRANCH')
createCube(defaultAppId, 'defaultNewAppCube')
}
return appNames
}
@SuppressWarnings("GroovyUnusedDeclaration")
Object[] getAppVersions(String app)
{
getAppVersions(app, null)
}
Object[] getAppVersions(String app, String status)
{
ApplicationID.validateApp(app)
Object[] vers = getVersions(app)
if (ArrayUtilities.isEmpty(vers))
{
return vers
}
// Filter out duplicates using Set, order by VersionComparator, remove trailing '-SNAPSHOT' and '-RELEASE'
Set versions = new TreeSet<>(new VersionComparator())
for (int i = 0; i < vers.length; i++)
{
String mvnVer = vers[i] as String
String[] verArr = mvnVer.split('-')
if (status == null || verArr[1] == status)
{
versions.add(verArr[0])
}
}
return versions.toArray()
}
Object[] getVersions(String app)
{
ApplicationID.validateApp(app)
Object[] versions = mutableClient.getVersions(app)
return versions
}
/**
* Create an n-cube (SNAPSHOT only) for non-Java clients.
*/
void createCube(ApplicationID appId, String cubeName)
{
NCube ncube = new NCube(cubeName)
ncube.applicationID = appId
Axis cols = new Axis("Column", AxisType.DISCRETE, AxisValueType.STRING, false, Axis.DISPLAY, 1)
cols.addColumn("A")
cols.addColumn("B")
cols.addColumn("C")
cols.addColumn("D")
cols.addColumn("E")
cols.addColumn("F")
cols.addColumn("G")
cols.addColumn("H")
cols.addColumn("I")
cols.addColumn("J")
Axis rows = new Axis("Row", AxisType.DISCRETE, AxisValueType.LONG, false, Axis.DISPLAY, 2)
rows.addColumn(1)
rows.addColumn(2)
rows.addColumn(3)
rows.addColumn(4)
rows.addColumn(5)
rows.addColumn(6)
rows.addColumn(7)
rows.addColumn(8)
rows.addColumn(9)
rows.addColumn(10)
ncube.addAxis(cols)
ncube.addAxis(rows)
createCube(ncube)
}
/**
* Create an n-cube (SNAPSHOT only) for Java clients.
*/
void createCube(NCube ncube)
{
ApplicationID appId = ncube.applicationID
if (!appId)
{
throw new IllegalArgumentException("New n-cube: ${ncube.name} must have an ApplicationID")
}
appId = addTenant(appId)
ncube.applicationID = appId
mutableClient.createCube(ncube)
}
void createCube(ApplicationID appId, String cubeName, byte[] cubeBytes)
{
if (!appId)
{
throw new IllegalArgumentException("New n-cube: ${cubeName} must have an ApplicationID")
}
appId = addTenant(appId)
mutableClient.createCube(appId, cubeName, cubeBytes)
}
Boolean updateCube(ApplicationID appId, String cubeName, byte[] cubeBytes)
{
addTenant(appId)
return mutableClient.updateCube(appId, cubeName, cubeBytes)
}
/**
* Delete an n-cube (SNAPSHOT only).
* @return boolean true if successful, otherwise a String error message.
*/
Boolean deleteCubes(ApplicationID appId, Object[] cubeNames)
{
if (ArrayUtilities.isEmpty(cubeNames))
{
throw new IllegalArgumentException('Must send at least one cube name')
}
appId = addTenant(appId)
if (!mutableClient.deleteCubes(appId, cubeNames))
{
throw new IllegalArgumentException("Cannot delete RELEASE n-cube.")
}
return true
}
/**
* Find all references from (out going) an n-cube.
* @return Object[] of String cube names that the passed in (named) cube references,
* otherwise a String error message.
*/
@SuppressWarnings("GroovyUnusedDeclaration")
Object[] getReferencesFrom(ApplicationID appId, String cubeName)
{
appId = addTenant(appId)
Set references = mutableClient.getReferencesFrom(appId, cubeName)
Object[] refs = references.toArray()
caseInsensitiveSort(refs)
return refs
}
/**
* Find all referenced input variables for a given n-cube (and through any n-cubes it
* references).
* @return Object[] of String names of each scope variable, otherwise a String error message.
*/
Object[] getRequiredScope(ApplicationID appId, String cubeName)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
Set refs = ncube.getRequiredScope([:], [:])
Object[] scopeKeys = refs.toArray()
caseInsensitiveSort(scopeKeys)
return scopeKeys
}
/**
* Duplicate the passed in cube, but change the name to newName AND the status of the new
* n-cube will be SNAPSHOT.
*/
Boolean duplicate(ApplicationID appId, ApplicationID destAppId, String cubeName, String newName)
{
appId = addTenant(appId)
destAppId = addTenant(destAppId)
return mutableClient.duplicate(appId, destAppId, cubeName, newName)
}
/**
* Release the passed in SNAPSHOT version (update their status_cd to RELEASE), and then
* duplicate all the n-cubes in the release, creating new ones in SNAPSHOT status with
* the version number set to the newSnapVer.
*/
Integer releaseCubes(ApplicationID appId, String newSnapVer = null)
{
appId = addTenant(appId)
int rowCount = mutableClient.releaseCubes(appId, newSnapVer)
return rowCount
}
/**
* Change the SNAPSHOT version number of an n-cube.
*/
@SuppressWarnings("GroovyUnusedDeclaration")
void changeVersionValue(ApplicationID appId, String newSnapVer)
{
appId = addTenant(appId)
mutableClient.changeVersionValue(appId, newSnapVer)
}
/**
* Add axis to an existing SNAPSHOT n-cube.
*/
void addAxis(ApplicationID appId, String cubeName, String axisName, String type, String valueType, Map axisOpts = [hasDefault:true, isSorted:false, fireAll:false])
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
if (isEmpty(axisName))
{
throw new IllegalArgumentException("Axis name cannot be empty.")
}
NCube ncube = getCubeInternal(appId, cubeName)
long maxId = -1
Iterator i = ncube.axes.iterator()
while (i.hasNext())
{
Axis axis = i.next()
if (axis.id > maxId)
{
maxId = axis.id
}
}
int sortedVal = axisOpts.isSorted ? Axis.SORTED : Axis.DISPLAY
Axis axis = new Axis(axisName, AxisType.valueOf(type), AxisValueType.valueOf(valueType), axisOpts.hasDefault, sortedVal, maxId + 1, axisOpts.fireAll)
ncube.addAxis(axis)
mutableClient.updateCube(ncube)
}
/**
* Add axis to an existing SNAPSHOT n-cube that is a reference to an axis in another cube.
*/
void addAxis(ApplicationID appId, String cubeName, String axisName, ApplicationID refAppId, String refCubeName, String refAxisName, ApplicationID transformAppId, String transformCubeName, Map axisOpts = [hasDefault:true])
{
appId = addTenant(appId)
NCube nCube = getCubeInternal(appId, cubeName)
if (isEmpty(axisName))
{
axisName = refAxisName
}
long maxId = -1
Iterator i = nCube.axes.iterator()
while (i.hasNext())
{
Axis axis = i.next()
if (axis.id > maxId)
{
maxId = axis.id
}
}
Map args = [:]
args[REF_TENANT] = refAppId.tenant
args[REF_APP] = refAppId.app
args[REF_VERSION] = refAppId.version
args[REF_STATUS] = refAppId.status
args[REF_BRANCH] = refAppId.branch
args[REF_CUBE_NAME] = refCubeName // cube name of the holder of the referring (pointing) axis
args[REF_AXIS_NAME] = refAxisName // axis name of the referring axis (the variable that you had missing earlier)
if (transformAppId?.app)
{
args[TRANSFORM_APP] = transformAppId.app // Notice no target tenant. User MUST stay within TENENT boundary
args[TRANSFORM_VERSION] = transformAppId.version
args[TRANSFORM_STATUS] = transformAppId.status
args[TRANSFORM_BRANCH] = transformAppId.branch
args[TRANSFORM_CUBE_NAME] = transformCubeName
}
ReferenceAxisLoader refAxisLoader = new ReferenceAxisLoader(cubeName, axisName, args)
Axis axis = new Axis(axisName, maxId + 1, axisOpts.hasDefault, refAxisLoader)
nCube.addAxis(axis)
mutableClient.updateCube(nCube)
}
/**
* Return the requested axis. The returned axis has some 'massaging' applied to it before
* being returned. First, it is being returned using the 'map-of-maps' format from json-io
* so that the column IDs can be converted from Longs to Strings, because Javascript cannot
* process a 64-bit long value (it stores numbers using a double, which means it can only
* reliably process 53-bits of a long). Converting the longs to Strings first, allows the
* column ID to round-trip to the UI and back, and json-io will 'mash' the String column ID
* into the Long column ID (within the JsonCommandServlet) as it receives the String. It
* senses the data-type mismatch (json-io does) and then attempts to convert the String to a
* numeric value (which succeeds). This allows the full 64-bit id to make it round trip.
*/
Map getAxis(ApplicationID appId, String cubeName, String axisName)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, null)
NCube ncube = getCubeInternal(appId, cubeName)
Axis axis = ncube.getAxis(axisName)
return convertAxis(axis)
}
/**
* Delete the passed in axis.
*/
void deleteAxis(ApplicationID appId, String cubeName, String axisName)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
NCube ncube = getCubeInternal(appId, cubeName)
if (ncube.numDimensions == 1)
{
throw new IllegalArgumentException("Could not delete axis '${axisName}' - at least one axis must exist on n-cube.")
}
ncube.deleteAxis(axisName)
mutableClient.updateCube(ncube)
}
@SuppressWarnings("GroovyUnusedDeclaration")
void updateAxis(ApplicationID appId, String cubeName, String origAxisName, String axisName, boolean hasDefault, boolean isSorted, boolean fireAll)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${origAxisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
NCube ncube = getCubeInternal(appId, cubeName)
// Rename axis
if (!origAxisName.equalsIgnoreCase(axisName))
{
ncube.renameAxis(origAxisName, axisName)
}
// Update default column setting (if changed)
Axis axis = ncube.getAxis(axisName)
if (axis.hasDefaultColumn() && !hasDefault)
{ // If it went from having default column to NOT having default column...
ncube.deleteColumn(axisName, null)
}
else if (!axis.hasDefaultColumn() && hasDefault)
{
if (axis.type != AxisType.NEAREST)
{
ncube.addColumn(axisName, null)
}
}
// update preferred column order
if (axis.type == AxisType.RULE)
{
axis.fireAll = fireAll
}
else
{
axis.columnOrder = isSorted ? Axis.SORTED : Axis.DISPLAY
}
ncube.clearSha1()
mutableClient.updateCube(ncube)
}
/**
* Update an entire set of columns on an axis at one time. The updatedAxis is not a real axis,
* but treated like an Axis-DTO where the list of columns within the axis are in display order.
*/
@SuppressWarnings("GroovyUnusedDeclaration")
void updateAxisColumns(ApplicationID appId, String cubeName, String axisName, Object[] cols)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
Set columns = new LinkedHashSet<>()
if (cols != null)
{
cols.each {
Object col ->
Column column = (Column) col
Object value = column.value
if (value == null || "".equals(value))
{
throw new IllegalArgumentException("Column cannot have empty value, n-cube: ${cubeName}, axis: ${axisName}")
}
columns.add(column)
}
}
NCube ncube = getCubeInternal(appId, cubeName)
ncube.updateColumns(axisName, columns)
mutableClient.updateCube(ncube)
}
@SuppressWarnings("GroovyUnusedDeclaration")
void breakAxisReference(ApplicationID appId, String cubeName, String axisName)
{
appId = addTenant(appId)
String resourceName = "${cubeName}/${axisName}"
mutableClient.assertPermissions(appId, resourceName, Action.UPDATE)
NCube ncube = getCubeInternal(appId, cubeName)
// Update default column setting (if changed)
ncube.breakAxisReference(axisName)
mutableClient.updateCube(ncube)
}
@SuppressWarnings("GroovyUnusedDeclaration")
void createRefAxis(ApplicationID appId, String cubeName, String axisName, ApplicationID refAppId, String refCubeName, String refAxisName)
{
appId = addTenant(appId)
refAppId = addTenant(refAppId)
mutableClient.createRefAxis(appId, cubeName, axisName, refAppId, refCubeName, refAxisName)
}
Boolean renameCube(ApplicationID appId, String oldName, String newName)
{
appId = addTenant(appId)
return mutableClient.renameCube(appId, oldName, newName)
}
/**
* Promote a previous revision of an NCube.
* @param cubeId long
* @return NCubeInfoDto for the revision of the passed in cubeId, NOT the newly created revision.
* NCubeInfDto does not contain bytes or testData
* Note: the restored dto and the dto to restore from differ only in revision number
*/
@SuppressWarnings("GroovyUnusedDeclaration")
NCubeInfoDto promoteRevision(long cubeId)
{
NCubeInfoDto record = mutableClient.promoteRevision(cubeId)
return record
}
@SuppressWarnings("GroovyUnusedDeclaration")
void saveJson(ApplicationID appId, String json)
{
appId = addTenant(appId)
json = json.trim()
List cubes
if (json.startsWith("["))
{
cubes = getCubes(json)
}
else
{
cubes = new ArrayList<>()
cubes.add(NCube.fromSimpleJson(json))
}
for (ncube in cubes)
{
ncube.applicationID = appId
try
{
mutableClient.updateCube(ncube)
}
catch (Exception ignore)
{
try
{
mutableClient.createCube(ncube)
}
catch (Exception ex)
{
throw new IllegalArgumentException("Unable to update or create cube: ${ncube.name}", ex)
}
}
}
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map runTests(ApplicationID appId)
{
verifyAllowExecute('runTests')
appId = addTenant(appId)
return runtimeClient.runTests(appId)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map runTests(ApplicationID appId, String cubeName, Object[] tests)
{
verifyAllowExecute('runTests')
appId = addTenant(appId)
return runtimeClient.runTests(appId, cubeName, tests)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map runTest(ApplicationID appId, String cubeName, NCubeTest test)
{
verifyAllowExecute('runTest')
appId = addTenant(appId)
return runtimeClient.runTest(appId, cubeName, test)
}
@SuppressWarnings("GroovyUnusedDeclaration")
Object[] getTests(ApplicationID appId, String cubeName)
{
appId = addTenant(appId)
Object[] tests = mutableClient.getTests(appId, cubeName)
return tests
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getAppTests(ApplicationID appId)
{
appId = addTenant(appId)
Map appTests = mutableClient.getAppTests(appId)
return appTests
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean saveTests(ApplicationID appId, String cubeName, Object[] tests)
{
appId = addTenant(appId)
NCube cube = getCubeInternal(appId, cubeName)
cube.testData = tests
return mutableClient.updateCube(cube)
}
@SuppressWarnings("GroovyUnusedDeclaration")
NCubeTest createNewTest(ApplicationID appId, String cubeName, String testName)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
if (isEmpty(testName))
{
throw new IllegalArgumentException("Test name cannot be empty, cube: ${cubeName}, app: ${appId}")
}
Set items = ncube.getRequiredScope([:], [:])
Map coords = new CompactCILinkedMap<>()
if (items?.size())
{
for (String s : items)
{
coords[s] = (CellInfo)null
}
}
CellInfo[] assertions = [ new CellInfo("exp", "output.return", false, false) ] as CellInfo[]
NCubeTest test = new NCubeTest(testName, coords, assertions)
return test
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean updateNotes(ApplicationID appId, String cubeName, String notes)
{
appId = addTenant(appId)
return mutableClient.updateNotes(appId, cubeName, notes)
}
@SuppressWarnings("GroovyUnusedDeclaration")
String getNotes(ApplicationID appId, String cubeName)
{
appId = addTenant(appId)
return mutableClient.getNotes(appId, cubeName)
}
/**
* In-place update of a cell.
*/
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean updateCell(ApplicationID appId, String cubeName, Object[] ids, CellInfo cellInfo)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
Set colIds = getCoordinate(ids)
if (cellInfo == null)
{
ncube.removeCellById(colIds)
}
else
{
ncube.setCellById(cellInfo.recreate(), colIds)
}
mutableClient.updateCube(ncube)
return true
}
@SuppressWarnings("GroovyUnusedDeclaration")
Boolean updateCellAt(ApplicationID appId, String cubeName, Map coordinate, CellInfo cellInfo)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
if (cellInfo == null)
{
ncube.removeCell(coordinate)
}
else
{
ncube.setCell(cellInfo.recreate(), coordinate)
}
mutableClient.updateCube(ncube)
return true
}
Map getCell(ApplicationID appId, String cubeName, Map coordinate, defaultValue = null)
{
verifyAllowExecute('getCell')
appId = addTenant(appId)
Map output = runtimeClient.getCell(appId, cubeName, coordinate, defaultValue)
return output
}
@SuppressWarnings("GroovyUnusedDeclaration")
Object getCellNoExecute(ApplicationID appId, String cubeName, Object[] ids)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
Set colIds = getCoordinate(ids)
Object cell = ncube.getCellByIdNoExecute(colIds)
CellInfo cellInfo = new CellInfo(cell)
cellInfo.collapseToUiSupportedTypes()
return cellInfo
}
@SuppressWarnings("GroovyUnusedDeclaration")
Object getCellNoExecuteByCoordinate(ApplicationID appId, String cubeName, Map coordinate)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
Object cell = ncube.getCellNoExecute(coordinate)
CellInfo cellInfo = new CellInfo(cell)
cellInfo.collapseToUiSupportedTypes()
return cellInfo
}
/**
* This API will fetch and execute particular cell values (identified by the idArrays) for the passed
* in appId and named cube. The idArrays is an Object[] of Object[]'s:
* [
* [1, 2, 3],
* [4, 5, 6],
* [7, 8, 9],
* ...
*]
* In the example above, the 1st entry [1, 2, 3] identifies the 1st cell to fetch. The 2nd entry [4, 5, 6]
* identifies the 2nd cell to fetch, and so on.
*
* @return Object[] The return value is an Object[] containing Object[]'s with the original coordinate
* as the first entry and the executed cell value as the 2nd entry:
* [
* [[1, 2, 3], {"type":"int", "value":75}],
* [[4, 5, 6], {"type":"double", "value":3.14159}],
* [[7, 8, 9], {"type":"string", "value":"hello"}],
* ...
* ]
*
*/
Object[] getCells(ApplicationID appId, String cubeName, Object[] idArrays, Map input, Object defaultValue = null)
{
verifyAllowExecute("getCells")
appId = addTenant(appId)
Object[] ret = runtimeClient.getCells(appId, cubeName, idArrays, input, [:], defaultValue)
return ret
}
/**
* This API will fetch particular cell values (identified by the idArrays) for the passed
* in appId and named cube. The idArrays is an Object[] of Object[]'s:
* [
* [1, 2, 3],
* [4, 5, 6],
* [7, 8, 9],
* ...
*]
* In the example above, the 1st entry [1, 2, 3] identifies the 1st cell to fetch. The 2nd entry [4, 5, 6]
* identifies the 2nd cell to fetch, and so on.
*
* @return Object[] The return value is an Object[] containing Object[]'s with the original coordinate
* as the first entry and the cell value as the 2nd entry:
* [
* [[1, 2, 3], {"type":"int", "value":75}],
* [[4, 5, 6], {"type":"exp", "cache":false, "value":"return 25"}],
* [[7, 8, 9], {"type":"string", "value":"hello"}],
* ...
* ]
*
*/
@SuppressWarnings("GroovyUnusedDeclaration")
Object[] getCellsNoExecute(ApplicationID appId, String cubeName, Object[] idArrays)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
if (ncube == null)
{
throw new IllegalArgumentException("Unable to fetch requested cells. NCube: ${cubeName} not found, app: ${appId}")
}
Object[] ret = new Object[idArrays.length]
Set key = new HashSet()
int idx = 0
for (coord in idArrays)
{
for (item in coord)
{
key.add(convertToLong(item))
}
if (ncube.containsCellById(key))
{
CellInfo cellInfo = new CellInfo(ncube.getCellByIdNoExecute(key))
cellInfo.collapseToUiSupportedTypes()
ret[idx++] = [coord, cellInfo as Map]
}
else
{
ret[idx++] = [coord, NO_CELL]
}
key.clear()
}
return ret
}
@SuppressWarnings("GroovyUnusedDeclaration")
Map getCellCoordinate(ApplicationID appId, String cubeName, Object[] ids)
{
appId = addTenant(appId)
NCube ncube = getCubeInternal(appId, cubeName)
Set colIds = getCoordinate(ids)
Map coord = ncube.getDisplayCoordinateFromIds(colIds)
Map niceCoord = [:]
coord.each { k, v ->
Comparable c = v as Comparable
niceCoord[k] = CellInfo.formatForDisplay(c)
}
return niceCoord
}
@SuppressWarnings("GroovyUnusedDeclaration")
String copyCells(ApplicationID appId, String cubeName, Object[] ids, boolean isCut)
{
appId = addTenant(appId)
if (ids == null || ids.length == 0)
{
throw new IllegalArgumentException("No IDs of cells to cut/clear were given.")
}
NCube ncube = getCubeInternal(appId, cubeName)
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy