Compare commits

..

3 Commits

Author SHA1 Message Date
Jeff McCune
d0ad3bfc69 (#150) Add Platform Detail to edit platform config
This patch adds a /platform/:id route path to a PlatformDetail
component.  The platform detail component calls the GetPlatform method
given the platform ID and renders the platform config form on the detail
tab.

The submit button is not yet wired up.

The API for adding platforms changes, allowing raw json bytes using the
RawConfig.  The raw bytes are not presented on the read path though,
calling GetPlatforms provides the platform and the config form inline in
the response.

Use the `raw_config` field instead of `config` when creating the form
data.

```
❯ grpcurl -H "x-oidc-id-token: $(holos token)" -d @ jeff.app.dev.k2.holos.run:443 holos.v1alpha1.PlatformService.AddPlatform <<EOF
{
  "platform": {
    "org_id": "018f27cd-e5ac-7f98-bfe1-2dbab208a48c",
    "name": "bare2",
    "raw_config": {
      "form": "$(cue export ./forms/platform/ --out json | jq -cM | base64 -w0)"
    }
  }
}
EOF
```
2024-04-30 14:02:49 -07:00
Jeff McCune
fe58a33747 (#150) Add holos.v1alpha1.PlatformService.GetForm
The GetForm method is intended for the Angular frontend to get
[FormlyFieldConfig][1] data for each section of the Platform config.

[1]: https://formly.dev/docs/api/core/#formlyfieldconfig

Steps to exercise for later testing:

Add the form definition to the database:

```
grpcurl -H "x-oidc-id-token: $(holos token)" -d @ jeff.app.dev.k2.holos.run:443 holos.v1alpha1.PlatformService.AddPlatform <<EOF
{
  "platform": {
    "org_id": "018f27cd-e5ac-7f98-bfe1-2dbab208a48c",
    "name": "bare${RANDOM}",
    "config": {
      "form": "$(cue export ./forms/platform/ --out json | jq -cM | base64 -w0)"
    }
  }
}
EOF
```

Get the form definition back out:

```

❯ grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"018f2bc1-6590-7670-958a-9f3bc02b658f"}' jeff.app.dev.k2.holos.run:443 holos.v1alpha1.PlatformService.GetForm
{
  "apiVersion": "forms.holos.run/v1alpha1",
  "kind": "PlatformForm",
  "metadata": {
    "name": "bare"
  },
  "spec": {
    "sections": [
      {
        "name": "org",
        "displayName": "Organization",
        "description": "Organization config values are used to derive more specific configuration values throughout the platform.",
        "fieldConfigs": [
          {
            "key": "name",
            "type": "input",
            "props": {
              "label": "Name",
              "placeholder": "example",
              "description": "DNS label, e.g. 'example'",
              "required": true
            }
          },
          {
            "key": "domain",
            "type": "input",
            "props": {
              "label": "Domain",
              "placeholder": "example.com",
              "description": "DNS domain, e.g. 'example.com'",
              "required": true
            }
          },
          {
            "key": "displayName",
            "type": "input",
            "props": {
              "label": "Display Name",
              "placeholder": "Example Organization",
              "description": "Display name, e.g. 'Example Organization'",
              "required": true
            }
          },
          {
            "key": "contactEmail",
            "type": "input",
            "props": {
              "label": "Contact Email",
              "placeholder": "platform-team@example.com",
              "description": "Technical contact email address",
              "required": true
            }
          }
        ]
      }
    ]
  }
}
```

References

```
❯ cue export ./forms/platform/ --out yaml | yq
apiVersion: forms.holos.run/v1alpha1
kind: PlatformForm
metadata:
  name: bare
spec:
  sections:
    - name: org
      displayName: Organization
      description: Organization config values are used to derive more specific configuration values throughout the platform.
      fieldConfigs:
        - key: name
          type: input
          props:
            label: Name
            placeholder: example
            description: DNS label, e.g. 'example'
            required: true
        - key: domain
          type: input
          props:
            label: Domain
            placeholder: example.com
            description: DNS domain, e.g. 'example.com'
            required: true
        - key: displayName
          type: input
          props:
            label: Display Name
            placeholder: Example Organization
            description: Display name, e.g. 'Example Organization'
            required: true
        - key: contactEmail
          type: input
          props:
            label: Contact Email
            placeholder: platform-team@example.com
            description: Technical contact email address
            required: true
```
2024-04-29 14:24:16 -07:00
Jeff McCune
26e537e768 (#150) Add platform config form, values, cue
This patch adds 4 fields to the Platform table:

 1. Config Form represents the JSON FormlyFieldConfig for the UI.
 2. Config CUE represents the CUE file containing a definition the
    Config Values must unify with.
 3. Config Definition is the CUE definition variable name used to unify
    the values with the cue code.  Should be #PlatformSpec in most
    cases.
 4. Config Values represents the JSON values provided by the UI.

The use case is the platform engineer defines the #PlatformSpec in cue,
and provides the form field config.  The platform engineer then provides
1-3 above when adding or updating a Platform.

The UI then presents the form to the end user and provides values for 4
when the user submits the form.

This patch also refactors the AddPlatform method to accept a Platform
message.  To do so we make the id field optional since it is server
assigned.

The patch also adds a database constraint to ensure platform names are
unique within the scope of an organization.

Results:

Note how the CUE representation of the Platform Form is exported to JSON
then converted to a base64 encoded string, which is the protobuf JSON
representation of a bytes[] value.

```
grpcurl -H "x-oidc-id-token: $(holos token)" -d @ jeff.app.dev.k2.holos.run:443 holos.v1alpha1.PlatformService.AddPlatform <<EOF
{
  "platform": {
    "id": "0d3dc0c0-bbc8-41f8-8c6e-75f0476509d6",
    "org_id": "018f27cd-e5ac-7f98-bfe1-2dbab208a48c",
    "name": "bare",
    "config": {
      "form": "$(cd internal/platforms/bare && cue export ./forms/platform/ --out json | jq -cM | base64 -w0)"
    }
  }
}
EOF
```

Note the requested platform ID is ignored.

```
{
  "platforms": [
    {
      "id": "018f2af9-f7ba-772a-9db6-f985ece8fed1",
      "timestamps": {
        "createdAt": "2024-04-29T17:49:36.058379Z",
        "updatedAt": "2024-04-29T17:49:36.058379Z"
      },
      "name": "bare",
      "creator": {
        "id": "018f27cd-e591-7f98-a9d2-416167282d37"
      },
      "config": {
        "form": "eyJhcGlWZXJzaW9uIjoiZm9ybXMuaG9sb3MucnVuL3YxYWxwaGExIiwia2luZCI6IlBsYXRmb3JtRm9ybSIsIm1ldGFkYXRhIjp7Im5hbWUiOiJiYXJlIn0sInNwZWMiOnsic2VjdGlvbnMiOlt7Im5hbWUiOiJvcmciLCJkaXNwbGF5TmFtZSI6Ik9yZ2FuaXphdGlvbiIsImRlc2NyaXB0aW9uIjoiT3JnYW5pemF0aW9uIGNvbmZpZyB2YWx1ZXMgYXJlIHVzZWQgdG8gZGVyaXZlIG1vcmUgc3BlY2lmaWMgY29uZmlndXJhdGlvbiB2YWx1ZXMgdGhyb3VnaG91dCB0aGUgcGxhdGZvcm0uIiwiZmllbGRDb25maWdzIjpbeyJrZXkiOiJuYW1lIiwidHlwZSI6ImlucHV0IiwicHJvcHMiOnsibGFiZWwiOiJOYW1lIiwicGxhY2Vob2xkZXIiOiJleGFtcGxlIiwiZGVzY3JpcHRpb24iOiJETlMgbGFiZWwsIGUuZy4gJ2V4YW1wbGUnIiwicmVxdWlyZWQiOnRydWV9fSx7ImtleSI6ImRvbWFpbiIsInR5cGUiOiJpbnB1dCIsInByb3BzIjp7ImxhYmVsIjoiRG9tYWluIiwicGxhY2Vob2xkZXIiOiJleGFtcGxlLmNvbSIsImRlc2NyaXB0aW9uIjoiRE5TIGRvbWFpbiwgZS5nLiAnZXhhbXBsZS5jb20nIiwicmVxdWlyZWQiOnRydWV9fSx7ImtleSI6ImRpc3BsYXlOYW1lIiwidHlwZSI6ImlucHV0IiwicHJvcHMiOnsibGFiZWwiOiJEaXNwbGF5IE5hbWUiLCJwbGFjZWhvbGRlciI6IkV4YW1wbGUgT3JnYW5pemF0aW9uIiwiZGVzY3JpcHRpb24iOiJEaXNwbGF5IG5hbWUsIGUuZy4gJ0V4YW1wbGUgT3JnYW5pemF0aW9uJyIsInJlcXVpcmVkIjp0cnVlfX0seyJrZXkiOiJjb250YWN0RW1haWwiLCJ0eXBlIjoiaW5wdXQiLCJwcm9wcyI6eyJsYWJlbCI6IkNvbnRhY3QgRW1haWwiLCJwbGFjZWhvbGRlciI6InBsYXRmb3JtLXRlYW1AZXhhbXBsZS5jb20iLCJkZXNjcmlwdGlvbiI6IlRlY2huaWNhbCBjb250YWN0IGVtYWlsIGFkZHJlc3MiLCJyZXF1aXJlZCI6dHJ1ZX19XX1dfX0K"
      }
    }
  ]
}
```
2024-04-29 10:53:23 -07:00
39 changed files with 2025 additions and 666 deletions

View File

@@ -38,6 +38,10 @@ 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_cue", Type: field.TypeBytes, Nullable: true},
{Name: "config_definition", Type: field.TypeString, Nullable: true},
{Name: "creator_id", Type: field.TypeUUID},
{Name: "org_id", Type: field.TypeUUID},
}
@@ -49,17 +53,24 @@ var (
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "platforms_users_creator",
Columns: []*schema.Column{PlatformsColumns[5]},
Columns: []*schema.Column{PlatformsColumns[9]},
RefColumns: []*schema.Column{UsersColumns[0]},
OnDelete: schema.NoAction,
},
{
Symbol: "platforms_organizations_organization",
Columns: []*schema.Column{PlatformsColumns[6]},
Columns: []*schema.Column{PlatformsColumns[10]},
RefColumns: []*schema.Column{OrganizationsColumns[0]},
OnDelete: schema.NoAction,
},
},
Indexes: []*schema.Index{
{
Name: "platform_org_id_name",
Unique: true,
Columns: []*schema.Column{PlatformsColumns[10], PlatformsColumns[3]},
},
},
}
// UsersColumns holds the columns for the "users" table.
UsersColumns = []*schema.Column{

View File

@@ -812,6 +812,10 @@ type PlatformMutation struct {
updated_at *time.Time
name *string
display_name *string
config_form *[]byte
config_values *[]byte
config_cue *[]byte
config_definition *string
clearedFields map[string]struct{}
creator *uuid.UUID
clearedcreator bool
@@ -1142,6 +1146,202 @@ func (m *PlatformMutation) ResetCreatorID() {
m.creator = nil
}
// SetConfigForm sets the "config_form" field.
func (m *PlatformMutation) SetConfigForm(b []byte) {
m.config_form = &b
}
// ConfigForm returns the value of the "config_form" field in the mutation.
func (m *PlatformMutation) ConfigForm() (r []byte, exists bool) {
v := m.config_form
if v == nil {
return
}
return *v, true
}
// 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) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldConfigForm is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldConfigForm requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldConfigForm: %w", err)
}
return oldValue.ConfigForm, nil
}
// ClearConfigForm clears the value of the "config_form" field.
func (m *PlatformMutation) ClearConfigForm() {
m.config_form = nil
m.clearedFields[platform.FieldConfigForm] = struct{}{}
}
// ConfigFormCleared returns if the "config_form" field was cleared in this mutation.
func (m *PlatformMutation) ConfigFormCleared() bool {
_, ok := m.clearedFields[platform.FieldConfigForm]
return ok
}
// ResetConfigForm resets all changes to the "config_form" field.
func (m *PlatformMutation) ResetConfigForm() {
m.config_form = nil
delete(m.clearedFields, platform.FieldConfigForm)
}
// SetConfigValues sets the "config_values" field.
func (m *PlatformMutation) SetConfigValues(b []byte) {
m.config_values = &b
}
// ConfigValues returns the value of the "config_values" field in the mutation.
func (m *PlatformMutation) ConfigValues() (r []byte, exists bool) {
v := m.config_values
if v == nil {
return
}
return *v, true
}
// 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) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldConfigValues is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldConfigValues requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldConfigValues: %w", err)
}
return oldValue.ConfigValues, nil
}
// ClearConfigValues clears the value of the "config_values" field.
func (m *PlatformMutation) ClearConfigValues() {
m.config_values = nil
m.clearedFields[platform.FieldConfigValues] = struct{}{}
}
// ConfigValuesCleared returns if the "config_values" field was cleared in this mutation.
func (m *PlatformMutation) ConfigValuesCleared() bool {
_, ok := m.clearedFields[platform.FieldConfigValues]
return ok
}
// ResetConfigValues resets all changes to the "config_values" field.
func (m *PlatformMutation) ResetConfigValues() {
m.config_values = nil
delete(m.clearedFields, platform.FieldConfigValues)
}
// SetConfigCue sets the "config_cue" field.
func (m *PlatformMutation) SetConfigCue(b []byte) {
m.config_cue = &b
}
// ConfigCue returns the value of the "config_cue" field in the mutation.
func (m *PlatformMutation) ConfigCue() (r []byte, exists bool) {
v := m.config_cue
if v == nil {
return
}
return *v, true
}
// OldConfigCue returns the old "config_cue" 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) OldConfigCue(ctx context.Context) (v []byte, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldConfigCue is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldConfigCue requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldConfigCue: %w", err)
}
return oldValue.ConfigCue, nil
}
// ClearConfigCue clears the value of the "config_cue" field.
func (m *PlatformMutation) ClearConfigCue() {
m.config_cue = nil
m.clearedFields[platform.FieldConfigCue] = struct{}{}
}
// ConfigCueCleared returns if the "config_cue" field was cleared in this mutation.
func (m *PlatformMutation) ConfigCueCleared() bool {
_, ok := m.clearedFields[platform.FieldConfigCue]
return ok
}
// ResetConfigCue resets all changes to the "config_cue" field.
func (m *PlatformMutation) ResetConfigCue() {
m.config_cue = nil
delete(m.clearedFields, platform.FieldConfigCue)
}
// SetConfigDefinition sets the "config_definition" field.
func (m *PlatformMutation) SetConfigDefinition(s string) {
m.config_definition = &s
}
// ConfigDefinition returns the value of the "config_definition" field in the mutation.
func (m *PlatformMutation) ConfigDefinition() (r string, exists bool) {
v := m.config_definition
if v == nil {
return
}
return *v, true
}
// OldConfigDefinition returns the old "config_definition" 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) OldConfigDefinition(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldConfigDefinition is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldConfigDefinition requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldConfigDefinition: %w", err)
}
return oldValue.ConfigDefinition, nil
}
// ClearConfigDefinition clears the value of the "config_definition" field.
func (m *PlatformMutation) ClearConfigDefinition() {
m.config_definition = nil
m.clearedFields[platform.FieldConfigDefinition] = struct{}{}
}
// ConfigDefinitionCleared returns if the "config_definition" field was cleared in this mutation.
func (m *PlatformMutation) ConfigDefinitionCleared() bool {
_, ok := m.clearedFields[platform.FieldConfigDefinition]
return ok
}
// ResetConfigDefinition resets all changes to the "config_definition" field.
func (m *PlatformMutation) ResetConfigDefinition() {
m.config_definition = nil
delete(m.clearedFields, platform.FieldConfigDefinition)
}
// ClearCreator clears the "creator" edge to the User entity.
func (m *PlatformMutation) ClearCreator() {
m.clearedcreator = true
@@ -1243,7 +1443,7 @@ func (m *PlatformMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *PlatformMutation) Fields() []string {
fields := make([]string, 0, 6)
fields := make([]string, 0, 10)
if m.created_at != nil {
fields = append(fields, platform.FieldCreatedAt)
}
@@ -1262,6 +1462,18 @@ func (m *PlatformMutation) Fields() []string {
if m.creator != nil {
fields = append(fields, platform.FieldCreatorID)
}
if m.config_form != nil {
fields = append(fields, platform.FieldConfigForm)
}
if m.config_values != nil {
fields = append(fields, platform.FieldConfigValues)
}
if m.config_cue != nil {
fields = append(fields, platform.FieldConfigCue)
}
if m.config_definition != nil {
fields = append(fields, platform.FieldConfigDefinition)
}
return fields
}
@@ -1282,6 +1494,14 @@ func (m *PlatformMutation) Field(name string) (ent.Value, bool) {
return m.DisplayName()
case platform.FieldCreatorID:
return m.CreatorID()
case platform.FieldConfigForm:
return m.ConfigForm()
case platform.FieldConfigValues:
return m.ConfigValues()
case platform.FieldConfigCue:
return m.ConfigCue()
case platform.FieldConfigDefinition:
return m.ConfigDefinition()
}
return nil, false
}
@@ -1303,6 +1523,14 @@ func (m *PlatformMutation) OldField(ctx context.Context, name string) (ent.Value
return m.OldDisplayName(ctx)
case platform.FieldCreatorID:
return m.OldCreatorID(ctx)
case platform.FieldConfigForm:
return m.OldConfigForm(ctx)
case platform.FieldConfigValues:
return m.OldConfigValues(ctx)
case platform.FieldConfigCue:
return m.OldConfigCue(ctx)
case platform.FieldConfigDefinition:
return m.OldConfigDefinition(ctx)
}
return nil, fmt.Errorf("unknown Platform field %s", name)
}
@@ -1354,6 +1582,34 @@ func (m *PlatformMutation) SetField(name string, value ent.Value) error {
}
m.SetCreatorID(v)
return nil
case platform.FieldConfigForm:
v, ok := value.([]byte)
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)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetConfigValues(v)
return nil
case platform.FieldConfigCue:
v, ok := value.([]byte)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetConfigCue(v)
return nil
case platform.FieldConfigDefinition:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetConfigDefinition(v)
return nil
}
return fmt.Errorf("unknown Platform field %s", name)
}
@@ -1383,7 +1639,20 @@ func (m *PlatformMutation) AddField(name string, value ent.Value) error {
// ClearedFields returns all nullable fields that were cleared during this
// mutation.
func (m *PlatformMutation) ClearedFields() []string {
return nil
var fields []string
if m.FieldCleared(platform.FieldConfigForm) {
fields = append(fields, platform.FieldConfigForm)
}
if m.FieldCleared(platform.FieldConfigValues) {
fields = append(fields, platform.FieldConfigValues)
}
if m.FieldCleared(platform.FieldConfigCue) {
fields = append(fields, platform.FieldConfigCue)
}
if m.FieldCleared(platform.FieldConfigDefinition) {
fields = append(fields, platform.FieldConfigDefinition)
}
return fields
}
// FieldCleared returns a boolean indicating if a field with the given name was
@@ -1396,6 +1665,20 @@ func (m *PlatformMutation) FieldCleared(name string) bool {
// ClearField clears the value of the field with the given name. It returns an
// error if the field is not defined in the schema.
func (m *PlatformMutation) ClearField(name string) error {
switch name {
case platform.FieldConfigForm:
m.ClearConfigForm()
return nil
case platform.FieldConfigValues:
m.ClearConfigValues()
return nil
case platform.FieldConfigCue:
m.ClearConfigCue()
return nil
case platform.FieldConfigDefinition:
m.ClearConfigDefinition()
return nil
}
return fmt.Errorf("unknown Platform nullable field %s", name)
}
@@ -1421,6 +1704,18 @@ func (m *PlatformMutation) ResetField(name string) error {
case platform.FieldCreatorID:
m.ResetCreatorID()
return nil
case platform.FieldConfigForm:
m.ResetConfigForm()
return nil
case platform.FieldConfigValues:
m.ResetConfigValues()
return nil
case platform.FieldConfigCue:
m.ResetConfigCue()
return nil
case platform.FieldConfigDefinition:
m.ResetConfigDefinition()
return nil
}
return fmt.Errorf("unknown Platform field %s", name)
}

View File

@@ -32,6 +32,14 @@ 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"`
// 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'
ConfigDefinition string `json:"config_definition,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the PlatformQuery when eager-loading is set.
Edges PlatformEdges `json:"edges"`
@@ -76,7 +84,9 @@ func (*Platform) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case platform.FieldName, platform.FieldDisplayName:
case platform.FieldConfigForm, platform.FieldConfigValues, platform.FieldConfigCue:
values[i] = new([]byte)
case platform.FieldName, platform.FieldDisplayName, platform.FieldConfigDefinition:
values[i] = new(sql.NullString)
case platform.FieldCreatedAt, platform.FieldUpdatedAt:
values[i] = new(sql.NullTime)
@@ -139,6 +149,30 @@ func (pl *Platform) assignValues(columns []string, values []any) error {
} else if value != nil {
pl.CreatorID = *value
}
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
}
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
}
case platform.FieldConfigCue:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field config_cue", values[i])
} else if value != nil {
pl.ConfigCue = *value
}
case platform.FieldConfigDefinition:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field config_definition", values[i])
} else if value.Valid {
pl.ConfigDefinition = value.String
}
default:
pl.selectValues.Set(columns[i], values[i])
}
@@ -202,6 +236,18 @@ func (pl *Platform) String() string {
builder.WriteString(", ")
builder.WriteString("creator_id=")
builder.WriteString(fmt.Sprintf("%v", pl.CreatorID))
builder.WriteString(", ")
builder.WriteString("config_form=")
builder.WriteString(fmt.Sprintf("%v", pl.ConfigForm))
builder.WriteString(", ")
builder.WriteString("config_values=")
builder.WriteString(fmt.Sprintf("%v", pl.ConfigValues))
builder.WriteString(", ")
builder.WriteString("config_cue=")
builder.WriteString(fmt.Sprintf("%v", pl.ConfigCue))
builder.WriteString(", ")
builder.WriteString("config_definition=")
builder.WriteString(pl.ConfigDefinition)
builder.WriteByte(')')
return builder.String()
}

View File

@@ -27,6 +27,14 @@ const (
FieldDisplayName = "display_name"
// FieldCreatorID holds the string denoting the creator_id field in the database.
FieldCreatorID = "creator_id"
// FieldConfigForm holds the string denoting the config_form field in the database.
FieldConfigForm = "config_form"
// FieldConfigValues holds the string denoting the config_values field in the database.
FieldConfigValues = "config_values"
// FieldConfigCue holds the string denoting the config_cue field in the database.
FieldConfigCue = "config_cue"
// FieldConfigDefinition holds the string denoting the config_definition field in the database.
FieldConfigDefinition = "config_definition"
// EdgeCreator holds the string denoting the creator edge name in mutations.
EdgeCreator = "creator"
// EdgeOrganization holds the string denoting the organization edge name in mutations.
@@ -58,6 +66,10 @@ var Columns = []string{
FieldName,
FieldDisplayName,
FieldCreatorID,
FieldConfigForm,
FieldConfigValues,
FieldConfigCue,
FieldConfigDefinition,
}
// ValidColumn reports if the column name is valid (part of the table columns).
@@ -121,6 +133,11 @@ func ByCreatorID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatorID, opts...).ToFunc()
}
// ByConfigDefinition orders the results by the config_definition field.
func ByConfigDefinition(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldConfigDefinition, opts...).ToFunc()
}
// ByCreatorField orders the results by creator field.
func ByCreatorField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {

View File

@@ -86,6 +86,26 @@ 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))
}
// ConfigDefinition applies equality check predicate on the "config_definition" field. It's identical to ConfigDefinitionEQ.
func ConfigDefinition(v string) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldConfigDefinition, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldCreatedAt, v))
@@ -336,6 +356,231 @@ 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))
}
// ConfigFormNotNil applies the NotNil predicate on the "config_form" field.
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))
}
// ConfigValuesNotNil applies the NotNil predicate on the "config_values" field.
func ConfigValuesNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldConfigValues))
}
// ConfigCueEQ applies the EQ predicate on the "config_cue" field.
func ConfigCueEQ(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldConfigCue, v))
}
// ConfigCueNEQ applies the NEQ predicate on the "config_cue" field.
func ConfigCueNEQ(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldNEQ(FieldConfigCue, v))
}
// ConfigCueIn applies the In predicate on the "config_cue" field.
func ConfigCueIn(vs ...[]byte) predicate.Platform {
return predicate.Platform(sql.FieldIn(FieldConfigCue, vs...))
}
// ConfigCueNotIn applies the NotIn predicate on the "config_cue" field.
func ConfigCueNotIn(vs ...[]byte) predicate.Platform {
return predicate.Platform(sql.FieldNotIn(FieldConfigCue, vs...))
}
// ConfigCueGT applies the GT predicate on the "config_cue" field.
func ConfigCueGT(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldGT(FieldConfigCue, v))
}
// ConfigCueGTE applies the GTE predicate on the "config_cue" field.
func ConfigCueGTE(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldGTE(FieldConfigCue, v))
}
// ConfigCueLT applies the LT predicate on the "config_cue" field.
func ConfigCueLT(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldLT(FieldConfigCue, v))
}
// ConfigCueLTE applies the LTE predicate on the "config_cue" field.
func ConfigCueLTE(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldLTE(FieldConfigCue, v))
}
// ConfigCueIsNil applies the IsNil predicate on the "config_cue" field.
func ConfigCueIsNil() predicate.Platform {
return predicate.Platform(sql.FieldIsNull(FieldConfigCue))
}
// ConfigCueNotNil applies the NotNil predicate on the "config_cue" field.
func ConfigCueNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldConfigCue))
}
// ConfigDefinitionEQ applies the EQ predicate on the "config_definition" field.
func ConfigDefinitionEQ(v string) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldConfigDefinition, v))
}
// ConfigDefinitionNEQ applies the NEQ predicate on the "config_definition" field.
func ConfigDefinitionNEQ(v string) predicate.Platform {
return predicate.Platform(sql.FieldNEQ(FieldConfigDefinition, v))
}
// ConfigDefinitionIn applies the In predicate on the "config_definition" field.
func ConfigDefinitionIn(vs ...string) predicate.Platform {
return predicate.Platform(sql.FieldIn(FieldConfigDefinition, vs...))
}
// ConfigDefinitionNotIn applies the NotIn predicate on the "config_definition" field.
func ConfigDefinitionNotIn(vs ...string) predicate.Platform {
return predicate.Platform(sql.FieldNotIn(FieldConfigDefinition, vs...))
}
// ConfigDefinitionGT applies the GT predicate on the "config_definition" field.
func ConfigDefinitionGT(v string) predicate.Platform {
return predicate.Platform(sql.FieldGT(FieldConfigDefinition, v))
}
// ConfigDefinitionGTE applies the GTE predicate on the "config_definition" field.
func ConfigDefinitionGTE(v string) predicate.Platform {
return predicate.Platform(sql.FieldGTE(FieldConfigDefinition, v))
}
// ConfigDefinitionLT applies the LT predicate on the "config_definition" field.
func ConfigDefinitionLT(v string) predicate.Platform {
return predicate.Platform(sql.FieldLT(FieldConfigDefinition, v))
}
// ConfigDefinitionLTE applies the LTE predicate on the "config_definition" field.
func ConfigDefinitionLTE(v string) predicate.Platform {
return predicate.Platform(sql.FieldLTE(FieldConfigDefinition, v))
}
// ConfigDefinitionContains applies the Contains predicate on the "config_definition" field.
func ConfigDefinitionContains(v string) predicate.Platform {
return predicate.Platform(sql.FieldContains(FieldConfigDefinition, v))
}
// ConfigDefinitionHasPrefix applies the HasPrefix predicate on the "config_definition" field.
func ConfigDefinitionHasPrefix(v string) predicate.Platform {
return predicate.Platform(sql.FieldHasPrefix(FieldConfigDefinition, v))
}
// ConfigDefinitionHasSuffix applies the HasSuffix predicate on the "config_definition" field.
func ConfigDefinitionHasSuffix(v string) predicate.Platform {
return predicate.Platform(sql.FieldHasSuffix(FieldConfigDefinition, v))
}
// ConfigDefinitionIsNil applies the IsNil predicate on the "config_definition" field.
func ConfigDefinitionIsNil() predicate.Platform {
return predicate.Platform(sql.FieldIsNull(FieldConfigDefinition))
}
// ConfigDefinitionNotNil applies the NotNil predicate on the "config_definition" field.
func ConfigDefinitionNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldConfigDefinition))
}
// ConfigDefinitionEqualFold applies the EqualFold predicate on the "config_definition" field.
func ConfigDefinitionEqualFold(v string) predicate.Platform {
return predicate.Platform(sql.FieldEqualFold(FieldConfigDefinition, v))
}
// ConfigDefinitionContainsFold applies the ContainsFold predicate on the "config_definition" field.
func ConfigDefinitionContainsFold(v string) predicate.Platform {
return predicate.Platform(sql.FieldContainsFold(FieldConfigDefinition, v))
}
// HasCreator applies the HasEdge predicate on the "creator" edge.
func HasCreator() predicate.Platform {
return predicate.Platform(func(s *sql.Selector) {

View File

@@ -78,6 +78,38 @@ func (pc *PlatformCreate) SetCreatorID(u uuid.UUID) *PlatformCreate {
return pc
}
// SetConfigForm sets the "config_form" field.
func (pc *PlatformCreate) SetConfigForm(b []byte) *PlatformCreate {
pc.mutation.SetConfigForm(b)
return pc
}
// SetConfigValues sets the "config_values" field.
func (pc *PlatformCreate) SetConfigValues(b []byte) *PlatformCreate {
pc.mutation.SetConfigValues(b)
return pc
}
// SetConfigCue sets the "config_cue" field.
func (pc *PlatformCreate) SetConfigCue(b []byte) *PlatformCreate {
pc.mutation.SetConfigCue(b)
return pc
}
// SetConfigDefinition sets the "config_definition" field.
func (pc *PlatformCreate) SetConfigDefinition(s string) *PlatformCreate {
pc.mutation.SetConfigDefinition(s)
return pc
}
// SetNillableConfigDefinition sets the "config_definition" field if the given value is not nil.
func (pc *PlatformCreate) SetNillableConfigDefinition(s *string) *PlatformCreate {
if s != nil {
pc.SetConfigDefinition(*s)
}
return pc
}
// SetID sets the "id" field.
func (pc *PlatformCreate) SetID(u uuid.UUID) *PlatformCreate {
pc.mutation.SetID(u)
@@ -240,6 +272,22 @@ func (pc *PlatformCreate) createSpec() (*Platform, *sqlgraph.CreateSpec) {
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
_node.DisplayName = value
}
if value, ok := pc.mutation.ConfigForm(); ok {
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
_node.ConfigForm = value
}
if value, ok := pc.mutation.ConfigValues(); ok {
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
_node.ConfigValues = value
}
if value, ok := pc.mutation.ConfigCue(); ok {
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
_node.ConfigCue = value
}
if value, ok := pc.mutation.ConfigDefinition(); ok {
_spec.SetField(platform.FieldConfigDefinition, field.TypeString, value)
_node.ConfigDefinition = value
}
if nodes := pc.mutation.CreatorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
@@ -386,6 +434,78 @@ func (u *PlatformUpsert) UpdateCreatorID() *PlatformUpsert {
return u
}
// SetConfigForm sets the "config_form" field.
func (u *PlatformUpsert) SetConfigForm(v []byte) *PlatformUpsert {
u.Set(platform.FieldConfigForm, v)
return u
}
// UpdateConfigForm sets the "config_form" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateConfigForm() *PlatformUpsert {
u.SetExcluded(platform.FieldConfigForm)
return u
}
// ClearConfigForm clears the value of the "config_form" field.
func (u *PlatformUpsert) ClearConfigForm() *PlatformUpsert {
u.SetNull(platform.FieldConfigForm)
return u
}
// SetConfigValues sets the "config_values" field.
func (u *PlatformUpsert) SetConfigValues(v []byte) *PlatformUpsert {
u.Set(platform.FieldConfigValues, v)
return u
}
// UpdateConfigValues sets the "config_values" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateConfigValues() *PlatformUpsert {
u.SetExcluded(platform.FieldConfigValues)
return u
}
// ClearConfigValues clears the value of the "config_values" field.
func (u *PlatformUpsert) ClearConfigValues() *PlatformUpsert {
u.SetNull(platform.FieldConfigValues)
return u
}
// SetConfigCue sets the "config_cue" field.
func (u *PlatformUpsert) SetConfigCue(v []byte) *PlatformUpsert {
u.Set(platform.FieldConfigCue, v)
return u
}
// UpdateConfigCue sets the "config_cue" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateConfigCue() *PlatformUpsert {
u.SetExcluded(platform.FieldConfigCue)
return u
}
// ClearConfigCue clears the value of the "config_cue" field.
func (u *PlatformUpsert) ClearConfigCue() *PlatformUpsert {
u.SetNull(platform.FieldConfigCue)
return u
}
// SetConfigDefinition sets the "config_definition" field.
func (u *PlatformUpsert) SetConfigDefinition(v string) *PlatformUpsert {
u.Set(platform.FieldConfigDefinition, v)
return u
}
// UpdateConfigDefinition sets the "config_definition" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateConfigDefinition() *PlatformUpsert {
u.SetExcluded(platform.FieldConfigDefinition)
return u
}
// ClearConfigDefinition clears the value of the "config_definition" field.
func (u *PlatformUpsert) ClearConfigDefinition() *PlatformUpsert {
u.SetNull(platform.FieldConfigDefinition)
return u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field.
// Using this option is equivalent to using:
//
@@ -507,6 +627,90 @@ func (u *PlatformUpsertOne) UpdateCreatorID() *PlatformUpsertOne {
})
}
// SetConfigForm sets the "config_form" field.
func (u *PlatformUpsertOne) SetConfigForm(v []byte) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigForm(v)
})
}
// UpdateConfigForm sets the "config_form" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateConfigForm() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigForm()
})
}
// ClearConfigForm clears the value of the "config_form" field.
func (u *PlatformUpsertOne) ClearConfigForm() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigForm()
})
}
// SetConfigValues sets the "config_values" field.
func (u *PlatformUpsertOne) SetConfigValues(v []byte) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigValues(v)
})
}
// UpdateConfigValues sets the "config_values" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateConfigValues() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigValues()
})
}
// ClearConfigValues clears the value of the "config_values" field.
func (u *PlatformUpsertOne) ClearConfigValues() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigValues()
})
}
// SetConfigCue sets the "config_cue" field.
func (u *PlatformUpsertOne) SetConfigCue(v []byte) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigCue(v)
})
}
// UpdateConfigCue sets the "config_cue" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateConfigCue() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigCue()
})
}
// ClearConfigCue clears the value of the "config_cue" field.
func (u *PlatformUpsertOne) ClearConfigCue() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigCue()
})
}
// SetConfigDefinition sets the "config_definition" field.
func (u *PlatformUpsertOne) SetConfigDefinition(v string) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigDefinition(v)
})
}
// UpdateConfigDefinition sets the "config_definition" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateConfigDefinition() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigDefinition()
})
}
// ClearConfigDefinition clears the value of the "config_definition" field.
func (u *PlatformUpsertOne) ClearConfigDefinition() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigDefinition()
})
}
// Exec executes the query.
func (u *PlatformUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
@@ -795,6 +999,90 @@ func (u *PlatformUpsertBulk) UpdateCreatorID() *PlatformUpsertBulk {
})
}
// SetConfigForm sets the "config_form" field.
func (u *PlatformUpsertBulk) SetConfigForm(v []byte) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigForm(v)
})
}
// UpdateConfigForm sets the "config_form" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateConfigForm() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigForm()
})
}
// ClearConfigForm clears the value of the "config_form" field.
func (u *PlatformUpsertBulk) ClearConfigForm() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigForm()
})
}
// SetConfigValues sets the "config_values" field.
func (u *PlatformUpsertBulk) SetConfigValues(v []byte) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigValues(v)
})
}
// UpdateConfigValues sets the "config_values" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateConfigValues() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigValues()
})
}
// ClearConfigValues clears the value of the "config_values" field.
func (u *PlatformUpsertBulk) ClearConfigValues() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigValues()
})
}
// SetConfigCue sets the "config_cue" field.
func (u *PlatformUpsertBulk) SetConfigCue(v []byte) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigCue(v)
})
}
// UpdateConfigCue sets the "config_cue" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateConfigCue() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigCue()
})
}
// ClearConfigCue clears the value of the "config_cue" field.
func (u *PlatformUpsertBulk) ClearConfigCue() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigCue()
})
}
// SetConfigDefinition sets the "config_definition" field.
func (u *PlatformUpsertBulk) SetConfigDefinition(v string) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetConfigDefinition(v)
})
}
// UpdateConfigDefinition sets the "config_definition" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateConfigDefinition() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateConfigDefinition()
})
}
// ClearConfigDefinition clears the value of the "config_definition" field.
func (u *PlatformUpsertBulk) ClearConfigDefinition() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearConfigDefinition()
})
}
// Exec executes the query.
func (u *PlatformUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {

View File

@@ -93,6 +93,62 @@ func (pu *PlatformUpdate) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate {
return pu
}
// SetConfigForm sets the "config_form" field.
func (pu *PlatformUpdate) SetConfigForm(b []byte) *PlatformUpdate {
pu.mutation.SetConfigForm(b)
return pu
}
// ClearConfigForm clears the value of the "config_form" field.
func (pu *PlatformUpdate) ClearConfigForm() *PlatformUpdate {
pu.mutation.ClearConfigForm()
return pu
}
// SetConfigValues sets the "config_values" field.
func (pu *PlatformUpdate) SetConfigValues(b []byte) *PlatformUpdate {
pu.mutation.SetConfigValues(b)
return pu
}
// ClearConfigValues clears the value of the "config_values" field.
func (pu *PlatformUpdate) ClearConfigValues() *PlatformUpdate {
pu.mutation.ClearConfigValues()
return pu
}
// SetConfigCue sets the "config_cue" field.
func (pu *PlatformUpdate) SetConfigCue(b []byte) *PlatformUpdate {
pu.mutation.SetConfigCue(b)
return pu
}
// ClearConfigCue clears the value of the "config_cue" field.
func (pu *PlatformUpdate) ClearConfigCue() *PlatformUpdate {
pu.mutation.ClearConfigCue()
return pu
}
// SetConfigDefinition sets the "config_definition" field.
func (pu *PlatformUpdate) SetConfigDefinition(s string) *PlatformUpdate {
pu.mutation.SetConfigDefinition(s)
return pu
}
// SetNillableConfigDefinition sets the "config_definition" field if the given value is not nil.
func (pu *PlatformUpdate) SetNillableConfigDefinition(s *string) *PlatformUpdate {
if s != nil {
pu.SetConfigDefinition(*s)
}
return pu
}
// ClearConfigDefinition clears the value of the "config_definition" field.
func (pu *PlatformUpdate) ClearConfigDefinition() *PlatformUpdate {
pu.mutation.ClearConfigDefinition()
return pu
}
// SetCreator sets the "creator" edge to the User entity.
func (pu *PlatformUpdate) SetCreator(u *User) *PlatformUpdate {
return pu.SetCreatorID(u.ID)
@@ -199,6 +255,30 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
if value, ok := pu.mutation.DisplayName(); ok {
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
}
if value, ok := pu.mutation.ConfigForm(); ok {
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
}
if pu.mutation.ConfigFormCleared() {
_spec.ClearField(platform.FieldConfigForm, field.TypeBytes)
}
if value, ok := pu.mutation.ConfigValues(); ok {
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
}
if pu.mutation.ConfigValuesCleared() {
_spec.ClearField(platform.FieldConfigValues, field.TypeBytes)
}
if value, ok := pu.mutation.ConfigCue(); ok {
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
}
if pu.mutation.ConfigCueCleared() {
_spec.ClearField(platform.FieldConfigCue, field.TypeBytes)
}
if value, ok := pu.mutation.ConfigDefinition(); ok {
_spec.SetField(platform.FieldConfigDefinition, field.TypeString, value)
}
if pu.mutation.ConfigDefinitionCleared() {
_spec.ClearField(platform.FieldConfigDefinition, field.TypeString)
}
if pu.mutation.CreatorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
@@ -339,6 +419,62 @@ func (puo *PlatformUpdateOne) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate
return puo
}
// SetConfigForm sets the "config_form" field.
func (puo *PlatformUpdateOne) SetConfigForm(b []byte) *PlatformUpdateOne {
puo.mutation.SetConfigForm(b)
return puo
}
// ClearConfigForm clears the value of the "config_form" field.
func (puo *PlatformUpdateOne) ClearConfigForm() *PlatformUpdateOne {
puo.mutation.ClearConfigForm()
return puo
}
// SetConfigValues sets the "config_values" field.
func (puo *PlatformUpdateOne) SetConfigValues(b []byte) *PlatformUpdateOne {
puo.mutation.SetConfigValues(b)
return puo
}
// ClearConfigValues clears the value of the "config_values" field.
func (puo *PlatformUpdateOne) ClearConfigValues() *PlatformUpdateOne {
puo.mutation.ClearConfigValues()
return puo
}
// SetConfigCue sets the "config_cue" field.
func (puo *PlatformUpdateOne) SetConfigCue(b []byte) *PlatformUpdateOne {
puo.mutation.SetConfigCue(b)
return puo
}
// ClearConfigCue clears the value of the "config_cue" field.
func (puo *PlatformUpdateOne) ClearConfigCue() *PlatformUpdateOne {
puo.mutation.ClearConfigCue()
return puo
}
// SetConfigDefinition sets the "config_definition" field.
func (puo *PlatformUpdateOne) SetConfigDefinition(s string) *PlatformUpdateOne {
puo.mutation.SetConfigDefinition(s)
return puo
}
// SetNillableConfigDefinition sets the "config_definition" field if the given value is not nil.
func (puo *PlatformUpdateOne) SetNillableConfigDefinition(s *string) *PlatformUpdateOne {
if s != nil {
puo.SetConfigDefinition(*s)
}
return puo
}
// ClearConfigDefinition clears the value of the "config_definition" field.
func (puo *PlatformUpdateOne) ClearConfigDefinition() *PlatformUpdateOne {
puo.mutation.ClearConfigDefinition()
return puo
}
// SetCreator sets the "creator" edge to the User entity.
func (puo *PlatformUpdateOne) SetCreator(u *User) *PlatformUpdateOne {
return puo.SetCreatorID(u.ID)
@@ -475,6 +611,30 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
if value, ok := puo.mutation.DisplayName(); ok {
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
}
if value, ok := puo.mutation.ConfigForm(); ok {
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
}
if puo.mutation.ConfigFormCleared() {
_spec.ClearField(platform.FieldConfigForm, field.TypeBytes)
}
if value, ok := puo.mutation.ConfigValues(); ok {
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
}
if puo.mutation.ConfigValuesCleared() {
_spec.ClearField(platform.FieldConfigValues, field.TypeBytes)
}
if value, ok := puo.mutation.ConfigCue(); ok {
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
}
if puo.mutation.ConfigCueCleared() {
_spec.ClearField(platform.FieldConfigCue, field.TypeBytes)
}
if value, ok := puo.mutation.ConfigDefinition(); ok {
_spec.SetField(platform.FieldConfigDefinition, field.TypeString, value)
}
if puo.mutation.ConfigDefinitionCleared() {
_spec.ClearField(platform.FieldConfigDefinition, field.TypeString)
}
if puo.mutation.CreatorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,

View File

@@ -4,6 +4,7 @@ import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/gofrs/uuid"
)
@@ -24,6 +25,18 @@ func (Platform) Fields() []ent.Field {
field.String("name").NotEmpty(),
field.String("display_name"),
field.UUID("creator_id", uuid.UUID{}),
field.Bytes("config_form").
Optional().
Comment("Opaque JSON bytes representing the platform config form."),
field.Bytes("config_values").
Optional().
Comment("Opaque JSON bytes representing the platform config values."),
field.Bytes("config_cue").
Optional().
Comment("Opaque bytes representing the CUE definition of the config struct."),
field.String("config_definition").
Optional().
Comment("The definition name to vet config_values against config_cue e.g. '#PlatformSpec'"),
}
}
@@ -39,3 +52,10 @@ func (Platform) Edges() []ent.Edge {
Required(),
}
}
func (Platform) Indexes() []ent.Index {
return []ent.Index{
// One org cannot have two platforms with the same name.
index.Fields("org_id", "name").Unique(),
}
}

View File

@@ -45,7 +45,7 @@
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",

View File

@@ -1,5 +1,5 @@
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { FormlyModule } from '@ngx-formly/core';
// import { provideHttpClient, withFetch } from '@angular/common/http';
import { routes } from './app.routes';
@@ -8,14 +8,16 @@ import { ConnectModule } from '../connect/connect.module';
import { provideClient } from "../connect/client.provider";
import { UserService } from './gen/holos/v1alpha1/user_connect';
import { OrganizationService } from './gen/holos/v1alpha1/organization_connect';
import { PlatformService } from './gen/holos/v1alpha1/platform_connect';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideRouter(routes, withComponentInputBinding()),
provideAnimationsAsync(),
// provideHttpClient(withFetch()),
provideClient(UserService),
provideClient(OrganizationService),
provideClient(PlatformService),
importProvidersFrom(
ConnectModule.forRoot({
baseUrl: window.location.origin

View File

@@ -1,15 +1,13 @@
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { ClusterListComponent } from './cluster-list/cluster-list.component';
import { ErrorNotFoundComponent } from './error-not-found/error-not-found.component';
import { PlatformConfigComponent } from './views/platform-config/platform-config.component';
import { AddressFormComponent } from './examples/address-form/address-form.component';
import { PlatformsComponent } from './views/platforms/platforms.component'
import { PlatformDetailComponent } from './views/platform-detail/platform-detail.component';
export const routes: Routes = [
{ path: 'platform-config', component: PlatformConfigComponent },
{ path: 'address-form', component: AddressFormComponent },
{ path: 'platform/:id', component: PlatformDetailComponent },
{ path: 'platforms', component: PlatformsComponent },
{ path: 'home', component: HomeComponent },
{ path: 'clusters', component: ClusterListComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '', redirectTo: '/platforms', pathMatch: 'full' },
{ path: '**', component: ErrorNotFoundComponent },
];

View File

@@ -1,26 +0,0 @@
<div class="grid-container">
<h1 class="mat-h1">Clusters</h1>
<mat-grid-list cols="2" rowHeight="350px">
@for (card of cards | async; track card) {
<mat-grid-tile [colspan]="card.cols" [rowspan]="card.rows">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>
{{card.title}}
<button mat-icon-button class="more-button" [matMenuTriggerFor]="menu" aria-label="Toggle menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu" xPosition="before">
<button mat-menu-item>Expand</button>
<button mat-menu-item>Remove</button>
</mat-menu>
</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div>Card Content Here</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
}
</mat-grid-list>
</div>

View File

@@ -1,21 +0,0 @@
.grid-container {
margin: 20px;
}
.dashboard-card {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
}
.more-button {
position: absolute;
top: 5px;
right: 10px;
}
.dashboard-card-content {
text-align: center;
}

View File

@@ -1,25 +0,0 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ClusterListComponent } from './cluster-list.component';
describe('ClusterListComponent', () => {
let component: ClusterListComponent;
let fixture: ComponentFixture<ClusterListComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClusterListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should compile', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,48 +0,0 @@
import { Component, inject } from '@angular/core';
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
import { map } from 'rxjs/operators';
import { AsyncPipe } from '@angular/common';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatMenuModule } from '@angular/material/menu';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
@Component({
selector: 'app-cluster-list',
templateUrl: './cluster-list.component.html',
styleUrl: './cluster-list.component.scss',
standalone: true,
imports: [
AsyncPipe,
MatGridListModule,
MatMenuModule,
MatIconModule,
MatButtonModule,
MatCardModule
]
})
export class ClusterListComponent {
private breakpointObserver = inject(BreakpointObserver);
/** Based on the screen size, switch from standard to one column per row */
cards = this.breakpointObserver.observe(Breakpoints.Handset).pipe(
map(({ matches }) => {
if (matches) {
return [
{ title: 'Card 1', cols: 1, rows: 1 },
{ title: 'Card 2', cols: 1, rows: 1 },
{ title: 'Card 3', cols: 1, rows: 1 },
{ title: 'Card 4', cols: 1, rows: 1 }
];
}
return [
{ title: 'Card 1', cols: 2, rows: 1 },
{ title: 'Card 2', cols: 1, rows: 1 },
{ title: 'Card 3', cols: 1, rows: 2 },
{ title: 'Card 4', cols: 1, rows: 1 }
];
})
);
}

View File

@@ -1,99 +0,0 @@
<form [formGroup]="addressForm" novalidate (ngSubmit)="onSubmit()">
<mat-card class="shipping-card">
<mat-card-header>
<mat-card-title>Shipping Information</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="row">
<div class="col">
<mat-form-field class="full-width">
<input matInput placeholder="Company" formControlName="company">
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col">
<mat-form-field class="full-width">
<input matInput placeholder="First name" formControlName="firstName">
@if (addressForm.controls['firstName'].hasError('required')) {
<mat-error>First name is <strong>required</strong></mat-error>
}
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="full-width">
<input matInput placeholder="Last name" formControlName="lastName">
@if (addressForm.controls['lastName'].hasError('required')) {
<mat-error>Last name is <strong>required</strong></mat-error>
}
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col">
<mat-form-field class="full-width">
<textarea matInput placeholder="Address" formControlName="address"></textarea>
@if (addressForm.controls['address'].hasError('required')) {
<mat-error>Address is <strong>required</strong></mat-error>
}
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col">
@if (hasUnitNumber) {
<mat-form-field class="full-width">
<textarea matInput placeholder="Address 2" formControlName="address2"></textarea>
</mat-form-field>
} @else {
<button mat-button type="button" (click)="hasUnitNumber = !hasUnitNumber">
+ Add C/O, Apt, Suite, Unit
</button>
}
</div>
</div>
<div class="row">
<div class="col">
<mat-form-field class="full-width">
<input matInput placeholder="City" formControlName="city">
@if (addressForm.controls['city'].hasError('required')) {
<mat-error>City is <strong>required</strong></mat-error>
}
</mat-form-field>
</div>
<div class="col">
<mat-form-field class="full-width">
<mat-select placeholder="State" formControlName="state">
@for (state of states; track state) {
<mat-option [value]="state.abbreviation">{{ state.name }}</mat-option>
}
</mat-select>
@if (addressForm.controls['state'].hasError('required')) {
<mat-error>State is <strong>required</strong></mat-error>
}
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col">
<mat-form-field class="full-width">
<input matInput #postalCode maxlength="5" placeholder="Postal Code" type="number" formControlName="postalCode">
<mat-hint align="end">{{postalCode.value.length}} / 5</mat-hint>
</mat-form-field>
</div>
</div>
<div class="row">
<div class="col">
<mat-radio-group formControlName="shipping">
<mat-radio-button value="free">Free Shipping</mat-radio-button>
<mat-radio-button value="priority">Priority Shipping</mat-radio-button>
<mat-radio-button value="nextday">Next Day Shipping</mat-radio-button>
</mat-radio-group>
</div>
</div>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" type="submit">Submit</button>
</mat-card-actions>
</mat-card>
</form>

View File

@@ -1,27 +0,0 @@
.full-width {
width: 100%;
}
.shipping-card {
min-width: 120px;
margin: 20px auto;
}
.mat-radio-button {
display: block;
margin: 5px 0;
}
.row {
display: flex;
flex-direction: row;
}
.col {
flex: 1;
margin-right: 20px;
}
.col:last-child {
margin-right: 0;
}

View File

@@ -1,25 +0,0 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AddressFormComponent } from './address-form.component';
describe('AddressFormComponent', () => {
let component: AddressFormComponent;
let fixture: ComponentFixture<AddressFormComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddressFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should compile', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,108 +0,0 @@
import { Component, inject } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatCardModule } from '@angular/material/card';
@Component({
selector: 'app-address-form',
templateUrl: './address-form.component.html',
styleUrl: './address-form.component.scss',
standalone: true,
imports: [
MatInputModule,
MatButtonModule,
MatSelectModule,
MatRadioModule,
MatCardModule,
ReactiveFormsModule
]
})
export class AddressFormComponent {
private fb = inject(FormBuilder);
addressForm = this.fb.group({
company: null,
firstName: [null, Validators.required],
lastName: [null, Validators.required],
address: [null, Validators.required],
address2: null,
city: [null, Validators.required],
state: [null, Validators.required],
postalCode: [null, Validators.compose([
Validators.required, Validators.minLength(5), Validators.maxLength(5)])
],
shipping: ['free', Validators.required]
});
hasUnitNumber = false;
states = [
{name: 'Alabama', abbreviation: 'AL'},
{name: 'Alaska', abbreviation: 'AK'},
{name: 'American Samoa', abbreviation: 'AS'},
{name: 'Arizona', abbreviation: 'AZ'},
{name: 'Arkansas', abbreviation: 'AR'},
{name: 'California', abbreviation: 'CA'},
{name: 'Colorado', abbreviation: 'CO'},
{name: 'Connecticut', abbreviation: 'CT'},
{name: 'Delaware', abbreviation: 'DE'},
{name: 'District Of Columbia', abbreviation: 'DC'},
{name: 'Federated States Of Micronesia', abbreviation: 'FM'},
{name: 'Florida', abbreviation: 'FL'},
{name: 'Georgia', abbreviation: 'GA'},
{name: 'Guam', abbreviation: 'GU'},
{name: 'Hawaii', abbreviation: 'HI'},
{name: 'Idaho', abbreviation: 'ID'},
{name: 'Illinois', abbreviation: 'IL'},
{name: 'Indiana', abbreviation: 'IN'},
{name: 'Iowa', abbreviation: 'IA'},
{name: 'Kansas', abbreviation: 'KS'},
{name: 'Kentucky', abbreviation: 'KY'},
{name: 'Louisiana', abbreviation: 'LA'},
{name: 'Maine', abbreviation: 'ME'},
{name: 'Marshall Islands', abbreviation: 'MH'},
{name: 'Maryland', abbreviation: 'MD'},
{name: 'Massachusetts', abbreviation: 'MA'},
{name: 'Michigan', abbreviation: 'MI'},
{name: 'Minnesota', abbreviation: 'MN'},
{name: 'Mississippi', abbreviation: 'MS'},
{name: 'Missouri', abbreviation: 'MO'},
{name: 'Montana', abbreviation: 'MT'},
{name: 'Nebraska', abbreviation: 'NE'},
{name: 'Nevada', abbreviation: 'NV'},
{name: 'New Hampshire', abbreviation: 'NH'},
{name: 'New Jersey', abbreviation: 'NJ'},
{name: 'New Mexico', abbreviation: 'NM'},
{name: 'New York', abbreviation: 'NY'},
{name: 'North Carolina', abbreviation: 'NC'},
{name: 'North Dakota', abbreviation: 'ND'},
{name: 'Northern Mariana Islands', abbreviation: 'MP'},
{name: 'Ohio', abbreviation: 'OH'},
{name: 'Oklahoma', abbreviation: 'OK'},
{name: 'Oregon', abbreviation: 'OR'},
{name: 'Palau', abbreviation: 'PW'},
{name: 'Pennsylvania', abbreviation: 'PA'},
{name: 'Puerto Rico', abbreviation: 'PR'},
{name: 'Rhode Island', abbreviation: 'RI'},
{name: 'South Carolina', abbreviation: 'SC'},
{name: 'South Dakota', abbreviation: 'SD'},
{name: 'Tennessee', abbreviation: 'TN'},
{name: 'Texas', abbreviation: 'TX'},
{name: 'Utah', abbreviation: 'UT'},
{name: 'Vermont', abbreviation: 'VT'},
{name: 'Virgin Islands', abbreviation: 'VI'},
{name: 'Virginia', abbreviation: 'VA'},
{name: 'Washington', abbreviation: 'WA'},
{name: 'West Virginia', abbreviation: 'WV'},
{name: 'Wisconsin', abbreviation: 'WI'},
{name: 'Wyoming', abbreviation: 'WY'}
];
onSubmit(): void {
alert('Thanks!');
}
}

View File

@@ -4,7 +4,7 @@
// @ts-nocheck
import { MethodKind } from "@bufbuild/protobuf";
import { AddPlatformRequest, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
import { AddPlatformRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
/**
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
@@ -33,3 +33,17 @@ export const addPlatform = {
typeName: "holos.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatform
*/
export const getPlatform = {
localName: "getPlatform",
name: "GetPlatform",
kind: MethodKind.Unary,
I: GetPlatformRequest,
O: GetPlatformResponse,
service: {
typeName: "holos.v1alpha1.PlatformService"
}
} as const;

View File

@@ -3,7 +3,7 @@
/* eslint-disable */
// @ts-nocheck
import { AddPlatformRequest, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
import { AddPlatformRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
import { MethodKind } from "@bufbuild/protobuf";
/**
@@ -30,6 +30,15 @@ export const PlatformService = {
O: GetPlatformsResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatform
*/
getPlatform: {
name: "GetPlatform",
I: GetPlatformRequest,
O: GetPlatformResponse,
kind: MethodKind.Unary,
},
}
} as const;

View File

@@ -8,6 +8,100 @@ import { Message, proto3 } from "@bufbuild/protobuf";
import { Timestamps } from "./timestamps_pb.js";
import { Creator } from "./user_pb.js";
/**
* RawConfig represents the raw form configuration as opaque bytes. Used for input.
*
* @generated from message holos.v1alpha1.RawConfig
*/
export class RawConfig extends Message<RawConfig> {
/**
* @generated from field: bytes form = 1;
*/
form = new Uint8Array(0);
/**
* @generated from field: bytes values = 2;
*/
values = new Uint8Array(0);
/**
* @generated from field: bytes cue = 3;
*/
cue = new Uint8Array(0);
/**
* @generated from field: string definition = 4;
*/
definition = "";
constructor(data?: PartialMessage<RawConfig>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.RawConfig";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "form", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
{ no: 2, name: "values", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
{ no: 3, name: "cue", kind: "scalar", T: 12 /* ScalarType.BYTES */ },
{ no: 4, name: "definition", kind: "scalar", T: 9 /* ScalarType.STRING */ },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RawConfig {
return new RawConfig().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RawConfig {
return new RawConfig().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RawConfig {
return new RawConfig().fromJsonString(jsonString, options);
}
static equals(a: RawConfig | PlainMessage<RawConfig> | undefined, b: RawConfig | PlainMessage<RawConfig> | undefined): boolean {
return proto3.util.equals(RawConfig, a, b);
}
}
/**
* @generated from message holos.v1alpha1.Config
*/
export class Config extends Message<Config> {
/**
* @generated from field: holos.v1alpha1.PlatformForm form = 1;
*/
form?: PlatformForm;
constructor(data?: PartialMessage<Config>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.Config";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "form", kind: "message", T: PlatformForm },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Config {
return new Config().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Config {
return new Config().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Config {
return new Config().fromJsonString(jsonString, options);
}
static equals(a: Config | PlainMessage<Config> | undefined, b: Config | PlainMessage<Config> | undefined): boolean {
return proto3.util.equals(Config, a, b);
}
}
/**
* @generated from message holos.v1alpha1.Platform
*/
@@ -20,25 +114,48 @@ export class Platform extends Message<Platform> {
id = "";
/**
* @generated from field: string name = 2;
*/
name = "";
/**
* @generated from field: string display_name = 3;
*/
displayName = "";
/**
* @generated from field: holos.v1alpha1.Timestamps timestamps = 4;
* @generated from field: holos.v1alpha1.Timestamps timestamps = 2;
*/
timestamps?: Timestamps;
/**
* @generated from field: holos.v1alpha1.Creator creator = 5;
* Organization ID resource owner.
*
* @generated from field: string org_id = 3;
*/
orgId = "";
/**
* name is the platform short name as a dns label.
*
* @generated from field: string name = 4;
*/
name = "";
/**
* @generated from field: string display_name = 5;
*/
displayName = "";
/**
* @generated from field: holos.v1alpha1.Creator creator = 6;
*/
creator?: Creator;
/**
* config represents the platform config form and values. Read only.
*
* @generated from field: holos.v1alpha1.Config config = 7;
*/
config?: Config;
/**
* raw_config represents the platform config form and values. Write only.
*
* @generated from field: holos.v1alpha1.RawConfig raw_config = 8;
*/
rawConfig?: RawConfig;
constructor(data?: PartialMessage<Platform>) {
super();
proto3.util.initPartial(data, this);
@@ -48,10 +165,13 @@ export class Platform extends Message<Platform> {
static readonly typeName = "holos.v1alpha1.Platform";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "display_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 4, name: "timestamps", kind: "message", T: Timestamps },
{ no: 5, name: "creator", kind: "message", T: Creator },
{ no: 2, name: "timestamps", kind: "message", T: Timestamps },
{ no: 3, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 4, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 5, name: "display_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 6, name: "creator", kind: "message", T: Creator },
{ no: 7, name: "config", kind: "message", T: Config },
{ no: 8, name: "raw_config", kind: "message", T: RawConfig },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Platform {
@@ -71,6 +191,202 @@ export class Platform extends Message<Platform> {
}
}
/**
* @generated from message holos.v1alpha1.FieldConfigProps
*/
export class FieldConfigProps extends Message<FieldConfigProps> {
/**
* @generated from field: string label = 1;
*/
label = "";
/**
* @generated from field: string placeholder = 2;
*/
placeholder = "";
/**
* @generated from field: string description = 3;
*/
description = "";
/**
* @generated from field: bool required = 4;
*/
required = false;
constructor(data?: PartialMessage<FieldConfigProps>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.FieldConfigProps";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "label", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "placeholder", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "description", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 4, name: "required", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FieldConfigProps {
return new FieldConfigProps().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): FieldConfigProps {
return new FieldConfigProps().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): FieldConfigProps {
return new FieldConfigProps().fromJsonString(jsonString, options);
}
static equals(a: FieldConfigProps | PlainMessage<FieldConfigProps> | undefined, b: FieldConfigProps | PlainMessage<FieldConfigProps> | undefined): boolean {
return proto3.util.equals(FieldConfigProps, a, b);
}
}
/**
* @generated from message holos.v1alpha1.FieldConfig
*/
export class FieldConfig extends Message<FieldConfig> {
/**
* @generated from field: string key = 1;
*/
key = "";
/**
* @generated from field: string type = 2;
*/
type = "";
/**
* @generated from field: holos.v1alpha1.FieldConfigProps props = 3;
*/
props?: FieldConfigProps;
constructor(data?: PartialMessage<FieldConfig>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.FieldConfig";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "key", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "type", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "props", kind: "message", T: FieldConfigProps },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FieldConfig {
return new FieldConfig().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): FieldConfig {
return new FieldConfig().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): FieldConfig {
return new FieldConfig().fromJsonString(jsonString, options);
}
static equals(a: FieldConfig | PlainMessage<FieldConfig> | undefined, b: FieldConfig | PlainMessage<FieldConfig> | undefined): boolean {
return proto3.util.equals(FieldConfig, a, b);
}
}
/**
* @generated from message holos.v1alpha1.ConfigSection
*/
export class ConfigSection extends Message<ConfigSection> {
/**
* @generated from field: string name = 1;
*/
name = "";
/**
* @generated from field: string displayName = 2;
*/
displayName = "";
/**
* @generated from field: string description = 3;
*/
description = "";
/**
* @generated from field: repeated holos.v1alpha1.FieldConfig fieldConfigs = 4;
*/
fieldConfigs: FieldConfig[] = [];
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: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "displayName", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "description", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ 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 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);
}
}
/**
* @generated from message holos.v1alpha1.PlatformFormSpec
*/
export class PlatformFormSpec extends Message<PlatformFormSpec> {
/**
* @generated from field: repeated holos.v1alpha1.ConfigSection sections = 1;
*/
sections: ConfigSection[] = [];
constructor(data?: PartialMessage<PlatformFormSpec>) {
super();
proto3.util.initPartial(data, this);
}
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 },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformFormSpec {
return new PlatformFormSpec().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformFormSpec {
return new PlatformFormSpec().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformFormSpec {
return new PlatformFormSpec().fromJsonString(jsonString, options);
}
static equals(a: PlatformFormSpec | PlainMessage<PlatformFormSpec> | undefined, b: PlatformFormSpec | PlainMessage<PlatformFormSpec> | undefined): boolean {
return proto3.util.equals(PlatformFormSpec, a, b);
}
}
/**
* @generated from message holos.v1alpha1.GetPlatformsRequest
*/
@@ -113,12 +429,7 @@ export class GetPlatformsRequest extends Message<GetPlatformsRequest> {
*/
export class GetPlatformsResponse extends Message<GetPlatformsResponse> {
/**
* @generated from field: string org_id = 1;
*/
orgId = "";
/**
* @generated from field: repeated holos.v1alpha1.Platform platforms = 2;
* @generated from field: repeated holos.v1alpha1.Platform platforms = 1;
*/
platforms: Platform[] = [];
@@ -130,8 +441,7 @@ export class GetPlatformsResponse extends Message<GetPlatformsResponse> {
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.GetPlatformsResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "platforms", kind: "message", T: Platform, repeated: true },
{ no: 1, name: "platforms", kind: "message", T: Platform, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetPlatformsResponse {
@@ -151,24 +461,51 @@ export class GetPlatformsResponse extends Message<GetPlatformsResponse> {
}
}
/**
* @generated from message holos.v1alpha1.GetPlatformResponse
*/
export class GetPlatformResponse extends Message<GetPlatformResponse> {
/**
* @generated from field: holos.v1alpha1.Platform platform = 1;
*/
platform?: Platform;
constructor(data?: PartialMessage<GetPlatformResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.GetPlatformResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform", kind: "message", T: Platform },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetPlatformResponse {
return new GetPlatformResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetPlatformResponse {
return new GetPlatformResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetPlatformResponse {
return new GetPlatformResponse().fromJsonString(jsonString, options);
}
static equals(a: GetPlatformResponse | PlainMessage<GetPlatformResponse> | undefined, b: GetPlatformResponse | PlainMessage<GetPlatformResponse> | undefined): boolean {
return proto3.util.equals(GetPlatformResponse, a, b);
}
}
/**
* @generated from message holos.v1alpha1.AddPlatformRequest
*/
export class AddPlatformRequest extends Message<AddPlatformRequest> {
/**
* @generated from field: string org_id = 1;
* @generated from field: holos.v1alpha1.Platform platform = 1;
*/
orgId = "";
/**
* @generated from field: string name = 2;
*/
name = "";
/**
* @generated from field: string display_name = 3;
*/
displayName = "";
platform?: Platform;
constructor(data?: PartialMessage<AddPlatformRequest>) {
super();
@@ -178,9 +515,7 @@ export class AddPlatformRequest extends Message<AddPlatformRequest> {
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.AddPlatformRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "display_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 1, name: "platform", kind: "message", T: Platform },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): AddPlatformRequest {
@@ -200,3 +535,169 @@ export class AddPlatformRequest extends Message<AddPlatformRequest> {
}
}
/**
* @generated from message holos.v1alpha1.GetPlatformRequest
*/
export class GetPlatformRequest extends Message<GetPlatformRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
constructor(data?: PartialMessage<GetPlatformRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.GetPlatformRequest";
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>): GetPlatformRequest {
return new GetPlatformRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetPlatformRequest {
return new GetPlatformRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetPlatformRequest {
return new GetPlatformRequest().fromJsonString(jsonString, options);
}
static equals(a: GetPlatformRequest | PlainMessage<GetPlatformRequest> | undefined, b: GetPlatformRequest | PlainMessage<GetPlatformRequest> | undefined): boolean {
return proto3.util.equals(GetPlatformRequest, a, b);
}
}
/**
* @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
*/
export class MetadataName extends Message<MetadataName> {
/**
* @generated from field: string name = 1;
*/
name = "";
constructor(data?: PartialMessage<MetadataName>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.MetadataName";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): MetadataName {
return new MetadataName().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): MetadataName {
return new MetadataName().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): MetadataName {
return new MetadataName().fromJsonString(jsonString, options);
}
static equals(a: MetadataName | PlainMessage<MetadataName> | undefined, b: MetadataName | PlainMessage<MetadataName> | undefined): boolean {
return proto3.util.equals(MetadataName, a, b);
}
}
/**
* @generated from message holos.v1alpha1.PlatformForm
*/
export class PlatformForm extends Message<PlatformForm> {
/**
* @generated from field: string apiVersion = 1;
*/
apiVersion = "";
/**
* @generated from field: string kind = 2;
*/
kind = "";
/**
* @generated from field: holos.v1alpha1.MetadataName metadata = 3;
*/
metadata?: MetadataName;
/**
* @generated from field: holos.v1alpha1.PlatformFormSpec spec = 4;
*/
spec?: PlatformFormSpec;
constructor(data?: PartialMessage<PlatformForm>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.PlatformForm";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "apiVersion", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "kind", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "metadata", kind: "message", T: MetadataName },
{ no: 4, name: "spec", kind: "message", T: PlatformFormSpec },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformForm {
return new PlatformForm().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformForm {
return new PlatformForm().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformForm {
return new PlatformForm().fromJsonString(jsonString, options);
}
static equals(a: PlatformForm | PlainMessage<PlatformForm> | undefined, b: PlatformForm | PlainMessage<PlatformForm> | undefined): boolean {
return proto3.util.equals(PlatformForm, a, b);
}
}

View File

@@ -7,10 +7,8 @@
<span>Menu</span>
</mat-toolbar>
<mat-nav-list>
<a mat-list-item routerLink="/platform-config" routerLinkActive="active-link">Platform Config</a>
<a mat-list-item routerLink="/home" routerLinkActive="active-link">Home</a>
<a mat-list-item routerLink="/clusters" routerLinkActive="active-link">Clusters</a>
<a mat-list-item routerLink="/address-form" routerLinkActive="active-link">Address Form</a>
<a mat-list-item routerLink="/platforms" routerLinkActive="active-link">Platforms</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { PlatformService } from './platform.service';
describe('PlatformService', () => {
let service: PlatformService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PlatformService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,30 @@
import { Inject, Injectable, inject } 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 { Organization } from '../gen/holos/v1alpha1/organization_pb';
@Injectable({
providedIn: 'root'
})
export class PlatformService {
getPlatforms(org: Observable<Organization>): Observable<Platform[]> {
return org.pipe(
switchMap(org => {
const req = new GetPlatformsRequest({ orgId: org.id })
return this.client.getPlatforms(req).pipe(
switchMap(resp => { return of(resp.platforms) })
)
})
)
}
getPlatform(id: string): Observable<Platform | undefined> {
return this.client.getPlatform({ platformId: id }).pipe(
switchMap((resp) => { return of(resp.platform) }),
)
}
constructor(@Inject(ConnectPlatformService) private client: ObservableClient<typeof ConnectPlatformService>) { }
}

View File

@@ -1,56 +0,0 @@
<mat-tab-group>
<mat-tab label="Organization">
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
<mat-card class="config-card">
<mat-card-header>
<mat-card-title>Organization</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>Organization config values are used to derive more specific configuration values throughout the platform.</p>
<formly-form [form]="form" [fields]="fields" [model]="model">
</formly-form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" type="submit">Submit</button>
</mat-card-actions>
</mat-card>
</form>
</mat-tab>
<mat-tab label="Integrations">
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
<mat-card class="config-card">
<mat-card-header>
<mat-card-title>Integrations</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>Configure integrations with your DNS and version control service providers.</p>
<formly-form [form]="form" [fields]="integrationFields" [model]="model">
</formly-form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" type="submit">Submit</button>
</mat-card-actions>
</mat-card>
</form>
</mat-tab>
<mat-tab label="Provisioner">
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
<mat-card class="config-card">
<mat-card-header>
<mat-card-title>Provisioner Cluster</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>Provide the connection details used to sync secrets among platform clusters.</p>
<formly-form [form]="form" [fields]="provisionerFields" [model]="model">
</formly-form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" type="submit">Submit</button>
</mat-card-actions>
</mat-card>
</form>
</mat-tab>
<mat-tab label="OAuth Clients"></mat-tab>
</mat-tab-group>

View File

@@ -1,27 +0,0 @@
.full-width {
width: 100%;
}
.config-card {
min-width: 120px;
margin: 20px auto;
}
.mat-radio-button {
display: block;
margin: 5px 0;
}
.row {
display: flex;
flex-direction: row;
}
.col {
flex: 1;
margin-right: 20px;
}
.col:last-child {
margin-right: 0;
}

View File

@@ -1,122 +0,0 @@
import { Component } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatTabsModule } from '@angular/material/tabs';
import { MatButton } from '@angular/material/button';
import { MatCard, MatCardActions, MatCardContent, MatCardHeader, MatCardTitle } from '@angular/material/card';
import { FormlyModule, FormlyFieldConfig } from '@ngx-formly/core';
import { FormlyMaterialModule } from '@ngx-formly/material';
@Component({
selector: 'app-platform-config',
standalone: true,
imports: [
ReactiveFormsModule,
FormlyMaterialModule,
FormlyModule,
MatTabsModule,
MatCard,
MatCardHeader,
MatCardTitle,
MatCardContent,
MatCardActions,
MatButton,
],
templateUrl: './platform-config.component.html',
styleUrl: './platform-config.component.scss'
})
export class PlatformConfigComponent {
form = new FormGroup({});
model: any = {};
fields: FormlyFieldConfig[] = [
{
key: 'name',
type: 'input',
props: {
label: 'Name',
placeholder: 'example',
required: true,
description: "DNS label, e.g. 'example'"
}
},
{
key: 'domain',
type: 'input',
props: {
label: 'Domain',
placeholder: 'example.com',
required: true,
description: "DNS domain, e.g. 'example.com'"
}
},
{
key: 'displayName',
type: 'input',
props: {
label: 'Display Name',
placeholder: 'Example Organization',
required: true,
description: "Display name, e.g. 'My Organization'"
}
},
{
key: 'contactEmail',
type: 'input',
props: {
label: 'Contact Email',
placeholder: '',
required: true,
description: "Organization technical contact."
}
},
];
integrationFields: FormlyFieldConfig[] = [
{
key: 'cloudflareEmail',
type: 'input',
props: {
label: 'Cloudflare Account',
placeholder: 'example@example.com',
required: true,
description: "Cloudflare account email address."
}
},
{
key: 'githubPrimaryOrg',
type: 'input',
props: {
label: 'Github Organization',
placeholder: 'ExampleOrg',
required: true,
description: "Github organization, e.g. 'ExampleOrg'"
}
}
];
provisionerFields: FormlyFieldConfig[] = [
{
key: 'provisionerCABundle',
type: 'input',
props: {
label: 'Provisioner API CA Bundle',
placeholder: 'LS0tLS1CRUdJTiBDRVJUSUZJQXXXXXXXXXXXXXXXXXXXXXXX',
required: true,
description: "kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'"
}
},
{
key: 'provisionerURL',
type: 'input',
props: {
label: 'Provisioner API URL',
placeholder: 'https://1.2.3.4',
required: true,
description: "kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'"
}
}
]
onSubmit(model: any) {
console.log(model);
}
}

View File

@@ -0,0 +1,25 @@
<mat-tab-group>
<mat-tab label="Detail">
<div class="grid-container">
@if (platform$ | async; as platform) {
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
@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>
}
<p></p>
<button type="submit" mat-flat-button color="primary">Submit</button>
</form>
}
</div>
</mat-tab>
<mat-tab label="Raw">
<div class="grid-container">
@if (platform$ | async; as platform) {
<pre>{{ platform | json }}</pre>
}
</div>
</mat-tab>
</mat-tab-group>

View File

@@ -0,0 +1,3 @@
.grid-container {
margin: 20px;
}

View File

@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PlatformConfigComponent } from './platform-config.component';
import { PlatformDetailComponent } from './platform-detail.component';
describe('PlatformConfigComponent', () => {
let component: PlatformConfigComponent;
let fixture: ComponentFixture<PlatformConfigComponent>;
describe('PlatformDetailComponent', () => {
let component: PlatformDetailComponent;
let fixture: ComponentFixture<PlatformDetailComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PlatformConfigComponent]
imports: [PlatformDetailComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PlatformConfigComponent);
fixture = TestBed.createComponent(PlatformDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -0,0 +1,49 @@
import { Component, Input, inject } from '@angular/core';
import { Observable, filter, shareReplay } from 'rxjs';
import { 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';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { FormlyMaterialModule } from '@ngx-formly/material';
import { FormlyModule } from '@ngx-formly/core';
import { MatButton } from '@angular/material/button';
import { MatDivider } from '@angular/material/divider';
@Component({
selector: 'app-platform-detail',
standalone: true,
imports: [
MatTabGroup,
MatTab,
AsyncPipe,
CommonModule,
FormlyModule,
ReactiveFormsModule,
FormlyMaterialModule,
MatButton,
MatDivider,
],
templateUrl: './platform-detail.component.html',
styleUrl: './platform-detail.component.scss'
})
export class PlatformDetailComponent {
private service = inject(PlatformService);
platform$!: Observable<Platform>;
form = new FormGroup({});
model = {};
onSubmit(model: any) {
console.log(model);
}
@Input()
set id(platformId: string) {
this.platform$ = this.service.getPlatform(platformId).pipe(
filter((platform): platform is Platform => platform !== undefined),
shareReplay(1)
)
}
}

View File

@@ -0,0 +1,12 @@
<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>
}
</mat-nav-list>
</div>

View File

@@ -0,0 +1,3 @@
.grid-container {
margin: 20px;
}

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PlatformsComponent } from './platforms.component';
describe('PlatformsComponent', () => {
let component: PlatformsComponent;
let fixture: ComponentFixture<PlatformsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PlatformsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PlatformsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
import { Platform } from '../../gen/holos/v1alpha1/platform_pb';
import { Component, inject } from '@angular/core';
import { MatListItem, MatNavList } from '@angular/material/list';
import { Observable, filter } from 'rxjs';
import { Organization } from '../../gen/holos/v1alpha1/organization_pb';
import { OrganizationService } from '../../services/organization.service';
import { PlatformService } from '../../services/platform.service';
import { AsyncPipe, CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-platforms',
standalone: true,
imports: [
MatNavList,
MatListItem,
AsyncPipe,
CommonModule,
RouterLink,
],
templateUrl: './platforms.component.html',
styleUrl: './platforms.component.scss'
})
export class PlatformsComponent {
private orgSvc = inject(OrganizationService);
private platformSvc = inject(PlatformService);
org$!: Observable<Organization | undefined>;
platforms$!: Observable<Platform[]>;
ngOnInit(): void {
this.org$ = this.orgSvc.activeOrg();
this.platforms$ = this.platformSvc.getPlatforms(this.org$.pipe(
filter((org): org is Organization => org !== undefined)
))
}
}

View File

@@ -2,11 +2,15 @@ package handler
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"connectrpc.com/connect"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/user"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/internal/server/middleware/authn"
@@ -40,7 +44,7 @@ func (h *PlatformHandler) AddPlatform(
ctx context.Context,
req *connect.Request[holos.AddPlatformRequest],
) (*connect.Response[holos.GetPlatformsResponse], error) {
dbUser, dbOrg, err := getAuthnUsersOrg(ctx, req.Msg.OrgId, h.db)
dbUser, dbOrg, err := getAuthnUsersOrg(ctx, req.Msg.Platform.OrgId, h.db)
if err != nil {
return nil, errors.Wrap(err)
}
@@ -48,8 +52,12 @@ func (h *PlatformHandler) AddPlatform(
platform, err := h.db.Platform.Create().
SetOrgID(dbOrg.ID).
SetCreatorID(dbUser.ID).
SetName(req.Msg.Name).
SetDisplayName(req.Msg.DisplayName).
SetName(req.Msg.Platform.Name).
SetDisplayName(req.Msg.Platform.DisplayName).
SetConfigForm(req.Msg.Platform.RawConfig.Form).
SetConfigValues(req.Msg.Platform.RawConfig.Values).
SetConfigCue(req.Msg.Platform.RawConfig.Cue).
SetConfigDefinition(req.Msg.Platform.RawConfig.Definition).
Save(ctx)
if err != nil {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
@@ -61,11 +69,83 @@ func (h *PlatformHandler) AddPlatform(
return resp, 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)
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 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.
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
platformID, err := uuid.FromString(req.Msg.PlatformId)
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))
}
}
rpcPlatform := PlatformToRPC(p)
res := connect.NewResponse(rpcPlatform.Config.Form)
return res, 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},
Timestamps: &holos.Timestamps{
CreatedAt: timestamppb.New(platform.CreatedAt),
UpdatedAt: timestamppb.New(platform.UpdatedAt),
@@ -101,6 +181,7 @@ func getAuthnUsersOrg(ctx context.Context, orgID string, db *ent.Client) (*ent.U
return nil, nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
// Check the user is a member of the organization.
var reqDBOrg *ent.Organization
wantOrgIDs := make([]uuid.UUID, 0, len(dbUser.Edges.Organizations))
for _, org := range dbUser.Edges.Organizations {
@@ -131,7 +212,6 @@ func getPlatformsResponse(reqDBOrg *ent.Organization) *connect.Response[holos.Ge
}
return connect.NewResponse(&holos.GetPlatformsResponse{
OrgId: reqDBOrg.ID.String(),
Platforms: rpcPlatforms,
})
}

View File

@@ -12,17 +12,57 @@ import "holos/v1alpha1/user.proto";
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
// RawConfig represents the raw form configuration as opaque bytes. Used for input.
message RawConfig {
bytes form = 1;
bytes values = 2;
bytes cue = 3;
string definition = 4;
}
message Config {
PlatformForm form = 1;
}
message Platform {
// Unique id assigned by the server.
string id = 1 [(buf.validate.field).string.uuid = true];
string name = 2 [(buf.validate.field).string.max_len = 100];
string display_name = 3 [(buf.validate.field).string.max_len = 100];
string id = 1;
Timestamps timestamps = 2;
// platform spec here?
// platform spec form config here?
// Organization ID resource owner.
string org_id = 3 [(buf.validate.field).string.uuid = true];
// name is the platform short name as a dns label.
string name = 4 [(buf.validate.field).string.max_len = 100];
string display_name = 5 [(buf.validate.field).string.max_len = 100];
Creator creator = 6;
// config represents the platform config form and values. Read only.
Config config = 7;
// raw_config represents the platform config form and values. Write only.
RawConfig raw_config = 8;
}
Timestamps timestamps = 4;
Creator creator = 5;
message FieldConfigProps {
string label = 1;
string placeholder = 2;
string description = 3;
bool required = 4;
}
message FieldConfig {
string key = 1;
string type = 2;
FieldConfigProps props = 3;
}
message ConfigSection {
string name = 1;
string displayName = 2;
string description = 3;
repeated FieldConfig fieldConfigs = 4;
}
message PlatformFormSpec {
repeated ConfigSection sections = 1;
}
message GetPlatformsRequest {
@@ -30,17 +70,38 @@ message GetPlatformsRequest {
}
message GetPlatformsResponse {
string org_id = 1 [(buf.validate.field).string.uuid = true];
repeated Platform platforms = 2;
repeated Platform platforms = 1;
}
message GetPlatformResponse {
Platform platform = 1;
}
message AddPlatformRequest {
string org_id = 1 [(buf.validate.field).string.uuid = true];
string name = 2 [(buf.validate.field).string.max_len = 100];
string display_name = 3 [(buf.validate.field).string.max_len = 100];
Platform platform = 1;
}
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;
}
message PlatformForm {
string apiVersion = 1;
string kind = 2;
MetadataName metadata = 3;
PlatformFormSpec spec = 4;
}
service PlatformService {
rpc GetPlatforms(GetPlatformsRequest) returns (GetPlatformsResponse) {}
rpc AddPlatform(AddPlatformRequest) returns (GetPlatformsResponse) {}
rpc GetPlatform(GetPlatformRequest) returns (GetPlatformResponse) {}
}

View File

@@ -1 +1 @@
2
4