-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathcloud_hub_database.dart
More file actions
141 lines (129 loc) · 4.19 KB
/
cloud_hub_database.dart
File metadata and controls
141 lines (129 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import 'dart:io';
import 'package:celest_cloud_auth/celest_cloud_auth.dart';
import 'package:celest_cloud_hub/src/auth/policy_set.g.dart';
import 'package:celest_cloud_hub/src/database/db_functions.dart';
import 'package:celest_cloud_hub/src/project.dart';
import 'package:celest_cloud_hub/src/services/service_mixin.dart';
import 'package:celest_core/_internal.dart';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'cloud_hub_database.drift.dart';
@DriftDatabase(
include: {
'schema/operations.drift',
'schema/organizations.drift',
'schema/projects.drift',
'schema/project_environments.drift',
'schema/user_memberships.drift',
// Cloud Auth
...CloudAuthDatabaseMixin.includes,
},
)
final class CloudHubDatabase extends $CloudHubDatabase
with CloudAuthDatabaseMixin {
CloudHubDatabase(super.e);
CloudHubDatabase.memory()
: this(NativeDatabase.memory(setup: (db) => db.addHelperFunctions()));
factory CloudHubDatabase.localFile(String path, {bool verbose = false}) {
return CloudHubDatabase(
NativeDatabase(
File(path),
logStatements: verbose,
setup: (db) => db.addHelperFunctions(),
cachePreparedStatements: true,
enableMigrations: true,
),
);
}
@override
int get schemaVersion => 1;
static final Entity rootOrg = Entity(
uid: const EntityUid.of('Celest::Organization', 'celest-dev'),
);
@override
MigrationStrategy get migration => MigrationStrategy(
onCreate: (m) async {
await m.createAll();
},
onUpgrade: (m, from, to) async {
await cloudAuth.onUpgrade(m);
},
beforeOpen: (details) async {
await withoutForeignKeys(() async {
if (details.wasCreated) {
await cloudAuth.seed(
additionalCedarTypes: {
'Celest::Operation',
'Celest::Organization',
'Celest::Organization::Member',
'Celest::Project',
'Celest::Project::Member',
'Celest::Project::Environment',
'Celest::Project::Environment::Member',
},
additionalCedarEntities: {
rootOrg.uid: rootOrg,
ProjectEnvironmentAction.deploy: Entity(
uid: ProjectEnvironmentAction.deploy,
parents: [CelestAction.owner],
),
},
additionalCedarPolicies: corePolicySet,
);
}
await cloudAuth.upsertProject(project: project);
});
},
);
/// Runs [action] in a context without foreign keys enabled.
Future<R> withoutForeignKeys<R>(Future<R> Function() action) async {
await customStatement('pragma foreign_keys = OFF');
R result;
try {
result = await transaction(action);
} finally {
if (kDebugMode) {
// Fail if the action broke foreign keys
final wrongForeignKeys =
await customSelect('PRAGMA foreign_key_check').get();
await _dumpBrokenCedarForeignKeys();
assert(
wrongForeignKeys.isEmpty,
'${wrongForeignKeys.map((e) => e.data)}',
);
}
await customStatement('pragma foreign_keys = ON');
}
return result;
}
// ignore: unused_element
Future<void> _dumpBrokenCedarForeignKeys() async {
final allEntitiesRaw = await cedarEntities.select().get();
final allEntities = {
for (final entity in allEntitiesRaw)
EntityUid.of(entity.entityType, entity.entityId),
};
final allRelationships = await cedarRelationships.select().get();
for (final relationship in allRelationships) {
final entityId = EntityUid.of(
relationship.entityType,
relationship.entityId,
);
final parentId = EntityUid.of(
relationship.parentType,
relationship.parentId,
);
if (!allEntities.contains(entityId)) {
print('Relationship has bad (entity_type, entity_id): $entityId');
print(' $relationship');
}
if (!allEntities.contains(parentId)) {
print('Relationship has bad (parent_type, parent_id): $parentId');
print(' $relationship');
}
}
}
Future<void> ping() async {
await customSelect('SELECT 1').get();
}
}