Collection Operation Configuration¶
Collection
s support configuration at multiple
levels with two types of configuration: Collection
-level configuration,
which may have implications for certain Collection operations, and
CollectionOperation
-specific configs.
CollectionOperation
-specific configs should be set on the Collection
. If
they are omitted, and an operation is enabled, they will be instantiated as if
they were initialized as {}
using the appropriate
OperationConfig subclass (e.g. if
insertConfig
is left as undefined
,
it will be instantiated as o({}, this.InsertConfigClass)
, where
this.InsertConfigClass
is a class member that allows subclasses of
Collection
to override the default config class).
Operations should be configured with the following properties:
insertConfig
findConfig
saveConfig
updateConfig
removeConfig
insertObjectConfig
findObjectConfig
saveObjectConfig
updateObjectConfig
removeObjectConfig
When instantiating an instance of Collection
,
you can configure each operation using the config property for that operation
(e.g. insertConfig
for the
insert
operation). When defining the config, you can either explicitly
instantiate a config instance using the appropriate config class (e.g.,
InsertConfig
for the insert
operation):
insertConfig: o({
_type: carbond.collections.MyCustomInsertConfig,
description: "My collection's insert operation",
parameters: {
foo: {
name: "foo",
location: "query",
schema: {
type: "string"
}
}
}
})
Or you can simply define an object that will be instantiated using a default
config class for that operation (in this case,
InsertConfig
by way of
InsertConfigClass
):
insertConfig: {
description: "My collection's insert operation",
parameters: {
foo: {
name: "foo",
location: "query",
schema: {
type: "string"
}
}
}
}
Subclasses that require additional parameters for certain operations or that can
not support certain features (e.g., returning removed objects), should subclass
the individual config classes and override these member properties in the
subclass. When the subclass is instantiated, it will use these overridden config
classes instead of the default ones as defined on
Collection
.
The OperationConfig class¶
CollectionOperationConfig
is the base class for
all Collection configs. It defines basic properties that are common to all
operation configs like:
InsertConfig¶
The InsertConfig
class is the base insert
operation config class and the default for
Collection
. It can be used to configure whether
or not inserted objects are returned
(returnsInsertedObjects
) in the
response body and to define a schema separate from the collection level schema
that will be used to verify incoming objects
(schema
).
...
insertConfig: {
description: 'My collection insert operation',
returnsInsertedObjects: false,
schema: {
type: 'array',
items: {
type: 'object',
properties: {
foo: {type: 'boolean'},
bar: {type: 'number'}
},
required: ['foo'],
additionalProperties: {type: 'string'}
}
}
},
...
In the previous example, the insert
operation on this collection will not
return the objects that were inserted and each incoming object must contain at
least the foo
property.
FindConfig¶
The FindConfig
class is the base find
operation config class and the default for
Collection
. By default, the find
operation
supports ID queries and pagination. ID queries on the find
operation are
used by Carbon to communicate the location (i.e., the Location
header) of
objects in response to a bulk insert. If this is disabled, then the insert
operation should likely be disabled as well (note, insertObject
makes use of
the ID path parameter instead). Enabling pagination adds the page
parameter
to the list of parameters for the collection operation. Note, the find
operation
parameter list includes two more parameters: skip
and limit
. When
pagination is enabled, it will be transparent to the operation handlers
themselves. Instead, Collection
will update
options.skip
and options.limit
to reflect the page start and size. This
allows the handler to be implemented without concern for whether pagination is
enabled or not.
...
findConfig: {
description: 'My collection find operation',
supportsIdQuery: false,
supportsPagination: false
},
...
In the previous example, ID queries and pagination are disabled. This will
result in the omission of both parameters from the collection operation
parameters list that are used to support these features (note, skip
and
limit
will still be present, but page
will not be honored).
SaveConfig¶
The SaveConfig
class is the base save
operation config class and the default for
Collection
. Similar to
InsertConfig
, this config class allows you to
specify a separate schema for the objects being saved and whether or not the
objects saved are returned in the response.
...
saveConfig: {
description: 'My collection save operation',
returnsSavedObjects: false,
schema: {
type: 'object',
properties: {
_id: {type: 'string'},
foo: {type: 'boolean'},
bar: {type: 'number'}
},
required: ['_id', 'foo'],
additionalProperties: {type: 'string'}
}
},
...
Note, unlike schema
, it is
necessary to specify the ID parameter (_id
in this case) on schema
.
Note, it should have the same name as
idParameterName
or an error will be thrown
on initialization.
UpdateConfig¶
The UpdateConfig
class is the base update
operation config class and the default for
Collection
. It allows you to configure an
update schema, whether or not upserts are supported, and whether upserted
objects are returned in the response body. The update schema is “loose” by
default and only specifies that it should be an object
. This should be
tailored depending on the update scheme that your collection/datastore
understands (e.g., json patch). If upserts are enabled, an upsert
parameter will be added to the list of parameters for the collection operation.
...
updateConfig: {
description: 'My collection update operation',
supportsUpsert: false,
schema: {
oneOf: [
{
type: 'object',
properties: {
inc: {
type: object,
minProperties: 1,
additionalProperties: {
type: 'integer',
minimum: 1
}
},
requiredProperties: ['inc'],
additionalProperties: false
},
{
type: 'object',
dec: {
type: object,
minProperties: 1,
additionalProperties: {
type: 'integer',
minimum: 1
}
},
requiredProperties: ['dec'],
additionalProperties: false
}
}
]
}
}
...
The example config above disallows upserts and specifies a simple schema that
allows updates to increment or decrement properties in the collection by an
arbitrary amount (e.g. {inc: {foo: 5}}
or {dec: {foo: 1}}
).
RemoveConfig¶
The RemoveConfig
class is the base remove
operation config class and the default for
Collection
. It allows you to configure whether
or not removed objects are returned.
...
removeConfig: {
description: 'My collection remove operation',
returnsRemovedObjects: true
}
...
InsertObjectConfig¶
The InsertObjectConfig
class is the base
insertObject
operation config class and the default for
Collection
. This config follows the same
pattern as InsertConfig
, allowing you to
configure a schema specific to this operation and to specify whether the
inserted object should be returned or not.
...
insertObjectConfig: {
description: 'My collection insertObject operation',
returnsInsertedObject: false,
schema: {
type: 'object',
properties: {
foo: {type: 'boolean'},
bar: {type: 'number'}
},
required: ['foo'],
additionalProperties: {type: 'string'}
}
},
...
FindObjectConfig¶
The FindObjectConfig
class is the base findObject
operation config class and the default for
Collection
.
...
findObjectConfig: {
description: 'My collection findObject operation'
},
...
SaveObjectConfig¶
The SaveObjectConfig
class is the base
saveObject
operation config class and the default for
Collection
. Like previous config classes, it
allows you to set a specific schema for the incoming object (again, an ID
property is required). Additionally, like the update
config class, the
saveObject
operation can be configured to create objects in the collection
(this is the default) and to return the object in the response to the client.
Note, unlike saveObject
, the save
operation never “creates” objects
since it is an operation at the collection level. Instead, it “replaces” the
collection.
...
saveObjectConfig: {
description: 'My collection saveObject operation',
supportsUpsert: false,
returnsSavedObject: false
}
...
UpdateObjectConfig¶
The UpdateObjectConfig
class is the base
updateObject
operation config class and the default for
Collection
. Like
UpdateConfig
, this config allows you to specify
an update spec schema, whether or not upserts are allowed, and, if they are
allowed, whether an object is returned if an upsert takes place.
...
updateObjectConfig: {
description: 'My collection updateObject operation',
supportsUpsert: true,
returnsUpsertedObject: true,
schema: {
oneOf: [
{
type: 'object',
properties: {
inc: {
type: object,
minProperties: 1,
additionalProperties: {
type: 'integer',
minimum: 1
}
},
requiredProperties: ['inc'],
additionalProperties: false
},
{
type: 'object',
dec: {
type: object,
minProperties: 1,
additionalProperties: {
type: 'integer',
minimum: 1
}
},
requiredProperties: ['dec'],
additionalProperties: false
}
}
]
}
},
...
RemoveObjectConfig¶
The RemoveObjectConfig
class is the base
removeObject
operation config class and the default for
Collection
. This config offers essentially the
same configuration parameters as
RemoveObjectConfig
.
...
removeObjectConfig: {
description: 'My collection remove operation',
returnsRemovedObject: true
},
...
Collection Operation Parameter and Response configuration (Advanced)¶
For the most part, you should not need to alter the parameter or response specs
themselves when instantiating a Collection
. The
Collection
class (or a subclass) and the
CollectionOperationConfig
classes themselves
should provide the levers necessary to specify the appropriate
parameter/response schemas. The InsertConfig
class, for example, has the property
insertSchema
, which allows you to
specify a custom schema for the body of a POST
request that ends up getting
routed to the collection. If
insertSchema
is not configured, it
will default to schema
.
If you do find that you need to further configure parameters/responses beyond
what is available via either the config or collection classes themselves, you
can do this using the various operators available via the o
operator (e.g., $merge
, $delete
, etc.). See Atom
for an in-depth description of the available operators.
For instance, if you want to configure a custom description for the 201
response returned for a successful insert, you can set that using a property
path:
...
insertConfig: {
responses: {
'$201.description': 'Foo bar baz'
}
},
...
If you would prefer to override the 201
response wholesale, you can use the
$merge
operator:
...
insertConfig: {
responses: {
$merge: {
201: {
statusCode: 201,
description: 'Foo bar baz',
schema: {
type: 'object',
properties: {
foo: {type: 'string'},
bar: {type: 'string'}
},
additionalProperties: false,
required: ['foo']
},
headers: ['Location', 'baz']
}
}
}
},
...
Similarly, you can add custom parameters using the $merge
operator:
...
insertConfig: {
parameters: {
$merge: {
baz: {
description: 'Foo bar baz',
location: 'header',
schema: {type: 'string'},
default: 'yaz'
}
}
}
},
...
If you’re paying close attention, these examples should strike you as odd given
that the operators provided by o
only work if they are applied
to the top level properties of the object being instantiated. For example,
$merge
would not get applied in the following case:
var proto = {a: {b: {c: 1}}}
var instance = o({a: {b: {$merge: {d: 2}}}}, proto)
console.dir(instance, {depth: 4}) // => { a: { b: { '$merge': { d: 1 } } } }
Whereas, if the $merge
operator is chained, you would get the expected
result:
var proto = {a: {b: {c: 1}}}
var instance = o({a: {$merge: {b: {$merge: {d: 2}}}}}, proto)
console.dir(instance, {depth: 4}) // => { a: { b: { c: 0, d: 1 } } }
The reason, this works in the context of
Collection
s is because the instantiation of
the various CollectionOperationConfig
s is
handled by the Collection
class during
initialization after construction.