Package io.inversion

Class Collection

All Implemented Interfaces:
Serializable, Comparable<Collection>

public class Collection extends Rule<Collection> implements Serializable
Represents a REST Collection and maps JSON properties property names and logical cross Collection data relationships to underlying Db tables and column names.

Api users interact with Collections and their JSON representation while Inversion abstracts the details of of the storage implementations.

Collections can remap ugly legacy column names to pretty JSON friendly camelCase names, and Collection Relationships can be used to create logical traversable foreign keys between Collections with the same underlying Db or even between Collections with different backend storage systems.

Generally it is the job of a Db to reflect on its underlying data source and automatically configure Collections and the associated Relationships that will be accessed and manipulated by Api caller.

The Engine inspects the inbound Request path and attempts to match a Collection to the call.

The default mapping would be: /${endpointPath}/[${collection}]/[${resource}]/[${relationship}]

Querying "/${endpointPath}/${collection}" would typically result an a paginated list of resources ie. rows from your underlying Db translated into JSON speak.

Querying "/${endpointPath}/${collection}/${resource}" will fetch a single resource or row.

Querying "/${endpointPath}/${collection}/${resource}/${relationship}" will fetch all members from the relationship target Collection that are related to resource.

RestGet/Post/Put/Patch/DeleteAction are responsible for handling basic Rest semantics for interacting with Dbs via Collections.

TODO: check on test cases related to hasName and path matching TODO: need tests for resource keys with commas

