Skip to content

Commit d6222ba

Browse files
committed
feat(cli): database reset interactive (wip)
1 parent efe5a6e commit d6222ba

File tree

7 files changed

+184
-27
lines changed

7 files changed

+184
-27
lines changed

examples/deploy/deploy.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import * as path from "path";
2020

2121
const tg = await typegraph({
2222
name: "deploy-example-node",
23-
disableAutoSerialization: true // disable print
2423
}, (g) => {
2524
const deno = new DenoRuntime();
2625
const python = new PythonRuntime();

examples/deploy/deploy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from os import path
1616

1717

18-
@typegraph(disable_auto_serialization=True) # disable print
18+
@typegraph()
1919
def deploy_example_python(g: Graph):
2020
deno = DenoRuntime()
2121
python = PythonRuntime()

meta-cli/src/cli/deploy.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl Deploy {
116116
.with_context(|| format!("building node from config: {node_config:#?}"))?;
117117

118118
ServerStore::with(Some(Command::Deploy), Some(config.as_ref().to_owned()));
119-
ServerStore::set_migration_action(MigrationAction {
119+
ServerStore::set_migration_action_glob(MigrationAction {
120120
create: deploy.options.create_migration,
121121
reset: deploy.options.allow_destructive, // reset on drift
122122
});
@@ -262,9 +262,13 @@ mod default_mode {
262262
match event {
263263
LoaderEvent::Typegraph(tg) => match tg.get_response_or_fail() {
264264
Ok(res) => {
265-
match PushResult::new(self.console.clone(), res.as_ref().clone()) {
265+
match PushResult::new(
266+
self.console.clone(),
267+
self.loader.clone(),
268+
res.as_ref().clone(),
269+
) {
266270
Ok(push) => {
267-
if let Err(e) = push.finalize() {
271+
if let Err(e) = push.finalize().await {
268272
panic!("{}", e.to_string());
269273
}
270274
}
@@ -399,9 +403,9 @@ mod watch_mode {
399403
.unwrap()
400404
.as_ref()
401405
.to_owned();
402-
match PushResult::new(console.clone(), response) {
406+
match PushResult::new(console.clone(), loader.clone(), response) {
403407
Ok(push) => {
404-
if let Err(e) = push.finalize() {
408+
if let Err(e) = push.finalize().await {
405409
panic!("{}", e.to_string());
406410
}
407411
RetryManager::clear_counter(&tg.path);

meta-cli/src/com/server.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,15 @@ struct QueryConfigParams {
5050

5151
#[get("/config")]
5252
async fn config(req: HttpRequest) -> impl Responder {
53-
let mut parsed = Query::<QueryConfigParams>::from_query(req.query_string()).unwrap();
53+
let parsed = Query::<QueryConfigParams>::from_query(req.query_string()).unwrap();
5454

55-
parsed.typegraph_path.pop(); // pop file.ext
56-
let artefact_base_dir = parsed.typegraph_path.clone();
55+
let mut artefact_base_dir = parsed.typegraph_path.clone();
56+
artefact_base_dir.pop(); // pop file.ext
5757

5858
let endpoint = ServerStore::get_endpoint();
5959
let secrets = ServerStore::get_secrets();
60-
let migration_action = ServerStore::get_migration_action();
60+
let migration_action = ServerStore::get_migration_action(&parsed.typegraph_path);
61+
6162
let prefix = ServerStore::get_prefix();
6263
match ServerStore::get_config() {
6364
Some(config) => {

meta-cli/src/com/store.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ pub struct MigrationAction {
5555
pub struct ServerStore {
5656
config: Option<Config>,
5757
command: Option<Command>,
58-
migration_action: MigrationAction,
58+
migration_action_glob: MigrationAction,
59+
migration_action: HashMap<PathBuf, Arc<MigrationAction>>,
5960
secrets: HashMap<String, String>,
6061
endpoint: Endpoint,
6162
prefix: Option<String>,
@@ -101,7 +102,7 @@ impl ServerStore {
101102

102103
pub fn add_response(tg_name: PathBuf, response: SDKResponse) {
103104
with_store_mut(|s| {
104-
s.sdk_responses.insert(tg_name, Arc::new(response));
105+
s.sdk_responses.insert(tg_name, response.into());
105106
})
106107
}
107108

@@ -120,12 +121,31 @@ impl ServerStore {
120121
with_store(|s| s.sdk_responses.clone())
121122
}
122123

123-
pub fn set_migration_action(option: MigrationAction) {
124-
with_store_mut(|s| s.migration_action = option)
124+
pub fn set_migration_action_glob(option: MigrationAction) {
125+
with_store_mut(|s| s.migration_action_glob = option)
125126
}
126127

127-
pub fn get_migration_action() -> MigrationAction {
128-
with_store(|s| s.migration_action.to_owned())
128+
pub fn get_migration_action_glob() -> MigrationAction {
129+
with_store(|s| s.migration_action_glob.to_owned())
130+
}
131+
132+
pub fn set_migration_action(tg_path: PathBuf, option: MigrationAction) {
133+
with_store_mut(|s| {
134+
s.migration_action.insert(tg_path, option.into());
135+
})
136+
}
137+
138+
pub fn get_migration_action(tg_path: &PathBuf) -> MigrationAction {
139+
with_store(|s| {
140+
if let Some(mig_action) = s.migration_action.get(tg_path) {
141+
println!(
142+
"Specific migration action was defined for {}",
143+
tg_path.display()
144+
);
145+
return mig_action.as_ref().to_owned();
146+
}
147+
s.migration_action_glob.to_owned()
148+
})
129149
}
130150

131151
pub fn set_prefix(prefix: Option<String>) {

meta-cli/src/deploy/push/pusher.rs

Lines changed: 141 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use anyhow::Result;
1313
use serde::Deserialize;
1414

1515
use crate::com::{responses::SDKResponse, store::ServerStore};
16+
use crate::deploy::actors::console::input::{Confirm, ConfirmHandler};
1617
use crate::deploy::actors::console::{Console, ConsoleActor};
18+
use crate::deploy::actors::loader::{LoadModule, LoaderActor};
1719

1820
use lazy_static::lazy_static;
1921

@@ -96,11 +98,16 @@ pub struct PushResult {
9698
failure: Option<PushFailure>,
9799
original_name: String,
98100
console: Addr<ConsoleActor>,
101+
loader: Addr<LoaderActor>,
99102
sdk_response: SDKResponse,
100103
}
101104

102105
impl PushResult {
103-
pub fn new(console: Addr<ConsoleActor>, sdk_response: SDKResponse) -> Result<Self> {
106+
pub fn new(
107+
console: Addr<ConsoleActor>,
108+
loader: Addr<LoaderActor>,
109+
sdk_response: SDKResponse,
110+
) -> Result<Self> {
104111
let raw = sdk_response.as_push_result()?;
105112

106113
let failure = match raw.failure {
@@ -115,11 +122,12 @@ impl PushResult {
115122
failure,
116123
original_name: sdk_response.typegraph_name.clone(),
117124
console,
125+
loader,
118126
sdk_response,
119127
})
120128
}
121129

122-
pub fn finalize(&self) -> Result<()> {
130+
pub async fn finalize(&self) -> Result<()> {
123131
let name = self.name.clone();
124132
self.console.info(format!(
125133
"{} Successfully pushed typegraph {name}.",
@@ -131,9 +139,9 @@ impl PushResult {
131139
.unwrap()
132140
.prisma_migrations_dir_rel(&self.original_name);
133141

142+
// TODO: use unpack from sdk? This requires another load event though.
134143
for migrations in self.migrations.iter() {
135144
let dest = migdir.join(&migrations.runtime);
136-
// TODO async??
137145
if let Err(e) = common::archive::unpack(&dest, Some(migrations.migrations.clone())) {
138146
self.console.error(format!(
139147
"Error while unpacking migrations into {:?}",
@@ -142,7 +150,7 @@ impl PushResult {
142150
self.console.error(format!("{e:?}"));
143151
} else {
144152
self.console.info(format!(
145-
"Successfully unpacked migrations for {name}/{} at {:?}!",
153+
"Successfully unpacked migrations for {name}/{} at {:?}",
146154
migrations.runtime, dest
147155
));
148156
}
@@ -158,17 +166,142 @@ impl PushResult {
158166
self.console.error(f.message);
159167
}
160168
PushFailure::DatabaseResetRequired(failure) => {
161-
todo!("database reset required {:?}", failure);
162-
}
163-
PushFailure::NullConstraintViolation(failure) => {
164-
todo!("null constraint violation {:?}", failure);
169+
handle_database_reset(
170+
self.console.clone(),
171+
self.loader.clone(),
172+
failure,
173+
self.sdk_response.clone(),
174+
)
175+
.await?
165176
}
177+
PushFailure::NullConstraintViolation(failure) => handle_null_constraint_violation(
178+
self.console.clone(),
179+
failure,
180+
self.sdk_response.clone(),
181+
),
166182
}
167183
}
168184
Ok(())
169185
}
170186
}
171187

188+
// DatabaseReset Handler + interactivity
189+
190+
#[derive(Debug)]
191+
struct ConfirmDatabaseResetRequired {
192+
sdk_response: SDKResponse,
193+
loader: Addr<LoaderActor>,
194+
}
195+
196+
impl ConfirmHandler for ConfirmDatabaseResetRequired {
197+
fn on_confirm(&self) {
198+
let tg_path = self.sdk_response.clone().typegraph_path;
199+
200+
// reset
201+
let mut option = ServerStore::get_migration_action(&tg_path);
202+
option.reset = true;
203+
ServerStore::set_migration_action(tg_path.clone(), option);
204+
205+
// reload
206+
self.loader.do_send(LoadModule(tg_path.into()));
207+
}
208+
}
209+
210+
async fn handle_database_reset(
211+
console: Addr<ConsoleActor>,
212+
loader: Addr<LoaderActor>,
213+
failure: DatabaseResetRequired,
214+
sdk_response: SDKResponse,
215+
) -> Result<()> {
216+
let DatabaseResetRequired {
217+
message,
218+
runtime_name,
219+
} = failure;
220+
221+
let name = sdk_response.typegraph_name.clone();
222+
223+
console.error(format!(
224+
"Database reset required for prisma runtime {rt} in typegraph {name}",
225+
rt = runtime_name.magenta(),
226+
));
227+
console.error(message);
228+
229+
let rt = runtime_name.clone();
230+
let _ = Confirm::new(
231+
console,
232+
format!("Do you want to reset the database for runtime {rt} on {name}?"),
233+
)
234+
.interact(Box::new(ConfirmDatabaseResetRequired {
235+
sdk_response: sdk_response.clone(),
236+
loader,
237+
}))
238+
.await?;
239+
240+
Ok(())
241+
}
242+
243+
pub fn handle_null_constraint_violation(
244+
console: Addr<ConsoleActor>,
245+
failure: NullConstraintViolation,
246+
sdk_response: SDKResponse,
247+
) {
248+
#[allow(unused)]
249+
let typegraph_name = sdk_response.typegraph_name;
250+
#[allow(unused)]
251+
let NullConstraintViolation {
252+
message,
253+
runtime_name,
254+
migration_name,
255+
is_new_column,
256+
column,
257+
table,
258+
} = failure;
259+
260+
console.error(message);
261+
262+
if is_new_column {
263+
console.info(format!("manually edit the migration {migration_name}; or remove the migration and add set a default value"));
264+
todo!("interactive choice")
265+
// let migration_dir: PathBuf = ServerStore::get_config()
266+
// .unwrap()
267+
// .prisma_migrations_dir_rel(&typegraph_name)
268+
// .join(&runtime_name)
269+
// .into();
270+
271+
// let remove_latest = RemoveLatestMigration {
272+
// runtime_name: runtime_name.clone(),
273+
// migration_name: migration_name.clone(),
274+
// migration_dir: migration_dir.clone(),
275+
// push_manager: self.push_manager.clone(),
276+
// console: self.console.clone(),
277+
// };
278+
279+
// let manual = ManualResolution {
280+
// runtime_name: runtime_name.clone(),
281+
// migration_name: migration_name.clone(),
282+
// migration_dir,
283+
// message: Some(format!(
284+
// "Set a default value for the column `{}` in the table `{}`",
285+
// column, table
286+
// )),
287+
// push_manager: self.push_manager.clone(),
288+
// console: self.console.clone(),
289+
// };
290+
291+
// let reset = ForceReset {
292+
// typegraph: typegraph.clone(),
293+
// runtime_name: runtime_name.clone(),
294+
// push_manager: self.push_manager.clone(),
295+
// };
296+
297+
// self.push_manager
298+
// .do_send(PushFinished::new(msg.push, false).select(
299+
// "Choose one of the following options".to_string(),
300+
// vec![Box::new(remove_latest), Box::new(manual), Box::new(reset)],
301+
// ));
302+
}
303+
}
304+
172305
lazy_static! {
173306
static ref RETRY_COUNTERS: Mutex<HashMap<PathBuf, Arc<u8>>> = Mutex::new(HashMap::new());
174307
}

meta-cli/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ fn main() -> Result<()> {
3434
setup_panic_hook();
3535
logger::init();
3636

37-
std::env::set_var("META_CLI_SERVER_PORT", get_instance_port().to_string());
38-
3937
let _ = actix::System::with_tokio_rt(|| {
4038
tokio::runtime::Builder::new_multi_thread()
4139
.enable_all()
@@ -75,6 +73,8 @@ fn main() -> Result<()> {
7573
});
7674
}
7775
_ => {
76+
std::env::set_var("META_CLI_SERVER_PORT", get_instance_port().to_string());
77+
7878
let command = command.run(args.gen);
7979
let server = spawn_server().map_err(|e| e.into());
8080
try_join!(command, server).unwrap_or_else(|e| {

0 commit comments

Comments
 (0)