Save information about the organization and user from ArcGIS

This commit is contained in:
Eli Ribble 2025-11-07 02:07:33 +00:00
parent 07d3b3ea76
commit a08cd87813
No known key found for this signature in database
20 changed files with 786 additions and 93 deletions

View file

@ -39,3 +39,14 @@ PSQL_DSN="postgresql://?host=/var/run/postgresql&sslmode=disable&dbname=nidus-sy
```
This will generate a bunch of files. They're already committed, you only need this if you change the database schema in some way.
### goose
This uses [goose](https://github.com/pressly/goose). You can use the goose command line to check status and make changes
```sh
> cd migrations
> GOOSE_DRIVER=postgres GOOSE_DBSTRING="dbname=nidus-sync sslmode=disable" goose status
> GOOSE_DRIVER=postgres GOOSE_DBSTRING="dbname=nidus-sync sslmode=disable" goose down
> GOOSE_DRIVER=postgres GOOSE_DBSTRING="dbname=nidus-sync sslmode=disable" goose up
```

@ -1 +1 @@
Subproject commit 5bc087a120a5dab09789b9abe913fd01b735dcff
Subproject commit a99b4a72b2bb3dcff642209f029eb4e7d746fa8d

View file

@ -6,6 +6,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
@ -19,6 +20,8 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/models"
"github.com/Gleipnir-Technology/nidus-sync/sql"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/jackc/pgx/v5"
)
var CodeVerifier string = "random_secure_string_min_43_chars_long_should_be_stored_in_session"
@ -63,7 +66,7 @@ func generateCodeVerifier() string {
}
// Find out what we can about this user
func getArcgisUserData(access_token string, expires time.Time, refresh_token string) {
func updateArcgisUserData(ctx context.Context, user *models.User, access_token string, expires time.Time, refresh_token string) {
client := arcgis.NewArcGIS(
arcgis.AuthenticatorOAuth{
AccessToken: access_token,
@ -71,15 +74,60 @@ func getArcgisUserData(access_token string, expires time.Time, refresh_token str
RefreshToken: refresh_token,
},
)
/*portal, err := client.PortalsSelf()
portal, err := client.PortalsSelf()
if err != nil {
slog.Error("Failed to get ArcGIS user data", slog.String("err", err.Error()))
return
}
slog.Info("Got portals data",
slog.String("Username", portal.User.Username),
slog.String("ID", portal.ID))
*/
slog.String("user_id", portal.User.ID),
slog.String("org_id", portal.User.OrgID),
slog.String("org_name", portal.Name),
slog.String("license_type_id", portal.User.UserLicenseTypeID))
_, err = sql.UpdateOauthTokenOrg(portal.User.ID, portal.User.UserLicenseTypeID, refresh_token).Exec(ctx, PGInstance.BobDB)
if err != nil {
slog.Error("Failed to update oauth token portal data", slog.String("err", err.Error()))
return
}
var org *models.Organization
orgs, err := models.Organizations.Query(models.SelectWhere.Organizations.ArcgisName.EQ(portal.Name)).All(ctx, PGInstance.BobDB)
switch len(orgs) {
case 0:
setter := models.OrganizationSetter{
Name: omitnull.From(portal.Name),
ArcgisID: omitnull.From(portal.User.OrgID),
ArcgisName: omitnull.From(portal.Name),
}
org, err = models.Organizations.Insert(&setter).One(ctx, PGInstance.BobDB)
if err != nil {
slog.Error("Failed to create new organization", slog.String("err", err.Error()))
return
}
slog.Info("Created new organization", slog.Int("org_id", int(org.ID)))
case 1:
org = orgs[0]
slog.Info("Organization already exists")
default:
slog.Error("Got too many organizations, bailing")
return
}
if err != nil {
LogErrorTypeInfo(err)
if errors.Is(err, pgx.ErrNoRows) {
} else {
slog.Error("Failed to query for existing org", slog.String("err", err.Error()))
return
}
}
err = org.AttachUser(ctx, PGInstance.BobDB, user)
if err != nil {
slog.Error("Failed to attach user to organization", slog.String("err", err.Error()), slog.Int("user_id", int(user.ID)), slog.Int("org_id", int(org.ID)))
return
}
search, err := client.Search("Fieldseeker")
if err != nil {
slog.Error("Failed to get search FieldseekerGIS data", slog.String("err", err.Error()))
@ -152,7 +200,7 @@ func handleOauthAccessCode(ctx context.Context, user *models.User, code string)
if err != nil {
return fmt.Errorf("Failed to save token to database: %v", err)
}
go getArcgisUserData(tokenResponse.AccessToken, expires, tokenResponse.RefreshToken)
go updateArcgisUserData(context.Background(), user, tokenResponse.AccessToken, expires, tokenResponse.RefreshToken)
return nil
}
@ -166,3 +214,7 @@ func hasFieldseekerConnection(ctx context.Context, user *models.User) (bool, err
func redirectURL() string {
return BaseURL + "/arcgis/oauth/callback"
}
// This is a goroutine that is in charge of getting Fieldseeker data and keeping it fresh.
func refreshFieldseekerData(newOauthCh <-chan int, done <-chan struct{}) {
}

View file

@ -69,6 +69,24 @@ var OauthTokens = Table[
Generated: false,
AutoIncr: false,
},
ArcgisID: column{
Name: "arcgis_id",
DBType: "text",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
ArcgisLicenseTypeID: column{
Name: "arcgis_license_type_id",
DBType: "text",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
},
Indexes: oauthTokenIndexes{
OauthTokenPkey: index{
@ -110,17 +128,19 @@ var OauthTokens = Table[
}
type oauthTokenColumns struct {
ID column
AccessToken column
Expires column
RefreshToken column
Username column
UserID column
ID column
AccessToken column
Expires column
RefreshToken column
Username column
UserID column
ArcgisID column
ArcgisLicenseTypeID column
}
func (c oauthTokenColumns) AsSlice() []column {
return []column{
c.ID, c.AccessToken, c.Expires, c.RefreshToken, c.Username, c.UserID,
c.ID, c.AccessToken, c.Expires, c.RefreshToken, c.Username, c.UserID, c.ArcgisID, c.ArcgisLicenseTypeID,
}
}

View file

@ -33,6 +33,24 @@ var Organizations = Table[
Generated: false,
AutoIncr: false,
},
ArcgisID: column{
Name: "arcgis_id",
DBType: "text",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
ArcgisName: column{
Name: "arcgis_name",
DBType: "text",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
},
Indexes: organizationIndexes{
OrganizationPkey: index{
@ -63,13 +81,15 @@ var Organizations = Table[
}
type organizationColumns struct {
ID column
Name column
ID column
Name column
ArcgisID column
ArcgisName column
}
func (c organizationColumns) AsSlice() []column {
return []column{
c.ID, c.Name,
c.ID, c.Name, c.ArcgisID, c.ArcgisName,
}
}

28
errors.go Normal file
View file

@ -0,0 +1,28 @@
package main
import (
"errors"
"log/slog"
"reflect"
)
func LogErrorTypeInfo(err error) {
if err == nil {
slog.Info("Error is nil")
return
}
// Log current error type
errType := reflect.TypeOf(err)
slog.Info("Error type info",
"type", errType.String(),
"pkgPath", errType.PkgPath(),
"error", err.Error())
// Recursively log wrapped errors
wrappedErr := errors.Unwrap(err)
if wrappedErr != nil {
slog.Info("Contains wrapped error")
LogErrorTypeInfo(wrappedErr)
}
}

View file

@ -76,6 +76,8 @@ func (f *Factory) FromExistingOauthToken(m *models.OauthToken) *OauthTokenTempla
o.RefreshToken = func() string { return m.RefreshToken }
o.Username = func() string { return m.Username }
o.UserID = func() int32 { return m.UserID }
o.ArcgisID = func() null.Val[string] { return m.ArcgisID }
o.ArcgisLicenseTypeID = func() null.Val[string] { return m.ArcgisLicenseTypeID }
ctx := context.Background()
if m.R.UserUser != nil {
@ -106,6 +108,8 @@ func (f *Factory) FromExistingOrganization(m *models.Organization) *Organization
o.ID = func() int32 { return m.ID }
o.Name = func() null.Val[string] { return m.Name }
o.ArcgisID = func() null.Val[string] { return m.ArcgisID }
o.ArcgisName = func() null.Val[string] { return m.ArcgisName }
ctx := context.Background()
if len(m.R.User) > 0 {

View file

@ -9,7 +9,9 @@ import (
"time"
models "github.com/Gleipnir-Technology/nidus-sync/models"
"github.com/aarondl/opt/null"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/jaswdr/faker/v2"
"github.com/stephenafamo/bob"
)
@ -35,12 +37,14 @@ func (mods OauthTokenModSlice) Apply(ctx context.Context, n *OauthTokenTemplate)
// OauthTokenTemplate is an object representing the database table.
// all columns are optional and should be set by mods
type OauthTokenTemplate struct {
ID func() int32
AccessToken func() string
Expires func() time.Time
RefreshToken func() string
Username func() string
UserID func() int32
ID func() int32
AccessToken func() string
Expires func() time.Time
RefreshToken func() string
Username func() string
UserID func() int32
ArcgisID func() null.Val[string]
ArcgisLicenseTypeID func() null.Val[string]
r oauthTokenR
f *Factory
@ -103,6 +107,14 @@ func (o OauthTokenTemplate) BuildSetter() *models.OauthTokenSetter {
val := o.UserID()
m.UserID = omit.From(val)
}
if o.ArcgisID != nil {
val := o.ArcgisID()
m.ArcgisID = omitnull.FromNull(val)
}
if o.ArcgisLicenseTypeID != nil {
val := o.ArcgisLicenseTypeID()
m.ArcgisLicenseTypeID = omitnull.FromNull(val)
}
return m
}
@ -143,6 +155,12 @@ func (o OauthTokenTemplate) Build() *models.OauthToken {
if o.UserID != nil {
m.UserID = o.UserID()
}
if o.ArcgisID != nil {
m.ArcgisID = o.ArcgisID()
}
if o.ArcgisLicenseTypeID != nil {
m.ArcgisLicenseTypeID = o.ArcgisLicenseTypeID()
}
o.setModelRels(m)
@ -308,6 +326,8 @@ func (m oauthTokenMods) RandomizeAllColumns(f *faker.Faker) OauthTokenMod {
OauthTokenMods.RandomRefreshToken(f),
OauthTokenMods.RandomUsername(f),
OauthTokenMods.RandomUserID(f),
OauthTokenMods.RandomArcgisID(f),
OauthTokenMods.RandomArcgisLicenseTypeID(f),
}
}
@ -497,6 +517,112 @@ func (m oauthTokenMods) RandomUserID(f *faker.Faker) OauthTokenMod {
})
}
// Set the model columns to this value
func (m oauthTokenMods) ArcgisID(val null.Val[string]) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisID = func() null.Val[string] { return val }
})
}
// Set the Column from the function
func (m oauthTokenMods) ArcgisIDFunc(f func() null.Val[string]) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisID = f
})
}
// Clear any values for the column
func (m oauthTokenMods) UnsetArcgisID() OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisID = nil
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is sometimes null
func (m oauthTokenMods) RandomArcgisID(f *faker.Faker) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisID = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is never null
func (m oauthTokenMods) RandomArcgisIDNotNull(f *faker.Faker) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisID = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
// Set the model columns to this value
func (m oauthTokenMods) ArcgisLicenseTypeID(val null.Val[string]) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisLicenseTypeID = func() null.Val[string] { return val }
})
}
// Set the Column from the function
func (m oauthTokenMods) ArcgisLicenseTypeIDFunc(f func() null.Val[string]) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisLicenseTypeID = f
})
}
// Clear any values for the column
func (m oauthTokenMods) UnsetArcgisLicenseTypeID() OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisLicenseTypeID = nil
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is sometimes null
func (m oauthTokenMods) RandomArcgisLicenseTypeID(f *faker.Faker) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisLicenseTypeID = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is never null
func (m oauthTokenMods) RandomArcgisLicenseTypeIDNotNull(f *faker.Faker) OauthTokenMod {
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
o.ArcgisLicenseTypeID = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
func (m oauthTokenMods) WithParentsCascading() OauthTokenMod {
return OauthTokenModFunc(func(ctx context.Context, o *OauthTokenTemplate) {
if isDone, _ := oauthTokenWithParentsCascadingCtx.Value(ctx); isDone {

View file

@ -36,8 +36,10 @@ func (mods OrganizationModSlice) Apply(ctx context.Context, n *OrganizationTempl
// OrganizationTemplate is an object representing the database table.
// all columns are optional and should be set by mods
type OrganizationTemplate struct {
ID func() int32
Name func() null.Val[string]
ID func() int32
Name func() null.Val[string]
ArcgisID func() null.Val[string]
ArcgisName func() null.Val[string]
r organizationR
f *Factory
@ -91,6 +93,14 @@ func (o OrganizationTemplate) BuildSetter() *models.OrganizationSetter {
val := o.Name()
m.Name = omitnull.FromNull(val)
}
if o.ArcgisID != nil {
val := o.ArcgisID()
m.ArcgisID = omitnull.FromNull(val)
}
if o.ArcgisName != nil {
val := o.ArcgisName()
m.ArcgisName = omitnull.FromNull(val)
}
return m
}
@ -119,6 +129,12 @@ func (o OrganizationTemplate) Build() *models.Organization {
if o.Name != nil {
m.Name = o.Name()
}
if o.ArcgisID != nil {
m.ArcgisID = o.ArcgisID()
}
if o.ArcgisName != nil {
m.ArcgisName = o.ArcgisName()
}
o.setModelRels(m)
@ -261,6 +277,8 @@ func (m organizationMods) RandomizeAllColumns(f *faker.Faker) OrganizationMod {
return OrganizationModSlice{
OrganizationMods.RandomID(f),
OrganizationMods.RandomName(f),
OrganizationMods.RandomArcgisID(f),
OrganizationMods.RandomArcgisName(f),
}
}
@ -348,6 +366,112 @@ func (m organizationMods) RandomNameNotNull(f *faker.Faker) OrganizationMod {
})
}
// Set the model columns to this value
func (m organizationMods) ArcgisID(val null.Val[string]) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisID = func() null.Val[string] { return val }
})
}
// Set the Column from the function
func (m organizationMods) ArcgisIDFunc(f func() null.Val[string]) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisID = f
})
}
// Clear any values for the column
func (m organizationMods) UnsetArcgisID() OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisID = nil
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is sometimes null
func (m organizationMods) RandomArcgisID(f *faker.Faker) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisID = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is never null
func (m organizationMods) RandomArcgisIDNotNull(f *faker.Faker) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisID = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
// Set the model columns to this value
func (m organizationMods) ArcgisName(val null.Val[string]) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisName = func() null.Val[string] { return val }
})
}
// Set the Column from the function
func (m organizationMods) ArcgisNameFunc(f func() null.Val[string]) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisName = f
})
}
// Clear any values for the column
func (m organizationMods) UnsetArcgisName() OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisName = nil
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is sometimes null
func (m organizationMods) RandomArcgisName(f *faker.Faker) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisName = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
// Generates a random value for the column using the given faker
// if faker is nil, a default faker is used
// The generated value is never null
func (m organizationMods) RandomArcgisNameNotNull(f *faker.Faker) OrganizationMod {
return OrganizationModFunc(func(_ context.Context, o *OrganizationTemplate) {
o.ArcgisName = func() null.Val[string] {
if f == nil {
f = &defaultFaker
}
val := random_string(f)
return null.From(val)
}
})
}
func (m organizationMods) WithParentsCascading() OrganizationMod {
return OrganizationModFunc(func(ctx context.Context, o *OrganizationTemplate) {
if isDone, _ := organizationWithParentsCascadingCtx.Value(ctx); isDone {

View file

@ -76,6 +76,9 @@ func main() {
localFS := http.Dir("./static")
FileServer(r, "/static", localFS, embeddedStaticFS, "static")
newTokenChannel := make(chan int)
endChannel := make(chan struct{})
go refreshFieldseekerData(newTokenChannel, endChannel)
log.Printf("Serving on %s", bind)
log.Fatal(http.ListenAndServe(bind, r))
}

View file

@ -0,0 +1,10 @@
-- +goose Up
ALTER TABLE organization ADD COLUMN arcgis_id TEXT;
ALTER TABLE organization ADD COLUMN arcgis_name TEXT;
ALTER TABLE oauth_token ADD COLUMN arcgis_id TEXT;
ALTER TABLE oauth_token ADD COLUMN arcgis_license_type_id TEXT;
-- +goose Down
ALTER TABLE organization DROP COLUMN arcgis_id;
ALTER TABLE organization DROP COLUMN arcgis_name;
ALTER TABLE oauth_token DROP COLUMN arcgis_id;
ALTER TABLE oauth_token DROP COLUMN arcgis_license_type_id;

View file

@ -9,7 +9,9 @@ import (
"io"
"time"
"github.com/aarondl/opt/null"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/psql"
"github.com/stephenafamo/bob/dialect/psql/dialect"
@ -24,12 +26,14 @@ import (
// OauthToken is an object representing the database table.
type OauthToken struct {
ID int32 `db:"id,pk" `
AccessToken string `db:"access_token" `
Expires time.Time `db:"expires" `
RefreshToken string `db:"refresh_token" `
Username string `db:"username" `
UserID int32 `db:"user_id" `
ID int32 `db:"id,pk" `
AccessToken string `db:"access_token" `
Expires time.Time `db:"expires" `
RefreshToken string `db:"refresh_token" `
Username string `db:"username" `
UserID int32 `db:"user_id" `
ArcgisID null.Val[string] `db:"arcgis_id" `
ArcgisLicenseTypeID null.Val[string] `db:"arcgis_license_type_id" `
R oauthTokenR `db:"-" `
}
@ -52,27 +56,31 @@ type oauthTokenR struct {
func buildOauthTokenColumns(alias string) oauthTokenColumns {
return oauthTokenColumns{
ColumnsExpr: expr.NewColumnsExpr(
"id", "access_token", "expires", "refresh_token", "username", "user_id",
"id", "access_token", "expires", "refresh_token", "username", "user_id", "arcgis_id", "arcgis_license_type_id",
).WithParent("oauth_token"),
tableAlias: alias,
ID: psql.Quote(alias, "id"),
AccessToken: psql.Quote(alias, "access_token"),
Expires: psql.Quote(alias, "expires"),
RefreshToken: psql.Quote(alias, "refresh_token"),
Username: psql.Quote(alias, "username"),
UserID: psql.Quote(alias, "user_id"),
tableAlias: alias,
ID: psql.Quote(alias, "id"),
AccessToken: psql.Quote(alias, "access_token"),
Expires: psql.Quote(alias, "expires"),
RefreshToken: psql.Quote(alias, "refresh_token"),
Username: psql.Quote(alias, "username"),
UserID: psql.Quote(alias, "user_id"),
ArcgisID: psql.Quote(alias, "arcgis_id"),
ArcgisLicenseTypeID: psql.Quote(alias, "arcgis_license_type_id"),
}
}
type oauthTokenColumns struct {
expr.ColumnsExpr
tableAlias string
ID psql.Expression
AccessToken psql.Expression
Expires psql.Expression
RefreshToken psql.Expression
Username psql.Expression
UserID psql.Expression
tableAlias string
ID psql.Expression
AccessToken psql.Expression
Expires psql.Expression
RefreshToken psql.Expression
Username psql.Expression
UserID psql.Expression
ArcgisID psql.Expression
ArcgisLicenseTypeID psql.Expression
}
func (c oauthTokenColumns) Alias() string {
@ -87,16 +95,18 @@ func (oauthTokenColumns) AliasedAs(alias string) oauthTokenColumns {
// All values are optional, and do not have to be set
// Generated columns are not included
type OauthTokenSetter struct {
ID omit.Val[int32] `db:"id,pk" `
AccessToken omit.Val[string] `db:"access_token" `
Expires omit.Val[time.Time] `db:"expires" `
RefreshToken omit.Val[string] `db:"refresh_token" `
Username omit.Val[string] `db:"username" `
UserID omit.Val[int32] `db:"user_id" `
ID omit.Val[int32] `db:"id,pk" `
AccessToken omit.Val[string] `db:"access_token" `
Expires omit.Val[time.Time] `db:"expires" `
RefreshToken omit.Val[string] `db:"refresh_token" `
Username omit.Val[string] `db:"username" `
UserID omit.Val[int32] `db:"user_id" `
ArcgisID omitnull.Val[string] `db:"arcgis_id" `
ArcgisLicenseTypeID omitnull.Val[string] `db:"arcgis_license_type_id" `
}
func (s OauthTokenSetter) SetColumns() []string {
vals := make([]string, 0, 6)
vals := make([]string, 0, 8)
if s.ID.IsValue() {
vals = append(vals, "id")
}
@ -115,6 +125,12 @@ func (s OauthTokenSetter) SetColumns() []string {
if s.UserID.IsValue() {
vals = append(vals, "user_id")
}
if !s.ArcgisID.IsUnset() {
vals = append(vals, "arcgis_id")
}
if !s.ArcgisLicenseTypeID.IsUnset() {
vals = append(vals, "arcgis_license_type_id")
}
return vals
}
@ -137,6 +153,12 @@ func (s OauthTokenSetter) Overwrite(t *OauthToken) {
if s.UserID.IsValue() {
t.UserID = s.UserID.MustGet()
}
if !s.ArcgisID.IsUnset() {
t.ArcgisID = s.ArcgisID.MustGetNull()
}
if !s.ArcgisLicenseTypeID.IsUnset() {
t.ArcgisLicenseTypeID = s.ArcgisLicenseTypeID.MustGetNull()
}
}
func (s *OauthTokenSetter) Apply(q *dialect.InsertQuery) {
@ -145,7 +167,7 @@ func (s *OauthTokenSetter) Apply(q *dialect.InsertQuery) {
})
q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) {
vals := make([]bob.Expression, 6)
vals := make([]bob.Expression, 8)
if s.ID.IsValue() {
vals[0] = psql.Arg(s.ID.MustGet())
} else {
@ -182,6 +204,18 @@ func (s *OauthTokenSetter) Apply(q *dialect.InsertQuery) {
vals[5] = psql.Raw("DEFAULT")
}
if !s.ArcgisID.IsUnset() {
vals[6] = psql.Arg(s.ArcgisID.MustGetNull())
} else {
vals[6] = psql.Raw("DEFAULT")
}
if !s.ArcgisLicenseTypeID.IsUnset() {
vals[7] = psql.Arg(s.ArcgisLicenseTypeID.MustGetNull())
} else {
vals[7] = psql.Raw("DEFAULT")
}
return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "")
}))
}
@ -191,7 +225,7 @@ func (s OauthTokenSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] {
}
func (s OauthTokenSetter) Expressions(prefix ...string) []bob.Expression {
exprs := make([]bob.Expression, 0, 6)
exprs := make([]bob.Expression, 0, 8)
if s.ID.IsValue() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
@ -235,6 +269,20 @@ func (s OauthTokenSetter) Expressions(prefix ...string) []bob.Expression {
}})
}
if !s.ArcgisID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "arcgis_id")...),
psql.Arg(s.ArcgisID),
}})
}
if !s.ArcgisLicenseTypeID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "arcgis_license_type_id")...),
psql.Arg(s.ArcgisLicenseTypeID),
}})
}
return exprs
}
@ -534,12 +582,14 @@ func (oauthToken0 *OauthToken) AttachUserUser(ctx context.Context, exec bob.Exec
}
type oauthTokenWhere[Q psql.Filterable] struct {
ID psql.WhereMod[Q, int32]
AccessToken psql.WhereMod[Q, string]
Expires psql.WhereMod[Q, time.Time]
RefreshToken psql.WhereMod[Q, string]
Username psql.WhereMod[Q, string]
UserID psql.WhereMod[Q, int32]
ID psql.WhereMod[Q, int32]
AccessToken psql.WhereMod[Q, string]
Expires psql.WhereMod[Q, time.Time]
RefreshToken psql.WhereMod[Q, string]
Username psql.WhereMod[Q, string]
UserID psql.WhereMod[Q, int32]
ArcgisID psql.WhereNullMod[Q, string]
ArcgisLicenseTypeID psql.WhereNullMod[Q, string]
}
func (oauthTokenWhere[Q]) AliasedAs(alias string) oauthTokenWhere[Q] {
@ -548,12 +598,14 @@ func (oauthTokenWhere[Q]) AliasedAs(alias string) oauthTokenWhere[Q] {
func buildOauthTokenWhere[Q psql.Filterable](cols oauthTokenColumns) oauthTokenWhere[Q] {
return oauthTokenWhere[Q]{
ID: psql.Where[Q, int32](cols.ID),
AccessToken: psql.Where[Q, string](cols.AccessToken),
Expires: psql.Where[Q, time.Time](cols.Expires),
RefreshToken: psql.Where[Q, string](cols.RefreshToken),
Username: psql.Where[Q, string](cols.Username),
UserID: psql.Where[Q, int32](cols.UserID),
ID: psql.Where[Q, int32](cols.ID),
AccessToken: psql.Where[Q, string](cols.AccessToken),
Expires: psql.Where[Q, time.Time](cols.Expires),
RefreshToken: psql.Where[Q, string](cols.RefreshToken),
Username: psql.Where[Q, string](cols.Username),
UserID: psql.Where[Q, int32](cols.UserID),
ArcgisID: psql.WhereNull[Q, string](cols.ArcgisID),
ArcgisLicenseTypeID: psql.WhereNull[Q, string](cols.ArcgisLicenseTypeID),
}
}

View file

@ -25,8 +25,10 @@ import (
// Organization is an object representing the database table.
type Organization struct {
ID int32 `db:"id,pk" `
Name null.Val[string] `db:"name" `
ID int32 `db:"id,pk" `
Name null.Val[string] `db:"name" `
ArcgisID null.Val[string] `db:"arcgis_id" `
ArcgisName null.Val[string] `db:"arcgis_name" `
R organizationR `db:"-" `
}
@ -49,11 +51,13 @@ type organizationR struct {
func buildOrganizationColumns(alias string) organizationColumns {
return organizationColumns{
ColumnsExpr: expr.NewColumnsExpr(
"id", "name",
"id", "name", "arcgis_id", "arcgis_name",
).WithParent("organization"),
tableAlias: alias,
ID: psql.Quote(alias, "id"),
Name: psql.Quote(alias, "name"),
ArcgisID: psql.Quote(alias, "arcgis_id"),
ArcgisName: psql.Quote(alias, "arcgis_name"),
}
}
@ -62,6 +66,8 @@ type organizationColumns struct {
tableAlias string
ID psql.Expression
Name psql.Expression
ArcgisID psql.Expression
ArcgisName psql.Expression
}
func (c organizationColumns) Alias() string {
@ -76,18 +82,26 @@ func (organizationColumns) AliasedAs(alias string) organizationColumns {
// All values are optional, and do not have to be set
// Generated columns are not included
type OrganizationSetter struct {
ID omit.Val[int32] `db:"id,pk" `
Name omitnull.Val[string] `db:"name" `
ID omit.Val[int32] `db:"id,pk" `
Name omitnull.Val[string] `db:"name" `
ArcgisID omitnull.Val[string] `db:"arcgis_id" `
ArcgisName omitnull.Val[string] `db:"arcgis_name" `
}
func (s OrganizationSetter) SetColumns() []string {
vals := make([]string, 0, 2)
vals := make([]string, 0, 4)
if s.ID.IsValue() {
vals = append(vals, "id")
}
if !s.Name.IsUnset() {
vals = append(vals, "name")
}
if !s.ArcgisID.IsUnset() {
vals = append(vals, "arcgis_id")
}
if !s.ArcgisName.IsUnset() {
vals = append(vals, "arcgis_name")
}
return vals
}
@ -98,6 +112,12 @@ func (s OrganizationSetter) Overwrite(t *Organization) {
if !s.Name.IsUnset() {
t.Name = s.Name.MustGetNull()
}
if !s.ArcgisID.IsUnset() {
t.ArcgisID = s.ArcgisID.MustGetNull()
}
if !s.ArcgisName.IsUnset() {
t.ArcgisName = s.ArcgisName.MustGetNull()
}
}
func (s *OrganizationSetter) Apply(q *dialect.InsertQuery) {
@ -106,7 +126,7 @@ func (s *OrganizationSetter) Apply(q *dialect.InsertQuery) {
})
q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) {
vals := make([]bob.Expression, 2)
vals := make([]bob.Expression, 4)
if s.ID.IsValue() {
vals[0] = psql.Arg(s.ID.MustGet())
} else {
@ -119,6 +139,18 @@ func (s *OrganizationSetter) Apply(q *dialect.InsertQuery) {
vals[1] = psql.Raw("DEFAULT")
}
if !s.ArcgisID.IsUnset() {
vals[2] = psql.Arg(s.ArcgisID.MustGetNull())
} else {
vals[2] = psql.Raw("DEFAULT")
}
if !s.ArcgisName.IsUnset() {
vals[3] = psql.Arg(s.ArcgisName.MustGetNull())
} else {
vals[3] = psql.Raw("DEFAULT")
}
return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "")
}))
}
@ -128,7 +160,7 @@ func (s OrganizationSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] {
}
func (s OrganizationSetter) Expressions(prefix ...string) []bob.Expression {
exprs := make([]bob.Expression, 0, 2)
exprs := make([]bob.Expression, 0, 4)
if s.ID.IsValue() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
@ -144,6 +176,20 @@ func (s OrganizationSetter) Expressions(prefix ...string) []bob.Expression {
}})
}
if !s.ArcgisID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "arcgis_id")...),
psql.Arg(s.ArcgisID),
}})
}
if !s.ArcgisName.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "arcgis_name")...),
psql.Arg(s.ArcgisName),
}})
}
return exprs
}
@ -463,8 +509,10 @@ func (organization0 *Organization) AttachUser(ctx context.Context, exec bob.Exec
}
type organizationWhere[Q psql.Filterable] struct {
ID psql.WhereMod[Q, int32]
Name psql.WhereNullMod[Q, string]
ID psql.WhereMod[Q, int32]
Name psql.WhereNullMod[Q, string]
ArcgisID psql.WhereNullMod[Q, string]
ArcgisName psql.WhereNullMod[Q, string]
}
func (organizationWhere[Q]) AliasedAs(alias string) organizationWhere[Q] {
@ -473,8 +521,10 @@ func (organizationWhere[Q]) AliasedAs(alias string) organizationWhere[Q] {
func buildOrganizationWhere[Q psql.Filterable](cols organizationColumns) organizationWhere[Q] {
return organizationWhere[Q]{
ID: psql.Where[Q, int32](cols.ID),
Name: psql.WhereNull[Q, string](cols.Name),
ID: psql.Where[Q, int32](cols.ID),
Name: psql.WhereNull[Q, string](cols.Name),
ArcgisID: psql.WhereNull[Q, string](cols.ArcgisID),
ArcgisName: psql.WhereNull[Q, string](cols.ArcgisName),
}
}

View file

@ -10,6 +10,7 @@ import (
"iter"
"time"
"github.com/aarondl/opt/null"
"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/psql"
"github.com/stephenafamo/bob/dialect/psql/dialect"
@ -20,7 +21,7 @@ import (
//go:embed oauth_by_user_id.bob.sql
var formattedQueries_oauth_by_user_id string
var oauthTokenByUserIdSQL = formattedQueries_oauth_by_user_id[156:440]
var oauthTokenByUserIdSQL = formattedQueries_oauth_by_user_id[156:550]
type OauthTokenByUserIdQuery = orm.ModQuery[*dialect.SelectQuery, oauthTokenByUserId, OauthTokenByUserIdRow, []OauthTokenByUserIdRow, oauthTokenByUserIdTransformer]
@ -47,6 +48,8 @@ func OauthTokenByUserId(UserID int32) *OauthTokenByUserIdQuery {
row.ScheduleScanByIndex(3, &t.RefreshToken)
row.ScheduleScanByIndex(4, &t.Username)
row.ScheduleScanByIndex(5, &t.UserID)
row.ScheduleScanByIndex(6, &t.ArcgisID)
row.ScheduleScanByIndex(7, &t.ArcgisLicenseTypeID)
return &t, nil
}, func(v any) (OauthTokenByUserIdRow, error) {
return *(v.(*OauthTokenByUserIdRow)), nil
@ -54,20 +57,22 @@ func OauthTokenByUserId(UserID int32) *OauthTokenByUserIdQuery {
},
},
Mod: bob.ModFunc[*dialect.SelectQuery](func(q *dialect.SelectQuery) {
q.AppendSelect(expressionTypArgs.subExpr(7, 247))
q.SetTable(expressionTypArgs.subExpr(253, 264))
q.AppendWhere(expressionTypArgs.subExpr(272, 284))
q.AppendSelect(expressionTypArgs.subExpr(7, 357))
q.SetTable(expressionTypArgs.subExpr(363, 374))
q.AppendWhere(expressionTypArgs.subExpr(382, 394))
}),
}
}
type OauthTokenByUserIdRow = struct {
ID int32 `db:"id"`
AccessToken string `db:"access_token"`
Expires time.Time `db:"expires"`
RefreshToken string `db:"refresh_token"`
Username string `db:"username"`
UserID int32 `db:"user_id"`
ID int32 `db:"id"`
AccessToken string `db:"access_token"`
Expires time.Time `db:"expires"`
RefreshToken string `db:"refresh_token"`
Username string `db:"username"`
UserID int32 `db:"user_id"`
ArcgisID null.Val[string] `db:"arcgis_id"`
ArcgisLicenseTypeID null.Val[string] `db:"arcgis_license_type_id"`
}
type oauthTokenByUserIdTransformer = bob.SliceTransformer[OauthTokenByUserIdRow, []OauthTokenByUserIdRow]
@ -80,8 +85,8 @@ func (o oauthTokenByUserId) args() iter.Seq[orm.ArgWithPosition] {
return func(yield func(arg orm.ArgWithPosition) bool) {
if !yield(orm.ArgWithPosition{
Name: "userID",
Start: 282,
Stop: 284,
Start: 392,
Stop: 394,
Expression: o.UserID,
}) {
return

View file

@ -2,5 +2,5 @@
-- This file is meant to be re-generated in place and/or deleted at any time.
-- OauthTokenByUserId
SELECT "oauth_token"."id" AS "id", "oauth_token"."access_token" AS "access_token", "oauth_token"."expires" AS "expires", "oauth_token"."refresh_token" AS "refresh_token", "oauth_token"."username" AS "username", "oauth_token"."user_id" AS "user_id" FROM oauth_token WHERE
SELECT "oauth_token"."id" AS "id", "oauth_token"."access_token" AS "access_token", "oauth_token"."expires" AS "expires", "oauth_token"."refresh_token" AS "refresh_token", "oauth_token"."username" AS "username", "oauth_token"."user_id" AS "user_id", "oauth_token"."arcgis_id" AS "arcgis_id", "oauth_token"."arcgis_license_type_id" AS "arcgis_license_type_id" FROM oauth_token WHERE
user_id = $1;

View file

@ -84,8 +84,8 @@ func TestOauthTokenByUserId(t *testing.T) {
t.Fatal(err)
}
if len(columns) != 6 {
t.Fatalf("expected %d columns, got %d", 6, len(columns))
if len(columns) != 8 {
t.Fatalf("expected %d columns, got %d", 8, len(columns))
}
if columns[0] != "id" {
@ -111,5 +111,13 @@ func TestOauthTokenByUserId(t *testing.T) {
if columns[5] != "user_id" {
t.Fatalf("expected column %d to be %s, got %s", 5, "user_id", columns[5])
}
if columns[6] != "arcgis_id" {
t.Fatalf("expected column %d to be %s, got %s", 6, "arcgis_id", columns[6])
}
if columns[7] != "arcgis_license_type_id" {
t.Fatalf("expected column %d to be %s, got %s", 7, "arcgis_license_type_id", columns[7])
}
})
}

View file

@ -0,0 +1,95 @@
// Code generated by BobGen psql v0.41.1. DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sql
import (
"context"
_ "embed"
"io"
"iter"
"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/psql"
"github.com/stephenafamo/bob/dialect/psql/dialect"
"github.com/stephenafamo/bob/orm"
)
//go:embed update_oauth_org.bob.sql
var formattedQueries_update_oauth_org string
var updateOauthTokenOrgSQL = formattedQueries_update_oauth_org[157:249]
type UpdateOauthTokenOrgQuery = orm.ModExecQuery[*dialect.UpdateQuery, updateOauthTokenOrg]
func UpdateOauthTokenOrg(ArcgisID string, ArcgisLicenseTypeID string, RefreshToken string) *UpdateOauthTokenOrgQuery {
var expressionTypArgs updateOauthTokenOrg
expressionTypArgs.ArcgisID = psql.Arg(ArcgisID)
expressionTypArgs.ArcgisLicenseTypeID = psql.Arg(ArcgisLicenseTypeID)
expressionTypArgs.RefreshToken = psql.Arg(RefreshToken)
return &UpdateOauthTokenOrgQuery{
ExecQuery: orm.ExecQuery[updateOauthTokenOrg]{
BaseQuery: bob.BaseQuery[updateOauthTokenOrg]{
Expression: expressionTypArgs,
Dialect: dialect.Dialect,
QueryType: bob.QueryTypeUpdate,
},
},
Mod: bob.ModFunc[*dialect.UpdateQuery](func(q *dialect.UpdateQuery) {
q.Table.Expression = expressionTypArgs.subExpr(7, 18)
q.AppendSet(expressionTypArgs.subExpr(23, 66))
q.AppendWhere(expressionTypArgs.subExpr(74, 92))
}),
}
}
type updateOauthTokenOrg struct {
ArcgisID bob.Expression
ArcgisLicenseTypeID bob.Expression
RefreshToken bob.Expression
}
func (o updateOauthTokenOrg) args() iter.Seq[orm.ArgWithPosition] {
return func(yield func(arg orm.ArgWithPosition) bool) {
if !yield(orm.ArgWithPosition{
Name: "arcgisID",
Start: 35,
Stop: 37,
Expression: o.ArcgisID,
}) {
return
}
if !yield(orm.ArgWithPosition{
Name: "arcgisLicenseTypeID",
Start: 64,
Stop: 66,
Expression: o.ArcgisLicenseTypeID,
}) {
return
}
if !yield(orm.ArgWithPosition{
Name: "refreshToken",
Start: 90,
Stop: 92,
Expression: o.RefreshToken,
}) {
return
}
}
}
func (o updateOauthTokenOrg) raw(from, to int) string {
return updateOauthTokenOrgSQL[from:to]
}
func (o updateOauthTokenOrg) subExpr(from, to int) bob.Expression {
return orm.ArgsToExpression(updateOauthTokenOrgSQL, from, to, o.args())
}
func (o updateOauthTokenOrg) WriteSQL(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) {
return o.subExpr(0, len(updateOauthTokenOrgSQL)).WriteSQL(ctx, w, d, start)
}

View file

@ -0,0 +1,6 @@
-- Code generated by BobGen psql v0.41.1. DO NOT EDIT.
-- This file is meant to be re-generated in place and/or deleted at any time.
-- UpdateOauthTokenOrg
UPDATE oauth_token SET arcgis_id = $1, arcgis_license_type_id = $2
WHERE refresh_token = $3;

View file

@ -0,0 +1,76 @@
// Code generated by BobGen psql v0.41.1. DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sql
import (
"context"
"fmt"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/psql"
testutils "github.com/stephenafamo/bob/test/utils"
)
func TestUpdateOauthTokenOrg(t *testing.T) {
t.Run("Base", func(t *testing.T) {
var sb strings.Builder
query := UpdateOauthTokenOrg(random_string(nil), random_string(nil), random_string(nil))
if _, err := query.WriteQuery(t.Context(), &sb, 1); err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(updateOauthTokenOrgSQL, sb.String()); diff != "" {
t.Fatalf("unexpected result (-got +want):\n%s", diff)
}
})
t.Run("Mod", func(t *testing.T) {
var sb strings.Builder
query := UpdateOauthTokenOrg(random_string(nil), random_string(nil), random_string(nil))
if _, err := psql.Update(query).WriteQuery(t.Context(), &sb, 1); err != nil {
t.Fatal(err)
}
queryDiff, err := testutils.QueryDiff(updateOauthTokenOrgSQL, sb.String(), formatQuery)
if err != nil {
t.Fatal(err)
}
if queryDiff != "" {
fmt.Println(sb.String())
t.Fatalf("unexpected result (-got +want):\n%s", queryDiff)
}
})
t.Run("Exec", func(t *testing.T) {
if testDB == nil {
t.Skip("skipping test, no DSN provided")
}
ctxTx, cancel := context.WithCancel(t.Context())
defer cancel()
tx, err := testDB.Begin(ctxTx)
if err != nil {
t.Fatalf("Error starting transaction: %v", err)
}
defer func() {
if err := tx.Rollback(ctxTx); err != nil {
t.Fatalf("Error rolling back transaction: %v", err)
}
}()
query := psql.Update(UpdateOauthTokenOrg(random_string(nil), random_string(nil), random_string(nil)))
if _, err := bob.Exec(ctxTx, tx, query); err != nil {
t.Fatal(err)
}
})
}

3
sql/update_oauth_org.sql Normal file
View file

@ -0,0 +1,3 @@
-- UpdateOauthTokenOrg
UPDATE oauth_token SET arcgis_id = $1, arcgis_license_type_id = $2
WHERE refresh_token = $3;