Skip to content

Commit 5030e38

Browse files
anshulsaoclaude
andauthored
feat(datastore/mongo): logical flavour (#362)
* feat(datastore/mongo): add reference flavour — passthrough of an existing mongo datastore's outputs (no resources) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PKZUrLtUbJS5GuEyRA8Vd5 * refactor(datastore/mongo): rename reference flavour to logical "reference" clashes with Facets' cross-blueprint reference concept. "logical" = a logical database hosted on a shared physical instance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PKZUrLtUbJS5GuEyRA8Vd5 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b7c9e55 commit 5030e38

5 files changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
intent: mongo
2+
flavor: logical
3+
version: '1.0'
4+
clouds:
5+
- aws
6+
- gcp
7+
- azure
8+
description: >
9+
Logical — a logical database hosted on an existing / shared mongo /
10+
DocumentDB instance; re-exposes its connection outputs (no resources created).
11+
intentDetails:
12+
type: Datastores
13+
description: Logical database on an existing / shared MongoDB-compatible instance
14+
displayName: MongoDB
15+
iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/mongo.svg
16+
spec:
17+
title: MongoDB Logical Database
18+
description: >
19+
Logical — a logical database hosted on an existing / shared mongo /
20+
DocumentDB instance; re-exposes its connection outputs (no resources
21+
created). Useful for consolidating multiple logical databases onto a single
22+
physical cluster (e.g. shared staging DB).
23+
type: object
24+
x-ui-order:
25+
- source
26+
- database_name
27+
properties:
28+
source:
29+
type: object
30+
title: Source MongoDB Datastore
31+
description: >
32+
Select an existing mongo / DocumentDB datastore. Its full outputs
33+
(interfaces + attributes) are resolved into this field. Override per
34+
environment to point at the right physical cluster.
35+
x-ui-output-type: '@facets/mongo'
36+
database_name:
37+
type: string
38+
title: Logical Database Name
39+
description: >
40+
Optional logical database to target in the connection string. When set,
41+
the database name is injected into the re-exposed connection strings
42+
while preserving tls / replicaSet parameters.
43+
required:
44+
- source
45+
inputs: {}
46+
outputs:
47+
default:
48+
type: '@facets/mongo'
49+
title: MongoDB Reference
50+
iac:
51+
validated_files:
52+
- main.tf
53+
- variables.tf
54+
- locals.tf
55+
sample:
56+
kind: mongo
57+
flavor: logical
58+
version: '1.0'
59+
disabled: true
60+
spec:
61+
source: {}
62+
database_name: app
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# locals.tf - Computations for the mongo reference (passthrough) flavour.
2+
# No cloud resources are created by this flavour.
3+
4+
locals {
5+
source_interfaces = lookup(var.instance.spec.source, "interfaces", {})
6+
7+
writer = lookup(local.source_interfaces, "writer", {})
8+
reader = lookup(local.source_interfaces, "reader", {})
9+
cluster = lookup(local.source_interfaces, "cluster", {})
10+
11+
database_name = lookup(var.instance.spec, "database_name", null)
12+
has_database = local.database_name != null && local.database_name != ""
13+
14+
# A mongo connection string looks like:
15+
# mongodb://user:pass@host:port/<db>?tls=true&replicaSet=rs0&...
16+
# To target a logical database we splice <db> between the authority and the
17+
# query string, preserving the "?..." params (tls / replicaSet / etc.).
18+
rewrite_db = {
19+
for k, cs in {
20+
writer = lookup(local.writer, "connection_string", null)
21+
reader = lookup(local.reader, "connection_string", null)
22+
cluster = lookup(local.cluster, "connection_string", null)
23+
} : k => (
24+
cs == null ? null :
25+
!local.has_database ? cs :
26+
length(split("?", cs)) > 1 ?
27+
format(
28+
"%s/%s?%s",
29+
replace(split("?", cs)[0], "/\\/[^/]*$/", ""),
30+
local.database_name,
31+
join("?", slice(split("?", cs), 1, length(split("?", cs))))
32+
) :
33+
format("%s/%s", replace(cs, "/\\/[^/]*$/", ""), local.database_name)
34+
)
35+
}
36+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# main.tf - Reference flavour for mongo.
2+
#
3+
# This flavour creates NO cloud resources and requires NO provider. It is a pure
4+
# passthrough that re-exposes the outputs of an already-provisioned mongo /
5+
# DocumentDB datastore selected via spec.source. See locals.tf for the contract
6+
# re-emission and outputs.tf for the exposed interfaces.
7+
8+
terraform {
9+
required_version = ">= 1.0"
10+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
locals {
2+
output_attributes = lookup(var.instance.spec.source, "attributes", {})
3+
4+
# Pure passthrough of the referenced datastore's mongo contract. Absent fields
5+
# surface as null; when database_name is set, connection strings and the
6+
# logical name target that database (query params such as tls / replicaSet are
7+
# preserved by local.rewrite_db).
8+
output_interfaces = {
9+
writer = {
10+
host = lookup(local.writer, "host", null)
11+
port = lookup(local.writer, "port", null)
12+
username = lookup(local.writer, "username", null)
13+
password = lookup(local.writer, "password", null)
14+
connection_string = local.rewrite_db["writer"]
15+
name = local.has_database ? local.database_name : lookup(local.writer, "name", null)
16+
secrets = lookup(local.writer, "secrets", ["password", "connection_string"])
17+
}
18+
reader = {
19+
host = lookup(local.reader, "host", null)
20+
port = lookup(local.reader, "port", null)
21+
username = lookup(local.reader, "username", null)
22+
password = lookup(local.reader, "password", null)
23+
connection_string = local.rewrite_db["reader"]
24+
name = local.has_database ? local.database_name : lookup(local.reader, "name", null)
25+
secrets = lookup(local.reader, "secrets", ["password", "connection_string"])
26+
}
27+
cluster = {
28+
endpoint = lookup(local.cluster, "endpoint", null)
29+
username = lookup(local.cluster, "username", null)
30+
password = lookup(local.cluster, "password", null)
31+
connection_string = local.rewrite_db["cluster"]
32+
secrets = lookup(local.cluster, "secrets", ["password", "connection_string"])
33+
}
34+
}
35+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
variable "instance" {
2+
description = "Reference an existing MongoDB / DocumentDB datastore (passthrough, no resources)."
3+
type = object({
4+
kind = string
5+
flavor = string
6+
version = string
7+
spec = object({
8+
# Full outputs of the selected source mongo datastore, resolved by the
9+
# platform from the spec field's x-ui-output-type ('@facets/mongo').
10+
source = object({
11+
attributes = optional(object({}), {})
12+
interfaces = optional(object({
13+
writer = optional(object({
14+
host = optional(string)
15+
port = optional(string)
16+
username = optional(string)
17+
password = optional(string)
18+
connection_string = optional(string)
19+
name = optional(string)
20+
secrets = optional(list(string), [])
21+
}), {})
22+
reader = optional(object({
23+
host = optional(string)
24+
port = optional(string)
25+
username = optional(string)
26+
password = optional(string)
27+
connection_string = optional(string)
28+
name = optional(string)
29+
secrets = optional(list(string), [])
30+
}), {})
31+
cluster = optional(object({
32+
endpoint = optional(string)
33+
username = optional(string)
34+
password = optional(string)
35+
connection_string = optional(string)
36+
secrets = optional(list(string), [])
37+
}), {})
38+
}), {})
39+
})
40+
# Optional logical database to target in the re-exposed connection string.
41+
database_name = optional(string)
42+
})
43+
})
44+
}
45+
46+
variable "instance_name" {
47+
description = "The architectural name of the resource as added in the blueprint."
48+
type = string
49+
default = ""
50+
}
51+
52+
variable "environment" {
53+
description = "Environment-level metadata injected by the platform."
54+
type = object({
55+
name = optional(string)
56+
unique_name = optional(string)
57+
namespace = optional(string)
58+
})
59+
default = {}
60+
}
61+
62+
variable "inputs" {
63+
description = "No dependency inputs — this flavour is a pure passthrough."
64+
type = object({})
65+
default = {}
66+
}

0 commit comments

Comments
 (0)