Merge pull request #196 from TheJltres/main

Seeders
This commit is contained in:
bobf 2025-05-03 22:35:39 +01:00 committed by GitHub
commit 76ae12a87d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 162 additions and 9 deletions

View File

@ -62,6 +62,7 @@ pub fn build(b: *std.Build) !void {
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.jetquery_migrations_path = @as([]const u8, "src/app/database/migrations"), .jetquery_migrations_path = @as([]const u8, "src/app/database/migrations"),
.jetquery_seeders_path = @as([]const u8, "src/app/database/seeders"),
.jetquery_config_path = @as([]const u8, "config/database.zig"), .jetquery_config_path = @as([]const u8, "config/database.zig"),
}); });
const jetcommon_dep = b.dependency("jetcommon", .{ .target = target, .optimize = optimize }); const jetcommon_dep = b.dependency("jetcommon", .{ .target = target, .optimize = optimize });
@ -76,6 +77,7 @@ pub fn build(b: *std.Build) !void {
b.modules.put("jetquery", jetquery_dep.module("jetquery")) catch @panic("Out of memory"); b.modules.put("jetquery", jetquery_dep.module("jetquery")) catch @panic("Out of memory");
b.modules.put("jetcommon", jetcommon_dep.module("jetcommon")) catch @panic("Out of memory"); b.modules.put("jetcommon", jetcommon_dep.module("jetcommon")) catch @panic("Out of memory");
b.modules.put("jetquery_migrate", jetquery_dep.module("jetquery_migrate")) catch @panic("Out of memory"); b.modules.put("jetquery_migrate", jetquery_dep.module("jetquery_migrate")) catch @panic("Out of memory");
b.modules.put("jetquery_seeder", jetquery_dep.module("jetquery_seeder")) catch @panic("Out of memory");
const smtp_client_dep = b.dependency("smtp_client", .{ const smtp_client_dep = b.dependency("smtp_client", .{
.target = target, .target = target,
@ -174,6 +176,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.jetquery_migrations_path = @as([]const u8, "src/app/database/migrations"), .jetquery_migrations_path = @as([]const u8, "src/app/database/migrations"),
.jetquery_seeders_path = @as([]const u8, "src/app/database/seeders"),
.jetquery_config_path = @as([]const u8, "config/database.zig"), .jetquery_config_path = @as([]const u8, "config/database.zig"),
}); });
@ -183,6 +186,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
const jetquery_module = jetzig_dep.module("jetquery"); const jetquery_module = jetzig_dep.module("jetquery");
const jetcommon_module = jetzig_dep.module("jetcommon"); const jetcommon_module = jetzig_dep.module("jetcommon");
const jetquery_migrate_module = jetzig_dep.module("jetquery_migrate"); const jetquery_migrate_module = jetzig_dep.module("jetquery_migrate");
const jetquery_seeder_module = jetzig_dep.module("jetquery_seeder");
const jetquery_reflect_module = jetquery_dep.module("jetquery_reflect"); const jetquery_reflect_module = jetquery_dep.module("jetquery_reflect");
const build_options = b.addOptions(); const build_options = b.addOptions();
@ -403,6 +407,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
exe_database.root_module.addImport("jetzig", jetzig_module); exe_database.root_module.addImport("jetzig", jetzig_module);
exe_database.root_module.addImport("jetcommon", jetcommon_module); exe_database.root_module.addImport("jetcommon", jetcommon_module);
exe_database.root_module.addImport("jetquery_migrate", jetquery_migrate_module); exe_database.root_module.addImport("jetquery_migrate", jetquery_migrate_module);
exe_database.root_module.addImport("jetquery_seeder", jetquery_seeder_module);
exe_database.root_module.addImport("jetquery_reflect", jetquery_reflect_module); exe_database.root_module.addImport("jetquery_reflect", jetquery_reflect_module);
exe_database.root_module.addImport("Schema", schema_module); exe_database.root_module.addImport("Schema", schema_module);
@ -421,6 +426,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
fn registerDatabaseSteps(b: *std.Build, exe_database: *std.Build.Step.Compile) void { fn registerDatabaseSteps(b: *std.Build, exe_database: *std.Build.Step.Compile) void {
const commands = .{ const commands = .{
.{ "migrate", "Migrate your Jetzig app's database." }, .{ "migrate", "Migrate your Jetzig app's database." },
.{ "seed", "Run seeds and set up initial data." },
.{ "rollback", "Roll back a migration in your Jetzig app's database." }, .{ "rollback", "Roll back a migration in your Jetzig app's database." },
.{ "create", "Create a database for your Jetzig app." }, .{ "create", "Create a database for your Jetzig app." },
.{ "drop", "Drop your Jetzig app's database." }, .{ "drop", "Drop your Jetzig app's database." },

View File

@ -33,8 +33,8 @@
.hash = "httpz-0.0.0-PNVzrEK4BgBpHQGA2m0RPqPGEjnTdDXHodBwzjYDrmps", .hash = "httpz-0.0.0-PNVzrEK4BgBpHQGA2m0RPqPGEjnTdDXHodBwzjYDrmps",
}, },
.jetquery = .{ .jetquery = .{
.url = "https://github.com/jetzig-framework/jetquery/archive/e1f969f2e3e0e1ad9cc30d56fde9739aa692fdc3.tar.gz", .url = "https://github.com/jetzig-framework/jetquery/archive/ed6b42e196573365057d8e5d17ff85b456b11e68.tar.gz",
.hash = "jetquery-0.0.0-TNf3zo2ABgBgcsIAvJ1Ud2B2zDzrBy9GQ31kKmTYZ7Ya", .hash = "jetquery-0.0.0-TNf3zs24BgCYXZZvMKUbEMb6hRfrMmYHJgHTNGWZM5B1",
}, },
}, },

View File

@ -18,6 +18,7 @@ pub fn build(b: *std.Build) !void {
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.jetquery_migrations_path = @as([]const u8, "src/app/database/migrations"), .jetquery_migrations_path = @as([]const u8, "src/app/database/migrations"),
.jetquery_seeders_path = @as([]const u8, "src/app/database/seeders"),
}); });
exe.root_module.addImport("jetquery", jetquery_dep.module("jetquery")); exe.root_module.addImport("jetquery", jetquery_dep.module("jetquery"));
exe.root_module.addImport("args", zig_args_dep.module("args")); exe.root_module.addImport("args", zig_args_dep.module("args"));