See Also:
  • Field Details

    • aliases

      protected final Set<String> aliases
      Additional names that should cause this Collection to match to a Request.

      For example, in an e-commerce environment, you may overload the "orders" collection with aliases "cart", "basket", and "bag".

    • properties

      protected final ArrayList<Property> properties
      Properties map database column names to JSON property names.
    • indexes

      protected final ArrayList<Index> indexes
      Representation of underlying Db datasource indexes.
    • relationships

      protected final ArrayList<Relationship> relationships
      Relationships like resources in one collection to the resources in another collection.
    • db

      protected transient Db db
      The backend storage adapter that probably generated this Collection and associated Indexes and Relationships.
    • tableName

      protected String tableName
      The backend datasource name that this Collection operates on.

      The tableName might be "ORDER_DETAIL" but the Collection might be named "orderDetails".

    • exclude

      protected boolean exclude
      Set this to true to prevent it from being automatically exposed through your Api.
  • Constructor Details

    • Collection

      public Collection()
    • Collection

      public Collection(String defaultName)
  • Method Details

    • encodeResourceKey

      public static String encodeResourceKey(Map values, Index index)
      Encodes the potentially multiple values of an index into a url path and query string safe single value.

      In a typical REST Api configuration where you url paths might map to something like "${endpoint}/${collection}/[${resource}][?{querystring}]", ${resource} is the primary index of the resource that has been encoded here.

      That might look like "/bookstore/books/12345" or in the case of a compound primary index It might look like "/bookstore/orders/4567~abcde" where the "~" character is used to separate parts of the key.

      The names of the index fields are not encoded, only the values, relying on index property order to remain consistent.

      This methods is used by various actions when constructing hypermedia urls that allow you to uniquely identify individual resources (records in a Db) or to traverse Relationships.

      The inverse of this method is decodeResourceKeys(Index, String) which is used to decode inbound Url path and query params to determine which resource is being referenced.

      Parameters:
      values - column name to Property value mapping for a resource
      index - the index identifying the values that should be encoded
      Returns:
      a url safe encoding of the index values separated by "~" characters or null if any of the values for an index key is null.
      See Also:
    • encodeResourceKey

      public static String encodeResourceKey(List pieces)
      Creates a "~" separated url safe concatenation of pieces
      Parameters:
      pieces - key parts to be encoded
      Returns:
      a url safe encoding of the pieces separated by "~" characters
      See Also:
    • main

      public static void main(String[] args)
    • encodeStr

      public static String encodeStr(String string)
      Encodes non url safe characters into a friendly "@FOUR_DIGIT_HEX_VALUE" equivalent that itself will not be modified by URLEncoder.encode(String).

      For example, encodeing "abcd/efg" would result in "abcd@002fefg" where "@002f" is the hex encoding for "/".

      While "~" characters are considered url safe, the are specifically included for encoding so that decodeResourceKeys(Index, String) can split a value on "~" before decoding its parts.

      Parameters:
      string - the string to encode
      Returns:
      a url safe string with non safe characters encoded as '@FOUR_DIGIT_HEX_VALUE'
      See Also:
    • decodeStr

      public static String decodeStr(String string)
      The reciprocal of encodeStr(String) that replaces "\@[0-9a-f]{4}" hex sequences with the unescaped oritional unescaped character.
      Parameters:
      string - the string to decode
      Returns:
      a string with characters escaped to their hex equivalent replaced with the unescaped value.
      See Also:
    • getDefaultIncludeMatch

      protected Rule.RuleMatcher getDefaultIncludeMatch()
      Description copied from class: Rule
      Designed to allow subclasses to provide a default match behavior of no configuration was provided by the developer.
      Overrides:
      getDefaultIncludeMatch in class Rule<Collection>
      Returns:
      the default collection match rule: "{_collection:" + getName() + "}/[:_resource]/[:_relationship]/*"
      See Also:
    • isLinkTbl

      public boolean isLinkTbl()
      Returns true if all columns are foreign keys.

      In an RDBMS system, this would indicate that the table is used to link both sides of a many-to-many relationship and it should NOT be a public REST Collection.

      Returns:
      the true if all columns are foreign keys.
    • getProperty

      public Property getProperty(String jsonOrColumnName)
      Convenience overload of findProperty(String).
      Parameters:
      jsonOrColumnName - the property to get
      Returns:
      the Property with a case insensitive json name or column name match.
      See Also:
    • findProperty

      public Property findProperty(String jsonOrColumnName)
      Finds the property with case insensitive jsonOrColumnName.

      The algo tries to find a matching json property name first before relooping over the props looking of a column name match.

      Parameters:
      jsonOrColumnName - the property to find
      Returns:
      the Property with a case insensitive json name or column name match.
    • getPropertyByJsonName

      public Property getPropertyByJsonName(String jsonName)
      Find the property with case insensitive jsonName
      Parameters:
      jsonName - the name of the property to get
      Returns:
      the property with jsonName
    • getPropertyByColumnName

      public Property getPropertyByColumnName(String columnName)
      Find the property with case insensitive columnName
      Parameters:
      columnName - the name of the property to get
      Returns:
      the property with columnName
    • getColumnName

      public String getColumnName(String jsonName)
    • equals

      public boolean equals(Object object)
      Overrides:
      equals in class Object
      Returns:
      true if object has the same Db and name as this Collection
    • getDb

      public Db getDb()
      Returns:
      the underlying Db
    • withDb

      public Collection withDb(Db db)
      Parameters:
      db - the db to set
      Returns:
      this
    • getTableName

      public String getTableName()
      Returns:
      the tableName backing this Collection in the Db.
    • withTableName

      public Collection withTableName(String name)
      Parameters:
      name - the name to set
      Returns:
      this
    • getName

      public String getName()
      Overrides:
      getName in class Rule<Collection>
      Returns:
      the name of the Collection defaulting to tableName if name is null.
    • getProperties

      public List<Property> getProperties()
      Returns:
      a shallow copy of properties
    • withProperties

      public Collection withProperties(Property... props)
      Adds the property definitions to this Collection.

      If there is an existing prop with a json name to json name match or a column name to column name match, the new prop will not be added as it conflicts with the existing one.

      Parameters:
      props - the properties to add
      Returns:
      this
    • withProperty

      public Collection withProperty(String name, String type)
      Fluent utility method for constructing a new Property and adding it to the Collection.
      Parameters:
      name - the name of the Property to add
      type - the type of the Property to add
      Returns:
      this
      See Also:
    • withProperty

      public Collection withProperty(String name, String type, boolean nullable)
      Fluent utility method for constructing a new Property and adding it to the Collection.
      Parameters:
      name - the name of the Property to add
      type - the type of the Property to add
      nullable - is the Property nullable
      Returns:
      this
      See Also:
    • removeProperty

      public void removeProperty(Property prop)
    • getPrimaryIndex

      public Index getPrimaryIndex()
      Finds the first unique Index with the fewest number of Properties.
      Returns:
      the Index that should be treated as the primary key for the Collection
      See Also:
    • getIndexByType

      public Index getIndexByType(String indexType)
      Parameters:
      indexType - the case insensative index type identifier
      Returns:
      the first index with type = indexType
    • getIndex

      public Index getIndex(String indexName)
      Gets an index by case insensitive name.
      Parameters:
      indexName - the name of the Index to get
      Returns:
      the requested Index
    • getIndexes

      public ArrayList<Index> getIndexes()
      Returns:
      a shallow copy of indexes
    • withIndexes

      public Collection withIndexes(Index... indexes)
    • withIndex

      public Collection withIndex(String name, String type, boolean unique, String... propertyNames)
      Fluent utility method for constructing and adding a new Index.

      If an Index with name exists it will be updated with the new information.

      All of the Properties in propertyNames must already exist.

      Parameters:
      name - the name of the Index to create/add
      type - the type of the Index to create/add
      unique - specifics if Index to create/add is unique
      propertyNames - the Properties that make up the index
      Returns:
      this
      See Also:
    • removeIndex

      public void removeIndex(Index index)
    • isExclude

      public boolean isExclude()
    • withExclude

      public Collection withExclude(boolean exclude)
    • getRelationship

      public Relationship getRelationship(String name)
      Parameters:
      name - the name of the Relationship to get
      Returns:
      the Relationship with a case insensitve name match
    • getRelationships

      public List<Relationship> getRelationships()
      Returns:
      a shallow copy of relationships.
    • removeRelationship

      public void removeRelationship(Relationship relationship)
    • withRelationships

      public Collection withRelationships(Relationship... relationships)
      Parameters:
      relationships - the relationships to set
      Returns:
      this
    • withRelationship

      public Collection withRelationship(Relationship relationship)
      Add a new Relationship if a Relationship with the same name does not already exist.
      Parameters:
      relationship - the Relationship to add
      Returns:
      this
    • withManyToOneRelationship

      public Collection withManyToOneRelationship(Collection parentCollection, String childPropertyName, String... childFkProps)
      Fluent utility method to construct a Relationship and associated Indexes.
      Parameters:
      parentCollection - the parent collection of the relationship being created
      childPropertyName - name of the json property that will hold this relationship reference
      childFkProps - names of the existing Properties that make up the foreign key
      Returns:
      this
      See Also:
    • withManyToOneRelationship

      public Collection withManyToOneRelationship(Collection parentCollection, String childPropertyName, Property... childFkProps)
      Fluent utility method to construct a Relationship and associated Indexes.

      In addition to the new Relationship a new foreign key Index will be created from childFkProps to parentCollection's primary Index.

      Parameters:
      parentCollection - the related parent Collection
      childPropertyName - what to call this relationship in the json representation of this Collection's resources.
      childFkProps - the Collections Properties that make up the foreign key
      Returns:
      this
    • withOneToManyRelationship

      public Collection withOneToManyRelationship(String parentPropertyName, Collection childCollection, String childPropertyName, String... childFkProps)
      Fluent utility method to construct a Relationship and associated Indexes.

      This is a convenience overload of withOneToManyRelationship(String, Collection, String, Property...) to be used when code wiring Apis and you don't want to lookup references to the actual Property objects.

      Parameters:
      parentPropertyName - the name of the json property for the parent that references the child
      childCollection - the target child collection
      childPropertyName - the name of hte json property for the child that references the parent
      childFkProps - names of the existing Properties that make up the foreign key
      Returns:
      this
      See Also:
    • withOneToManyRelationship

      public Collection withOneToManyRelationship(String parentPropertyName, Collection childCollection, String childPropertyName, Property... childFkProps)
      Fluent utility method to construct a Relationship and associated Indexes.

      In addition to the new Relationship a new foreign key Index will be created from childFkProps to this Collection's primary Index.

      Parameters:
      parentPropertyName - the name of the json property for the parent that references the child
      childCollection - the target child collection
      childPropertyName - the name of hte json property for the child that references the parent
      childFkProps - Properties that make up the foreign key
      Returns:
      this
    • hasName

      public boolean hasName(String nameOrAlias)
      Parameters:
      nameOrAlias - the name or alias to check for
      Returns:
      true if the name or aliases match
    • getAliases

      public Set<String> getAliases()
      Returns:
      a shallow clone of aliases
    • withAliases

      public Collection withAliases(String... aliases)
    • encodeResourceKey

      public String encodeResourceKey(Map<String,Object> values)
      Encodes the potentially multiple values of a resources primary index into a url path safe single value.
      Parameters:
      values - the key value pairs to encode
      Returns:
      a url safe encoding of the resources primary index values
      See Also:
    • decodeResourceKey

      public Rows.Row decodeResourceKey(String inKey)
      Decodes a resource key into its columnName / value parts.
      Parameters:
      inKey - the resource key to decode
      Returns:
      the decoded columnName / value pairs.
      See Also:
    • decodeResourceKeys

      public Rows decodeResourceKeys(String inKeys)
    • decodeResourceKey

      public Rows.Row decodeResourceKey(Index index, String inKey)
      Decodes a resource key into its columnName / value parts.
      Parameters:
      index - identifies the columnNames by position
      inKey - the encoded string to decode
      Returns:
      the decoded columnName / value pairs.
      See Also:
    • decodeResourceKeys

      public Rows decodeResourceKeys(Index index, String inKeys)
      Decodes a comma separated list of encoded resource keys.
      Parameters:
      index - identifies the columnNames to decode
      inKeys - a comma separated list of encoded resource keys
      Returns:
      a list of decoded columnName value pairs
      See Also:
    • copy

      public Collection copy()
      Performs a deep clone operation via object serialization/deserialization.

      It is useful when you want to manually wire up numerous copies of a collection but tweak each one a bit differently.

      For example, if you were connecting to a DynamoDb or CosmosDb where a single table is overloaded to support different domain objects.

      This feature requires Collection, Relationship and Index to be Serializable.

      The Db reference here is transient and reconnected to the clone so that this instance and the copy reference the same Db.

      Returns:
      a deep copy of this Collection referencing the same underlying Db instance.