use bson::{doc, Bson, Document};
use futures::TryStreamExt;
use mongodb::options::{IndexOptions, ReturnDocument};
use mongodb::{Client, Database, IndexModel};
use parse_rust_core::{
ClassLevelPermissions, ErrorCode, ParseError, ParseMap, ParseValue, DUPLICATE_VALUE_MESSAGE,
};
use parse_rust_schema::storage_format::{
field_type_to_storage, storage_to_field_type, NON_FIELD_KEYS,
};
use parse_rust_storage::{
join_table_name, AddFieldOutcome, ClassSchema, FieldType, Query, QueryOptions, Row,
SchemaIndex, SortDirection, StorageAdapter, Update, WriteResult,
};
use crate::transform::{
bson_document_to_parse_map, index_key_to_bson, mongo_object_to_parse,
parse_map_to_bson_document, parse_object_to_mongo_create, storage_key, transform_update,
transform_where,
};
const SCHEMA_COLLECTION: &str = "_SCHEMA";
const METADATA_CLASS_PERMISSIONS: &str = "_metadata.class_permissions";
const METADATA_INDEXES: &str = "_metadata.indexes";
const METADATA_FIELDS_OPTIONS: &str = "_metadata.fields_options";
const EMPTY_CLPS_KEYS: [&str; 8] = [
"find",
"count",
"get",
"create",
"update",
"delete",
"addField",
"protectedFields",
];
pub struct MongoAdapter {
db: Database,
}
impl MongoAdapter {
pub async fn connect(uri: &str, database: &str) -> Result<Self, ParseError> {
let client = Client::with_uri_str(uri).await.map_err(mongo_err)?;
Ok(Self {
db: client.database(database),
})
}
async fn schema_document(&self, class_name: &str) -> Result<Option<Document>, ParseError> {
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.find_one(doc! { "_id": class_name })
.await
.map_err(mongo_err)
}
async fn reservation_refused(
&self,
class_name: &str,
field_name: &str,
requested: &FieldType,
) -> Result<AddFieldOutcome, ParseError> {
if matches!(requested, FieldType::GeoPoint) {
let already = self
.schema_document(class_name)
.await?
.map(|d| {
d.iter()
.any(|(key, value)| key != field_name && value.as_str() == Some("geopoint"))
})
.unwrap_or(false);
if already {
return Err(ParseError::incorrect_type(
"MongoDB only supports one GeoPoint field in a class.".to_string(),
));
}
}
self.classify_existing_field(class_name, field_name, requested)
.await
}
async fn classify_existing_field(
&self,
class_name: &str,
field_name: &str,
requested: &FieldType,
) -> Result<AddFieldOutcome, ParseError> {
let stored = self
.schema_document(class_name)
.await?
.and_then(|d| d.get_str(field_name).ok().map(str::to_string));
match stored.as_deref().and_then(storage_to_field_type) {
Some(existing) if &existing == requested => Ok(AddFieldOutcome::AlreadyPresentSameType),
Some(existing) => Ok(AddFieldOutcome::Conflict { existing }),
None => Err(ParseError::invalid_json(format!(
"Could not add field {field_name}"
))),
}
}
}
fn is_duplicate_key(e: &mongodb::error::Error) -> bool {
match e.kind.as_ref() {
mongodb::error::ErrorKind::Write(mongodb::error::WriteFailure::WriteError(we)) => {
we.code == 11000
}
mongodb::error::ErrorKind::Command(ce) => ce.code == 11000,
_ => false,
}
}
fn is_namespace_not_found(e: &mongodb::error::Error) -> bool {
if let mongodb::error::ErrorKind::Command(ce) = e.kind.as_ref() {
return ce.code == 26;
}
false
}
fn is_transient(e: &mongodb::error::Error) -> bool {
if e.contains_label(mongodb::error::TRANSIENT_TRANSACTION_ERROR) {
return true;
}
matches!(
e.kind.as_ref(),
mongodb::error::ErrorKind::ServerSelection { .. }
| mongodb::error::ErrorKind::ConnectionPoolCleared { .. }
| mongodb::error::ErrorKind::Io(_)
)
}
fn driver_message(e: &mongodb::error::Error) -> Option<&str> {
match e.kind.as_ref() {
mongodb::error::ErrorKind::Write(mongodb::error::WriteFailure::WriteError(we)) => {
Some(we.message.as_str())
}
mongodb::error::ErrorKind::Command(ce) => Some(ce.message.as_str()),
_ => None,
}
}
fn field_options_path(field_name: &str) -> String {
format!("{METADATA_FIELDS_OPTIONS}.{field_name}")
}
fn schema_fields_document(schema: &ClassSchema) -> Document {
let mut out = Document::new();
for (name, ty) in &schema.fields {
let s = field_type_to_storage(ty);
if s.is_empty() {
continue;
}
out.insert(name.clone(), s);
}
out
}
fn schema_metadata_document(schema: &ClassSchema) -> Result<Document, ParseError> {
let mut out = Document::new();
if let Some(clp) = &schema.clp {
out.insert("class_permissions", parse_map_to_bson_document(clp.raw())?);
}
if let Some(indexes) = &schema.indexes {
out.insert("indexes", parse_map_to_bson_document(indexes)?);
}
if let Some(field_options) = &schema.field_options {
out.insert("fields_options", parse_map_to_bson_document(field_options)?);
}
Ok(out)
}
fn index_name(message: &str) -> Option<&str> {
let (_, rest) = message.split_once(" index: ")?;
rest.split_whitespace().next()
}
fn duplicated_field(message: &str) -> Option<String> {
let name = index_name(message)?;
if let Some(prefix) = name.strip_suffix("_1") {
let field = prefix.rsplit(['$', '.']).next().unwrap_or(prefix);
let recoverable = !field.is_empty()
&& field
.chars()
.all(|c| c.is_ascii_alphabetic() || c == '_' || c == '-');
if recoverable {
return Some(field.to_string());
}
}
if name.starts_with("_auth_data_") && name.ends_with("_id") {
return Some(name.to_string());
}
None
}
fn mongo_err(e: mongodb::error::Error) -> ParseError {
if is_duplicate_key(&e) {
let err = ParseError::new(ErrorCode::DuplicateValue, DUPLICATE_VALUE_MESSAGE);
return match driver_message(&e).and_then(duplicated_field) {
Some(field) => err.with_duplicated_field(field),
None => err,
};
}
if is_transient(&e) {
return ParseError::new(ErrorCode::InternalServerError, "Database error");
}
ParseError::internal(format!("storage error: {e}"))
}
fn merge_over_empty_clps(stored: &Document) -> Result<ParseMap, ParseError> {
let mut out = ParseMap::new();
for key in EMPTY_CLPS_KEYS {
out.insert(key.to_string(), ParseValue::Object(ParseMap::new()));
}
for (key, value) in stored {
out.insert(key.clone(), crate::transform::bson_to_parse_value(value)?);
}
Ok(out)
}
impl StorageAdapter for MongoAdapter {
async fn all_schemas(&self) -> Result<Vec<ClassSchema>, ParseError> {
let mut cursor = self
.db
.collection::<Document>(SCHEMA_COLLECTION)
.find(doc! {})
.await
.map_err(mongo_err)?;
let mut out = Vec::new();
while let Some(doc) = cursor.try_next().await.map_err(mongo_err)? {
let Some(class_name) = doc.get_str("_id").ok() else {
continue;
};
let mut schema = parse_rust_schema::default_schema(class_name);
for (key, value) in &doc {
if NON_FIELD_KEYS.contains(&key.as_str()) {
continue;
}
let Bson::String(type_str) = value else {
continue;
};
if let Some(ty) = storage_to_field_type(type_str) {
schema.fields.insert(key.clone(), ty);
}
}
if let Ok(metadata) = doc.get_document("_metadata") {
if let Ok(class_permissions) = metadata.get_document("class_permissions") {
schema.clp = Some(ClassLevelPermissions::from_map(merge_over_empty_clps(
class_permissions,
)?));
}
if let Ok(indexes) = metadata.get_document("indexes") {
schema.indexes = Some(bson_document_to_parse_map(indexes)?);
}
if let Ok(fields_options) = metadata.get_document("fields_options") {
schema.field_options = Some(bson_document_to_parse_map(fields_options)?);
}
}
out.push(schema);
}
Ok(out)
}
async fn insert_schema(&self, schema: &ClassSchema) -> Result<(), ParseError> {
let mut document = schema_fields_document(schema);
document.insert("_id", &schema.class_name);
let metadata = schema_metadata_document(schema)?;
if !metadata.is_empty() {
document.insert("_metadata", metadata);
}
match self
.db
.collection::<Document>(SCHEMA_COLLECTION)
.insert_one(document)
.await
{
Ok(_) => Ok(()),
Err(e) if is_duplicate_key(&e) => Err(ParseError::new(
ErrorCode::DuplicateValue,
"Class already exists.",
)),
Err(e) => Err(mongo_err(e)),
}
}
async fn upsert_schema(&self, schema: &ClassSchema) -> Result<(), ParseError> {
let mut set = schema_fields_document(schema);
for (key, value) in schema_metadata_document(schema)? {
let path = match key.as_str() {
"class_permissions" => METADATA_CLASS_PERMISSIONS,
"indexes" => METADATA_INDEXES,
_ => METADATA_FIELDS_OPTIONS,
};
set.insert(path, value);
}
if set.is_empty() {
let mut bare = Document::new();
bare.insert("_id", &schema.class_name);
return match self
.db
.collection::<Document>(SCHEMA_COLLECTION)
.insert_one(bare)
.await
{
Ok(_) => Ok(()),
Err(e) if is_duplicate_key(&e) => Ok(()),
Err(e) => Err(mongo_err(e)),
};
}
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(doc! { "_id": &schema.class_name }, doc! { "$set": set })
.upsert(true)
.await
.map_err(mongo_err)?;
Ok(())
}
async fn reserve_field(
&self,
class_name: &str,
field_name: &str,
field_type: &FieldType,
options: Option<&ParseMap>,
) -> Result<AddFieldOutcome, ParseError> {
let type_string = field_type_to_storage(field_type);
if type_string.is_empty() {
return Err(ParseError::invalid_json(format!(
"Could not add field {field_name}"
)));
}
let mut filter = Document::new();
filter.insert("_id", class_name);
filter.insert(field_name, doc! { "$exists": false });
let mut set = Document::new();
set.insert(field_name, &type_string);
if let Some(options) = options.filter(|o| !o.is_empty()) {
set.insert(
field_options_path(field_name),
parse_map_to_bson_document(options)?,
);
}
if matches!(field_type, FieldType::GeoPoint) {
let mut guarded = filter.clone();
guarded.insert(
"$expr",
doc! { "$eq": [ { "$size": { "$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$eq": ["$$this.v", &type_string] },
} } }, 0 ] },
);
let guarded_update = || async {
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(guarded.clone(), doc! { "$set": set.clone() })
.await
.map_err(mongo_err)
};
if guarded_update().await?.modified_count > 0 {
return Ok(AddFieldOutcome::Added);
}
let mut new_row = Document::new();
new_row.insert("_id", class_name);
new_row.insert(field_name, &type_string);
if let Some(options) = options.filter(|o| !o.is_empty()) {
new_row.insert(
"_metadata",
doc! { "fields_options": doc! {
field_name: parse_map_to_bson_document(options)?,
} },
);
}
return match self
.db
.collection::<Document>(SCHEMA_COLLECTION)
.insert_one(new_row)
.await
{
Ok(_) => Ok(AddFieldOutcome::Added),
Err(e) if is_duplicate_key(&e) => {
if guarded_update().await?.modified_count > 0 {
return Ok(AddFieldOutcome::Added);
}
self.reservation_refused(class_name, field_name, field_type)
.await
}
Err(e) => Err(mongo_err(e)),
};
}
let result = self
.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(filter, doc! { "$set": set })
.upsert(true)
.await;
match result {
Ok(r) if r.upserted_id.is_some() || r.modified_count > 0 => Ok(AddFieldOutcome::Added),
Ok(_) => {
self.reservation_refused(class_name, field_name, field_type)
.await
}
Err(e) if is_duplicate_key(&e) => {
self.reservation_refused(class_name, field_name, field_type)
.await
}
Err(e) => Err(mongo_err(e)),
}
}
async fn set_field_options(
&self,
class_name: &str,
field_name: &str,
options: &ParseMap,
) -> Result<(), ParseError> {
let mut filter = Document::new();
filter.insert("_id", class_name);
filter.insert(field_name, doc! { "$exists": true });
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(
filter,
doc! { "$set": { field_options_path(field_name): parse_map_to_bson_document(options)? } },
)
.await
.map_err(mongo_err)?;
Ok(())
}
async fn set_indexes(&self, class_name: &str, indexes: &ParseMap) -> Result<(), ParseError> {
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(
doc! { "_id": class_name },
doc! { "$set": { METADATA_INDEXES: parse_map_to_bson_document(indexes)? } },
)
.await
.map_err(mongo_err)?;
Ok(())
}
async fn set_class_permissions(
&self,
class_name: &str,
clp: Option<&ClassLevelPermissions>,
) -> Result<(), ParseError> {
let update = match clp {
Some(clp) => {
doc! { "$set": { METADATA_CLASS_PERMISSIONS: parse_map_to_bson_document(clp.raw())? } }
}
None => doc! { "$unset": { METADATA_CLASS_PERMISSIONS: "" } },
};
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(doc! { "_id": class_name }, update)
.await
.map_err(mongo_err)?;
Ok(())
}
async fn delete_class(&self, schema: &ClassSchema) -> Result<(), ParseError> {
let joins: Vec<String> = schema
.relation_fields()
.map(|(field, _)| join_table_name(&schema.class_name, field))
.collect();
if let Err(e) = self
.db
.collection::<Document>(&schema.class_name)
.drop()
.await
{
if !is_namespace_not_found(&e) {
return Err(mongo_err(e));
}
}
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.delete_one(doc! { "_id": &schema.class_name })
.await
.map_err(mongo_err)?;
for join in joins {
if let Err(e) = self.db.collection::<Document>(&join).drop().await {
if !is_namespace_not_found(&e) {
return Err(mongo_err(e));
}
}
}
Ok(())
}
async fn delete_fields(
&self,
schema: &ClassSchema,
fields: &[String],
) -> Result<(), ParseError> {
let mut column_unset = Document::new();
let mut existence: Vec<Bson> = Vec::new();
let mut schema_unset = Document::new();
for name in fields {
schema_unset.insert(name.clone(), Bson::Null);
schema_unset.insert(format!("{METADATA_FIELDS_OPTIONS}.{name}"), Bson::Null);
let column = match schema.field(name) {
Some(FieldType::Relation { .. }) => continue,
Some(FieldType::Pointer { .. }) => format!("_p_{name}"),
_ => name.clone(),
};
existence.push(Bson::Document(doc! { &column: { "$exists": true } }));
column_unset.insert(column, Bson::Null);
}
if !column_unset.is_empty() {
self.db
.collection::<Document>(&schema.class_name)
.update_many(doc! { "$or": existence }, doc! { "$unset": column_unset })
.await
.map_err(mongo_err)?;
}
if !schema_unset.is_empty() {
self.db
.collection::<Document>(SCHEMA_COLLECTION)
.update_one(
doc! { "_id": &schema.class_name },
doc! { "$unset": schema_unset },
)
.await
.map_err(mongo_err)?;
}
Ok(())
}
async fn create(&self, schema: &ClassSchema, row: &Row) -> Result<WriteResult, ParseError> {
let doc = parse_object_to_mongo_create(schema, row)?;
let object_id = doc
.get_str("_id")
.map_err(|_| ParseError::new(ErrorCode::MissingObjectId, "objectId is required"))?
.to_string();
self.db
.collection::<Document>(&schema.class_name)
.insert_one(doc)
.await
.map_err(mongo_err)?;
Ok(WriteResult { object_id })
}
async fn upsert_one(
&self,
schema: &ClassSchema,
query: &Query,
row: &Row,
) -> Result<(), ParseError> {
let filter = transform_where(schema, query)?;
let set = parse_object_to_mongo_create(schema, row)?;
if set.is_empty() {
return Err(ParseError::invalid_json(
"upsert requires at least one value".to_string(),
));
}
self.db
.collection::<Document>(&schema.class_name)
.update_one(filter, doc! { "$set": set })
.upsert(true)
.await
.map_err(mongo_err)?;
Ok(())
}
async fn find(
&self,
schema: &ClassSchema,
query: &Query,
options: &QueryOptions,
) -> Result<Vec<Row>, ParseError> {
let filter = transform_where(schema, query)?;
let collection = self.db.collection::<Document>(&schema.class_name);
let mut find = collection.find(filter);
if options.case_insensitive {
find = find.collation(
mongodb::options::Collation::builder()
.locale("en_US".to_string())
.strength(mongodb::options::CollationStrength::Secondary)
.build(),
);
}
if let Some(limit) = options.limit {
if limit == 0 {
return Ok(Vec::new());
}
find = find.limit(limit as i64);
}
if let Some(skip) = options.skip {
find = find.skip(skip as u64);
}
if !options.order.is_empty() {
let mut sort = Document::new();
for (key, dir) in &options.order {
let dir = match dir {
SortDirection::Ascending => 1,
SortDirection::Descending => -1,
};
sort.insert(storage_key(schema, key), dir);
}
find = find.sort(sort);
}
if let Some(keys) = &options.keys {
let mut projection = Document::new();
for k in keys {
projection.insert(storage_key(schema, k), 1);
}
for always in ["_rperm", "_wperm", "_created_at", "_updated_at"] {
projection.insert(always, 1);
}
find = find.projection(projection);
}
let mut cursor = find.await.map_err(mongo_err)?;
let mut out = Vec::new();
while let Some(doc) = cursor.try_next().await.map_err(mongo_err)? {
out.push(mongo_object_to_parse(schema, &doc)?);
}
Ok(out)
}
async fn count(&self, schema: &ClassSchema, query: &Query) -> Result<u64, ParseError> {
let filter = transform_where(schema, query)?;
self.db
.collection::<Document>(&schema.class_name)
.count_documents(filter)
.await
.map_err(mongo_err)
}
async fn update(
&self,
schema: &ClassSchema,
query: &Query,
update: &Update,
) -> Result<u64, ParseError> {
let filter = transform_where(schema, query)?;
let compiled = transform_update(schema, update)?;
if compiled.is_empty() {
return Ok(0);
}
let res = self
.db
.collection::<Document>(&schema.class_name)
.update_many(filter, compiled)
.await
.map_err(mongo_err)?;
Ok(res.matched_count)
}
async fn update_one_returning(
&self,
schema: &ClassSchema,
query: &Query,
update: &Update,
) -> Result<Option<Row>, ParseError> {
let filter = transform_where(schema, query)?;
let compiled = transform_update(schema, update)?;
if compiled.is_empty() {
return Ok(None);
}
let collection = self.db.collection::<Document>(&schema.class_name);
let found = collection
.find_one_and_update(filter, compiled)
.return_document(ReturnDocument::After)
.await
.map_err(mongo_err)?;
found
.as_ref()
.map(|doc| mongo_object_to_parse(schema, doc))
.transpose()
}
async fn delete(&self, schema: &ClassSchema, query: &Query) -> Result<u64, ParseError> {
let filter = transform_where(schema, query)?;
let res = self
.db
.collection::<Document>(&schema.class_name)
.delete_many(filter)
.await
.map_err(mongo_err)?;
Ok(res.deleted_count)
}
async fn ensure_index(
&self,
class_name: &str,
fields: &[&str],
name: Option<&str>,
unique: bool,
case_insensitive: bool,
) -> Result<(), ParseError> {
let mut keys = Document::new();
for f in fields {
keys.insert(f.to_string(), 1);
}
let mut opts = IndexOptions::builder().unique(unique).sparse(true).build();
opts.name = name.map(str::to_string);
if case_insensitive {
opts.collation = Some(
mongodb::options::Collation::builder()
.locale("en_US".to_string())
.strength(mongodb::options::CollationStrength::Secondary)
.build(),
);
}
self.db
.collection::<Document>(class_name)
.create_index(IndexModel::builder().keys(keys).options(opts).build())
.await
.map_err(mongo_err)?;
Ok(())
}
async fn create_indexes(
&self,
class_name: &str,
indexes: &[SchemaIndex],
) -> Result<(), ParseError> {
if indexes.is_empty() {
return Ok(());
}
let mut models = Vec::with_capacity(indexes.len());
for index in indexes {
let mut keys = Document::new();
for (field, direction) in &index.keys {
keys.insert(
field.clone(),
index_key_to_bson(&index.name, field, direction)?,
);
}
let mut opts = IndexOptions::default();
opts.name = Some(index.name.clone());
models.push(IndexModel::builder().keys(keys).options(opts).build());
}
self.db
.collection::<Document>(class_name)
.create_indexes(models)
.await
.map_err(mongo_err)?;
Ok(())
}
async fn drop_index(&self, class_name: &str, name: &str) -> Result<(), ParseError> {
self.db
.collection::<Document>(class_name)
.drop_index(name)
.await
.map_err(mongo_err)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn clp_doc() -> Document {
doc! { "find": { "*": true }, "protectedFields": { "*": ["email"] } }
}
fn e11000(namespace: &str, index: &str, field: &str, value: &str) -> String {
format!(
"E11000 duplicate key error collection: {namespace} index: {index} dup key: {{ {field}: \"{value}\" }}"
)
}
#[test]
fn the_auto_generated_index_name_yields_its_field() {
for (index, field) in [
("username_1", "username"),
("email_1", "email"),
("name_1", "name"),
] {
let message = e11000("appdb._User", index, field, "alice");
assert_eq!(duplicated_field(&message).as_deref(), Some(field));
}
}
#[test]
fn the_legacy_namespace_qualified_index_name_yields_its_field() {
let message =
"E11000 duplicate key error index: appdb.$username_1 dup key: { : \"alice\" }";
assert_eq!(duplicated_field(message).as_deref(), Some("username"));
}
#[test]
fn the_auth_data_index_name_is_carried_whole() {
let message = e11000(
"appdb._User",
"_auth_data_facebook_id",
"authData.facebook.id",
"7",
);
assert_eq!(
duplicated_field(&message).as_deref(),
Some("_auth_data_facebook_id")
);
}
#[test]
fn an_index_name_outside_the_two_shapes_yields_nothing() {
for index in [
"case_insensitive_username",
"username_2",
"field1_1", "_id_",
] {
let message = e11000("appdb._User", index, "username", "alice");
assert_eq!(duplicated_field(&message), None, "{index}");
}
assert_eq!(duplicated_field("some unrelated driver text"), None);
}
#[test]
fn nothing_but_the_field_name_survives_the_parse() {
let message = e11000("secret_prod_db._User", "username_1", "username", "alice");
let field = duplicated_field(&message).expect("field");
assert!(!field.contains("secret_prod_db"));
assert!(!field.contains("alice"));
}
#[test]
fn a_present_clp_block_merges_over_empty_clps() {
let merged = merge_over_empty_clps(&clp_doc()).expect("merge");
for key in EMPTY_CLPS_KEYS {
assert!(merged.contains_key(key), "{key} must be present");
}
assert!(
matches!(merged.get("update"), Some(ParseValue::Object(m)) if m.is_empty()),
"an unmentioned operation reads back as deny-all, not as absent"
);
assert!(
matches!(merged.get("find"), Some(ParseValue::Object(m)) if m.contains_key("*")),
"the stored value wins over the empty base"
);
assert!(
!merged.contains_key("ACL"),
"the present-but-partial case never carries an ACL key"
);
}
#[test]
fn the_merged_block_keeps_empty_clps_key_order() {
let merged =
merge_over_empty_clps(&doc! { "delete": { "*": true }, "later": {} }).expect("merge");
let keys: Vec<&str> = merged.keys().map(String::as_str).collect();
assert_eq!(&keys[..8], &EMPTY_CLPS_KEYS);
assert_eq!(
keys[8], "later",
"a key only the stored block has goes last"
);
}
#[test]
fn a_clp_block_round_trips_through_bson() {
let mut stored = clp_doc();
stored.insert("someFutureKey", doc! { "x": 1 });
let merged = merge_over_empty_clps(&stored).expect("merge");
let clp = ClassLevelPermissions::from_map(merged);
let written = parse_map_to_bson_document(clp.raw()).expect("lower");
assert_eq!(
written.get_document("someFutureKey").expect("kept"),
&doc! { "x": 1 }
);
assert!(written.get_document("update").expect("update").is_empty());
}
}