View File

@ -10,8 +10,8 @@
.hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH", .hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH",
}, },
.jetquery = .{ .jetquery = .{
.url = "https://github.com/jetzig-framework/jetquery/archive/e1f969f2e3e0e1ad9cc30d56fde9739aa692fdc3.tar.gz", .url = "https://github.com/jetzig-framework/jetquery/archive/ed6b42e196573365057d8e5d17ff85b456b11e68.tar.gz",
.hash = "jetquery-0.0.0-TNf3zo2ABgBgcsIAvJ1Ud2B2zDzrBy9GQ31kKmTYZ7Ya", .hash = "jetquery-0.0.0-TNf3zs24BgCYXZZvMKUbEMb6hRfrMmYHJgHTNGWZM5B1",
}, },
}, },
.paths = .{ .paths = .{

View File

@ -5,6 +5,7 @@ const args = @import("args");
const util = @import("../util.zig"); const util = @import("../util.zig");
const cli = @import("../cli.zig"); const cli = @import("../cli.zig");
const migrate = @import("database/migrate.zig"); const migrate = @import("database/migrate.zig");
const seed = @import("database/seed.zig");
const rollback = @import("database/rollback.zig"); const rollback = @import("database/rollback.zig");
const create = @import("database/create.zig"); const create = @import("database/create.zig");
const drop = @import("database/drop.zig"); const drop = @import("database/drop.zig");
@ -41,9 +42,19 @@ pub fn run(
defer arena.deinit(); defer arena.deinit();
const alloc = arena.allocator(); const alloc = arena.allocator();
const Action = enum { migrate, rollback, create, drop, reflect, update, setup }; const Action = enum {
migrate,
seed,
rollback,
create,
drop,
reflect,
update,
setup,
};
const map = std.StaticStringMap(Action).initComptime(.{ const map = std.StaticStringMap(Action).initComptime(.{
.{ "migrate", .migrate }, .{ "migrate", .migrate },
.{ "seed", .seed },
.{ "rollback", .rollback }, .{ "rollback", .rollback },
.{ "create", .create }, .{ "create", .create },
.{ "drop", .drop }, .{ "drop", .drop },
@ -74,6 +85,7 @@ pub fn run(
break :blk switch (capture) { break :blk switch (capture) {
.migrate => migrate.run(alloc, cwd, sub_args, options, T, main_options), .migrate => migrate.run(alloc, cwd, sub_args, options, T, main_options),
.seed => seed.run(alloc, cwd, sub_args, options, T, main_options),
.rollback => rollback.run(alloc, cwd, sub_args, options, T, main_options), .rollback => rollback.run(alloc, cwd, sub_args, options, T, main_options),
.create => create.run(alloc, cwd, sub_args, options, T, main_options), .create => create.run(alloc, cwd, sub_args, options, T, main_options),
.drop => drop.run(alloc, cwd, sub_args, options, T, main_options), .drop => drop.run(alloc, cwd, sub_args, options, T, main_options),

View File

@ -0,0 +1,36 @@
const std = @import("std");
const cli = @import("../../cli.zig");
const util = @import("../../util.zig");
pub fn run(
allocator: std.mem.Allocator,
cwd: std.fs.Dir,
args: []const []const u8,
options: cli.database.Options,
T: type,
main_options: T,
) !void {
_ = cwd;
_ = options;
if (main_options.options.help or args.len != 0) {
std.debug.print(
\\Run database seeders.
\\
\\Example:
\\
\\ jetzig database seed
\\ jetzig --environment=testing database seed
\\
, .{});
return if (main_options.options.help) {} else error.JetzigCommandError;
}
try util.execCommand(allocator, &.{
"zig",
"build",
util.environmentBuildOption(main_options.options.environment),
"jetzig:database:seed",
});
}

View File

@ -10,11 +10,12 @@ const middleware = @import("generate/middleware.zig");
const job = @import("generate/job.zig"); const job = @import("generate/job.zig");
const mailer = @import("generate/mailer.zig"); const mailer = @import("generate/mailer.zig");
const migration = @import("generate/migration.zig"); const migration = @import("generate/migration.zig");
const seeder = @import("generate/seeder.zig");
/// Command line options for the `generate` command. /// Command line options for the `generate` command.
pub const Options = struct { pub const Options = struct {
pub const meta = .{ pub const meta = .{
.usage_summary = "[view|partial|layout|mailer|middleware|job|secret|migration] [options]", .usage_summary = "[view|partial|layout|mailer|middleware|job|secret|migration|seeder] [options]",
.full_text = .full_text =
\\Generate scaffolding for views, middleware, and other objects. \\Generate scaffolding for views, middleware, and other objects.
\\ \\
@ -39,7 +40,17 @@ pub fn run(
_ = options; _ = options;
const Generator = enum { view, partial, layout, mailer, middleware, job, secret, migration }; const Generator = enum {
view,
partial,
layout,
mailer,
middleware,
job,
secret,
migration,
seeder,
};
var sub_args = std.ArrayList([]const u8).init(allocator); var sub_args = std.ArrayList([]const u8).init(allocator);
defer sub_args.deinit(); defer sub_args.deinit();
@ -55,6 +66,7 @@ pub fn run(
.{ "middleware", .middleware }, .{ "middleware", .middleware },
.{ "secret", .secret }, .{ "secret", .secret },
.{ "migration", .migration }, .{ "migration", .migration },
.{ "seeder", .seeder },
}); });
for (map.keys()) |key| try available_buf.append(key); for (map.keys()) |key| try available_buf.append(key);
@ -92,6 +104,7 @@ pub fn run(
.middleware => middleware.run(arena, cwd, sub_args.items, main_options.options.help), .middleware => middleware.run(arena, cwd, sub_args.items, main_options.options.help),
.secret => secret.run(arena, cwd, sub_args.items, main_options.options.help), .secret => secret.run(arena, cwd, sub_args.items, main_options.options.help),
.migration => migration.run(arena, cwd, sub_args.items, main_options.options.help), .migration => migration.run(arena, cwd, sub_args.items, main_options.options.help),
.seeder => seeder.run(arena, cwd, sub_args.items, main_options.options.help),
}; };
} }
} }

View File

@ -0,0 +1,40 @@
const std = @import("std");
const jetquery = @import("jetquery");
/// Run the seeder generator. Create a seed in `src/app/database/seeders/`
pub fn run(allocator: std.mem.Allocator, cwd: std.fs.Dir, args: [][]const u8, help: bool) !void {
if (help or args.len < 1) {
std.debug.print(
\\Generate a new Seeder. Seeders is a way to set up some inital data for your application.
\\
\\Example:
\\
\\ jetzig generate seeder iguana
\\
\\ More information: https://www.jetzig.dev/documentation/sections/database/command_line_tools
\\
, .{});
if (help) return;
return error.JetzigCommandError;
}
const name = args[0];
const seeders_dir = try cwd.makeOpenPath(
try std.fs.path.join(allocator, &.{ "src", "app", "database", "seeders" }),
.{},
);
const seed = jetquery.Seeder.init(
allocator,
name,
.{
.seeders_path = try seeders_dir.realpathAlloc(allocator, "."),
},
);
const path = try seed.save();
std.log.info("Saved seed: {s}", .{path});
}

View File

@ -0,0 +1,19 @@
const std = @import("std");
pub fn run(repo: anytype) !void {
try repo.insert(
.User,
.{
.email = "iguana@jetzig.dev",
.password_hash = "not_secure",
},
);
try repo.insert(
.User,
.{
.email = "admin@jetzig.dev",
.password_hash = "do_not_use",
},
);
}

View File

@ -6,6 +6,8 @@ const jetquery = @import("jetquery");
const jetzig = @import("jetzig"); const jetzig = @import("jetzig");
const Migrate = @import("jetquery_migrate").Migrate; const Migrate = @import("jetquery_migrate").Migrate;
const MigrateSchema = @import("jetquery_migrate").MigrateSchema; const MigrateSchema = @import("jetquery_migrate").MigrateSchema;
const Seeder = @import("jetquery_seeder").Seed;
const SeederSchema = @import("jetquery_seeder").SeederSchema;
const Schema = @import("Schema"); const Schema = @import("Schema");
const util = @import("util.zig"); const util = @import("util.zig");
@ -15,7 +17,7 @@ const production_drop_failure_message = "To drop a production database, " ++
const environment = jetzig.build_options.environment; const environment = jetzig.build_options.environment;
const config = @field(jetquery.config.database, @tagName(environment)); const config = @field(jetquery.config.database, @tagName(environment));
const Action = enum { migrate, rollback, create, drop, reflect, setup, update }; const Action = enum { migrate, rollback, create, drop, reflect, setup, update, seed };
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
@ -42,6 +44,7 @@ pub fn main() !void {
const map = std.StaticStringMap(Action).initComptime(.{ const map = std.StaticStringMap(Action).initComptime(.{
.{ "migrate", .migrate }, .{ "migrate", .migrate },
.{ "seed", .seed },
.{ "rollback", .rollback }, .{ "rollback", .rollback },
.{ "create", .create }, .{ "create", .create },
.{ "drop", .drop }, .{ "drop", .drop },
@ -69,6 +72,11 @@ pub fn main() !void {
defer repo.deinit(); defer repo.deinit();
try Migrate(config.adapter).init(&repo).migrate(); try Migrate(config.adapter).init(&repo).migrate();
}, },
.seed => {
var repo = try seedersRepo(action, allocator, repo_env);
defer repo.deinit();
try Seeder(config.adapter, Schema).init(&repo).seed();
},
.rollback => { .rollback => {
var repo = try migrationsRepo(action, allocator, repo_env); var repo = try migrationsRepo(action, allocator, repo_env);
defer repo.deinit(); defer repo.deinit();
@ -136,7 +144,7 @@ fn migrationsRepo(action: Action, allocator: std.mem.Allocator, repo_env: anytyp
@field(jetquery.Environment, @tagName(environment)), @field(jetquery.Environment, @tagName(environment)),
.{ .{
.admin = switch (action) { .admin = switch (action) {
.migrate, .rollback, .update => false, .migrate, .seed, .rollback, .update => false,
.create, .drop => true, .create, .drop => true,
.reflect => unreachable, // We use a separate repo for schema reflection. .reflect => unreachable, // We use a separate repo for schema reflection.
.setup => unreachable, // Setup uses `create` and then `update` .setup => unreachable, // Setup uses `create` and then `update`
@ -147,6 +155,24 @@ fn migrationsRepo(action: Action, allocator: std.mem.Allocator, repo_env: anytyp
); );
} }
const SeedersRepo = jetquery.Repo(config.adapter, Schema);
fn seedersRepo(action: Action, allocator: std.mem.Allocator, repo_env: anytype) !SeedersRepo {
return try SeedersRepo.loadConfig(
allocator,
@field(jetquery.Environment, @tagName(environment)),
.{
.admin = switch (action) {
.migrate, .seed, .rollback, .update => false,
.create, .drop => false,
.reflect => unreachable, // We use a separate repo for schema reflection.
.setup => unreachable, // Setup uses `create` and then `update`
},
.context = .seed,
.env = repo_env,
},
);
}
fn reflectSchema(allocator: std.mem.Allocator, repo_env: anytype) !void { fn reflectSchema(allocator: std.mem.Allocator, repo_env: anytype) !void {
var cwd = try jetzig.util.detectJetzigProjectDir(); var cwd = try jetzig.util.detectJetzigProjectDir();
defer cwd.close(); defer cwd.close();