mirror of
https://github.com/holos-run/holos.git
synced 2026-03-20 09:15:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64a117b0c3 | ||
|
|
cf006be9cf | ||
|
|
45ad3d8e63 | ||
|
|
441c968c4f | ||
|
|
99f2763fdf | ||
|
|
1312395a11 | ||
|
|
615f147bcb |
16
hack/setup/bare
Executable file
16
hack/setup/bare
Executable file
@@ -0,0 +1,16 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
TOPLEVEL="$(cd $(dirname "$0") && git rev-parse --show-toplevel)"
|
||||
|
||||
host="jeff.app.dev.k2.holos.run:443"
|
||||
|
||||
read -p "Reset all data in $host? " choice
|
||||
case "$choice" in
|
||||
y|Y) echo "proceeding...";;
|
||||
*) exit 1;;
|
||||
esac
|
||||
|
||||
|
||||
grpcurl -H "x-oidc-id-token: $(holos token)" $host holos.v1alpha1.SystemService.DropTables
|
||||
grpcurl -H "x-oidc-id-token: $(holos token)" $host holos.v1alpha1.SystemService.SeedDatabase
|
||||
@@ -38,8 +38,8 @@ var (
|
||||
{Name: "updated_at", Type: field.TypeTime},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "display_name", Type: field.TypeString},
|
||||
{Name: "config_form", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "config_values", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "config_form", Type: field.TypeJSON, Nullable: true},
|
||||
{Name: "config_values", Type: field.TypeJSON, Nullable: true},
|
||||
{Name: "config_cue", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "config_definition", Type: field.TypeString, Nullable: true},
|
||||
{Name: "creator_id", Type: field.TypeUUID},
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -812,8 +813,8 @@ type PlatformMutation struct {
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
display_name *string
|
||||
config_form *[]byte
|
||||
config_values *[]byte
|
||||
config_form **holos.PlatformForm
|
||||
config_values **holos.ConfigValues
|
||||
config_cue *[]byte
|
||||
config_definition *string
|
||||
clearedFields map[string]struct{}
|
||||
@@ -1147,12 +1148,12 @@ func (m *PlatformMutation) ResetCreatorID() {
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (m *PlatformMutation) SetConfigForm(b []byte) {
|
||||
m.config_form = &b
|
||||
func (m *PlatformMutation) SetConfigForm(hf *holos.PlatformForm) {
|
||||
m.config_form = &hf
|
||||
}
|
||||
|
||||
// ConfigForm returns the value of the "config_form" field in the mutation.
|
||||
func (m *PlatformMutation) ConfigForm() (r []byte, exists bool) {
|
||||
func (m *PlatformMutation) ConfigForm() (r *holos.PlatformForm, exists bool) {
|
||||
v := m.config_form
|
||||
if v == nil {
|
||||
return
|
||||
@@ -1163,7 +1164,7 @@ func (m *PlatformMutation) ConfigForm() (r []byte, exists bool) {
|
||||
// OldConfigForm returns the old "config_form" field's value of the Platform entity.
|
||||
// If the Platform object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PlatformMutation) OldConfigForm(ctx context.Context) (v []byte, err error) {
|
||||
func (m *PlatformMutation) OldConfigForm(ctx context.Context) (v *holos.PlatformForm, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldConfigForm is only allowed on UpdateOne operations")
|
||||
}
|
||||
@@ -1196,12 +1197,12 @@ func (m *PlatformMutation) ResetConfigForm() {
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (m *PlatformMutation) SetConfigValues(b []byte) {
|
||||
m.config_values = &b
|
||||
func (m *PlatformMutation) SetConfigValues(hv *holos.ConfigValues) {
|
||||
m.config_values = &hv
|
||||
}
|
||||
|
||||
// ConfigValues returns the value of the "config_values" field in the mutation.
|
||||
func (m *PlatformMutation) ConfigValues() (r []byte, exists bool) {
|
||||
func (m *PlatformMutation) ConfigValues() (r *holos.ConfigValues, exists bool) {
|
||||
v := m.config_values
|
||||
if v == nil {
|
||||
return
|
||||
@@ -1212,7 +1213,7 @@ func (m *PlatformMutation) ConfigValues() (r []byte, exists bool) {
|
||||
// OldConfigValues returns the old "config_values" field's value of the Platform entity.
|
||||
// If the Platform object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PlatformMutation) OldConfigValues(ctx context.Context) (v []byte, err error) {
|
||||
func (m *PlatformMutation) OldConfigValues(ctx context.Context) (v *holos.ConfigValues, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldConfigValues is only allowed on UpdateOne operations")
|
||||
}
|
||||
@@ -1583,14 +1584,14 @@ func (m *PlatformMutation) SetField(name string, value ent.Value) error {
|
||||
m.SetCreatorID(v)
|
||||
return nil
|
||||
case platform.FieldConfigForm:
|
||||
v, ok := value.([]byte)
|
||||
v, ok := value.(*holos.PlatformForm)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetConfigForm(v)
|
||||
return nil
|
||||
case platform.FieldConfigValues:
|
||||
v, ok := value.([]byte)
|
||||
v, ok := value.(*holos.ConfigValues)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
// Platform is the model entity for the Platform schema.
|
||||
@@ -32,10 +34,10 @@ type Platform struct {
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
// CreatorID holds the value of the "creator_id" field.
|
||||
CreatorID uuid.UUID `json:"creator_id,omitempty"`
|
||||
// Opaque JSON bytes representing the platform config form.
|
||||
ConfigForm []byte `json:"config_form,omitempty"`
|
||||
// Opaque JSON bytes representing the platform config values.
|
||||
ConfigValues []byte `json:"config_values,omitempty"`
|
||||
// JSON holos.PlatformForm representing the platform data entry form.
|
||||
ConfigForm *holos.PlatformForm `json:"config_form,omitempty"`
|
||||
// JSON holos.ConfigValues representing the platform config values.
|
||||
ConfigValues *holos.ConfigValues `json:"config_values,omitempty"`
|
||||
// Opaque bytes representing the CUE definition of the config struct.
|
||||
ConfigCue []byte `json:"config_cue,omitempty"`
|
||||
// The definition name to vet config_values against config_cue e.g. '#PlatformSpec'
|
||||
@@ -152,14 +154,18 @@ func (pl *Platform) assignValues(columns []string, values []any) error {
|
||||
case platform.FieldConfigForm:
|
||||
if value, ok := values[i].(*[]byte); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field config_form", values[i])
|
||||
} else if value != nil {
|
||||
pl.ConfigForm = *value
|
||||
} else if value != nil && len(*value) > 0 {
|
||||
if err := json.Unmarshal(*value, &pl.ConfigForm); err != nil {
|
||||
return fmt.Errorf("unmarshal field config_form: %w", err)
|
||||
}
|
||||
}
|
||||
case platform.FieldConfigValues:
|
||||
if value, ok := values[i].(*[]byte); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field config_values", values[i])
|
||||
} else if value != nil {
|
||||
pl.ConfigValues = *value
|
||||
} else if value != nil && len(*value) > 0 {
|
||||
if err := json.Unmarshal(*value, &pl.ConfigValues); err != nil {
|
||||
return fmt.Errorf("unmarshal field config_values: %w", err)
|
||||
}
|
||||
}
|
||||
case platform.FieldConfigCue:
|
||||
if value, ok := values[i].(*[]byte); !ok {
|
||||
|
||||
@@ -86,16 +86,6 @@ func CreatorID(v uuid.UUID) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldCreatorID, v))
|
||||
}
|
||||
|
||||
// ConfigForm applies equality check predicate on the "config_form" field. It's identical to ConfigFormEQ.
|
||||
func ConfigForm(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigValues applies equality check predicate on the "config_values" field. It's identical to ConfigValuesEQ.
|
||||
func ConfigValues(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigCue applies equality check predicate on the "config_cue" field. It's identical to ConfigCueEQ.
|
||||
func ConfigCue(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigCue, v))
|
||||
@@ -356,46 +346,6 @@ func CreatorIDNotIn(vs ...uuid.UUID) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldCreatorID, vs...))
|
||||
}
|
||||
|
||||
// ConfigFormEQ applies the EQ predicate on the "config_form" field.
|
||||
func ConfigFormEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigFormNEQ applies the NEQ predicate on the "config_form" field.
|
||||
func ConfigFormNEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigFormIn applies the In predicate on the "config_form" field.
|
||||
func ConfigFormIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldConfigForm, vs...))
|
||||
}
|
||||
|
||||
// ConfigFormNotIn applies the NotIn predicate on the "config_form" field.
|
||||
func ConfigFormNotIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldConfigForm, vs...))
|
||||
}
|
||||
|
||||
// ConfigFormGT applies the GT predicate on the "config_form" field.
|
||||
func ConfigFormGT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigFormGTE applies the GTE predicate on the "config_form" field.
|
||||
func ConfigFormGTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigFormLT applies the LT predicate on the "config_form" field.
|
||||
func ConfigFormLT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigFormLTE applies the LTE predicate on the "config_form" field.
|
||||
func ConfigFormLTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldConfigForm, v))
|
||||
}
|
||||
|
||||
// ConfigFormIsNil applies the IsNil predicate on the "config_form" field.
|
||||
func ConfigFormIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldConfigForm))
|
||||
@@ -406,46 +356,6 @@ func ConfigFormNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldConfigForm))
|
||||
}
|
||||
|
||||
// ConfigValuesEQ applies the EQ predicate on the "config_values" field.
|
||||
func ConfigValuesEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigValuesNEQ applies the NEQ predicate on the "config_values" field.
|
||||
func ConfigValuesNEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigValuesIn applies the In predicate on the "config_values" field.
|
||||
func ConfigValuesIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldConfigValues, vs...))
|
||||
}
|
||||
|
||||
// ConfigValuesNotIn applies the NotIn predicate on the "config_values" field.
|
||||
func ConfigValuesNotIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldConfigValues, vs...))
|
||||
}
|
||||
|
||||
// ConfigValuesGT applies the GT predicate on the "config_values" field.
|
||||
func ConfigValuesGT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigValuesGTE applies the GTE predicate on the "config_values" field.
|
||||
func ConfigValuesGTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigValuesLT applies the LT predicate on the "config_values" field.
|
||||
func ConfigValuesLT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigValuesLTE applies the LTE predicate on the "config_values" field.
|
||||
func ConfigValuesLTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigValuesIsNil applies the IsNil predicate on the "config_values" field.
|
||||
func ConfigValuesIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldConfigValues))
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
// PlatformCreate is the builder for creating a Platform entity.
|
||||
@@ -79,14 +80,14 @@ func (pc *PlatformCreate) SetCreatorID(u uuid.UUID) *PlatformCreate {
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (pc *PlatformCreate) SetConfigForm(b []byte) *PlatformCreate {
|
||||
pc.mutation.SetConfigForm(b)
|
||||
func (pc *PlatformCreate) SetConfigForm(hf *holos.PlatformForm) *PlatformCreate {
|
||||
pc.mutation.SetConfigForm(hf)
|
||||
return pc
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (pc *PlatformCreate) SetConfigValues(b []byte) *PlatformCreate {
|
||||
pc.mutation.SetConfigValues(b)
|
||||
func (pc *PlatformCreate) SetConfigValues(hv *holos.ConfigValues) *PlatformCreate {
|
||||
pc.mutation.SetConfigValues(hv)
|
||||
return pc
|
||||
}
|
||||
|
||||
@@ -273,11 +274,11 @@ func (pc *PlatformCreate) createSpec() (*Platform, *sqlgraph.CreateSpec) {
|
||||
_node.DisplayName = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigForm(); ok {
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeJSON, value)
|
||||
_node.ConfigForm = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigValues(); ok {
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeJSON, value)
|
||||
_node.ConfigValues = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigCue(); ok {
|
||||
@@ -435,7 +436,7 @@ func (u *PlatformUpsert) UpdateCreatorID() *PlatformUpsert {
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (u *PlatformUpsert) SetConfigForm(v []byte) *PlatformUpsert {
|
||||
func (u *PlatformUpsert) SetConfigForm(v *holos.PlatformForm) *PlatformUpsert {
|
||||
u.Set(platform.FieldConfigForm, v)
|
||||
return u
|
||||
}
|
||||
@@ -453,7 +454,7 @@ func (u *PlatformUpsert) ClearConfigForm() *PlatformUpsert {
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (u *PlatformUpsert) SetConfigValues(v []byte) *PlatformUpsert {
|
||||
func (u *PlatformUpsert) SetConfigValues(v *holos.ConfigValues) *PlatformUpsert {
|
||||
u.Set(platform.FieldConfigValues, v)
|
||||
return u
|
||||
}
|
||||
@@ -628,7 +629,7 @@ func (u *PlatformUpsertOne) UpdateCreatorID() *PlatformUpsertOne {
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (u *PlatformUpsertOne) SetConfigForm(v []byte) *PlatformUpsertOne {
|
||||
func (u *PlatformUpsertOne) SetConfigForm(v *holos.PlatformForm) *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigForm(v)
|
||||
})
|
||||
@@ -649,7 +650,7 @@ func (u *PlatformUpsertOne) ClearConfigForm() *PlatformUpsertOne {
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (u *PlatformUpsertOne) SetConfigValues(v []byte) *PlatformUpsertOne {
|
||||
func (u *PlatformUpsertOne) SetConfigValues(v *holos.ConfigValues) *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigValues(v)
|
||||
})
|
||||
@@ -1000,7 +1001,7 @@ func (u *PlatformUpsertBulk) UpdateCreatorID() *PlatformUpsertBulk {
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (u *PlatformUpsertBulk) SetConfigForm(v []byte) *PlatformUpsertBulk {
|
||||
func (u *PlatformUpsertBulk) SetConfigForm(v *holos.PlatformForm) *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigForm(v)
|
||||
})
|
||||
@@ -1021,7 +1022,7 @@ func (u *PlatformUpsertBulk) ClearConfigForm() *PlatformUpsertBulk {
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (u *PlatformUpsertBulk) SetConfigValues(v []byte) *PlatformUpsertBulk {
|
||||
func (u *PlatformUpsertBulk) SetConfigValues(v *holos.ConfigValues) *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigValues(v)
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
// PlatformUpdate is the builder for updating Platform entities.
|
||||
@@ -94,8 +95,8 @@ func (pu *PlatformUpdate) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate {
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (pu *PlatformUpdate) SetConfigForm(b []byte) *PlatformUpdate {
|
||||
pu.mutation.SetConfigForm(b)
|
||||
func (pu *PlatformUpdate) SetConfigForm(hf *holos.PlatformForm) *PlatformUpdate {
|
||||
pu.mutation.SetConfigForm(hf)
|
||||
return pu
|
||||
}
|
||||
|
||||
@@ -106,8 +107,8 @@ func (pu *PlatformUpdate) ClearConfigForm() *PlatformUpdate {
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (pu *PlatformUpdate) SetConfigValues(b []byte) *PlatformUpdate {
|
||||
pu.mutation.SetConfigValues(b)
|
||||
func (pu *PlatformUpdate) SetConfigValues(hv *holos.ConfigValues) *PlatformUpdate {
|
||||
pu.mutation.SetConfigValues(hv)
|
||||
return pu
|
||||
}
|
||||
|
||||
@@ -256,16 +257,16 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigForm(); ok {
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeJSON, value)
|
||||
}
|
||||
if pu.mutation.ConfigFormCleared() {
|
||||
_spec.ClearField(platform.FieldConfigForm, field.TypeBytes)
|
||||
_spec.ClearField(platform.FieldConfigForm, field.TypeJSON)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigValues(); ok {
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeJSON, value)
|
||||
}
|
||||
if pu.mutation.ConfigValuesCleared() {
|
||||
_spec.ClearField(platform.FieldConfigValues, field.TypeBytes)
|
||||
_spec.ClearField(platform.FieldConfigValues, field.TypeJSON)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigCue(); ok {
|
||||
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
|
||||
@@ -420,8 +421,8 @@ func (puo *PlatformUpdateOne) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (puo *PlatformUpdateOne) SetConfigForm(b []byte) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigForm(b)
|
||||
func (puo *PlatformUpdateOne) SetConfigForm(hf *holos.PlatformForm) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigForm(hf)
|
||||
return puo
|
||||
}
|
||||
|
||||
@@ -432,8 +433,8 @@ func (puo *PlatformUpdateOne) ClearConfigForm() *PlatformUpdateOne {
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (puo *PlatformUpdateOne) SetConfigValues(b []byte) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigValues(b)
|
||||
func (puo *PlatformUpdateOne) SetConfigValues(hv *holos.ConfigValues) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigValues(hv)
|
||||
return puo
|
||||
}
|
||||
|
||||
@@ -612,16 +613,16 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
|
||||
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigForm(); ok {
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeJSON, value)
|
||||
}
|
||||
if puo.mutation.ConfigFormCleared() {
|
||||
_spec.ClearField(platform.FieldConfigForm, field.TypeBytes)
|
||||
_spec.ClearField(platform.FieldConfigForm, field.TypeJSON)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigValues(); ok {
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeJSON, value)
|
||||
}
|
||||
if puo.mutation.ConfigValuesCleared() {
|
||||
_spec.ClearField(platform.FieldConfigValues, field.TypeBytes)
|
||||
_spec.ClearField(platform.FieldConfigValues, field.TypeJSON)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigCue(); ok {
|
||||
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
"github.com/gofrs/uuid"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
type Platform struct {
|
||||
@@ -25,12 +26,12 @@ func (Platform) Fields() []ent.Field {
|
||||
field.String("name").NotEmpty(),
|
||||
field.String("display_name"),
|
||||
field.UUID("creator_id", uuid.UUID{}),
|
||||
field.Bytes("config_form").
|
||||
field.JSON("config_form", &holos.PlatformForm{}).
|
||||
Optional().
|
||||
Comment("Opaque JSON bytes representing the platform config form."),
|
||||
field.Bytes("config_values").
|
||||
Comment("JSON holos.PlatformForm representing the platform data entry form."),
|
||||
field.JSON("config_values", &holos.ConfigValues{}).
|
||||
Optional().
|
||||
Comment("Opaque JSON bytes representing the platform config values."),
|
||||
Comment("JSON holos.ConfigValues representing the platform config values."),
|
||||
field.Bytes("config_cue").
|
||||
Optional().
|
||||
Comment("Opaque bytes representing the CUE definition of the config struct."),
|
||||
|
||||
@@ -4,21 +4,7 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { AddPlatformRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
|
||||
*/
|
||||
export const getPlatforms = {
|
||||
localName: "getPlatforms",
|
||||
name: "GetPlatforms",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetPlatformsRequest,
|
||||
O: GetPlatformsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
import { AddPlatformRequest, ConfigValues, GetPlatformConfigRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse, PutPlatformConfigRequest } from "./platform_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.AddPlatform
|
||||
@@ -34,6 +20,20 @@ export const addPlatform = {
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
|
||||
*/
|
||||
export const getPlatforms = {
|
||||
localName: "getPlatforms",
|
||||
name: "GetPlatforms",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetPlatformsRequest,
|
||||
O: GetPlatformsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatform
|
||||
*/
|
||||
@@ -47,3 +47,33 @@ export const getPlatform = {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.PutPlatformConfig
|
||||
*/
|
||||
export const putPlatformConfig = {
|
||||
localName: "putPlatformConfig",
|
||||
name: "PutPlatformConfig",
|
||||
kind: MethodKind.Unary,
|
||||
I: PutPlatformConfigRequest,
|
||||
O: GetPlatformResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* GetConfig provides the unmarshalled config values for use with CUE
|
||||
*
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetConfig
|
||||
*/
|
||||
export const getConfig = {
|
||||
localName: "getConfig",
|
||||
name: "GetConfig",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetPlatformConfigRequest,
|
||||
O: ConfigValues,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { AddPlatformRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
|
||||
import { AddPlatformRequest, ConfigValues, GetPlatformConfigRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse, PutPlatformConfigRequest } from "./platform_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
@@ -12,15 +12,6 @@ import { MethodKind } from "@bufbuild/protobuf";
|
||||
export const PlatformService = {
|
||||
typeName: "holos.v1alpha1.PlatformService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
|
||||
*/
|
||||
getPlatforms: {
|
||||
name: "GetPlatforms",
|
||||
I: GetPlatformsRequest,
|
||||
O: GetPlatformsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.AddPlatform
|
||||
*/
|
||||
@@ -30,6 +21,15 @@ export const PlatformService = {
|
||||
O: GetPlatformsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
|
||||
*/
|
||||
getPlatforms: {
|
||||
name: "GetPlatforms",
|
||||
I: GetPlatformsRequest,
|
||||
O: GetPlatformsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatform
|
||||
*/
|
||||
@@ -39,6 +39,26 @@ export const PlatformService = {
|
||||
O: GetPlatformResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.PutPlatformConfig
|
||||
*/
|
||||
putPlatformConfig: {
|
||||
name: "PutPlatformConfig",
|
||||
I: PutPlatformConfigRequest,
|
||||
O: GetPlatformResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* GetConfig provides the unmarshalled config values for use with CUE
|
||||
*
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetConfig
|
||||
*/
|
||||
getConfig: {
|
||||
name: "GetConfig",
|
||||
I: GetPlatformConfigRequest,
|
||||
O: ConfigValues,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3 } from "@bufbuild/protobuf";
|
||||
import { Message, proto3, Value } from "@bufbuild/protobuf";
|
||||
import { Timestamps } from "./timestamps_pb.js";
|
||||
import { Creator } from "./user_pb.js";
|
||||
|
||||
@@ -74,6 +74,13 @@ export class Config extends Message<Config> {
|
||||
*/
|
||||
form?: PlatformForm;
|
||||
|
||||
/**
|
||||
* Values are the user supplied config values organized by section.
|
||||
*
|
||||
* @generated from field: holos.v1alpha1.ConfigValues values = 2;
|
||||
*/
|
||||
values?: ConfigValues;
|
||||
|
||||
constructor(data?: PartialMessage<Config>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
@@ -83,6 +90,7 @@ export class Config extends Message<Config> {
|
||||
static readonly typeName = "holos.v1alpha1.Config";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "form", kind: "message", T: PlatformForm },
|
||||
{ no: 2, name: "values", kind: "message", T: ConfigValues },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Config {
|
||||
@@ -102,6 +110,82 @@ export class Config extends Message<Config> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.ConfigSection
|
||||
*/
|
||||
export class ConfigSection extends Message<ConfigSection> {
|
||||
/**
|
||||
* @generated from field: map<string, google.protobuf.Value> fields = 1;
|
||||
*/
|
||||
fields: { [key: string]: Value } = {};
|
||||
|
||||
constructor(data?: PartialMessage<ConfigSection>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.ConfigSection";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "fields", kind: "map", K: 9 /* ScalarType.STRING */, V: {kind: "message", T: Value} },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ConfigSection {
|
||||
return new ConfigSection().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ConfigSection {
|
||||
return new ConfigSection().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ConfigSection {
|
||||
return new ConfigSection().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: ConfigSection | PlainMessage<ConfigSection> | undefined, b: ConfigSection | PlainMessage<ConfigSection> | undefined): boolean {
|
||||
return proto3.util.equals(ConfigSection, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ConfigValues represents user defined configuration values.
|
||||
*
|
||||
* @generated from message holos.v1alpha1.ConfigValues
|
||||
*/
|
||||
export class ConfigValues extends Message<ConfigValues> {
|
||||
/**
|
||||
* @generated from field: map<string, holos.v1alpha1.ConfigSection> sections = 1;
|
||||
*/
|
||||
sections: { [key: string]: ConfigSection } = {};
|
||||
|
||||
constructor(data?: PartialMessage<ConfigValues>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.ConfigValues";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "sections", kind: "map", K: 9 /* ScalarType.STRING */, V: {kind: "message", T: ConfigSection} },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ConfigValues {
|
||||
return new ConfigValues().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ConfigValues {
|
||||
return new ConfigValues().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ConfigValues {
|
||||
return new ConfigValues().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: ConfigValues | PlainMessage<ConfigValues> | undefined, b: ConfigValues | PlainMessage<ConfigValues> | undefined): boolean {
|
||||
return proto3.util.equals(ConfigValues, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.Platform
|
||||
*/
|
||||
@@ -296,9 +380,9 @@ export class FieldConfig extends Message<FieldConfig> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.ConfigSection
|
||||
* @generated from message holos.v1alpha1.ConfigFormSection
|
||||
*/
|
||||
export class ConfigSection extends Message<ConfigSection> {
|
||||
export class ConfigFormSection extends Message<ConfigFormSection> {
|
||||
/**
|
||||
* @generated from field: string name = 1;
|
||||
*/
|
||||
@@ -319,13 +403,13 @@ export class ConfigSection extends Message<ConfigSection> {
|
||||
*/
|
||||
fieldConfigs: FieldConfig[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<ConfigSection>) {
|
||||
constructor(data?: PartialMessage<ConfigFormSection>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.ConfigSection";
|
||||
static readonly typeName = "holos.v1alpha1.ConfigFormSection";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "displayName", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
@@ -333,20 +417,20 @@ export class ConfigSection extends Message<ConfigSection> {
|
||||
{ no: 4, name: "fieldConfigs", kind: "message", T: FieldConfig, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ConfigSection {
|
||||
return new ConfigSection().fromBinary(bytes, options);
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ConfigFormSection {
|
||||
return new ConfigFormSection().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ConfigSection {
|
||||
return new ConfigSection().fromJson(jsonValue, options);
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ConfigFormSection {
|
||||
return new ConfigFormSection().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ConfigSection {
|
||||
return new ConfigSection().fromJsonString(jsonString, options);
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ConfigFormSection {
|
||||
return new ConfigFormSection().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: ConfigSection | PlainMessage<ConfigSection> | undefined, b: ConfigSection | PlainMessage<ConfigSection> | undefined): boolean {
|
||||
return proto3.util.equals(ConfigSection, a, b);
|
||||
static equals(a: ConfigFormSection | PlainMessage<ConfigFormSection> | undefined, b: ConfigFormSection | PlainMessage<ConfigFormSection> | undefined): boolean {
|
||||
return proto3.util.equals(ConfigFormSection, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,9 +439,9 @@ export class ConfigSection extends Message<ConfigSection> {
|
||||
*/
|
||||
export class PlatformFormSpec extends Message<PlatformFormSpec> {
|
||||
/**
|
||||
* @generated from field: repeated holos.v1alpha1.ConfigSection sections = 1;
|
||||
* @generated from field: repeated holos.v1alpha1.ConfigFormSection sections = 1;
|
||||
*/
|
||||
sections: ConfigSection[] = [];
|
||||
sections: ConfigFormSection[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<PlatformFormSpec>) {
|
||||
super();
|
||||
@@ -367,7 +451,7 @@ export class PlatformFormSpec extends Message<PlatformFormSpec> {
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.PlatformFormSpec";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "sections", kind: "message", T: ConfigSection, repeated: true },
|
||||
{ no: 1, name: "sections", kind: "message", T: ConfigFormSection, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformFormSpec {
|
||||
@@ -572,43 +656,6 @@ export class GetPlatformRequest extends Message<GetPlatformRequest> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetPlatformFormRequest
|
||||
*/
|
||||
export class GetPlatformFormRequest extends Message<GetPlatformFormRequest> {
|
||||
/**
|
||||
* @generated from field: string platform_id = 1;
|
||||
*/
|
||||
platformId = "";
|
||||
|
||||
constructor(data?: PartialMessage<GetPlatformFormRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetPlatformFormRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetPlatformFormRequest {
|
||||
return new GetPlatformFormRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetPlatformFormRequest {
|
||||
return new GetPlatformFormRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetPlatformFormRequest {
|
||||
return new GetPlatformFormRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetPlatformFormRequest | PlainMessage<GetPlatformFormRequest> | undefined, b: GetPlatformFormRequest | PlainMessage<GetPlatformFormRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetPlatformFormRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.MetadataName
|
||||
*/
|
||||
@@ -701,3 +748,83 @@ export class PlatformForm extends Message<PlatformForm> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.PutPlatformConfigRequest
|
||||
*/
|
||||
export class PutPlatformConfigRequest extends Message<PutPlatformConfigRequest> {
|
||||
/**
|
||||
* @generated from field: string platform_id = 1;
|
||||
*/
|
||||
platformId = "";
|
||||
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.ConfigValues values = 2;
|
||||
*/
|
||||
values?: ConfigValues;
|
||||
|
||||
constructor(data?: PartialMessage<PutPlatformConfigRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.PutPlatformConfigRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "values", kind: "message", T: ConfigValues },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutPlatformConfigRequest {
|
||||
return new PutPlatformConfigRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutPlatformConfigRequest {
|
||||
return new PutPlatformConfigRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutPlatformConfigRequest {
|
||||
return new PutPlatformConfigRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: PutPlatformConfigRequest | PlainMessage<PutPlatformConfigRequest> | undefined, b: PutPlatformConfigRequest | PlainMessage<PutPlatformConfigRequest> | undefined): boolean {
|
||||
return proto3.util.equals(PutPlatformConfigRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetPlatformConfigRequest
|
||||
*/
|
||||
export class GetPlatformConfigRequest extends Message<GetPlatformConfigRequest> {
|
||||
/**
|
||||
* @generated from field: string platform_id = 1;
|
||||
*/
|
||||
platformId = "";
|
||||
|
||||
constructor(data?: PartialMessage<GetPlatformConfigRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetPlatformConfigRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetPlatformConfigRequest {
|
||||
return new GetPlatformConfigRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetPlatformConfigRequest {
|
||||
return new GetPlatformConfigRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetPlatformConfigRequest {
|
||||
return new GetPlatformConfigRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetPlatformConfigRequest | PlainMessage<GetPlatformConfigRequest> | undefined, b: GetPlatformConfigRequest | PlainMessage<GetPlatformConfigRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetPlatformConfigRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { EmptyRequest, EmptyResponse } from "./system_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.SystemService.SeedDatabase
|
||||
*/
|
||||
export const seedDatabase = {
|
||||
localName: "seedDatabase",
|
||||
name: "SeedDatabase",
|
||||
kind: MethodKind.Unary,
|
||||
I: EmptyRequest,
|
||||
O: EmptyResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.SystemService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.SystemService.DropTables
|
||||
*/
|
||||
export const dropTables = {
|
||||
localName: "dropTables",
|
||||
name: "DropTables",
|
||||
kind: MethodKind.Unary,
|
||||
I: EmptyRequest,
|
||||
O: EmptyResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.SystemService"
|
||||
}
|
||||
} as const;
|
||||
@@ -0,0 +1,35 @@
|
||||
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { EmptyRequest, EmptyResponse } from "./system_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from service holos.v1alpha1.SystemService
|
||||
*/
|
||||
export const SystemService = {
|
||||
typeName: "holos.v1alpha1.SystemService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.SystemService.SeedDatabase
|
||||
*/
|
||||
seedDatabase: {
|
||||
name: "SeedDatabase",
|
||||
I: EmptyRequest,
|
||||
O: EmptyResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.SystemService.DropTables
|
||||
*/
|
||||
dropTables: {
|
||||
name: "DropTables",
|
||||
I: EmptyRequest,
|
||||
O: EmptyResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3 } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.EmptyRequest
|
||||
*/
|
||||
export class EmptyRequest extends Message<EmptyRequest> {
|
||||
constructor(data?: PartialMessage<EmptyRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.EmptyRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): EmptyRequest {
|
||||
return new EmptyRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EmptyRequest {
|
||||
return new EmptyRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EmptyRequest {
|
||||
return new EmptyRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: EmptyRequest | PlainMessage<EmptyRequest> | undefined, b: EmptyRequest | PlainMessage<EmptyRequest> | undefined): boolean {
|
||||
return proto3.util.equals(EmptyRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.EmptyResponse
|
||||
*/
|
||||
export class EmptyResponse extends Message<EmptyResponse> {
|
||||
constructor(data?: PartialMessage<EmptyResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.EmptyResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): EmptyResponse {
|
||||
return new EmptyResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EmptyResponse {
|
||||
return new EmptyResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EmptyResponse {
|
||||
return new EmptyResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: EmptyResponse | PlainMessage<EmptyResponse> | undefined, b: EmptyResponse | PlainMessage<EmptyResponse> | undefined): boolean {
|
||||
return proto3.util.equals(EmptyResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { Inject, Injectable, inject } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { PlatformService as ConnectPlatformService } from '../gen/holos/v1alpha1/platform_connect';
|
||||
import { Observable, filter, of, switchMap } from 'rxjs';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { GetPlatformFormRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse, Platform, PlatformForm } from '../gen/holos/v1alpha1/platform_pb';
|
||||
import { Config, ConfigSection, ConfigValues, GetPlatformsRequest, Platform, PutPlatformConfigRequest } from '../gen/holos/v1alpha1/platform_pb';
|
||||
import { Organization } from '../gen/holos/v1alpha1/organization_pb';
|
||||
import { Struct, Value } from '@bufbuild/protobuf';
|
||||
|
||||
export interface Section {
|
||||
[field: string]: any;
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
[section: string]: Section;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -20,9 +29,31 @@ export class PlatformService {
|
||||
)
|
||||
}
|
||||
|
||||
getPlatform(id: string): Observable<Platform | undefined> {
|
||||
getPlatform(id: string): Observable<Platform> {
|
||||
return this.client.getPlatform({ platformId: id }).pipe(
|
||||
switchMap((resp) => { return of(resp.platform) }),
|
||||
switchMap(resp => {
|
||||
return of(resp.platform);
|
||||
}),
|
||||
filter((platform): platform is Platform => platform !== undefined),
|
||||
)
|
||||
}
|
||||
|
||||
putConfig(id: string, model: Model): Observable<Platform> {
|
||||
const values = new ConfigValues
|
||||
// Set string values from the model
|
||||
Object.keys(model).forEach(sectionName => {
|
||||
values.sections[sectionName] = new ConfigSection
|
||||
Object.keys(model[sectionName]).forEach(fieldName => {
|
||||
const val = new Value
|
||||
val.fromJson(model[sectionName][fieldName])
|
||||
values.sections[sectionName].fields[fieldName] = val
|
||||
})
|
||||
})
|
||||
|
||||
const req = new PutPlatformConfigRequest({ platformId: id, values: values })
|
||||
return this.client.putPlatformConfig(req).pipe(
|
||||
switchMap(resp => { return of(resp.platform) }),
|
||||
filter((platform): platform is Platform => platform !== undefined),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@for (section of platform.config?.form?.spec?.sections; track section.name) {
|
||||
<h2>{{section.displayName ? section.displayName : section.name }}</h2>
|
||||
<p>{{ section.description }}</p>
|
||||
<formly-form [form]="form" [fields]="section.fieldConfigs" [model]="model"></formly-form>
|
||||
<formly-form [form]="form" [fields]="section.fieldConfigs" [model]="model[section.name]"></formly-form>
|
||||
}
|
||||
<p></p>
|
||||
<button type="submit" mat-flat-button color="primary">Submit</button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, Input, inject } from '@angular/core';
|
||||
import { Observable, filter, shareReplay } from 'rxjs';
|
||||
import { PlatformService } from '../../services/platform.service';
|
||||
import { Observable, map, shareReplay } from 'rxjs';
|
||||
import { Model, PlatformService } from '../../services/platform.service';
|
||||
import { Platform } from '../../gen/holos/v1alpha1/platform_pb';
|
||||
import { MatTab, MatTabGroup } from '@angular/material/tabs';
|
||||
import { AsyncPipe, CommonModule } from '@angular/common';
|
||||
@@ -29,20 +29,40 @@ import { MatDivider } from '@angular/material/divider';
|
||||
})
|
||||
export class PlatformDetailComponent {
|
||||
private service = inject(PlatformService);
|
||||
private platformId: string = "";
|
||||
|
||||
platform$!: Observable<Platform>;
|
||||
|
||||
form = new FormGroup({});
|
||||
model = {};
|
||||
model: Model = {};
|
||||
|
||||
onSubmit(model: any) {
|
||||
console.log(model);
|
||||
onSubmit(model: Model) {
|
||||
console.log(model)
|
||||
// if (this.form.valid) {
|
||||
this.service.putConfig(this.platformId, model).pipe(shareReplay(1)).subscribe()
|
||||
// }
|
||||
}
|
||||
|
||||
@Input()
|
||||
set id(platformId: string) {
|
||||
this.platformId = platformId;
|
||||
this.platform$ = this.service.getPlatform(platformId).pipe(
|
||||
filter((platform): platform is Platform => platform !== undefined),
|
||||
map(project => {
|
||||
// Initialize the model container for each section of the form config
|
||||
project.config?.form?.spec?.sections.forEach(section => {
|
||||
this.model[section.name] = {}
|
||||
})
|
||||
// Load existing values into the form
|
||||
const sections = project.config?.values?.sections
|
||||
if (sections !== undefined) {
|
||||
Object.keys(sections).forEach(sectionName => {
|
||||
Object.keys(sections[sectionName].fields).forEach(fieldName => {
|
||||
this.model[sectionName][fieldName] = sections[sectionName].fields[fieldName].toJson()
|
||||
})
|
||||
})
|
||||
}
|
||||
return project
|
||||
}),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<div class="grid-container">
|
||||
<mat-nav-list>
|
||||
<h3 mat-subheader>Platforms</h3>
|
||||
@for (platform of platforms$ | async; track platform.id) {
|
||||
<mat-list-item>
|
||||
<a [routerLink]="['/platform', platform.id]">
|
||||
{{ platform.displayName ? platform.displayName : platform.name }}
|
||||
</a>
|
||||
</mat-list-item>
|
||||
<p>Select a platform to manage.</p>
|
||||
@for (platform of (platforms$ | async); track platform.id) {
|
||||
<a mat-list-item [routerLink]="['/platform', platform.id]">
|
||||
{{ platform.displayName ? platform.displayName : platform.name }}
|
||||
</a>
|
||||
}
|
||||
</mat-nav-list>
|
||||
</div>
|
||||
|
||||
@@ -9,32 +9,37 @@ package handler
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
// WithTx runs callbacks in a transaction as described in https://entgo.io/docs/transactions/#best-practices
|
||||
func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
|
||||
log := logger.FromContext(ctx)
|
||||
tx, err := client.Tx(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if v := recover(); v != nil {
|
||||
slog.ErrorContext(ctx, "panic", "v", v)
|
||||
log.ErrorContext(ctx, "panic", "v", v)
|
||||
_ = tx.Rollback()
|
||||
panic(v)
|
||||
}
|
||||
}()
|
||||
if err := fn(tx); err != nil {
|
||||
if rerr := tx.Rollback(); rerr != nil {
|
||||
err = fmt.Errorf("%w: rolling back transaction: %v", err, rerr)
|
||||
log.ErrorContext(ctx, "could not roll back tx", "err", rerr)
|
||||
err = fmt.Errorf("coult not roll back tx: %w: %w", rerr, err)
|
||||
} else {
|
||||
log.WarnContext(ctx, "rolled back failed tx", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("committing transaction: %w", err)
|
||||
log.ErrorContext(ctx, "could not commit transaction", "err", err)
|
||||
return fmt.Errorf("could not commit: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -66,12 +67,12 @@ func (h *OrganizationHandler) CreateCallerOrganization(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerOrganizationRequest],
|
||||
) (*connect.Response[holos.GetCallerOrganizationsResponse], error) {
|
||||
log := logger.FromContext(ctx)
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
// todo get user by iss, sub
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Email())
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Issuer(), authnID.Subject())
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
@@ -90,14 +91,14 @@ func (h *OrganizationHandler) CreateCallerOrganization(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbUser, err = dbUser.Update().
|
||||
AddOrganizations(org).
|
||||
Save(ctx)
|
||||
return err
|
||||
return tx.Organization.UpdateOne(org).AddUsers(dbUser).Exec(ctx)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, errors.Wrap(err))
|
||||
}
|
||||
log = log.With("organization", org)
|
||||
|
||||
log.InfoContext(ctx, "created organization")
|
||||
|
||||
// TODO: prefetch organizations
|
||||
dbOrgs, err := dbUser.QueryOrganizations().All(ctx)
|
||||
|
||||
@@ -49,13 +49,27 @@ func (h *PlatformHandler) AddPlatform(
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
var hf holos.PlatformForm
|
||||
if len(req.Msg.Platform.RawConfig.Form) > 0 {
|
||||
if err := json.Unmarshal(req.Msg.Platform.RawConfig.Form, &hf); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
}
|
||||
|
||||
var hv holos.ConfigValues
|
||||
if len(req.Msg.Platform.RawConfig.Values) > 0 {
|
||||
if err := json.Unmarshal(req.Msg.Platform.RawConfig.Values, &hv); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
}
|
||||
|
||||
platform, err := h.db.Platform.Create().
|
||||
SetOrgID(dbOrg.ID).
|
||||
SetCreatorID(dbUser.ID).
|
||||
SetName(req.Msg.Platform.Name).
|
||||
SetDisplayName(req.Msg.Platform.DisplayName).
|
||||
SetConfigForm(req.Msg.Platform.RawConfig.Form).
|
||||
SetConfigValues(req.Msg.Platform.RawConfig.Values).
|
||||
SetConfigForm(&hf).
|
||||
SetConfigValues(&hv).
|
||||
SetConfigCue(req.Msg.Platform.RawConfig.Cue).
|
||||
SetConfigDefinition(req.Msg.Platform.RawConfig.Definition).
|
||||
Save(ctx)
|
||||
@@ -69,51 +83,59 @@ func (h *PlatformHandler) AddPlatform(
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) getPlatform(ctx context.Context, id string, uid authn.Identity) (*ent.Platform, error) {
|
||||
platformID, err := uuid.FromString(id)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.db.Platform.Query().
|
||||
Where(platform.ID(platformID)).
|
||||
Where(platform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(uid.Issuer()),
|
||||
user.Sub(uid.Subject()),
|
||||
))).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) GetPlatform(ctx context.Context, req *connect.Request[holos.GetPlatformRequest]) (*connect.Response[holos.GetPlatformResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
platformID, err := uuid.FromString(req.Msg.PlatformId)
|
||||
p, err := h.getPlatform(ctx, req.Msg.PlatformId, authnID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.db.Platform.Query().
|
||||
Where(platform.ID(platformID)).
|
||||
Where(platform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(authnID.Issuer()),
|
||||
user.Sub(authnID.Subject()),
|
||||
))).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.GetPlatformResponse{Platform: PlatformToRPC(p)}), nil
|
||||
}
|
||||
|
||||
// GetForm provides the FormlyFieldConfig for the platform to make the web ui form for user input.
|
||||
func (h *PlatformHandler) GetForm(ctx context.Context, req *connect.Request[holos.GetPlatformFormRequest]) (*connect.Response[holos.PlatformForm], error) {
|
||||
// Boilerplate to get the platform by id where the user is a member of the org.
|
||||
func (h *PlatformHandler) PutPlatformConfig(ctx context.Context, req *connect.Request[holos.PutPlatformConfigRequest]) (*connect.Response[holos.GetPlatformResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
platformID, err := uuid.FromString(req.Msg.PlatformId)
|
||||
id, err := uuid.FromString(req.Msg.PlatformId)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
// Get the platform so we can validate the values.
|
||||
p, err := h.db.Platform.Query().
|
||||
Where(platform.ID(platformID)).
|
||||
Where(platform.ID(id)).
|
||||
Where(platform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(authnID.Issuer()),
|
||||
@@ -128,24 +150,47 @@ func (h *PlatformHandler) GetForm(ctx context.Context, req *connect.Request[holo
|
||||
}
|
||||
}
|
||||
|
||||
rpcPlatform := PlatformToRPC(p)
|
||||
res := connect.NewResponse(rpcPlatform.Config.Form)
|
||||
return res, nil
|
||||
slog.WarnContext(ctx, "todo: validate the platform config against cue definitions", "action", "todo", "cue", len(p.ConfigCue))
|
||||
|
||||
up, err := h.db.Platform.UpdateOneID(id).
|
||||
Where(platform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(authnID.Issuer()),
|
||||
user.Sub(authnID.Subject()),
|
||||
))).
|
||||
SetConfigValues(req.Msg.Values).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.GetPlatformResponse{Platform: PlatformToRPC(up)}), nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) GetConfig(ctx context.Context, req *connect.Request[holos.GetPlatformConfigRequest]) (*connect.Response[holos.ConfigValues], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.getPlatform(ctx, req.Msg.PlatformId, authnID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
return connect.NewResponse(p.ConfigValues), nil
|
||||
}
|
||||
|
||||
func PlatformToRPC(platform *ent.Platform) *holos.Platform {
|
||||
var form holos.PlatformForm
|
||||
if err := json.Unmarshal(platform.ConfigForm, &form); err != nil {
|
||||
slog.Error("could not unmarshal platform config form", "platform_id", platform.ID.String(), "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &holos.Platform{
|
||||
Id: platform.ID.String(),
|
||||
Name: platform.Name,
|
||||
DisplayName: platform.DisplayName,
|
||||
OrgId: platform.OrgID.String(),
|
||||
Config: &holos.Config{Form: &form},
|
||||
Config: &holos.Config{
|
||||
Form: platform.ConfigForm,
|
||||
Values: platform.ConfigValues,
|
||||
},
|
||||
Timestamps: &holos.Timestamps{
|
||||
CreatedAt: timestamppb.New(platform.CreatedAt),
|
||||
UpdatedAt: timestamppb.New(platform.UpdatedAt),
|
||||
|
||||
215
internal/server/handler/system.go
Normal file
215
internal/server/handler/system.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
const AdminEmail = "jeff@openinfrastructure.co"
|
||||
|
||||
// NewSystemHandler returns a new SystemService implementation.
|
||||
func NewSystemHandler(db *ent.Client) *SystemHandler {
|
||||
return &SystemHandler{db: db}
|
||||
}
|
||||
|
||||
// SystemHandler implements the PlatformService interface.
|
||||
type SystemHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *SystemHandler) checkAdmin(ctx context.Context) error {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
if authnID.Email() != AdminEmail {
|
||||
err := fmt.Errorf("not an admin:\n\thave (%+v)\n\twant (%+v)", authnID.Email(), AdminEmail)
|
||||
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SystemHandler) DropTables(ctx context.Context, req *connect.Request[holos.EmptyRequest]) (*connect.Response[holos.EmptyResponse], error) {
|
||||
if err := h.checkAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
|
||||
var n int
|
||||
if n, err = tx.Platform.Delete().Exec(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.WarnContext(ctx, "deleted platforms", "count", n)
|
||||
if n, err = tx.Organization.Delete().Exec(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.WarnContext(ctx, "deleted organizations", "count", n)
|
||||
if n, err = tx.User.Delete().Exec(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.WarnContext(ctx, "deleted users", "count", n)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.EmptyResponse{}), nil
|
||||
}
|
||||
|
||||
func (h *SystemHandler) SeedDatabase(ctx context.Context, req *connect.Request[holos.EmptyRequest]) (*connect.Response[holos.EmptyResponse], error) {
|
||||
if err := h.checkAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
|
||||
jeff, err := tx.User.Create().
|
||||
SetEmail("jeff@openinfrastructure.co").
|
||||
SetIss("https://login.ois.run").
|
||||
SetSub("261773693724656988").
|
||||
SetName("Jeff McCune").
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
nate, err := tx.User.Create().
|
||||
SetEmail("nate@openinfrastructure.co").
|
||||
SetIss("https://login.ois.run").
|
||||
SetSub("261775487611699776").
|
||||
SetName("Nate McCurdy").
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
gary, err := tx.User.Create().
|
||||
SetEmail("gary@openinfrastructure.co").
|
||||
SetIss("https://login.ois.run").
|
||||
SetSub("261775531836441152").
|
||||
SetName("Gary Larizza").
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Create the org
|
||||
org, err := tx.Organization.Create().
|
||||
SetName("ois").
|
||||
SetDisplayName("Open Infrastructure Services").
|
||||
SetCreator(jeff).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Add org memebers
|
||||
org, err = org.Update().AddUsers(jeff, gary, nate).Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
var hf holos.PlatformForm
|
||||
if err := json.Unmarshal([]byte(BareForm), &hf); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Add a platform
|
||||
err = tx.Platform.Create().
|
||||
SetName("bare").
|
||||
SetDisplayName("Bare Platform").
|
||||
SetConfigForm(&hf).
|
||||
SetCreator(jeff).
|
||||
SetOrgID(org.ID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
stuff := []string{"Jeff", "Gary", "Nate"}
|
||||
for _, name := range stuff {
|
||||
err := tx.Platform.Create().
|
||||
SetName(strings.ToLower(name)).
|
||||
SetDisplayName(name + "'s Platform").
|
||||
SetConfigForm(&hf).
|
||||
SetCreator(jeff).
|
||||
SetOrgID(org.ID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.EmptyResponse{}), nil
|
||||
}
|
||||
|
||||
const BareForm = `{
|
||||
"kind": "PlatformForm",
|
||||
"spec": {
|
||||
"sections": [
|
||||
{
|
||||
"name": "org",
|
||||
"description": "Organization config values are used to derive more specific configuration values throughout the platform.",
|
||||
"displayName": "Organization",
|
||||
"fieldConfigs": [
|
||||
{
|
||||
"key": "name",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Name",
|
||||
"required": true,
|
||||
"description": "DNS label, e.g. 'example'",
|
||||
"placeholder": "example"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "domain",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Domain",
|
||||
"required": true,
|
||||
"description": "DNS domain, e.g. 'example.com'",
|
||||
"placeholder": "example.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "displayName",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Display Name",
|
||||
"required": true,
|
||||
"description": "Display name, e.g. 'Example Organization'",
|
||||
"placeholder": "Example Organization"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "contactEmail",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Contact Email",
|
||||
"required": true,
|
||||
"description": "Technical contact email address",
|
||||
"placeholder": "platform-team@example.com"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"name": "bare"
|
||||
},
|
||||
"apiVersion": "forms.holos.run/v1alpha1"
|
||||
}`
|
||||
@@ -56,7 +56,7 @@ func (h *UserHandler) GetCallerUser(
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Email())
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Issuer(), authnID.Subject())
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
@@ -68,10 +68,7 @@ func (h *UserHandler) GetCallerUser(
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateCallerUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerUserRequest],
|
||||
) (*connect.Response[holos.CreateCallerUserResponse], error) {
|
||||
func (h *UserHandler) createCallerUser(ctx context.Context) (*ent.User, error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
@@ -87,6 +84,18 @@ func (h *UserHandler) CreateCallerUser(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createdUser, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateCallerUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerUserRequest],
|
||||
) (*connect.Response[holos.CreateCallerUserResponse], error) {
|
||||
createdUser, err := h.createCallerUser(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.CreateCallerUserResponse{
|
||||
User: UserToRPC(createdUser),
|
||||
})
|
||||
@@ -107,11 +116,16 @@ func UserToRPC(u *ent.User) *holos.User {
|
||||
return &iamUser
|
||||
}
|
||||
|
||||
func getUser(ctx context.Context, client *ent.Client, email string) (*ent.User, error) {
|
||||
func getUser(ctx context.Context, client *ent.Client, iss string, sub string) (*ent.User, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
user, err := client.User.Query().Where(user.Email(email)).Only(ctx)
|
||||
user, err := client.User.Query().
|
||||
Where(
|
||||
user.Iss(iss),
|
||||
user.Sub(sub),
|
||||
).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
log.DebugContext(ctx, "could not get user", "err", err, "email", email)
|
||||
log.DebugContext(ctx, "could not get user", "err", err, "iss", iss, "sub", sub)
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return user, nil
|
||||
|
||||
@@ -115,11 +115,13 @@ func (s *Server) registerConnectRpc() error {
|
||||
s.handle(holosconnect.NewUserServiceHandler(handler.NewUserHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewOrganizationServiceHandler(handler.NewOrganizationHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewPlatformServiceHandler(handler.NewPlatformHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewSystemServiceHandler(handler.NewSystemHandler(s.db), opts))
|
||||
|
||||
reflector := grpcreflect.NewStaticReflector(
|
||||
holosconnect.UserServiceName,
|
||||
holosconnect.OrganizationServiceName,
|
||||
holosconnect.PlatformServiceName,
|
||||
holosconnect.SystemServiceName,
|
||||
)
|
||||
|
||||
s.mux.Handle(grpcreflect.NewHandlerV1(reflector))
|
||||
|
||||
@@ -9,6 +9,8 @@ import "buf/validate/validate.proto";
|
||||
import "holos/v1alpha1/timestamps.proto";
|
||||
import "holos/v1alpha1/organization.proto";
|
||||
import "holos/v1alpha1/user.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
|
||||
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
|
||||
|
||||
@@ -22,6 +24,17 @@ message RawConfig {
|
||||
|
||||
message Config {
|
||||
PlatformForm form = 1;
|
||||
// Values are the user supplied config values organized by section.
|
||||
ConfigValues values = 2;
|
||||
}
|
||||
|
||||
message ConfigSection {
|
||||
map<string, google.protobuf.Value> fields = 1;
|
||||
}
|
||||
|
||||
// ConfigValues represents user defined configuration values.
|
||||
message ConfigValues {
|
||||
map<string, ConfigSection> sections = 1;
|
||||
}
|
||||
|
||||
message Platform {
|
||||
@@ -54,7 +67,7 @@ message FieldConfig {
|
||||
FieldConfigProps props = 3;
|
||||
}
|
||||
|
||||
message ConfigSection {
|
||||
message ConfigFormSection {
|
||||
string name = 1;
|
||||
string displayName = 2;
|
||||
string description = 3;
|
||||
@@ -62,7 +75,7 @@ message ConfigSection {
|
||||
}
|
||||
|
||||
message PlatformFormSpec {
|
||||
repeated ConfigSection sections = 1;
|
||||
repeated ConfigFormSection sections = 1;
|
||||
}
|
||||
|
||||
message GetPlatformsRequest {
|
||||
@@ -85,10 +98,6 @@ message GetPlatformRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
message GetPlatformFormRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
message MetadataName {
|
||||
string name = 1;
|
||||
}
|
||||
@@ -100,8 +109,20 @@ message PlatformForm {
|
||||
PlatformFormSpec spec = 4;
|
||||
}
|
||||
|
||||
service PlatformService {
|
||||
rpc GetPlatforms(GetPlatformsRequest) returns (GetPlatformsResponse) {}
|
||||
rpc AddPlatform(AddPlatformRequest) returns (GetPlatformsResponse) {}
|
||||
rpc GetPlatform(GetPlatformRequest) returns (GetPlatformResponse) {}
|
||||
message PutPlatformConfigRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
ConfigValues values = 2;
|
||||
}
|
||||
|
||||
message GetPlatformConfigRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
service PlatformService {
|
||||
rpc AddPlatform(AddPlatformRequest) returns (GetPlatformsResponse) {}
|
||||
rpc GetPlatforms(GetPlatformsRequest) returns (GetPlatformsResponse) {}
|
||||
rpc GetPlatform(GetPlatformRequest) returns (GetPlatformResponse) {}
|
||||
rpc PutPlatformConfig(PutPlatformConfigRequest) returns (GetPlatformResponse) {}
|
||||
// GetConfig provides the unmarshalled config values for use with CUE
|
||||
rpc GetConfig(GetPlatformConfigRequest) returns (ConfigValues) {}
|
||||
}
|
||||
|
||||
20
service/holos/v1alpha1/system.proto
Normal file
20
service/holos/v1alpha1/system.proto
Normal file
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package holos.v1alpha1;
|
||||
|
||||
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
|
||||
|
||||
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
|
||||
import "buf/validate/validate.proto";
|
||||
import "holos/v1alpha1/timestamps.proto";
|
||||
import "holos/v1alpha1/user.proto";
|
||||
|
||||
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
|
||||
|
||||
message EmptyRequest {}
|
||||
message EmptyResponse {}
|
||||
|
||||
service SystemService {
|
||||
rpc SeedDatabase(EmptyRequest) returns (EmptyResponse) {}
|
||||
rpc DropTables(EmptyRequest) returns (EmptyResponse) {}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
70
|
||||
72
|
||||
|
||||
@@ -1 +1 @@
|
||||
4
|
||||
1
|
||||
|
||||
Reference in New Issue
Block a user