GenProjectConfig.parse constructor
Implementation
factory GenProjectConfig.parse(Map<String, Object?> config) {
final permissions = ((config['permissions'] as Map?) ?? const {})
.cast<String, Object?>();
({Set<String> serverAssigned, Set<String> redacted}) markersFor(
String modelName,
) {
final entry = (permissions[modelName] as Map?)?.cast<String, Object?>();
if (entry == null) {
return (serverAssigned: const {}, redacted: const {});
}
final assigned = <String>{};
final redacted = <String>{};
final operations = ((entry['operations'] as Map?) ?? const {})
.cast<String, Object?>();
final create =
(operations['create'] as Map?)?.cast<String, Object?>() ?? const {};
if (create['allow'] == 'owner' && create['ownerField'] is String) {
assigned.add(create['ownerField'] as String);
}
final markers = ((entry['fields'] as Map?) ?? const {})
.cast<String, Object?>();
for (final marker in markers.entries) {
// readOnly: readable, never writable. writeOnly: writable, redacted
// from reads (rules.ts renderRecord). serverOnly: both.
if (marker.value == 'readOnly' || marker.value == 'serverOnly') {
assigned.add(marker.key);
}
if (marker.value == 'writeOnly' || marker.value == 'serverOnly') {
redacted.add(marker.key);
}
}
return (serverAssigned: assigned, redacted: redacted);
}
final models = <GenModel>[
for (final entry
in ((config['models'] as Map?) ?? const {})
.cast<String, Object?>()
.entries)
GenModel(
name: entry.key,
pluralName:
((entry.value as Map)['pluralName'] as String?) ??
'${entry.key}s',
description: ((entry.value as Map)['description'] as String?) ?? '',
fields: [
for (final field
in (((entry.value as Map)['fields'] as Map?) ?? const {})
.cast<String, Object?>()
.entries)
GenField.parse(
field.key,
(field.value as Map).cast<String, Object?>(),
),
],
serverAssignedFields: markersFor(entry.key).serverAssigned,
redactedFields: markersFor(entry.key).redacted,
),
];
GenModel? modelNamed(String name) {
for (final model in models) {
if (model.name == name) return model;
}
return null;
}
final queries = <GenQuery>[];
for (final entry
in ((config['queries'] as Map?) ?? const {})
.cast<String, Object?>()
.entries) {
final spec = (entry.value as Map).cast<String, Object?>();
final queryModel = spec['model'] as String;
final where = ((spec['where'] as List?) ?? const [])
.whereType<Map>()
.map((clause) => clause.cast<String, Object?>())
.toList();
// Resolves the model field (and its owner namespace) a param binds
// to: a map subfield's owner is '<model> <mapField>', matching the
// owner its support types are emitted under.
(GenField, String)? bindingFor(String paramName) {
for (final clause in where) {
if (clause['param'] != paramName) continue;
final path = clause['field'] as String;
final root = path.split('.').first;
final field = modelNamed(queryModel)?.field(root);
if (field == null) return null;
if (path.contains('.') && field.kind == GenFieldKind.map) {
final sub = path.split('.')[1];
for (final mapField in field.mapFields) {
if (mapField.name == sub) {
return (mapField, '$queryModel ${field.name}');
}
}
return null;
}
return (field, queryModel);
}
return null;
}
final params = <GenParam>[];
for (final param
in ((spec['params'] as Map?) ?? const {})
.cast<String, Object?>()
.entries) {
final paramSpec = (param.value as Map).cast<String, Object?>();
final type = paramSpec['type'] as String;
final isList = type.startsWith('list<');
final base = isList ? type.substring(5, type.length - 1) : type;
final bound = bindingFor(param.key);
params.add(
GenParam(
name: param.key,
kind: GenFieldKind.parseBase(base),
isList: isList,
description: (paramSpec['description'] as String?) ?? '',
referenceModel: paramSpec['model'] as String?,
enumValues: bound?.$1.enumValues ?? const [],
boundField: bound?.$1,
boundFieldOwner: bound?.$2,
),
);
}
queries.add(
GenQuery(
name: entry.key,
description: (spec['description'] as String?) ?? '',
isAggregate: spec['kind'] == 'aggregate',
model: queryModel,
params: params,
aggregations: [
for (final aggregation
in ((spec['aggregations'] as List?) ?? const [])
.whereType<Map>())
GenAggregation(
aggregate: aggregation['aggregate'] as String,
field: aggregation['field'] as String?,
alias: aggregation['alias'] as String,
),
],
pageSizeDefault: (spec['pageSizeDefault'] as num?)?.toInt(),
pageSizeMax: (spec['pageSizeMax'] as num?)?.toInt(),
deprecated: (spec['deprecated'] as bool?) ?? false,
),
);
}
final storage = ((config['storage'] as Map?) ?? const {})
.cast<String, Object?>();
final fileCollections = <GenFileCollection>[
for (final entry
in ((storage['fileCollections'] as Map?) ?? const {})
.cast<String, Object?>()
.entries)
GenFileCollection(
name: entry.key,
description: ((entry.value as Map)['description'] as String?) ?? '',
visibility:
((entry.value as Map)['visibility'] as String?) ?? 'private',
maxSizeBytes:
(((entry.value as Map)['maxSizeBytes']) as num?)?.toInt() ?? 0,
allowedContentTypes:
(((entry.value as Map)['allowedContentTypes']) as List? ??
const [])
.cast<String>(),
metadataFields: [
for (final field
in (((entry.value as Map)['metadataFields'] as Map?) ??
const {})
.cast<String, Object?>()
.entries)
GenField.parse(
field.key,
(field.value as Map).cast<String, Object?>(),
),
],
),
];
final auth = ((config['auth'] as Map?) ?? const {}).cast<String, Object?>();
final profileFields = <GenField>[
for (final entry
in ((auth['profileFields'] as Map?) ?? const {})
.cast<String, Object?>()
.entries)
GenField.parse(entry.key, (entry.value as Map).cast<String, Object?>()),
];
final functions = validateFunctionDefinitions(
config['functions'],
modelNames: models.map((model) => model.name).toSet(),
queryNames: queries.map((query) => query.name).toSet(),
fileCollectionNames: fileCollections.map((files) => files.name).toSet(),
);
return GenProjectConfig(
functions: [
for (final entry in functions.entries)
GenFunction._(entry.key, entry.value),
],
models: models,
queries: queries,
fileCollections: fileCollections,
profileFields: profileFields,
sells: config['payments'] is Map,
);
}