forked from stephenafamo/bob
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add bobgen-prisma driver to generate models/factory from a prisma schema
- Loading branch information
1 parent
28efc5b
commit 7d4ccce
Showing
15 changed files
with
5,988 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# Bob Gen for Prisma | ||
|
||
Generates an ORM based on a prisma schema file | ||
|
||
## How to Generate | ||
|
||
1. Initialise a new Go project. | ||
The generator must be run from inside a go module, so either the current directory or a parent directory should contain a `go.mod` file. | ||
If you don't have a Go project yet, initialise one using Go modules: | ||
|
||
```shell script | ||
mkdir demo && cd demo | ||
go mod init demo | ||
``` | ||
|
||
1. Get the Bob Prisma Generator. Install the Go module in your project by running: | ||
|
||
```shell script | ||
go get github.com/stephenafamo/bob/gen/bobgen-prisma | ||
``` | ||
|
||
1. Prepare your database schema in a `schema.prisma` file. For example, a simple schema with a postgres database and the Bob Prisma generator with two models would look like this: | ||
|
||
```prisma | ||
datasource db { | ||
// only postgresql is supported for now | ||
provider = "postgresql" | ||
url = env("DATABASE_URL") | ||
} | ||
generator db { | ||
provider = "go run github.com/stephenafamo/bob/gen/bobgen-prisma" | ||
output = "./prisma" // Optional. Default: ./db | ||
configFile = "./config/bobgen.yml" // Optional. Default ./bobgen.yaml | ||
} | ||
model Post { | ||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid | ||
created_at DateTime @default(now()) @db.Timestamptz(6) | ||
updated_at DateTime @db.Timestamptz(6) | ||
title String | ||
published Boolean | ||
desc String? | ||
} | ||
``` | ||
1. Generate the models using `prisma generate` | ||
## How to Use | ||
[Detailed documentation on how to use the Bob ORM can be found here](..) | ||
A small taste | ||
```go | ||
package main | ||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
db "random/prisma/bob" | ||
"github.com/aarondl/opt/omit" | ||
"github.com/aarondl/opt/omitnull" | ||
) | ||
func main() { | ||
if err := run(context.Background()); err != nil { | ||
panic(err) | ||
} | ||
} | ||
func run(ctx context.Context) error { | ||
client, err := db.New() | ||
if err != nil { | ||
return err | ||
} | ||
defer client.Close() | ||
// create a post | ||
createdPost, err := db.PostsTable.Insert(ctx, client, &db.OptionalPost{ | ||
Title: omit.From("Hi from Prisma!"), | ||
Published: omit.From(true), | ||
Desc: omitnull.From("Prisma is a database toolkit and makes databases easy."), | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
result, _ := json.MarshalIndent(createdPost, "", " ") | ||
fmt.Printf("created post: %s\n", result) | ||
// find a single post | ||
post, err := db.FindPost(ctx, client, createdPost.ID) | ||
if err != nil { | ||
return err | ||
} | ||
result, _ = json.MarshalIndent(post, "", " ") | ||
fmt.Printf("post: %s\n", result) | ||
// For optional/nullable values, the field will have a generic null wrapper | ||
// with some helpful methods | ||
if post.Desc.IsNull() { | ||
return fmt.Errorf("post's description is null") | ||
} | ||
fmt.Printf("The posts's description is: %s\n", post.Desc.MustGet()) | ||
return nil | ||
} | ||
``` | ||
[Detailed documentation on how to use the Bob ORM can be found here](..) | ||
## Driver Configuration | ||
The configuration for the prisma driver must all be prefixed by the driver name. | ||
You must use a configuration file or environment variables for configuring the database driver. | ||
In the configuration file for prisma for example you would do: | ||
```yaml | ||
prisma: | ||
schema: "public" | ||
``` | ||
When you use an environment variable it must also be prefixed by the driver | ||
name: | ||
```sh | ||
PRISMA_SCHEMA="public" | ||
``` | ||
The values that exist for the drivers: | ||
| Name | Required | Prisma Default | | ||
| --------- | --------- | -------------- | | ||
| schema | no | "public" | | ||
| whitelist | no | [] | | ||
| blacklist | no | [] | | ||
Example of whitelist/blacklist: | ||
```yaml | ||
prisma: | ||
# Removes migrations table, the name column from the addresses table, and | ||
# secret_col of any table from being generated. Foreign keys that reference tables | ||
# or columns that are no longer generated because of whitelists or blacklists may | ||
# cause problems. | ||
blacklist: ["migrations", "addresses.name", "*.secret_col"] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package driver | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/stephenafamo/bob/gen/drivers" | ||
) | ||
|
||
func (d *Driver) Constraints(colFilter drivers.ColumnFilter) (drivers.DBConstraints, error) { | ||
ret := drivers.DBConstraints{ | ||
PKs: map[string]*drivers.Constraint{}, | ||
FKs: map[string][]drivers.ForeignKey{}, | ||
Uniques: map[string][]drivers.Constraint{}, | ||
} | ||
|
||
allfilter := colFilter["*"] | ||
|
||
for _, model := range d.config.Datamodel.Models { | ||
filter := colFilter[model.Name] | ||
include := append(allfilter.Include, filter.Include...) | ||
exclude := append(allfilter.Exclude, filter.Exclude...) | ||
|
||
// If it is a composite primary key defined on the model | ||
if len(model.PrimaryKey.Fields) > 0 { | ||
shouldSkip := false | ||
cols := make([]string, len(model.PrimaryKey.Fields)) | ||
|
||
for i, f := range model.PrimaryKey.Fields { | ||
if skip(f, include, exclude) { | ||
shouldSkip = true | ||
} | ||
cols[i] = f | ||
} | ||
|
||
if !shouldSkip { | ||
pkName := model.PrimaryKey.Name | ||
if pkName == "" { | ||
pkName = "pk_" + model.Name | ||
} | ||
ret.PKs[model.Name] = &drivers.Constraint{ | ||
Name: pkName, | ||
Columns: cols, | ||
} | ||
} | ||
} | ||
|
||
for _, unique := range model.UniqueIndexes { | ||
shouldSkip := false | ||
cols := make([]string, len(unique.Fields)) | ||
|
||
for i, f := range unique.Fields { | ||
if skip(f, include, exclude) { | ||
shouldSkip = true | ||
} | ||
cols[i] = f | ||
} | ||
|
||
if !shouldSkip { | ||
keyName := unique.InternalName | ||
if keyName == "" { | ||
keyName = fmt.Sprintf("unique_%s_%s", model.Name, strings.Join(cols, "_")) | ||
} | ||
|
||
ret.Uniques[model.Name] = append(ret.Uniques[model.Name], drivers.Constraint{ | ||
Name: keyName, | ||
Columns: cols, | ||
}) | ||
} | ||
} | ||
|
||
// If one of the fields has an @id attribute | ||
for _, field := range model.Fields { | ||
if skip(field.Name, include, exclude) { | ||
continue | ||
} | ||
|
||
if field.IsID { | ||
ret.PKs[model.Name] = &drivers.Constraint{ | ||
Name: "pk_" + model.Name, | ||
Columns: []string{field.Name}, | ||
} | ||
} | ||
|
||
if field.IsUnique { | ||
ret.Uniques[model.Name] = append(ret.Uniques[model.Name], drivers.Constraint{ | ||
Name: fmt.Sprintf("unique_%s_%s", model.Name, field.Name), | ||
Columns: []string{field.Name}, | ||
}) | ||
} | ||
|
||
if field.Kind == FieldKindObject && len(field.RelationFromFields) > 0 { | ||
ret.FKs[model.Name] = append(ret.FKs[model.Name], drivers.ForeignKey{ | ||
Constraint: drivers.Constraint{ | ||
Name: field.RelationName, | ||
Columns: field.RelationFromFields, | ||
}, | ||
ForeignTable: field.Type, | ||
ForeignColumns: field.RelationToFields, | ||
}) | ||
} | ||
} | ||
} | ||
|
||
return ret, nil | ||
} |
Oops, something went wrong.