Compare commits

..

16 Commits

Author SHA1 Message Date
Jeff McCune
6894f45b6c (#165) Deploy Holos to Dev
This patch deploys holos to the dev environment on the k2 cluster.  It's
accessible at https://app.dev.k2.holos.run/ behind the auth proxy by
default.
2024-05-06 11:10:29 -07:00
Jeff McCune
89d25be837 (#161) Wrap main router outlet in <main></main> 2024-05-06 09:16:15 -07:00
Jeff McCune
5b33e48552 (#161) Reasonably advanced form modeling the reference platform
This form goes a good way toward capturing what we need to configure the
entire reference platform.  Elements and sections are responsive to
which cloud providers are selected, which achieves my goal of modeling a
reasonably advanced form using only JSON data produced by CUE.

To write the form via the API:

    cue export ./forms/platform/ --out json \
      | jq '{platform_id: "'${platformId}'", fields: .spec.fields}' \
      | grpcurl -H "x-oidc-id-token: $(holos token)" -d @ ${host}:443 \
      holos.platform.v1alpha1.PlatformService.PutForm
2024-05-04 20:16:09 -07:00
Jeff McCune
79e8ab639a (#161) Fix the FormGroup & Refactor API
The way we were organizing fields into section broke Formly validation.
This patch fixes the problem by using the recommended approach of
[Nested Forms][1].

This patch also refactors the PlatformService API to clean it up.
GetForm / PutForm are separated from the Platform methods.  Similarly
GetModel / PutModel are separated out and are specific to get and put
the model data.

NOTE: I'm not sure we should have separated out the platform service
into it's own protobuf package.  Seems maybe unnecessary.

❯ grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94"}' jeff.app.dev.k2.holos.run:443 holos.platform.v1alpha1.PlatformService.GetModel
{
  "model": {
    "org": {
      "contactEmail": "platform@openinfrastructure.co",
      "displayName": "Open Infrastructure Services LLC",
      "domain": "ois.run",
      "name": "ois"
    },
    "privacy": {
      "country": "earth",
      "regions": [
        "us-east-2",
        "us-west-2"
      ]
    },
    "terms": {
      "didAgree": true
    }
  }
}

[1]: https://formly.dev/docs/examples/other/nested-formly-forms
2024-05-04 10:14:37 -07:00
Jeff McCune
a0cc673736 (#150) Wire up select and multi select boxes
This patch wires up a Select and a Multi Select box.  This patch also
establishes a decision as it relates to Formly TypeScript / gRPC Proto3
/ CUE definitions of the form data structure.  The decision is to use
gRPC as a transport for any JSON to avoid friction trying to fit Formly
types into Proto3 messages.

Note when using google.protobuf.Value messages with bufbuild/connect-es,
we need to round trip them one last time through JSON to get the
original JSON on the other side.  This is because connect-es preserves
the type discriminators in the case and value fields of the message.

Refer to: [Accessing oneof
groups](https://github.com/bufbuild/protobuf-es/blob/main/docs/runtime_api.md#accessing-oneof-groups)

NOTE: On the wire, carry any JSON as field configs for expedience.  I
attempted to reflect FormlyFieldConfig in protobuf, but it was too time
consuming.  The loosely defined Formly json data API creates significant
friction when joined with a well defined protobuf API.  Therefore, we do
not specify anything about the Forms API, convey any valid JSON, and
leave it up to CUE and Formly on the sending and receiving side of the
API.

We use CUE to define our own holos form elements as a subset of the loose
Formly definitions.  We further hope Formly will move toward a better JSON
data API, but it's unlikely.  Consider replacing Formly entirely and
building on top of the strongly typed Angular Dyanmic Forms API.

Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L15
Consider: https://angular.io/guide/dynamic-form

Usage:

Generate the form from CUE

    cue export ./forms/platform/ --out json | jq -cM | pbcopy

Store the form JSON in the config_values column of the platforms table.

View the form, and submit some data. Then get the data back out for use rendering the platform:

    grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"'${platformId}'"}' $holos holos.v1alpha1.PlatformService.GetConfig

```json
{
  "platform": {
    "spec": {
      "config": {
        "user": {
          "sections": {
            "org": {
              "fields": {
                "contactEmail": "jeff@openinfrastructure.co",
                "displayName": "Open Infrastructure Services LLC",
                "domain": "ois.run",
                "name": "ois"
              }
            },
            "privacy": {
              "fields": {
                "country": "earth",
                "regions": [
                  "us-east-2",
                  "us-west-2"
                ]
              }
            },
            "terms": {
              "fields": {
                "didAgree": true
              }
            }
          }
        }
      }
    }
  }
}
```
2024-05-03 10:42:03 -07:00
Jeff McCune
d06ecfadc8 (#150) Refactor PlatformService.GetConfig for use with CUE
Problem:
The GetConfig response value isn't directly usable with CUE without some
gymnastics.

Solution:
Refactor the protobuf definition and response output to make the user
defined and supplied config values provided by the API directly usable
in the CUE code that defines the platform.

Result:

The top level platform config is directly usable in the
`internal/platforms/bare` directory:

    grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"'${platformID}'"}' $host \
      holos.v1alpha1.PlatformService.GetConfig \
      > platform.holos.json

Vet the user supplied data:

    cue vet ./ -d '#PlatformConfig' platform.holos.json

Build the holos component.  The ConfigMap consumes the user supplied
data:

    cue export --out yaml -t cluster=k2 ./components/configmap platform.holos.json \
      | yq .spec.components

Note the data provided by the input form is embedded into the
ConfigMap managed by Holos:

```yaml
KubernetesObjectsList:
  - metadata:
      name: platform-configmap
    apiObjectMap:
      ConfigMap:
        platform: |
          metadata:
            name: platform
            namespace: default
            labels:
              app.holos.run/managed: "true"
          data:
            platform: |
              kind: Platform
              spec:
                config:
                  user:
                    sections:
                      org:
                        fields:
                          contactEmail: jeff@openinfrastructure.co
                          displayName: Open Infrastructure Services LLC
                          domain: ois.run
                          name: ois
              apiVersion: app.holos.run/v1alpha1
              metadata:
                name: bare
                labels: {}
                annotations: {}
              holos:
                flags:
                  cluster: k2
          kind: ConfigMap
          apiVersion: v1
    Skip: false
```
2024-05-02 06:39:33 -07:00
Jeff McCune
64a117b0c3 (#150) Add PlatformService.GetConfig and refactor ConfigValues proto
Problem:
The use of google.protobuf.Any was making it awkward to work with the
data provided by the user.  The structure of the form data is defined by
the platform engineer, so the intent of Any was to wrap the data in a
way we can pass over the network and persist in the database.

The escaped JSON encoding was problematic and error prone to decode on
the other end.

Solution:
Define the Platform values as a two level map with string keys, but with
protobuf message fields "sections" and "fields" respectively.  Use
google.protobuf.Value from the struct package to encode the actual
value.

Result:
In TypeScript, google.protobuf.Value encodes and decodes easily to a
JSON value.  On the go side, connect correctly handles the value as
well.

No more ugly error prone escaping:

```
❯ grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"'${platformId}'"}' $host holos.v1alpha1.PlatformService.GetConfig
{
  "sections": {
    "org": {
      "fields": {
        "contactEmail": "jeff@openinfrastructure.co",
        "displayName": "Open Infrastructure Services LLC",
        "domain": "ois.run",
        "name": "ois"
      }
    }
  }
}
```

This return value is intended to be directly usable in the CUE code, so
we may further nest the values into a platform.spec key.
2024-05-01 21:30:30 -07:00
Jeff McCune
cf006be9cf (#150) Add SystemService DropTables and SeedDatabase
Makes it easier to reset the database and give Gary and Nate access to
the same organization I'm in so they can provide feedback.
2024-05-01 14:30:13 -07:00
Jeff McCune
45ad3d8e63 (#150) Fix 500 error when config values aren't provided
AddPlatform was failing with a 500 error trying to decode a nil byte
slice when adding a platform without providing any values.
2024-05-01 11:31:25 -07:00
Jeff McCune
441c968c4f (#150) Look up user by iss sub, not email.
Also log when orgs are created.
2024-05-01 10:02:08 -07:00
Jeff McCune
99f2763fdf (#150) Store Platform Config Form and Values as JSON
This patch changes the backend to store the platform config form
definition and the config values supplied by the form as JSON in the
database.

The gRPC API does not change with this patch, but may need to depending
on how this works and how easy it is to evolve the data model and add
features.
2024-05-01 09:11:53 -07:00
Jeff McCune
1312395a11 (#150) Fix platforms page links
The links were hard to click.  This makes the links a much larger click
target following the example at https://material.angular.io/components/list/overview#navigation-lists
2024-05-01 08:51:29 -07:00
Jeff McCune
615f147bcb (#150) Add PutPlatformConfig to store the config values
This patch is a work in progress wiring up the form to put the values to
the holos server using grpc.

In an effort to simplify the platform configuration, the structure is a
two level map with the top level being configuration sections and the
second level being the fields associated with the config section.

To support multiple kinds of values and field controls, the values are
serialized to JSON for rpc over the network and for storage in the
database.  When they values are used, either by the UI or by the `holos
render` command, they're to be unmarshalled and in-lined into the
Platform Config data structure.

Pick back up ensuring the Platform rpc handler correctly encodes and
decodes the structure to the database.

Consider changing the config_form and config_values fields to JSON field
types in the database.  It will likely make working with this a lot
easier.

With this patch we're ready to wire up the holos render command to fetch
the platform configuration and create the end to end demo.

Here's essentially what the render command will fetch and lay down as a
json file for CUE:

```
❯ grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"018f2c4e-ecde-7bcb-8b89-27a99e6cc7a1"}' jeff.app.dev.k2.holos.run:443 holos.v1alpha1.PlatformService.GetPlatform | jq .platform.config.values
{
  "sections": {
    "org": {
      "values": {
        "contactEmail": "\"platform@openinfrastructure.co\"",
        "displayName": "\"Open Infrastructure Services  LLC\"",
        "domain": "\"ois.run\"",
        "name": "\"ois\""
      }
    }
  }
}
```
2024-04-30 20:21:15 -07:00
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
77 changed files with 5121 additions and 1056 deletions

View File

@@ -18,6 +18,7 @@ import "encoding/yaml"
Issuer?: [Name=_]: #Issuer & {metadata: name: Name}
Gateway?: [Name=_]: #Gateway & {metadata: name: Name}
ConfigMap?: [Name=_]: #ConfigMap & {metadata: name: Name}
ServiceAccount?: [Name=_]: #ServiceAccount & {metadata: name: Name}
Deployment?: [_]: #Deployment
StatefulSet?: [_]: #StatefulSet

View File

@@ -0,0 +1,73 @@
package holos
let Namespace = "dev-holos"
let Holos = "holos"
// spec represents the output provided to holos
spec: components: KubernetesObjectsList: [
#KubernetesObjects & {
metadata: name: "dev-holos-app"
apiObjectMap: OBJECTS.apiObjectMap
},
]
// OBJECTS represents the kubernetes api objects to manage.
let OBJECTS = #APIObjects & {
apiObjects: Deployment: holos: {
metadata: {
name: Holos
namespace: Namespace
labels: app: Holos
}
spec: {
selector: matchLabels: app: Holos
template: metadata: labels: {
app: Holos
"sidecar.istio.io/inject": "true"
}
strategy: rollingUpdate: maxSurge: 1
strategy: rollingUpdate: maxUnavailable: 0
template: {
spec: {
serviceAccountName: Holos
securityContext: seccompProfile: type: "RuntimeDefault"
containers: [
{
name: Holos
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:0.73.0"
imagePullPolicy: "Always"
env: [
{
name: "TZ"
value: "America/Los_Angeles"
},
{
name: "DATABASE_URL"
valueFrom: secretKeyRef: {
key: "uri"
name: "holos-pguser-holos"
}
},
]
ports: [
{
containerPort: 3000
name: "http"
protocol: "TCP"
},
]
securityContext: capabilities: drop: ["ALL"]
securityContext: allowPrivilegeEscalation: false
securityContext: runAsNonRoot: true
resources: limits: {
cpu: "0.25"
memory: "256Mi"
}
resources: requests: resources.limits
},
]
}
}
}
}
}

View File

@@ -0,0 +1,129 @@
package holos
let Namespace = "dev-holos"
let Holos = "holos"
// spec represents the output provided to holos
spec: components: KubernetesObjectsList: [
#KubernetesObjects & {
metadata: name: "dev-holos-infra"
apiObjectMap: OBJECTS.apiObjectMap
},
]
let Metadata = {
name: Holos
namespace: Namespace
labels: app: Holos
}
// OBJECTS represents the kubernetes api objects to manage.
let OBJECTS = #APIObjects & {
// Postgres
// Deployment
// VirtualService
apiObjects: ServiceAccount: holos: {
metadata: Metadata
imagePullSecrets: [{name: "kube-system-ecr-image-pull-creds"}]
}
apiObjects: PostgresCluster: holos: {
apiVersion: "postgres-operator.crunchydata.com/v1beta1"
metadata: Metadata
spec: {
image: "registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.1-0"
instances: [{
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: [{
podAffinityTerm: {
labelSelector: matchLabels: "postgres-operator.crunchydata.com/cluster": "holos"
topologyKey: "kubernetes.io/hostname"
}
weight: 1
}]
dataVolumeClaimSpec: {
accessModes: ["ReadWriteOnce"]
resources: requests: storage: "1Gi"
}
name: "db"
replicas: 1
}]
port: 5432
postgresVersion: 16
users: [{
databases: ["holos"]
name: "holos"
options: "SUPERUSER"
}]
backups: pgbackrest: {
global: {
"archive-async": "y"
"archive-push-queue-max": "100MiB"
"spool-path": "/pgdata/backups"
}
image: "registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.47-2"
repos: [{
name: "repo1"
volume: volumeClaimSpec: {
accessModes: ["ReadWriteOnce"]
resources: requests: storage: "1Gi"
}
}]
}
}
}
apiObjects: Service: holos: {
apiVersion: "v1"
metadata: Metadata
spec: {
type: "ClusterIP"
selector: app: "holos"
ports: [{
appProtocol: "http2"
name: "http"
port: 3000
protocol: "TCP"
targetPort: 3000
}, {
appProtocol: "http"
name: "metrics"
port: 9090
protocol: "TCP"
targetPort: 9090
}]
}
}
apiObjects: VirtualService: holos: {
apiVersion: "networking.istio.io/v1beta1"
metadata: Metadata
spec: {
gateways: ["istio-ingress/default"]
hosts: [
"app.dev.holos.run",
"app.dev.\(#ClusterName).holos.run",
]
http: [{
match: [{
uri: prefix: "/ui"
}]
name: "ui"
route: [{
destination: {
host: "holos"
port: number: 3000
}
}]
}, {
name: "api"
route: [{
destination: {
host: "holos"
port: number: 3000
}
}]
}]
}
}
}

16
hack/setup/bare Executable file
View File

@@ -0,0 +1,16 @@
#! /bin/bash
set -euo pipefail
TOPLEVEL="$(cd $(dirname "$0") && git rev-parse --show-toplevel)"
host="jeff.app.dev.k2.holos.run:443"
read -p "Reset all data in $host? " choice
case "$choice" in
y|Y) echo "proceeding...";;
*) exit 1;;
esac
grpcurl -H "x-oidc-id-token: $(holos token)" $host holos.v1alpha1.SystemService.DropTables
grpcurl -H "x-oidc-id-token: $(holos token)" $host holos.v1alpha1.SystemService.SeedDatabase

View File

@@ -3,5 +3,6 @@ USER root
WORKDIR /app
ADD bin bin
RUN chown -R app: /app
USER app
# Kubernetes requires the user to be numeric
USER 8192
ENTRYPOINT bin/holos server

View File

@@ -17,7 +17,7 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -365,7 +365,7 @@ func (c *OrganizationClient) QueryPlatforms(o *Organization) *PlatformQuery {
id := o.ID
step := sqlgraph.NewStep(
sqlgraph.From(organization.Table, organization.FieldID, id),
sqlgraph.To(platform.Table, platform.FieldID),
sqlgraph.To(entplatform.Table, entplatform.FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, organization.PlatformsTable, organization.PlatformsColumn),
)
fromV = sqlgraph.Neighbors(o.driver.Dialect(), step)
@@ -410,13 +410,13 @@ func NewPlatformClient(c config) *PlatformClient {
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `platform.Hooks(f(g(h())))`.
// A call to `Use(f, g, h)` equals to `entplatform.Hooks(f(g(h())))`.
func (c *PlatformClient) Use(hooks ...Hook) {
c.hooks.Platform = append(c.hooks.Platform, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `platform.Intercept(f(g(h())))`.
// A call to `Intercept(f, g, h)` equals to `entplatform.Intercept(f(g(h())))`.
func (c *PlatformClient) Intercept(interceptors ...Interceptor) {
c.inters.Platform = append(c.inters.Platform, interceptors...)
}
@@ -478,7 +478,7 @@ func (c *PlatformClient) DeleteOne(pl *Platform) *PlatformDeleteOne {
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *PlatformClient) DeleteOneID(id uuid.UUID) *PlatformDeleteOne {
builder := c.Delete().Where(platform.ID(id))
builder := c.Delete().Where(entplatform.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &PlatformDeleteOne{builder}
@@ -495,7 +495,7 @@ func (c *PlatformClient) Query() *PlatformQuery {
// Get returns a Platform entity by its id.
func (c *PlatformClient) Get(ctx context.Context, id uuid.UUID) (*Platform, error) {
return c.Query().Where(platform.ID(id)).Only(ctx)
return c.Query().Where(entplatform.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
@@ -513,9 +513,9 @@ func (c *PlatformClient) QueryCreator(pl *Platform) *UserQuery {
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := pl.ID
step := sqlgraph.NewStep(
sqlgraph.From(platform.Table, platform.FieldID, id),
sqlgraph.From(entplatform.Table, entplatform.FieldID, id),
sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, platform.CreatorTable, platform.CreatorColumn),
sqlgraph.Edge(sqlgraph.M2O, false, entplatform.CreatorTable, entplatform.CreatorColumn),
)
fromV = sqlgraph.Neighbors(pl.driver.Dialect(), step)
return fromV, nil
@@ -529,9 +529,9 @@ func (c *PlatformClient) QueryOrganization(pl *Platform) *OrganizationQuery {
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := pl.ID
step := sqlgraph.NewStep(
sqlgraph.From(platform.Table, platform.FieldID, id),
sqlgraph.From(entplatform.Table, entplatform.FieldID, id),
sqlgraph.To(organization.Table, organization.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, platform.OrganizationTable, platform.OrganizationColumn),
sqlgraph.Edge(sqlgraph.M2O, false, entplatform.OrganizationTable, entplatform.OrganizationColumn),
)
fromV = sqlgraph.Neighbors(pl.driver.Dialect(), step)
return fromV, nil

View File

@@ -13,7 +13,8 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -76,7 +77,7 @@ func checkColumn(table, column string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
organization.Table: organization.ValidColumn,
platform.Table: platform.ValidColumn,
entplatform.Table: entplatform.ValidColumn,
user.Table: user.ValidColumn,
})
})

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: "form", Type: field.TypeJSON, Nullable: true},
{Name: "model", Type: field.TypeJSON, Nullable: true},
{Name: "cue", Type: field.TypeBytes, Nullable: true},
{Name: "cue_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

@@ -13,9 +13,10 @@ import (
"entgo.io/ent/dialect/sql"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/predicate"
"github.com/holos-run/holos/internal/ent/user"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
const (
@@ -812,6 +813,10 @@ type PlatformMutation struct {
updated_at *time.Time
name *string
display_name *string
form **platform.Form
model **platform.Model
cue *[]byte
cue_definition *string
clearedFields map[string]struct{}
creator *uuid.UUID
clearedcreator bool
@@ -1142,10 +1147,206 @@ func (m *PlatformMutation) ResetCreatorID() {
m.creator = nil
}
// SetForm sets the "form" field.
func (m *PlatformMutation) SetForm(pl *platform.Form) {
m.form = &pl
}
// Form returns the value of the "form" field in the mutation.
func (m *PlatformMutation) Form() (r *platform.Form, exists bool) {
v := m.form
if v == nil {
return
}
return *v, true
}
// OldForm returns the old "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) OldForm(ctx context.Context) (v *platform.Form, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldForm is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldForm requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldForm: %w", err)
}
return oldValue.Form, nil
}
// ClearForm clears the value of the "form" field.
func (m *PlatformMutation) ClearForm() {
m.form = nil
m.clearedFields[entplatform.FieldForm] = struct{}{}
}
// FormCleared returns if the "form" field was cleared in this mutation.
func (m *PlatformMutation) FormCleared() bool {
_, ok := m.clearedFields[entplatform.FieldForm]
return ok
}
// ResetForm resets all changes to the "form" field.
func (m *PlatformMutation) ResetForm() {
m.form = nil
delete(m.clearedFields, entplatform.FieldForm)
}
// SetModel sets the "model" field.
func (m *PlatformMutation) SetModel(pl *platform.Model) {
m.model = &pl
}
// Model returns the value of the "model" field in the mutation.
func (m *PlatformMutation) Model() (r *platform.Model, exists bool) {
v := m.model
if v == nil {
return
}
return *v, true
}
// OldModel returns the old "model" 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) OldModel(ctx context.Context) (v *platform.Model, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldModel is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldModel requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldModel: %w", err)
}
return oldValue.Model, nil
}
// ClearModel clears the value of the "model" field.
func (m *PlatformMutation) ClearModel() {
m.model = nil
m.clearedFields[entplatform.FieldModel] = struct{}{}
}
// ModelCleared returns if the "model" field was cleared in this mutation.
func (m *PlatformMutation) ModelCleared() bool {
_, ok := m.clearedFields[entplatform.FieldModel]
return ok
}
// ResetModel resets all changes to the "model" field.
func (m *PlatformMutation) ResetModel() {
m.model = nil
delete(m.clearedFields, entplatform.FieldModel)
}
// SetCue sets the "cue" field.
func (m *PlatformMutation) SetCue(b []byte) {
m.cue = &b
}
// Cue returns the value of the "cue" field in the mutation.
func (m *PlatformMutation) Cue() (r []byte, exists bool) {
v := m.cue
if v == nil {
return
}
return *v, true
}
// OldCue returns the old "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) OldCue(ctx context.Context) (v []byte, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCue is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCue requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCue: %w", err)
}
return oldValue.Cue, nil
}
// ClearCue clears the value of the "cue" field.
func (m *PlatformMutation) ClearCue() {
m.cue = nil
m.clearedFields[entplatform.FieldCue] = struct{}{}
}
// CueCleared returns if the "cue" field was cleared in this mutation.
func (m *PlatformMutation) CueCleared() bool {
_, ok := m.clearedFields[entplatform.FieldCue]
return ok
}
// ResetCue resets all changes to the "cue" field.
func (m *PlatformMutation) ResetCue() {
m.cue = nil
delete(m.clearedFields, entplatform.FieldCue)
}
// SetCueDefinition sets the "cue_definition" field.
func (m *PlatformMutation) SetCueDefinition(s string) {
m.cue_definition = &s
}
// CueDefinition returns the value of the "cue_definition" field in the mutation.
func (m *PlatformMutation) CueDefinition() (r string, exists bool) {
v := m.cue_definition
if v == nil {
return
}
return *v, true
}
// OldCueDefinition returns the old "cue_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) OldCueDefinition(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCueDefinition is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCueDefinition requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCueDefinition: %w", err)
}
return oldValue.CueDefinition, nil
}
// ClearCueDefinition clears the value of the "cue_definition" field.
func (m *PlatformMutation) ClearCueDefinition() {
m.cue_definition = nil
m.clearedFields[entplatform.FieldCueDefinition] = struct{}{}
}
// CueDefinitionCleared returns if the "cue_definition" field was cleared in this mutation.
func (m *PlatformMutation) CueDefinitionCleared() bool {
_, ok := m.clearedFields[entplatform.FieldCueDefinition]
return ok
}
// ResetCueDefinition resets all changes to the "cue_definition" field.
func (m *PlatformMutation) ResetCueDefinition() {
m.cue_definition = nil
delete(m.clearedFields, entplatform.FieldCueDefinition)
}
// ClearCreator clears the "creator" edge to the User entity.
func (m *PlatformMutation) ClearCreator() {
m.clearedcreator = true
m.clearedFields[platform.FieldCreatorID] = struct{}{}
m.clearedFields[entplatform.FieldCreatorID] = struct{}{}
}
// CreatorCleared reports if the "creator" edge to the User entity was cleared.
@@ -1177,7 +1378,7 @@ func (m *PlatformMutation) SetOrganizationID(id uuid.UUID) {
// ClearOrganization clears the "organization" edge to the Organization entity.
func (m *PlatformMutation) ClearOrganization() {
m.clearedorganization = true
m.clearedFields[platform.FieldOrgID] = struct{}{}
m.clearedFields[entplatform.FieldOrgID] = struct{}{}
}
// OrganizationCleared reports if the "organization" edge to the Organization entity was cleared.
@@ -1243,24 +1444,36 @@ 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)
fields = append(fields, entplatform.FieldCreatedAt)
}
if m.updated_at != nil {
fields = append(fields, platform.FieldUpdatedAt)
fields = append(fields, entplatform.FieldUpdatedAt)
}
if m.organization != nil {
fields = append(fields, platform.FieldOrgID)
fields = append(fields, entplatform.FieldOrgID)
}
if m.name != nil {
fields = append(fields, platform.FieldName)
fields = append(fields, entplatform.FieldName)
}
if m.display_name != nil {
fields = append(fields, platform.FieldDisplayName)
fields = append(fields, entplatform.FieldDisplayName)
}
if m.creator != nil {
fields = append(fields, platform.FieldCreatorID)
fields = append(fields, entplatform.FieldCreatorID)
}
if m.form != nil {
fields = append(fields, entplatform.FieldForm)
}
if m.model != nil {
fields = append(fields, entplatform.FieldModel)
}
if m.cue != nil {
fields = append(fields, entplatform.FieldCue)
}
if m.cue_definition != nil {
fields = append(fields, entplatform.FieldCueDefinition)
}
return fields
}
@@ -1270,18 +1483,26 @@ func (m *PlatformMutation) Fields() []string {
// schema.
func (m *PlatformMutation) Field(name string) (ent.Value, bool) {
switch name {
case platform.FieldCreatedAt:
case entplatform.FieldCreatedAt:
return m.CreatedAt()
case platform.FieldUpdatedAt:
case entplatform.FieldUpdatedAt:
return m.UpdatedAt()
case platform.FieldOrgID:
case entplatform.FieldOrgID:
return m.OrgID()
case platform.FieldName:
case entplatform.FieldName:
return m.Name()
case platform.FieldDisplayName:
case entplatform.FieldDisplayName:
return m.DisplayName()
case platform.FieldCreatorID:
case entplatform.FieldCreatorID:
return m.CreatorID()
case entplatform.FieldForm:
return m.Form()
case entplatform.FieldModel:
return m.Model()
case entplatform.FieldCue:
return m.Cue()
case entplatform.FieldCueDefinition:
return m.CueDefinition()
}
return nil, false
}
@@ -1291,18 +1512,26 @@ func (m *PlatformMutation) Field(name string) (ent.Value, bool) {
// database failed.
func (m *PlatformMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case platform.FieldCreatedAt:
case entplatform.FieldCreatedAt:
return m.OldCreatedAt(ctx)
case platform.FieldUpdatedAt:
case entplatform.FieldUpdatedAt:
return m.OldUpdatedAt(ctx)
case platform.FieldOrgID:
case entplatform.FieldOrgID:
return m.OldOrgID(ctx)
case platform.FieldName:
case entplatform.FieldName:
return m.OldName(ctx)
case platform.FieldDisplayName:
case entplatform.FieldDisplayName:
return m.OldDisplayName(ctx)
case platform.FieldCreatorID:
case entplatform.FieldCreatorID:
return m.OldCreatorID(ctx)
case entplatform.FieldForm:
return m.OldForm(ctx)
case entplatform.FieldModel:
return m.OldModel(ctx)
case entplatform.FieldCue:
return m.OldCue(ctx)
case entplatform.FieldCueDefinition:
return m.OldCueDefinition(ctx)
}
return nil, fmt.Errorf("unknown Platform field %s", name)
}
@@ -1312,48 +1541,76 @@ func (m *PlatformMutation) OldField(ctx context.Context, name string) (ent.Value
// type.
func (m *PlatformMutation) SetField(name string, value ent.Value) error {
switch name {
case platform.FieldCreatedAt:
case entplatform.FieldCreatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCreatedAt(v)
return nil
case platform.FieldUpdatedAt:
case entplatform.FieldUpdatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUpdatedAt(v)
return nil
case platform.FieldOrgID:
case entplatform.FieldOrgID:
v, ok := value.(uuid.UUID)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetOrgID(v)
return nil
case platform.FieldName:
case entplatform.FieldName:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetName(v)
return nil
case platform.FieldDisplayName:
case entplatform.FieldDisplayName:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetDisplayName(v)
return nil
case platform.FieldCreatorID:
case entplatform.FieldCreatorID:
v, ok := value.(uuid.UUID)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCreatorID(v)
return nil
case entplatform.FieldForm:
v, ok := value.(*platform.Form)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetForm(v)
return nil
case entplatform.FieldModel:
v, ok := value.(*platform.Model)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetModel(v)
return nil
case entplatform.FieldCue:
v, ok := value.([]byte)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCue(v)
return nil
case entplatform.FieldCueDefinition:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCueDefinition(v)
return nil
}
return fmt.Errorf("unknown Platform field %s", name)
}
@@ -1383,7 +1640,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(entplatform.FieldForm) {
fields = append(fields, entplatform.FieldForm)
}
if m.FieldCleared(entplatform.FieldModel) {
fields = append(fields, entplatform.FieldModel)
}
if m.FieldCleared(entplatform.FieldCue) {
fields = append(fields, entplatform.FieldCue)
}
if m.FieldCleared(entplatform.FieldCueDefinition) {
fields = append(fields, entplatform.FieldCueDefinition)
}
return fields
}
// FieldCleared returns a boolean indicating if a field with the given name was
@@ -1396,6 +1666,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 entplatform.FieldForm:
m.ClearForm()
return nil
case entplatform.FieldModel:
m.ClearModel()
return nil
case entplatform.FieldCue:
m.ClearCue()
return nil
case entplatform.FieldCueDefinition:
m.ClearCueDefinition()
return nil
}
return fmt.Errorf("unknown Platform nullable field %s", name)
}
@@ -1403,24 +1687,36 @@ func (m *PlatformMutation) ClearField(name string) error {
// It returns an error if the field is not defined in the schema.
func (m *PlatformMutation) ResetField(name string) error {
switch name {
case platform.FieldCreatedAt:
case entplatform.FieldCreatedAt:
m.ResetCreatedAt()
return nil
case platform.FieldUpdatedAt:
case entplatform.FieldUpdatedAt:
m.ResetUpdatedAt()
return nil
case platform.FieldOrgID:
case entplatform.FieldOrgID:
m.ResetOrgID()
return nil
case platform.FieldName:
case entplatform.FieldName:
m.ResetName()
return nil
case platform.FieldDisplayName:
case entplatform.FieldDisplayName:
m.ResetDisplayName()
return nil
case platform.FieldCreatorID:
case entplatform.FieldCreatorID:
m.ResetCreatorID()
return nil
case entplatform.FieldForm:
m.ResetForm()
return nil
case entplatform.FieldModel:
m.ResetModel()
return nil
case entplatform.FieldCue:
m.ResetCue()
return nil
case entplatform.FieldCueDefinition:
m.ResetCueDefinition()
return nil
}
return fmt.Errorf("unknown Platform field %s", name)
}
@@ -1429,10 +1725,10 @@ func (m *PlatformMutation) ResetField(name string) error {
func (m *PlatformMutation) AddedEdges() []string {
edges := make([]string, 0, 2)
if m.creator != nil {
edges = append(edges, platform.EdgeCreator)
edges = append(edges, entplatform.EdgeCreator)
}
if m.organization != nil {
edges = append(edges, platform.EdgeOrganization)
edges = append(edges, entplatform.EdgeOrganization)
}
return edges
}
@@ -1441,11 +1737,11 @@ func (m *PlatformMutation) AddedEdges() []string {
// name in this mutation.
func (m *PlatformMutation) AddedIDs(name string) []ent.Value {
switch name {
case platform.EdgeCreator:
case entplatform.EdgeCreator:
if id := m.creator; id != nil {
return []ent.Value{*id}
}
case platform.EdgeOrganization:
case entplatform.EdgeOrganization:
if id := m.organization; id != nil {
return []ent.Value{*id}
}
@@ -1469,10 +1765,10 @@ func (m *PlatformMutation) RemovedIDs(name string) []ent.Value {
func (m *PlatformMutation) ClearedEdges() []string {
edges := make([]string, 0, 2)
if m.clearedcreator {
edges = append(edges, platform.EdgeCreator)
edges = append(edges, entplatform.EdgeCreator)
}
if m.clearedorganization {
edges = append(edges, platform.EdgeOrganization)
edges = append(edges, entplatform.EdgeOrganization)
}
return edges
}
@@ -1481,9 +1777,9 @@ func (m *PlatformMutation) ClearedEdges() []string {
// was cleared in this mutation.
func (m *PlatformMutation) EdgeCleared(name string) bool {
switch name {
case platform.EdgeCreator:
case entplatform.EdgeCreator:
return m.clearedcreator
case platform.EdgeOrganization:
case entplatform.EdgeOrganization:
return m.clearedorganization
}
return false
@@ -1493,10 +1789,10 @@ func (m *PlatformMutation) EdgeCleared(name string) bool {
// if that edge is not defined in the schema.
func (m *PlatformMutation) ClearEdge(name string) error {
switch name {
case platform.EdgeCreator:
case entplatform.EdgeCreator:
m.ClearCreator()
return nil
case platform.EdgeOrganization:
case entplatform.EdgeOrganization:
m.ClearOrganization()
return nil
}
@@ -1507,10 +1803,10 @@ func (m *PlatformMutation) ClearEdge(name string) error {
// It returns an error if the edge is not defined in the schema.
func (m *PlatformMutation) ResetEdge(name string) error {
switch name {
case platform.EdgeCreator:
case entplatform.EdgeCreator:
m.ResetCreator()
return nil
case platform.EdgeOrganization:
case entplatform.EdgeOrganization:
m.ResetOrganization()
return nil
}

View File

@@ -48,7 +48,7 @@ const (
// PlatformsTable is the table that holds the platforms relation/edge.
PlatformsTable = "platforms"
// PlatformsInverseTable is the table name for the Platform entity.
// It exists in this package in order to avoid circular dependency with the "platform" package.
// It exists in this package in order to avoid circular dependency with the "entplatform" package.
PlatformsInverseTable = "platforms"
// PlatformsColumn is the table column denoting the platforms relation/edge.
PlatformsColumn = "org_id"

View File

@@ -14,7 +14,7 @@ import (
"entgo.io/ent/schema/field"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -288,7 +288,7 @@ func (oc *OrganizationCreate) createSpec() (*Organization, *sqlgraph.CreateSpec)
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {

View File

@@ -13,7 +13,7 @@ import (
"entgo.io/ent/schema/field"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/predicate"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -121,7 +121,7 @@ func (oq *OrganizationQuery) QueryPlatforms() *PlatformQuery {
}
step := sqlgraph.NewStep(
sqlgraph.From(organization.Table, organization.FieldID, selector),
sqlgraph.To(platform.Table, platform.FieldID),
sqlgraph.To(entplatform.Table, entplatform.FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, organization.PlatformsTable, organization.PlatformsColumn),
)
fromU = sqlgraph.SetNeighbors(oq.driver.Dialect(), step)
@@ -590,7 +590,7 @@ func (oq *OrganizationQuery) loadPlatforms(ctx context.Context, query *PlatformQ
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(platform.FieldOrgID)
query.ctx.AppendFieldOnce(entplatform.FieldOrgID)
}
query.Where(predicate.Platform(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(organization.PlatformsColumn), fks...))

View File

@@ -13,7 +13,7 @@ import (
"entgo.io/ent/schema/field"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/predicate"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -319,7 +319,7 @@ func (ou *OrganizationUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -332,7 +332,7 @@ func (ou *OrganizationUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
@@ -348,7 +348,7 @@ func (ou *OrganizationUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
@@ -694,7 +694,7 @@ func (ouo *OrganizationUpdateOne) sqlSave(ctx context.Context) (_node *Organizat
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@@ -707,7 +707,7 @@ func (ouo *OrganizationUpdateOne) sqlSave(ctx context.Context) (_node *Organizat
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
@@ -723,7 +723,7 @@ func (ouo *OrganizationUpdateOne) sqlSave(ctx context.Context) (_node *Organizat
Columns: []string{organization.PlatformsColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID),
IDSpec: sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {

View File

@@ -3,6 +3,7 @@
package ent
import (
"encoding/json"
"fmt"
"strings"
"time"
@@ -11,8 +12,9 @@ import (
"entgo.io/ent/dialect/sql"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/user"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
// Platform is the model entity for the Platform schema.
@@ -32,6 +34,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"`
// JSON representation of FormlyFormConfig[] refer to https://github.com/holos-run/holos/issues/161
Form *platform.Form `json:"form,omitempty"`
// JSON representation of the form model which holds user input values refer to https://github.com/holos-run/holos/issues/161
Model *platform.Model `json:"model,omitempty"`
// CUE definition to vet the model against e.g. #PlatformConfig
Cue []byte `json:"cue,omitempty"`
// The definition name to vet config_values against config_cue e.g. '#PlatformSpec'
CueDefinition string `json:"cue_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,11 +86,13 @@ 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 entplatform.FieldForm, entplatform.FieldModel, entplatform.FieldCue:
values[i] = new([]byte)
case entplatform.FieldName, entplatform.FieldDisplayName, entplatform.FieldCueDefinition:
values[i] = new(sql.NullString)
case platform.FieldCreatedAt, platform.FieldUpdatedAt:
case entplatform.FieldCreatedAt, entplatform.FieldUpdatedAt:
values[i] = new(sql.NullTime)
case platform.FieldID, platform.FieldOrgID, platform.FieldCreatorID:
case entplatform.FieldID, entplatform.FieldOrgID, entplatform.FieldCreatorID:
values[i] = new(uuid.UUID)
default:
values[i] = new(sql.UnknownType)
@@ -97,48 +109,76 @@ func (pl *Platform) assignValues(columns []string, values []any) error {
}
for i := range columns {
switch columns[i] {
case platform.FieldID:
case entplatform.FieldID:
if value, ok := values[i].(*uuid.UUID); !ok {
return fmt.Errorf("unexpected type %T for field id", values[i])
} else if value != nil {
pl.ID = *value
}
case platform.FieldCreatedAt:
case entplatform.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
pl.CreatedAt = value.Time
}
case platform.FieldUpdatedAt:
case entplatform.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
pl.UpdatedAt = value.Time
}
case platform.FieldOrgID:
case entplatform.FieldOrgID:
if value, ok := values[i].(*uuid.UUID); !ok {
return fmt.Errorf("unexpected type %T for field org_id", values[i])
} else if value != nil {
pl.OrgID = *value
}
case platform.FieldName:
case entplatform.FieldName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field name", values[i])
} else if value.Valid {
pl.Name = value.String
}
case platform.FieldDisplayName:
case entplatform.FieldDisplayName:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field display_name", values[i])
} else if value.Valid {
pl.DisplayName = value.String
}
case platform.FieldCreatorID:
case entplatform.FieldCreatorID:
if value, ok := values[i].(*uuid.UUID); !ok {
return fmt.Errorf("unexpected type %T for field creator_id", values[i])
} else if value != nil {
pl.CreatorID = *value
}
case entplatform.FieldForm:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field form", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &pl.Form); err != nil {
return fmt.Errorf("unmarshal field form: %w", err)
}
}
case entplatform.FieldModel:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field model", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &pl.Model); err != nil {
return fmt.Errorf("unmarshal field model: %w", err)
}
}
case entplatform.FieldCue:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field cue", values[i])
} else if value != nil {
pl.Cue = *value
}
case entplatform.FieldCueDefinition:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field cue_definition", values[i])
} else if value.Valid {
pl.CueDefinition = value.String
}
default:
pl.selectValues.Set(columns[i], values[i])
}
@@ -202,6 +242,18 @@ func (pl *Platform) String() string {
builder.WriteString(", ")
builder.WriteString("creator_id=")
builder.WriteString(fmt.Sprintf("%v", pl.CreatorID))
builder.WriteString(", ")
builder.WriteString("form=")
builder.WriteString(fmt.Sprintf("%v", pl.Form))
builder.WriteString(", ")
builder.WriteString("model=")
builder.WriteString(fmt.Sprintf("%v", pl.Model))
builder.WriteString(", ")
builder.WriteString("cue=")
builder.WriteString(fmt.Sprintf("%v", pl.Cue))
builder.WriteString(", ")
builder.WriteString("cue_definition=")
builder.WriteString(pl.CueDefinition)
builder.WriteByte(')')
return builder.String()
}

View File

@@ -1,6 +1,6 @@
// Code generated by ent, DO NOT EDIT.
package platform
package entplatform
import (
"time"
@@ -27,6 +27,14 @@ const (
FieldDisplayName = "display_name"
// FieldCreatorID holds the string denoting the creator_id field in the database.
FieldCreatorID = "creator_id"
// FieldForm holds the string denoting the form field in the database.
FieldForm = "form"
// FieldModel holds the string denoting the model field in the database.
FieldModel = "model"
// FieldCue holds the string denoting the cue field in the database.
FieldCue = "cue"
// FieldCueDefinition holds the string denoting the cue_definition field in the database.
FieldCueDefinition = "cue_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,
FieldForm,
FieldModel,
FieldCue,
FieldCueDefinition,
}
// 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()
}
// ByCueDefinition orders the results by the cue_definition field.
func ByCueDefinition(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCueDefinition, opts...).ToFunc()
}
// ByCreatorField orders the results by creator field.
func ByCreatorField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {

View File

@@ -1,6 +1,6 @@
// Code generated by ent, DO NOT EDIT.
package platform
package entplatform
import (
"time"
@@ -86,6 +86,16 @@ func CreatorID(v uuid.UUID) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldCreatorID, v))
}
// Cue applies equality check predicate on the "cue" field. It's identical to CueEQ.
func Cue(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldCue, v))
}
// CueDefinition applies equality check predicate on the "cue_definition" field. It's identical to CueDefinitionEQ.
func CueDefinition(v string) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldCueDefinition, 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 +346,151 @@ func CreatorIDNotIn(vs ...uuid.UUID) predicate.Platform {
return predicate.Platform(sql.FieldNotIn(FieldCreatorID, vs...))
}
// FormIsNil applies the IsNil predicate on the "form" field.
func FormIsNil() predicate.Platform {
return predicate.Platform(sql.FieldIsNull(FieldForm))
}
// FormNotNil applies the NotNil predicate on the "form" field.
func FormNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldForm))
}
// ModelIsNil applies the IsNil predicate on the "model" field.
func ModelIsNil() predicate.Platform {
return predicate.Platform(sql.FieldIsNull(FieldModel))
}
// ModelNotNil applies the NotNil predicate on the "model" field.
func ModelNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldModel))
}
// CueEQ applies the EQ predicate on the "cue" field.
func CueEQ(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldCue, v))
}
// CueNEQ applies the NEQ predicate on the "cue" field.
func CueNEQ(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldNEQ(FieldCue, v))
}
// CueIn applies the In predicate on the "cue" field.
func CueIn(vs ...[]byte) predicate.Platform {
return predicate.Platform(sql.FieldIn(FieldCue, vs...))
}
// CueNotIn applies the NotIn predicate on the "cue" field.
func CueNotIn(vs ...[]byte) predicate.Platform {
return predicate.Platform(sql.FieldNotIn(FieldCue, vs...))
}
// CueGT applies the GT predicate on the "cue" field.
func CueGT(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldGT(FieldCue, v))
}
// CueGTE applies the GTE predicate on the "cue" field.
func CueGTE(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldGTE(FieldCue, v))
}
// CueLT applies the LT predicate on the "cue" field.
func CueLT(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldLT(FieldCue, v))
}
// CueLTE applies the LTE predicate on the "cue" field.
func CueLTE(v []byte) predicate.Platform {
return predicate.Platform(sql.FieldLTE(FieldCue, v))
}
// CueIsNil applies the IsNil predicate on the "cue" field.
func CueIsNil() predicate.Platform {
return predicate.Platform(sql.FieldIsNull(FieldCue))
}
// CueNotNil applies the NotNil predicate on the "cue" field.
func CueNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldCue))
}
// CueDefinitionEQ applies the EQ predicate on the "cue_definition" field.
func CueDefinitionEQ(v string) predicate.Platform {
return predicate.Platform(sql.FieldEQ(FieldCueDefinition, v))
}
// CueDefinitionNEQ applies the NEQ predicate on the "cue_definition" field.
func CueDefinitionNEQ(v string) predicate.Platform {
return predicate.Platform(sql.FieldNEQ(FieldCueDefinition, v))
}
// CueDefinitionIn applies the In predicate on the "cue_definition" field.
func CueDefinitionIn(vs ...string) predicate.Platform {
return predicate.Platform(sql.FieldIn(FieldCueDefinition, vs...))
}
// CueDefinitionNotIn applies the NotIn predicate on the "cue_definition" field.
func CueDefinitionNotIn(vs ...string) predicate.Platform {
return predicate.Platform(sql.FieldNotIn(FieldCueDefinition, vs...))
}
// CueDefinitionGT applies the GT predicate on the "cue_definition" field.
func CueDefinitionGT(v string) predicate.Platform {
return predicate.Platform(sql.FieldGT(FieldCueDefinition, v))
}
// CueDefinitionGTE applies the GTE predicate on the "cue_definition" field.
func CueDefinitionGTE(v string) predicate.Platform {
return predicate.Platform(sql.FieldGTE(FieldCueDefinition, v))
}
// CueDefinitionLT applies the LT predicate on the "cue_definition" field.
func CueDefinitionLT(v string) predicate.Platform {
return predicate.Platform(sql.FieldLT(FieldCueDefinition, v))
}
// CueDefinitionLTE applies the LTE predicate on the "cue_definition" field.
func CueDefinitionLTE(v string) predicate.Platform {
return predicate.Platform(sql.FieldLTE(FieldCueDefinition, v))
}
// CueDefinitionContains applies the Contains predicate on the "cue_definition" field.
func CueDefinitionContains(v string) predicate.Platform {
return predicate.Platform(sql.FieldContains(FieldCueDefinition, v))
}
// CueDefinitionHasPrefix applies the HasPrefix predicate on the "cue_definition" field.
func CueDefinitionHasPrefix(v string) predicate.Platform {
return predicate.Platform(sql.FieldHasPrefix(FieldCueDefinition, v))
}
// CueDefinitionHasSuffix applies the HasSuffix predicate on the "cue_definition" field.
func CueDefinitionHasSuffix(v string) predicate.Platform {
return predicate.Platform(sql.FieldHasSuffix(FieldCueDefinition, v))
}
// CueDefinitionIsNil applies the IsNil predicate on the "cue_definition" field.
func CueDefinitionIsNil() predicate.Platform {
return predicate.Platform(sql.FieldIsNull(FieldCueDefinition))
}
// CueDefinitionNotNil applies the NotNil predicate on the "cue_definition" field.
func CueDefinitionNotNil() predicate.Platform {
return predicate.Platform(sql.FieldNotNull(FieldCueDefinition))
}
// CueDefinitionEqualFold applies the EqualFold predicate on the "cue_definition" field.
func CueDefinitionEqualFold(v string) predicate.Platform {
return predicate.Platform(sql.FieldEqualFold(FieldCueDefinition, v))
}
// CueDefinitionContainsFold applies the ContainsFold predicate on the "cue_definition" field.
func CueDefinitionContainsFold(v string) predicate.Platform {
return predicate.Platform(sql.FieldContainsFold(FieldCueDefinition, v))
}
// HasCreator applies the HasEdge predicate on the "creator" edge.
func HasCreator() predicate.Platform {
return predicate.Platform(func(s *sql.Selector) {

View File

@@ -14,8 +14,9 @@ import (
"entgo.io/ent/schema/field"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/user"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
// PlatformCreate is the builder for creating a Platform entity.
@@ -78,6 +79,38 @@ func (pc *PlatformCreate) SetCreatorID(u uuid.UUID) *PlatformCreate {
return pc
}
// SetForm sets the "form" field.
func (pc *PlatformCreate) SetForm(pl *platform.Form) *PlatformCreate {
pc.mutation.SetForm(pl)
return pc
}
// SetModel sets the "model" field.
func (pc *PlatformCreate) SetModel(pl *platform.Model) *PlatformCreate {
pc.mutation.SetModel(pl)
return pc
}
// SetCue sets the "cue" field.
func (pc *PlatformCreate) SetCue(b []byte) *PlatformCreate {
pc.mutation.SetCue(b)
return pc
}
// SetCueDefinition sets the "cue_definition" field.
func (pc *PlatformCreate) SetCueDefinition(s string) *PlatformCreate {
pc.mutation.SetCueDefinition(s)
return pc
}
// SetNillableCueDefinition sets the "cue_definition" field if the given value is not nil.
func (pc *PlatformCreate) SetNillableCueDefinition(s *string) *PlatformCreate {
if s != nil {
pc.SetCueDefinition(*s)
}
return pc
}
// SetID sets the "id" field.
func (pc *PlatformCreate) SetID(u uuid.UUID) *PlatformCreate {
pc.mutation.SetID(u)
@@ -144,15 +177,15 @@ func (pc *PlatformCreate) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (pc *PlatformCreate) defaults() {
if _, ok := pc.mutation.CreatedAt(); !ok {
v := platform.DefaultCreatedAt()
v := entplatform.DefaultCreatedAt()
pc.mutation.SetCreatedAt(v)
}
if _, ok := pc.mutation.UpdatedAt(); !ok {
v := platform.DefaultUpdatedAt()
v := entplatform.DefaultUpdatedAt()
pc.mutation.SetUpdatedAt(v)
}
if _, ok := pc.mutation.ID(); !ok {
v := platform.DefaultID()
v := entplatform.DefaultID()
pc.mutation.SetID(v)
}
}
@@ -172,7 +205,7 @@ func (pc *PlatformCreate) check() error {
return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Platform.name"`)}
}
if v, ok := pc.mutation.Name(); ok {
if err := platform.NameValidator(v); err != nil {
if err := entplatform.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Platform.name": %w`, err)}
}
}
@@ -217,7 +250,7 @@ func (pc *PlatformCreate) sqlSave(ctx context.Context) (*Platform, error) {
func (pc *PlatformCreate) createSpec() (*Platform, *sqlgraph.CreateSpec) {
var (
_node = &Platform{config: pc.config}
_spec = sqlgraph.NewCreateSpec(platform.Table, sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID))
_spec = sqlgraph.NewCreateSpec(entplatform.Table, sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID))
)
_spec.OnConflict = pc.conflict
if id, ok := pc.mutation.ID(); ok {
@@ -225,27 +258,43 @@ func (pc *PlatformCreate) createSpec() (*Platform, *sqlgraph.CreateSpec) {
_spec.ID.Value = &id
}
if value, ok := pc.mutation.CreatedAt(); ok {
_spec.SetField(platform.FieldCreatedAt, field.TypeTime, value)
_spec.SetField(entplatform.FieldCreatedAt, field.TypeTime, value)
_node.CreatedAt = value
}
if value, ok := pc.mutation.UpdatedAt(); ok {
_spec.SetField(platform.FieldUpdatedAt, field.TypeTime, value)
_spec.SetField(entplatform.FieldUpdatedAt, field.TypeTime, value)
_node.UpdatedAt = value
}
if value, ok := pc.mutation.Name(); ok {
_spec.SetField(platform.FieldName, field.TypeString, value)
_spec.SetField(entplatform.FieldName, field.TypeString, value)
_node.Name = value
}
if value, ok := pc.mutation.DisplayName(); ok {
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
_spec.SetField(entplatform.FieldDisplayName, field.TypeString, value)
_node.DisplayName = value
}
if value, ok := pc.mutation.Form(); ok {
_spec.SetField(entplatform.FieldForm, field.TypeJSON, value)
_node.Form = value
}
if value, ok := pc.mutation.Model(); ok {
_spec.SetField(entplatform.FieldModel, field.TypeJSON, value)
_node.Model = value
}
if value, ok := pc.mutation.Cue(); ok {
_spec.SetField(entplatform.FieldCue, field.TypeBytes, value)
_node.Cue = value
}
if value, ok := pc.mutation.CueDefinition(); ok {
_spec.SetField(entplatform.FieldCueDefinition, field.TypeString, value)
_node.CueDefinition = value
}
if nodes := pc.mutation.CreatorIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.CreatorTable,
Columns: []string{platform.CreatorColumn},
Table: entplatform.CreatorTable,
Columns: []string{entplatform.CreatorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
@@ -261,8 +310,8 @@ func (pc *PlatformCreate) createSpec() (*Platform, *sqlgraph.CreateSpec) {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.OrganizationTable,
Columns: []string{platform.OrganizationColumn},
Table: entplatform.OrganizationTable,
Columns: []string{entplatform.OrganizationColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
@@ -328,61 +377,133 @@ type (
// SetUpdatedAt sets the "updated_at" field.
func (u *PlatformUpsert) SetUpdatedAt(v time.Time) *PlatformUpsert {
u.Set(platform.FieldUpdatedAt, v)
u.Set(entplatform.FieldUpdatedAt, v)
return u
}
// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateUpdatedAt() *PlatformUpsert {
u.SetExcluded(platform.FieldUpdatedAt)
u.SetExcluded(entplatform.FieldUpdatedAt)
return u
}
// SetOrgID sets the "org_id" field.
func (u *PlatformUpsert) SetOrgID(v uuid.UUID) *PlatformUpsert {
u.Set(platform.FieldOrgID, v)
u.Set(entplatform.FieldOrgID, v)
return u
}
// UpdateOrgID sets the "org_id" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateOrgID() *PlatformUpsert {
u.SetExcluded(platform.FieldOrgID)
u.SetExcluded(entplatform.FieldOrgID)
return u
}
// SetName sets the "name" field.
func (u *PlatformUpsert) SetName(v string) *PlatformUpsert {
u.Set(platform.FieldName, v)
u.Set(entplatform.FieldName, v)
return u
}
// UpdateName sets the "name" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateName() *PlatformUpsert {
u.SetExcluded(platform.FieldName)
u.SetExcluded(entplatform.FieldName)
return u
}
// SetDisplayName sets the "display_name" field.
func (u *PlatformUpsert) SetDisplayName(v string) *PlatformUpsert {
u.Set(platform.FieldDisplayName, v)
u.Set(entplatform.FieldDisplayName, v)
return u
}
// UpdateDisplayName sets the "display_name" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateDisplayName() *PlatformUpsert {
u.SetExcluded(platform.FieldDisplayName)
u.SetExcluded(entplatform.FieldDisplayName)
return u
}
// SetCreatorID sets the "creator_id" field.
func (u *PlatformUpsert) SetCreatorID(v uuid.UUID) *PlatformUpsert {
u.Set(platform.FieldCreatorID, v)
u.Set(entplatform.FieldCreatorID, v)
return u
}
// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateCreatorID() *PlatformUpsert {
u.SetExcluded(platform.FieldCreatorID)
u.SetExcluded(entplatform.FieldCreatorID)
return u
}
// SetForm sets the "form" field.
func (u *PlatformUpsert) SetForm(v *platform.Form) *PlatformUpsert {
u.Set(entplatform.FieldForm, v)
return u
}
// UpdateForm sets the "form" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateForm() *PlatformUpsert {
u.SetExcluded(entplatform.FieldForm)
return u
}
// ClearForm clears the value of the "form" field.
func (u *PlatformUpsert) ClearForm() *PlatformUpsert {
u.SetNull(entplatform.FieldForm)
return u
}
// SetModel sets the "model" field.
func (u *PlatformUpsert) SetModel(v *platform.Model) *PlatformUpsert {
u.Set(entplatform.FieldModel, v)
return u
}
// UpdateModel sets the "model" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateModel() *PlatformUpsert {
u.SetExcluded(entplatform.FieldModel)
return u
}
// ClearModel clears the value of the "model" field.
func (u *PlatformUpsert) ClearModel() *PlatformUpsert {
u.SetNull(entplatform.FieldModel)
return u
}
// SetCue sets the "cue" field.
func (u *PlatformUpsert) SetCue(v []byte) *PlatformUpsert {
u.Set(entplatform.FieldCue, v)
return u
}
// UpdateCue sets the "cue" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateCue() *PlatformUpsert {
u.SetExcluded(entplatform.FieldCue)
return u
}
// ClearCue clears the value of the "cue" field.
func (u *PlatformUpsert) ClearCue() *PlatformUpsert {
u.SetNull(entplatform.FieldCue)
return u
}
// SetCueDefinition sets the "cue_definition" field.
func (u *PlatformUpsert) SetCueDefinition(v string) *PlatformUpsert {
u.Set(entplatform.FieldCueDefinition, v)
return u
}
// UpdateCueDefinition sets the "cue_definition" field to the value that was provided on create.
func (u *PlatformUpsert) UpdateCueDefinition() *PlatformUpsert {
u.SetExcluded(entplatform.FieldCueDefinition)
return u
}
// ClearCueDefinition clears the value of the "cue_definition" field.
func (u *PlatformUpsert) ClearCueDefinition() *PlatformUpsert {
u.SetNull(entplatform.FieldCueDefinition)
return u
}
@@ -393,7 +514,7 @@ func (u *PlatformUpsert) UpdateCreatorID() *PlatformUpsert {
// OnConflict(
// sql.ResolveWithNewValues(),
// sql.ResolveWith(func(u *sql.UpdateSet) {
// u.SetIgnore(platform.FieldID)
// u.SetIgnore(entplatform.FieldID)
// }),
// ).
// Exec(ctx)
@@ -401,10 +522,10 @@ func (u *PlatformUpsertOne) UpdateNewValues() *PlatformUpsertOne {
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
if _, exists := u.create.mutation.ID(); exists {
s.SetIgnore(platform.FieldID)
s.SetIgnore(entplatform.FieldID)
}
if _, exists := u.create.mutation.CreatedAt(); exists {
s.SetIgnore(platform.FieldCreatedAt)
s.SetIgnore(entplatform.FieldCreatedAt)
}
}))
return u
@@ -507,6 +628,90 @@ func (u *PlatformUpsertOne) UpdateCreatorID() *PlatformUpsertOne {
})
}
// SetForm sets the "form" field.
func (u *PlatformUpsertOne) SetForm(v *platform.Form) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetForm(v)
})
}
// UpdateForm sets the "form" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateForm() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateForm()
})
}
// ClearForm clears the value of the "form" field.
func (u *PlatformUpsertOne) ClearForm() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearForm()
})
}
// SetModel sets the "model" field.
func (u *PlatformUpsertOne) SetModel(v *platform.Model) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetModel(v)
})
}
// UpdateModel sets the "model" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateModel() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateModel()
})
}
// ClearModel clears the value of the "model" field.
func (u *PlatformUpsertOne) ClearModel() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearModel()
})
}
// SetCue sets the "cue" field.
func (u *PlatformUpsertOne) SetCue(v []byte) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetCue(v)
})
}
// UpdateCue sets the "cue" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateCue() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateCue()
})
}
// ClearCue clears the value of the "cue" field.
func (u *PlatformUpsertOne) ClearCue() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearCue()
})
}
// SetCueDefinition sets the "cue_definition" field.
func (u *PlatformUpsertOne) SetCueDefinition(v string) *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.SetCueDefinition(v)
})
}
// UpdateCueDefinition sets the "cue_definition" field to the value that was provided on create.
func (u *PlatformUpsertOne) UpdateCueDefinition() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.UpdateCueDefinition()
})
}
// ClearCueDefinition clears the value of the "cue_definition" field.
func (u *PlatformUpsertOne) ClearCueDefinition() *PlatformUpsertOne {
return u.Update(func(s *PlatformUpsert) {
s.ClearCueDefinition()
})
}
// Exec executes the query.
func (u *PlatformUpsertOne) Exec(ctx context.Context) error {
if len(u.create.conflict) == 0 {
@@ -679,7 +884,7 @@ type PlatformUpsertBulk struct {
// OnConflict(
// sql.ResolveWithNewValues(),
// sql.ResolveWith(func(u *sql.UpdateSet) {
// u.SetIgnore(platform.FieldID)
// u.SetIgnore(entplatform.FieldID)
// }),
// ).
// Exec(ctx)
@@ -688,10 +893,10 @@ func (u *PlatformUpsertBulk) UpdateNewValues() *PlatformUpsertBulk {
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
for _, b := range u.create.builders {
if _, exists := b.mutation.ID(); exists {
s.SetIgnore(platform.FieldID)
s.SetIgnore(entplatform.FieldID)
}
if _, exists := b.mutation.CreatedAt(); exists {
s.SetIgnore(platform.FieldCreatedAt)
s.SetIgnore(entplatform.FieldCreatedAt)
}
}
}))
@@ -795,6 +1000,90 @@ func (u *PlatformUpsertBulk) UpdateCreatorID() *PlatformUpsertBulk {
})
}
// SetForm sets the "form" field.
func (u *PlatformUpsertBulk) SetForm(v *platform.Form) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetForm(v)
})
}
// UpdateForm sets the "form" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateForm() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateForm()
})
}
// ClearForm clears the value of the "form" field.
func (u *PlatformUpsertBulk) ClearForm() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearForm()
})
}
// SetModel sets the "model" field.
func (u *PlatformUpsertBulk) SetModel(v *platform.Model) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetModel(v)
})
}
// UpdateModel sets the "model" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateModel() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateModel()
})
}
// ClearModel clears the value of the "model" field.
func (u *PlatformUpsertBulk) ClearModel() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearModel()
})
}
// SetCue sets the "cue" field.
func (u *PlatformUpsertBulk) SetCue(v []byte) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetCue(v)
})
}
// UpdateCue sets the "cue" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateCue() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateCue()
})
}
// ClearCue clears the value of the "cue" field.
func (u *PlatformUpsertBulk) ClearCue() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearCue()
})
}
// SetCueDefinition sets the "cue_definition" field.
func (u *PlatformUpsertBulk) SetCueDefinition(v string) *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.SetCueDefinition(v)
})
}
// UpdateCueDefinition sets the "cue_definition" field to the value that was provided on create.
func (u *PlatformUpsertBulk) UpdateCueDefinition() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.UpdateCueDefinition()
})
}
// ClearCueDefinition clears the value of the "cue_definition" field.
func (u *PlatformUpsertBulk) ClearCueDefinition() *PlatformUpsertBulk {
return u.Update(func(s *PlatformUpsert) {
s.ClearCueDefinition()
})
}
// Exec executes the query.
func (u *PlatformUpsertBulk) Exec(ctx context.Context) error {
if u.create.err != nil {

View File

@@ -8,8 +8,9 @@ import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/predicate"
entplatform "github.com/holos-run/holos/internal/ent/platform"
)
// PlatformDelete is the builder for deleting a Platform entity.
@@ -40,7 +41,7 @@ func (pd *PlatformDelete) ExecX(ctx context.Context) int {
}
func (pd *PlatformDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(platform.Table, sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID))
_spec := sqlgraph.NewDeleteSpec(entplatform.Table, sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID))
if ps := pd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -74,7 +75,7 @@ func (pdo *PlatformDeleteOne) Exec(ctx context.Context) error {
case err != nil:
return err
case n == 0:
return &NotFoundError{platform.Label}
return &NotFoundError{entplatform.Label}
default:
return nil
}

View File

@@ -12,7 +12,7 @@ import (
"entgo.io/ent/schema/field"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/predicate"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -21,7 +21,7 @@ import (
type PlatformQuery struct {
config
ctx *QueryContext
order []platform.OrderOption
order []entplatform.OrderOption
inters []Interceptor
predicates []predicate.Platform
withCreator *UserQuery
@@ -57,7 +57,7 @@ func (pq *PlatformQuery) Unique(unique bool) *PlatformQuery {
}
// Order specifies how the records should be ordered.
func (pq *PlatformQuery) Order(o ...platform.OrderOption) *PlatformQuery {
func (pq *PlatformQuery) Order(o ...entplatform.OrderOption) *PlatformQuery {
pq.order = append(pq.order, o...)
return pq
}
@@ -74,9 +74,9 @@ func (pq *PlatformQuery) QueryCreator() *UserQuery {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(platform.Table, platform.FieldID, selector),
sqlgraph.From(entplatform.Table, entplatform.FieldID, selector),
sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, platform.CreatorTable, platform.CreatorColumn),
sqlgraph.Edge(sqlgraph.M2O, false, entplatform.CreatorTable, entplatform.CreatorColumn),
)
fromU = sqlgraph.SetNeighbors(pq.driver.Dialect(), step)
return fromU, nil
@@ -96,9 +96,9 @@ func (pq *PlatformQuery) QueryOrganization() *OrganizationQuery {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(platform.Table, platform.FieldID, selector),
sqlgraph.From(entplatform.Table, entplatform.FieldID, selector),
sqlgraph.To(organization.Table, organization.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, platform.OrganizationTable, platform.OrganizationColumn),
sqlgraph.Edge(sqlgraph.M2O, false, entplatform.OrganizationTable, entplatform.OrganizationColumn),
)
fromU = sqlgraph.SetNeighbors(pq.driver.Dialect(), step)
return fromU, nil
@@ -114,7 +114,7 @@ func (pq *PlatformQuery) First(ctx context.Context) (*Platform, error) {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{platform.Label}
return nil, &NotFoundError{entplatform.Label}
}
return nodes[0], nil
}
@@ -136,7 +136,7 @@ func (pq *PlatformQuery) FirstID(ctx context.Context) (id uuid.UUID, err error)
return
}
if len(ids) == 0 {
err = &NotFoundError{platform.Label}
err = &NotFoundError{entplatform.Label}
return
}
return ids[0], nil
@@ -163,9 +163,9 @@ func (pq *PlatformQuery) Only(ctx context.Context) (*Platform, error) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{platform.Label}
return nil, &NotFoundError{entplatform.Label}
default:
return nil, &NotSingularError{platform.Label}
return nil, &NotSingularError{entplatform.Label}
}
}
@@ -190,9 +190,9 @@ func (pq *PlatformQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{platform.Label}
err = &NotFoundError{entplatform.Label}
default:
err = &NotSingularError{platform.Label}
err = &NotSingularError{entplatform.Label}
}
return
}
@@ -231,7 +231,7 @@ func (pq *PlatformQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) {
pq.Unique(true)
}
ctx = setContextOp(ctx, pq.ctx, "IDs")
if err = pq.Select(platform.FieldID).Scan(ctx, &ids); err != nil {
if err = pq.Select(entplatform.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
@@ -295,7 +295,7 @@ func (pq *PlatformQuery) Clone() *PlatformQuery {
return &PlatformQuery{
config: pq.config,
ctx: pq.ctx.Clone(),
order: append([]platform.OrderOption{}, pq.order...),
order: append([]entplatform.OrderOption{}, pq.order...),
inters: append([]Interceptor{}, pq.inters...),
predicates: append([]predicate.Platform{}, pq.predicates...),
withCreator: pq.withCreator.Clone(),
@@ -339,14 +339,14 @@ func (pq *PlatformQuery) WithOrganization(opts ...func(*OrganizationQuery)) *Pla
// }
//
// client.Platform.Query().
// GroupBy(platform.FieldCreatedAt).
// GroupBy(entplatform.FieldCreatedAt).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (pq *PlatformQuery) GroupBy(field string, fields ...string) *PlatformGroupBy {
pq.ctx.Fields = append([]string{field}, fields...)
grbuild := &PlatformGroupBy{build: pq}
grbuild.flds = &pq.ctx.Fields
grbuild.label = platform.Label
grbuild.label = entplatform.Label
grbuild.scan = grbuild.Scan
return grbuild
}
@@ -361,12 +361,12 @@ func (pq *PlatformQuery) GroupBy(field string, fields ...string) *PlatformGroupB
// }
//
// client.Platform.Query().
// Select(platform.FieldCreatedAt).
// Select(entplatform.FieldCreatedAt).
// Scan(ctx, &v)
func (pq *PlatformQuery) Select(fields ...string) *PlatformSelect {
pq.ctx.Fields = append(pq.ctx.Fields, fields...)
sbuild := &PlatformSelect{PlatformQuery: pq}
sbuild.label = platform.Label
sbuild.label = entplatform.Label
sbuild.flds, sbuild.scan = &pq.ctx.Fields, sbuild.Scan
return sbuild
}
@@ -388,7 +388,7 @@ func (pq *PlatformQuery) prepareQuery(ctx context.Context) error {
}
}
for _, f := range pq.ctx.Fields {
if !platform.ValidColumn(f) {
if !entplatform.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
@@ -513,7 +513,7 @@ func (pq *PlatformQuery) sqlCount(ctx context.Context) (int, error) {
}
func (pq *PlatformQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(platform.Table, platform.Columns, sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID))
_spec := sqlgraph.NewQuerySpec(entplatform.Table, entplatform.Columns, sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID))
_spec.From = pq.sql
if unique := pq.ctx.Unique; unique != nil {
_spec.Unique = *unique
@@ -522,17 +522,17 @@ func (pq *PlatformQuery) querySpec() *sqlgraph.QuerySpec {
}
if fields := pq.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, platform.FieldID)
_spec.Node.Columns = append(_spec.Node.Columns, entplatform.FieldID)
for i := range fields {
if fields[i] != platform.FieldID {
if fields[i] != entplatform.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if pq.withCreator != nil {
_spec.Node.AddColumnOnce(platform.FieldCreatorID)
_spec.Node.AddColumnOnce(entplatform.FieldCreatorID)
}
if pq.withOrganization != nil {
_spec.Node.AddColumnOnce(platform.FieldOrgID)
_spec.Node.AddColumnOnce(entplatform.FieldOrgID)
}
}
if ps := pq.predicates; len(ps) > 0 {
@@ -560,10 +560,10 @@ func (pq *PlatformQuery) querySpec() *sqlgraph.QuerySpec {
func (pq *PlatformQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(pq.driver.Dialect())
t1 := builder.Table(platform.Table)
t1 := builder.Table(entplatform.Table)
columns := pq.ctx.Fields
if len(columns) == 0 {
columns = platform.Columns
columns = entplatform.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if pq.sql != nil {

View File

@@ -13,9 +13,10 @@ import (
"entgo.io/ent/schema/field"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/predicate"
"github.com/holos-run/holos/internal/ent/user"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
// PlatformUpdate is the builder for updating Platform entities.
@@ -93,6 +94,62 @@ func (pu *PlatformUpdate) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate {
return pu
}
// SetForm sets the "form" field.
func (pu *PlatformUpdate) SetForm(pl *platform.Form) *PlatformUpdate {
pu.mutation.SetForm(pl)
return pu
}
// ClearForm clears the value of the "form" field.
func (pu *PlatformUpdate) ClearForm() *PlatformUpdate {
pu.mutation.ClearForm()
return pu
}
// SetModel sets the "model" field.
func (pu *PlatformUpdate) SetModel(pl *platform.Model) *PlatformUpdate {
pu.mutation.SetModel(pl)
return pu
}
// ClearModel clears the value of the "model" field.
func (pu *PlatformUpdate) ClearModel() *PlatformUpdate {
pu.mutation.ClearModel()
return pu
}
// SetCue sets the "cue" field.
func (pu *PlatformUpdate) SetCue(b []byte) *PlatformUpdate {
pu.mutation.SetCue(b)
return pu
}
// ClearCue clears the value of the "cue" field.
func (pu *PlatformUpdate) ClearCue() *PlatformUpdate {
pu.mutation.ClearCue()
return pu
}
// SetCueDefinition sets the "cue_definition" field.
func (pu *PlatformUpdate) SetCueDefinition(s string) *PlatformUpdate {
pu.mutation.SetCueDefinition(s)
return pu
}
// SetNillableCueDefinition sets the "cue_definition" field if the given value is not nil.
func (pu *PlatformUpdate) SetNillableCueDefinition(s *string) *PlatformUpdate {
if s != nil {
pu.SetCueDefinition(*s)
}
return pu
}
// ClearCueDefinition clears the value of the "cue_definition" field.
func (pu *PlatformUpdate) ClearCueDefinition() *PlatformUpdate {
pu.mutation.ClearCueDefinition()
return pu
}
// SetCreator sets the "creator" edge to the User entity.
func (pu *PlatformUpdate) SetCreator(u *User) *PlatformUpdate {
return pu.SetCreatorID(u.ID)
@@ -157,7 +214,7 @@ func (pu *PlatformUpdate) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (pu *PlatformUpdate) defaults() {
if _, ok := pu.mutation.UpdatedAt(); !ok {
v := platform.UpdateDefaultUpdatedAt()
v := entplatform.UpdateDefaultUpdatedAt()
pu.mutation.SetUpdatedAt(v)
}
}
@@ -165,7 +222,7 @@ func (pu *PlatformUpdate) defaults() {
// check runs all checks and user-defined validators on the builder.
func (pu *PlatformUpdate) check() error {
if v, ok := pu.mutation.Name(); ok {
if err := platform.NameValidator(v); err != nil {
if err := entplatform.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Platform.name": %w`, err)}
}
}
@@ -182,7 +239,7 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := pu.check(); err != nil {
return n, err
}
_spec := sqlgraph.NewUpdateSpec(platform.Table, platform.Columns, sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID))
_spec := sqlgraph.NewUpdateSpec(entplatform.Table, entplatform.Columns, sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID))
if ps := pu.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
@@ -191,20 +248,44 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
}
if value, ok := pu.mutation.UpdatedAt(); ok {
_spec.SetField(platform.FieldUpdatedAt, field.TypeTime, value)
_spec.SetField(entplatform.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := pu.mutation.Name(); ok {
_spec.SetField(platform.FieldName, field.TypeString, value)
_spec.SetField(entplatform.FieldName, field.TypeString, value)
}
if value, ok := pu.mutation.DisplayName(); ok {
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
_spec.SetField(entplatform.FieldDisplayName, field.TypeString, value)
}
if value, ok := pu.mutation.Form(); ok {
_spec.SetField(entplatform.FieldForm, field.TypeJSON, value)
}
if pu.mutation.FormCleared() {
_spec.ClearField(entplatform.FieldForm, field.TypeJSON)
}
if value, ok := pu.mutation.Model(); ok {
_spec.SetField(entplatform.FieldModel, field.TypeJSON, value)
}
if pu.mutation.ModelCleared() {
_spec.ClearField(entplatform.FieldModel, field.TypeJSON)
}
if value, ok := pu.mutation.Cue(); ok {
_spec.SetField(entplatform.FieldCue, field.TypeBytes, value)
}
if pu.mutation.CueCleared() {
_spec.ClearField(entplatform.FieldCue, field.TypeBytes)
}
if value, ok := pu.mutation.CueDefinition(); ok {
_spec.SetField(entplatform.FieldCueDefinition, field.TypeString, value)
}
if pu.mutation.CueDefinitionCleared() {
_spec.ClearField(entplatform.FieldCueDefinition, field.TypeString)
}
if pu.mutation.CreatorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.CreatorTable,
Columns: []string{platform.CreatorColumn},
Table: entplatform.CreatorTable,
Columns: []string{entplatform.CreatorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
@@ -216,8 +297,8 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.CreatorTable,
Columns: []string{platform.CreatorColumn},
Table: entplatform.CreatorTable,
Columns: []string{entplatform.CreatorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
@@ -232,8 +313,8 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.OrganizationTable,
Columns: []string{platform.OrganizationColumn},
Table: entplatform.OrganizationTable,
Columns: []string{entplatform.OrganizationColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
@@ -245,8 +326,8 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.OrganizationTable,
Columns: []string{platform.OrganizationColumn},
Table: entplatform.OrganizationTable,
Columns: []string{entplatform.OrganizationColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
@@ -259,7 +340,7 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
if n, err = sqlgraph.UpdateNodes(ctx, pu.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{platform.Label}
err = &NotFoundError{entplatform.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
@@ -339,6 +420,62 @@ func (puo *PlatformUpdateOne) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate
return puo
}
// SetForm sets the "form" field.
func (puo *PlatformUpdateOne) SetForm(pl *platform.Form) *PlatformUpdateOne {
puo.mutation.SetForm(pl)
return puo
}
// ClearForm clears the value of the "form" field.
func (puo *PlatformUpdateOne) ClearForm() *PlatformUpdateOne {
puo.mutation.ClearForm()
return puo
}
// SetModel sets the "model" field.
func (puo *PlatformUpdateOne) SetModel(pl *platform.Model) *PlatformUpdateOne {
puo.mutation.SetModel(pl)
return puo
}
// ClearModel clears the value of the "model" field.
func (puo *PlatformUpdateOne) ClearModel() *PlatformUpdateOne {
puo.mutation.ClearModel()
return puo
}
// SetCue sets the "cue" field.
func (puo *PlatformUpdateOne) SetCue(b []byte) *PlatformUpdateOne {
puo.mutation.SetCue(b)
return puo
}
// ClearCue clears the value of the "cue" field.
func (puo *PlatformUpdateOne) ClearCue() *PlatformUpdateOne {
puo.mutation.ClearCue()
return puo
}
// SetCueDefinition sets the "cue_definition" field.
func (puo *PlatformUpdateOne) SetCueDefinition(s string) *PlatformUpdateOne {
puo.mutation.SetCueDefinition(s)
return puo
}
// SetNillableCueDefinition sets the "cue_definition" field if the given value is not nil.
func (puo *PlatformUpdateOne) SetNillableCueDefinition(s *string) *PlatformUpdateOne {
if s != nil {
puo.SetCueDefinition(*s)
}
return puo
}
// ClearCueDefinition clears the value of the "cue_definition" field.
func (puo *PlatformUpdateOne) ClearCueDefinition() *PlatformUpdateOne {
puo.mutation.ClearCueDefinition()
return puo
}
// SetCreator sets the "creator" edge to the User entity.
func (puo *PlatformUpdateOne) SetCreator(u *User) *PlatformUpdateOne {
return puo.SetCreatorID(u.ID)
@@ -416,7 +553,7 @@ func (puo *PlatformUpdateOne) ExecX(ctx context.Context) {
// defaults sets the default values of the builder before save.
func (puo *PlatformUpdateOne) defaults() {
if _, ok := puo.mutation.UpdatedAt(); !ok {
v := platform.UpdateDefaultUpdatedAt()
v := entplatform.UpdateDefaultUpdatedAt()
puo.mutation.SetUpdatedAt(v)
}
}
@@ -424,7 +561,7 @@ func (puo *PlatformUpdateOne) defaults() {
// check runs all checks and user-defined validators on the builder.
func (puo *PlatformUpdateOne) check() error {
if v, ok := puo.mutation.Name(); ok {
if err := platform.NameValidator(v); err != nil {
if err := entplatform.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Platform.name": %w`, err)}
}
}
@@ -441,7 +578,7 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
if err := puo.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(platform.Table, platform.Columns, sqlgraph.NewFieldSpec(platform.FieldID, field.TypeUUID))
_spec := sqlgraph.NewUpdateSpec(entplatform.Table, entplatform.Columns, sqlgraph.NewFieldSpec(entplatform.FieldID, field.TypeUUID))
id, ok := puo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Platform.id" for update`)}
@@ -449,12 +586,12 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
_spec.Node.ID.Value = id
if fields := puo.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, platform.FieldID)
_spec.Node.Columns = append(_spec.Node.Columns, entplatform.FieldID)
for _, f := range fields {
if !platform.ValidColumn(f) {
if !entplatform.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != platform.FieldID {
if f != entplatform.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
@@ -467,20 +604,44 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
}
}
if value, ok := puo.mutation.UpdatedAt(); ok {
_spec.SetField(platform.FieldUpdatedAt, field.TypeTime, value)
_spec.SetField(entplatform.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := puo.mutation.Name(); ok {
_spec.SetField(platform.FieldName, field.TypeString, value)
_spec.SetField(entplatform.FieldName, field.TypeString, value)
}
if value, ok := puo.mutation.DisplayName(); ok {
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
_spec.SetField(entplatform.FieldDisplayName, field.TypeString, value)
}
if value, ok := puo.mutation.Form(); ok {
_spec.SetField(entplatform.FieldForm, field.TypeJSON, value)
}
if puo.mutation.FormCleared() {
_spec.ClearField(entplatform.FieldForm, field.TypeJSON)
}
if value, ok := puo.mutation.Model(); ok {
_spec.SetField(entplatform.FieldModel, field.TypeJSON, value)
}
if puo.mutation.ModelCleared() {
_spec.ClearField(entplatform.FieldModel, field.TypeJSON)
}
if value, ok := puo.mutation.Cue(); ok {
_spec.SetField(entplatform.FieldCue, field.TypeBytes, value)
}
if puo.mutation.CueCleared() {
_spec.ClearField(entplatform.FieldCue, field.TypeBytes)
}
if value, ok := puo.mutation.CueDefinition(); ok {
_spec.SetField(entplatform.FieldCueDefinition, field.TypeString, value)
}
if puo.mutation.CueDefinitionCleared() {
_spec.ClearField(entplatform.FieldCueDefinition, field.TypeString)
}
if puo.mutation.CreatorCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.CreatorTable,
Columns: []string{platform.CreatorColumn},
Table: entplatform.CreatorTable,
Columns: []string{entplatform.CreatorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
@@ -492,8 +653,8 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.CreatorTable,
Columns: []string{platform.CreatorColumn},
Table: entplatform.CreatorTable,
Columns: []string{entplatform.CreatorColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
@@ -508,8 +669,8 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.OrganizationTable,
Columns: []string{platform.OrganizationColumn},
Table: entplatform.OrganizationTable,
Columns: []string{entplatform.OrganizationColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
@@ -521,8 +682,8 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: false,
Table: platform.OrganizationTable,
Columns: []string{platform.OrganizationColumn},
Table: entplatform.OrganizationTable,
Columns: []string{entplatform.OrganizationColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
@@ -538,7 +699,7 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, puo.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{platform.Label}
err = &NotFoundError{entplatform.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}

View File

@@ -9,7 +9,7 @@ import (
// Organization is the predicate function for organization builders.
type Organization func(*sql.Selector)
// Platform is the predicate function for platform builders.
// Platform is the predicate function for entplatform builders.
type Platform func(*sql.Selector)
// User is the predicate function for user builders.

View File

@@ -7,7 +7,7 @@ import (
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent/organization"
"github.com/holos-run/holos/internal/ent/platform"
entplatform "github.com/holos-run/holos/internal/ent/platform"
"github.com/holos-run/holos/internal/ent/schema"
"github.com/holos-run/holos/internal/ent/user"
)
@@ -41,31 +41,31 @@ func init() {
organizationDescID := organizationMixinFields0[0].Descriptor()
// organization.DefaultID holds the default value on creation for the id field.
organization.DefaultID = organizationDescID.Default.(func() uuid.UUID)
platformMixin := schema.Platform{}.Mixin()
platformMixinFields0 := platformMixin[0].Fields()
_ = platformMixinFields0
platformMixinFields1 := platformMixin[1].Fields()
_ = platformMixinFields1
platformFields := schema.Platform{}.Fields()
_ = platformFields
// platformDescCreatedAt is the schema descriptor for created_at field.
platformDescCreatedAt := platformMixinFields1[0].Descriptor()
// platform.DefaultCreatedAt holds the default value on creation for the created_at field.
platform.DefaultCreatedAt = platformDescCreatedAt.Default.(func() time.Time)
// platformDescUpdatedAt is the schema descriptor for updated_at field.
platformDescUpdatedAt := platformMixinFields1[1].Descriptor()
// platform.DefaultUpdatedAt holds the default value on creation for the updated_at field.
platform.DefaultUpdatedAt = platformDescUpdatedAt.Default.(func() time.Time)
// platform.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
platform.UpdateDefaultUpdatedAt = platformDescUpdatedAt.UpdateDefault.(func() time.Time)
// platformDescName is the schema descriptor for name field.
platformDescName := platformFields[1].Descriptor()
// platform.NameValidator is a validator for the "name" field. It is called by the builders before save.
platform.NameValidator = platformDescName.Validators[0].(func(string) error)
// platformDescID is the schema descriptor for id field.
platformDescID := platformMixinFields0[0].Descriptor()
// platform.DefaultID holds the default value on creation for the id field.
platform.DefaultID = platformDescID.Default.(func() uuid.UUID)
entplatformMixin := schema.Platform{}.Mixin()
entplatformMixinFields0 := entplatformMixin[0].Fields()
_ = entplatformMixinFields0
entplatformMixinFields1 := entplatformMixin[1].Fields()
_ = entplatformMixinFields1
entplatformFields := schema.Platform{}.Fields()
_ = entplatformFields
// entplatformDescCreatedAt is the schema descriptor for created_at field.
entplatformDescCreatedAt := entplatformMixinFields1[0].Descriptor()
// entplatform.DefaultCreatedAt holds the default value on creation for the created_at field.
entplatform.DefaultCreatedAt = entplatformDescCreatedAt.Default.(func() time.Time)
// entplatformDescUpdatedAt is the schema descriptor for updated_at field.
entplatformDescUpdatedAt := entplatformMixinFields1[1].Descriptor()
// entplatform.DefaultUpdatedAt holds the default value on creation for the updated_at field.
entplatform.DefaultUpdatedAt = entplatformDescUpdatedAt.Default.(func() time.Time)
// entplatform.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
entplatform.UpdateDefaultUpdatedAt = entplatformDescUpdatedAt.UpdateDefault.(func() time.Time)
// entplatformDescName is the schema descriptor for name field.
entplatformDescName := entplatformFields[1].Descriptor()
// entplatform.NameValidator is a validator for the "name" field. It is called by the builders before save.
entplatform.NameValidator = entplatformDescName.Validators[0].(func(string) error)
// entplatformDescID is the schema descriptor for id field.
entplatformDescID := entplatformMixinFields0[0].Descriptor()
// entplatform.DefaultID holds the default value on creation for the id field.
entplatform.DefaultID = entplatformDescID.Default.(func() uuid.UUID)
userMixin := schema.User{}.Mixin()
userMixinFields0 := userMixin[0].Fields()
_ = userMixinFields0

View File

@@ -4,7 +4,9 @@ import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/gofrs/uuid"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
type Platform struct {
@@ -24,6 +26,18 @@ func (Platform) Fields() []ent.Field {
field.String("name").NotEmpty(),
field.String("display_name"),
field.UUID("creator_id", uuid.UUID{}),
field.JSON("form", &platform.Form{}).
Optional().
Comment("JSON representation of FormlyFormConfig[] refer to https://github.com/holos-run/holos/issues/161"),
field.JSON("model", &platform.Model{}).
Optional().
Comment("JSON representation of the form model which holds user input values refer to https://github.com/holos-run/holos/issues/161"),
field.Bytes("cue").
Optional().
Comment("CUE definition to vet the model against e.g. #PlatformConfig"),
field.String("cue_definition").
Optional().
Comment("The definition name to vet config_values against config_cue e.g. '#PlatformSpec'"),
}
}
@@ -39,3 +53,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,19 +8,24 @@ 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';
import { HolosPanelWrapperComponent } from '../wrappers/holos-panel-wrapper/holos-panel-wrapper.component';
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
}),
FormlyModule.forRoot(),
FormlyModule.forRoot({
wrappers: [{ name: 'holos-panel', component: HolosPanelWrapperComponent }],
}),
),
]
};

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

@@ -0,0 +1,105 @@
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
// @generated from file holos/platform/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import { MethodKind } from "@bufbuild/protobuf";
import { AddPlatformRequest, AddPlatformResponse, GetFormRequest, GetFormResponse, GetModelRequest, GetModelResponse, GetPlatformRequest, GetPlatformResponse, ListPlatformsRequest, ListPlatformsResponse, PutFormRequest, PutFormResponse, PutModelRequest, PutModelResponse } from "./platform_pb.js";
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.AddPlatform
*/
export const addPlatform = {
localName: "addPlatform",
name: "AddPlatform",
kind: MethodKind.Unary,
I: AddPlatformRequest,
O: AddPlatformResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetPlatform
*/
export const getPlatform = {
localName: "getPlatform",
name: "GetPlatform",
kind: MethodKind.Unary,
I: GetPlatformRequest,
O: GetPlatformResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.ListPlatforms
*/
export const listPlatforms = {
localName: "listPlatforms",
name: "ListPlatforms",
kind: MethodKind.Unary,
I: ListPlatformsRequest,
O: ListPlatformsResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetForm
*/
export const getForm = {
localName: "getForm",
name: "GetForm",
kind: MethodKind.Unary,
I: GetFormRequest,
O: GetFormResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.PutForm
*/
export const putForm = {
localName: "putForm",
name: "PutForm",
kind: MethodKind.Unary,
I: PutFormRequest,
O: PutFormResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetModel
*/
export const getModel = {
localName: "getModel",
name: "GetModel",
kind: MethodKind.Unary,
I: GetModelRequest,
O: GetModelResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.PutModel
*/
export const putModel = {
localName: "putModel",
name: "PutModel",
kind: MethodKind.Unary,
I: PutModelRequest,
O: PutModelResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;

View File

@@ -0,0 +1,80 @@
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
// @generated from file holos/platform/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import { AddPlatformRequest, AddPlatformResponse, GetFormRequest, GetFormResponse, GetModelRequest, GetModelResponse, GetPlatformRequest, GetPlatformResponse, ListPlatformsRequest, ListPlatformsResponse, PutFormRequest, PutFormResponse, PutModelRequest, PutModelResponse } from "./platform_pb.js";
import { MethodKind } from "@bufbuild/protobuf";
/**
* @generated from service holos.platform.v1alpha1.PlatformService
*/
export const PlatformService = {
typeName: "holos.platform.v1alpha1.PlatformService",
methods: {
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.AddPlatform
*/
addPlatform: {
name: "AddPlatform",
I: AddPlatformRequest,
O: AddPlatformResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetPlatform
*/
getPlatform: {
name: "GetPlatform",
I: GetPlatformRequest,
O: GetPlatformResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.ListPlatforms
*/
listPlatforms: {
name: "ListPlatforms",
I: ListPlatformsRequest,
O: ListPlatformsResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetForm
*/
getForm: {
name: "GetForm",
I: GetFormRequest,
O: GetFormResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.PutForm
*/
putForm: {
name: "PutForm",
I: PutFormRequest,
O: PutFormResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetModel
*/
getModel: {
name: "GetModel",
I: GetModelRequest,
O: GetModelResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.PutModel
*/
putModel: {
name: "PutModel",
I: PutModelRequest,
O: PutModelResponse,
kind: MethodKind.Unary,
},
}
} as const;

View File

@@ -0,0 +1,732 @@
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
// @generated from file holos/platform/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
import { Message, proto3, Struct } from "@bufbuild/protobuf";
/**
* @generated from message holos.platform.v1alpha1.Platform
*/
export class Platform extends Message<Platform> {
/**
* Unique id assigned by the server.
*
* @generated from field: string id = 1;
*/
id = "";
/**
* Organization ID resource owner.
*
* @generated from field: string org_id = 2;
*/
orgId = "";
/**
* name is the platform short name as a dns label.
*
* @generated from field: string name = 3;
*/
name = "";
/**
* @generated from field: string display_name = 4;
*/
displayName = "";
/**
* @generated from field: holos.platform.v1alpha1.PlatformSpec spec = 5;
*/
spec?: PlatformSpec;
constructor(data?: PartialMessage<Platform>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.Platform";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 4, name: "display_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 5, name: "spec", kind: "message", T: PlatformSpec },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Platform {
return new Platform().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Platform {
return new Platform().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Platform {
return new Platform().fromJsonString(jsonString, options);
}
static equals(a: Platform | PlainMessage<Platform> | undefined, b: Platform | PlainMessage<Platform> | undefined): boolean {
return proto3.util.equals(Platform, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PlatformSpec
*/
export class PlatformSpec extends Message<PlatformSpec> {
/**
* model represents the user-defined and user-supplied form field values.
*
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
constructor(data?: PartialMessage<PlatformSpec>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PlatformSpec";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformSpec {
return new PlatformSpec().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformSpec {
return new PlatformSpec().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformSpec {
return new PlatformSpec().fromJsonString(jsonString, options);
}
static equals(a: PlatformSpec | PlainMessage<PlatformSpec> | undefined, b: PlatformSpec | PlainMessage<PlatformSpec> | undefined): boolean {
return proto3.util.equals(PlatformSpec, a, b);
}
}
/**
* Form represents the Formly input form.
*
* @generated from message holos.platform.v1alpha1.Form
*/
export class Form extends Message<Form> {
/**
* fields represents FormlyFieldConfig[] encoded as a JSON array.
*
* @generated from field: repeated google.protobuf.Struct fields = 1;
*/
fields: Struct[] = [];
constructor(data?: PartialMessage<Form>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.Form";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "fields", kind: "message", T: Struct, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Form {
return new Form().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Form {
return new Form().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Form {
return new Form().fromJsonString(jsonString, options);
}
static equals(a: Form | PlainMessage<Form> | undefined, b: Form | PlainMessage<Form> | undefined): boolean {
return proto3.util.equals(Form, a, b);
}
}
/**
* Model represents the values entered into the form, stored in the form's model
* in the web app, and persisted into the backend database. The model is
* ultimately intended as the input to platform rendering.
*
* @generated from message holos.platform.v1alpha1.Model
*/
export class Model extends Message<Model> {
/**
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
constructor(data?: PartialMessage<Model>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.Model";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Model {
return new Model().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Model {
return new Model().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Model {
return new Model().fromJsonString(jsonString, options);
}
static equals(a: Model | PlainMessage<Model> | undefined, b: Model | PlainMessage<Model> | undefined): boolean {
return proto3.util.equals(Model, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.ListPlatformsRequest
*/
export class ListPlatformsRequest extends Message<ListPlatformsRequest> {
/**
* @generated from field: string org_id = 1;
*/
orgId = "";
constructor(data?: PartialMessage<ListPlatformsRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.ListPlatformsRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ListPlatformsRequest {
return new ListPlatformsRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ListPlatformsRequest {
return new ListPlatformsRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ListPlatformsRequest {
return new ListPlatformsRequest().fromJsonString(jsonString, options);
}
static equals(a: ListPlatformsRequest | PlainMessage<ListPlatformsRequest> | undefined, b: ListPlatformsRequest | PlainMessage<ListPlatformsRequest> | undefined): boolean {
return proto3.util.equals(ListPlatformsRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.ListPlatformsResponse
*/
export class ListPlatformsResponse extends Message<ListPlatformsResponse> {
/**
* @generated from field: repeated holos.platform.v1alpha1.Platform platforms = 1;
*/
platforms: Platform[] = [];
constructor(data?: PartialMessage<ListPlatformsResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.ListPlatformsResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platforms", kind: "message", T: Platform, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ListPlatformsResponse {
return new ListPlatformsResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ListPlatformsResponse {
return new ListPlatformsResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ListPlatformsResponse {
return new ListPlatformsResponse().fromJsonString(jsonString, options);
}
static equals(a: ListPlatformsResponse | PlainMessage<ListPlatformsResponse> | undefined, b: ListPlatformsResponse | PlainMessage<ListPlatformsResponse> | undefined): boolean {
return proto3.util.equals(ListPlatformsResponse, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.AddPlatformRequest
*/
export class AddPlatformRequest extends Message<AddPlatformRequest> {
/**
* @generated from field: holos.platform.v1alpha1.Platform platform = 1;
*/
platform?: Platform;
constructor(data?: PartialMessage<AddPlatformRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.AddPlatformRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform", kind: "message", T: Platform },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): AddPlatformRequest {
return new AddPlatformRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): AddPlatformRequest {
return new AddPlatformRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): AddPlatformRequest {
return new AddPlatformRequest().fromJsonString(jsonString, options);
}
static equals(a: AddPlatformRequest | PlainMessage<AddPlatformRequest> | undefined, b: AddPlatformRequest | PlainMessage<AddPlatformRequest> | undefined): boolean {
return proto3.util.equals(AddPlatformRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.AddPlatformResponse
*/
export class AddPlatformResponse extends Message<AddPlatformResponse> {
/**
* @generated from field: repeated holos.platform.v1alpha1.Platform platforms = 1;
*/
platforms: Platform[] = [];
constructor(data?: PartialMessage<AddPlatformResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.AddPlatformResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platforms", kind: "message", T: Platform, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): AddPlatformResponse {
return new AddPlatformResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): AddPlatformResponse {
return new AddPlatformResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): AddPlatformResponse {
return new AddPlatformResponse().fromJsonString(jsonString, options);
}
static equals(a: AddPlatformResponse | PlainMessage<AddPlatformResponse> | undefined, b: AddPlatformResponse | PlainMessage<AddPlatformResponse> | undefined): boolean {
return proto3.util.equals(AddPlatformResponse, a, b);
}
}
/**
* @generated from message holos.platform.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.platform.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.platform.v1alpha1.GetPlatformResponse
*/
export class GetPlatformResponse extends Message<GetPlatformResponse> {
/**
* @generated from field: holos.platform.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.platform.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.platform.v1alpha1.GetFormRequest
*/
export class GetFormRequest extends Message<GetFormRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
constructor(data?: PartialMessage<GetFormRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetFormRequest";
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>): GetFormRequest {
return new GetFormRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetFormRequest {
return new GetFormRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetFormRequest {
return new GetFormRequest().fromJsonString(jsonString, options);
}
static equals(a: GetFormRequest | PlainMessage<GetFormRequest> | undefined, b: GetFormRequest | PlainMessage<GetFormRequest> | undefined): boolean {
return proto3.util.equals(GetFormRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.GetFormResponse
*/
export class GetFormResponse extends Message<GetFormResponse> {
/**
* @generated from field: repeated google.protobuf.Struct fields = 1;
*/
fields: Struct[] = [];
/**
* @generated from field: google.protobuf.Struct model = 2;
*/
model?: Struct;
constructor(data?: PartialMessage<GetFormResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetFormResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "fields", kind: "message", T: Struct, repeated: true },
{ no: 2, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetFormResponse {
return new GetFormResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetFormResponse {
return new GetFormResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetFormResponse {
return new GetFormResponse().fromJsonString(jsonString, options);
}
static equals(a: GetFormResponse | PlainMessage<GetFormResponse> | undefined, b: GetFormResponse | PlainMessage<GetFormResponse> | undefined): boolean {
return proto3.util.equals(GetFormResponse, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.GetModelRequest
*/
export class GetModelRequest extends Message<GetModelRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
constructor(data?: PartialMessage<GetModelRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetModelRequest";
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>): GetModelRequest {
return new GetModelRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetModelRequest {
return new GetModelRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetModelRequest {
return new GetModelRequest().fromJsonString(jsonString, options);
}
static equals(a: GetModelRequest | PlainMessage<GetModelRequest> | undefined, b: GetModelRequest | PlainMessage<GetModelRequest> | undefined): boolean {
return proto3.util.equals(GetModelRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.GetModelResponse
*/
export class GetModelResponse extends Message<GetModelResponse> {
/**
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
constructor(data?: PartialMessage<GetModelResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetModelResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetModelResponse {
return new GetModelResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetModelResponse {
return new GetModelResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetModelResponse {
return new GetModelResponse().fromJsonString(jsonString, options);
}
static equals(a: GetModelResponse | PlainMessage<GetModelResponse> | undefined, b: GetModelResponse | PlainMessage<GetModelResponse> | undefined): boolean {
return proto3.util.equals(GetModelResponse, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PutModelRequest
*/
export class PutModelRequest extends Message<PutModelRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
/**
* @generated from field: google.protobuf.Struct model = 2;
*/
model?: Struct;
constructor(data?: PartialMessage<PutModelRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PutModelRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutModelRequest {
return new PutModelRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutModelRequest {
return new PutModelRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutModelRequest {
return new PutModelRequest().fromJsonString(jsonString, options);
}
static equals(a: PutModelRequest | PlainMessage<PutModelRequest> | undefined, b: PutModelRequest | PlainMessage<PutModelRequest> | undefined): boolean {
return proto3.util.equals(PutModelRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PutModelResponse
*/
export class PutModelResponse extends Message<PutModelResponse> {
/**
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
constructor(data?: PartialMessage<PutModelResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PutModelResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutModelResponse {
return new PutModelResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutModelResponse {
return new PutModelResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutModelResponse {
return new PutModelResponse().fromJsonString(jsonString, options);
}
static equals(a: PutModelResponse | PlainMessage<PutModelResponse> | undefined, b: PutModelResponse | PlainMessage<PutModelResponse> | undefined): boolean {
return proto3.util.equals(PutModelResponse, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PutFormRequest
*/
export class PutFormRequest extends Message<PutFormRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
/**
* @generated from field: repeated google.protobuf.Struct fields = 2;
*/
fields: Struct[] = [];
constructor(data?: PartialMessage<PutFormRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PutFormRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "fields", kind: "message", T: Struct, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutFormRequest {
return new PutFormRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutFormRequest {
return new PutFormRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutFormRequest {
return new PutFormRequest().fromJsonString(jsonString, options);
}
static equals(a: PutFormRequest | PlainMessage<PutFormRequest> | undefined, b: PutFormRequest | PlainMessage<PutFormRequest> | undefined): boolean {
return proto3.util.equals(PutFormRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PutFormResponse
*/
export class PutFormResponse extends Message<PutFormResponse> {
/**
* @generated from field: repeated google.protobuf.Struct fields = 1;
*/
fields: Struct[] = [];
constructor(data?: PartialMessage<PutFormResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PutFormResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "fields", kind: "message", T: Struct, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutFormResponse {
return new PutFormResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutFormResponse {
return new PutFormResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutFormResponse {
return new PutFormResponse().fromJsonString(jsonString, options);
}
static equals(a: PutFormResponse | PlainMessage<PutFormResponse> | undefined, b: PutFormResponse | PlainMessage<PutFormResponse> | undefined): boolean {
return proto3.util.equals(PutFormResponse, a, b);
}
}

View File

@@ -1,35 +1,91 @@
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
// @generated from file holos/v1alpha1/platform.proto (package holos.v1alpha1, syntax proto3)
// @generated from file holos/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import { MethodKind } from "@bufbuild/protobuf";
import { AddPlatformRequest, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
import { AddPlatformRequest, AddPlatformResponse, GetFormRequest, GetFormResponse, GetModelRequest, GetModelResponse, GetPlatformRequest, GetPlatformResponse, ListPlatformsRequest, ListPlatformsResponse, PutModelRequest, PutModelResponse } from "./platform_pb.js";
/**
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
*/
export const getPlatforms = {
localName: "getPlatforms",
name: "GetPlatforms",
kind: MethodKind.Unary,
I: GetPlatformsRequest,
O: GetPlatformsResponse,
service: {
typeName: "holos.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.v1alpha1.PlatformService.AddPlatform
* @generated from rpc holos.platform.v1alpha1.PlatformService.AddPlatform
*/
export const addPlatform = {
localName: "addPlatform",
name: "AddPlatform",
kind: MethodKind.Unary,
I: AddPlatformRequest,
O: GetPlatformsResponse,
O: AddPlatformResponse,
service: {
typeName: "holos.v1alpha1.PlatformService"
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetPlatform
*/
export const getPlatform = {
localName: "getPlatform",
name: "GetPlatform",
kind: MethodKind.Unary,
I: GetPlatformRequest,
O: GetPlatformResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.ListPlatforms
*/
export const listPlatforms = {
localName: "listPlatforms",
name: "ListPlatforms",
kind: MethodKind.Unary,
I: ListPlatformsRequest,
O: ListPlatformsResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetForm
*/
export const getForm = {
localName: "getForm",
name: "GetForm",
kind: MethodKind.Unary,
I: GetFormRequest,
O: GetFormResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetModel
*/
export const getModel = {
localName: "getModel",
name: "GetModel",
kind: MethodKind.Unary,
I: GetModelRequest,
O: GetModelResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.PutModel
*/
export const putModel = {
localName: "putModel",
name: "PutModel",
kind: MethodKind.Unary,
I: PutModelRequest,
O: PutModelResponse,
service: {
typeName: "holos.platform.v1alpha1.PlatformService"
}
} as const;

View File

@@ -1,33 +1,69 @@
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
// @generated from file holos/v1alpha1/platform.proto (package holos.v1alpha1, syntax proto3)
// @generated from file holos/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import { AddPlatformRequest, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
import { AddPlatformRequest, AddPlatformResponse, GetFormRequest, GetFormResponse, GetModelRequest, GetModelResponse, GetPlatformRequest, GetPlatformResponse, ListPlatformsRequest, ListPlatformsResponse, PutModelRequest, PutModelResponse } from "./platform_pb.js";
import { MethodKind } from "@bufbuild/protobuf";
/**
* @generated from service holos.v1alpha1.PlatformService
* @generated from service holos.platform.v1alpha1.PlatformService
*/
export const PlatformService = {
typeName: "holos.v1alpha1.PlatformService",
typeName: "holos.platform.v1alpha1.PlatformService",
methods: {
/**
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
*/
getPlatforms: {
name: "GetPlatforms",
I: GetPlatformsRequest,
O: GetPlatformsResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.v1alpha1.PlatformService.AddPlatform
* @generated from rpc holos.platform.v1alpha1.PlatformService.AddPlatform
*/
addPlatform: {
name: "AddPlatform",
I: AddPlatformRequest,
O: GetPlatformsResponse,
O: AddPlatformResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetPlatform
*/
getPlatform: {
name: "GetPlatform",
I: GetPlatformRequest,
O: GetPlatformResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.ListPlatforms
*/
listPlatforms: {
name: "ListPlatforms",
I: ListPlatformsRequest,
O: ListPlatformsResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetForm
*/
getForm: {
name: "GetForm",
I: GetFormRequest,
O: GetFormResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.GetModel
*/
getModel: {
name: "GetModel",
I: GetModelRequest,
O: GetModelResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.platform.v1alpha1.PlatformService.PutModel
*/
putModel: {
name: "PutModel",
I: PutModelRequest,
O: PutModelResponse,
kind: MethodKind.Unary,
},
}

View File

@@ -1,15 +1,13 @@
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
// @generated from file holos/v1alpha1/platform.proto (package holos.v1alpha1, syntax proto3)
// @generated from file holos/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
import { Message, proto3 } from "@bufbuild/protobuf";
import { Timestamps } from "./timestamps_pb.js";
import { Creator } from "./user_pb.js";
import { Message, proto3, Struct } from "@bufbuild/protobuf";
/**
* @generated from message holos.v1alpha1.Platform
* @generated from message holos.platform.v1alpha1.Platform
*/
export class Platform extends Message<Platform> {
/**
@@ -20,24 +18,28 @@ export class Platform extends Message<Platform> {
id = "";
/**
* @generated from field: string name = 2;
* Organization ID resource owner.
*
* @generated from field: string org_id = 2;
*/
orgId = "";
/**
* name is the platform short name as a dns label.
*
* @generated from field: string name = 3;
*/
name = "";
/**
* @generated from field: string display_name = 3;
* @generated from field: string display_name = 4;
*/
displayName = "";
/**
* @generated from field: holos.v1alpha1.Timestamps timestamps = 4;
* @generated from field: holos.platform.v1alpha1.PlatformSpec spec = 5;
*/
timestamps?: Timestamps;
/**
* @generated from field: holos.v1alpha1.Creator creator = 5;
*/
creator?: Creator;
spec?: PlatformSpec;
constructor(data?: PartialMessage<Platform>) {
super();
@@ -45,13 +47,13 @@ export class Platform extends Message<Platform> {
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.Platform";
static readonly typeName = "holos.platform.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: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 3, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 4, name: "display_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 5, name: "spec", kind: "message", T: PlatformSpec },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Platform {
@@ -72,103 +74,126 @@ export class Platform extends Message<Platform> {
}
/**
* @generated from message holos.v1alpha1.GetPlatformsRequest
* @generated from message holos.platform.v1alpha1.PlatformSpec
*/
export class GetPlatformsRequest extends Message<GetPlatformsRequest> {
export class PlatformSpec extends Message<PlatformSpec> {
/**
* @generated from field: string org_id = 1;
* model represents the user-defined and user-supplied form field values.
*
* @generated from field: google.protobuf.Struct model = 1;
*/
orgId = "";
model?: Struct;
constructor(data?: PartialMessage<GetPlatformsRequest>) {
constructor(data?: PartialMessage<PlatformSpec>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.GetPlatformsRequest";
static readonly typeName = "holos.platform.v1alpha1.PlatformSpec";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetPlatformsRequest {
return new GetPlatformsRequest().fromBinary(bytes, options);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformSpec {
return new PlatformSpec().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetPlatformsRequest {
return new GetPlatformsRequest().fromJson(jsonValue, options);
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformSpec {
return new PlatformSpec().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetPlatformsRequest {
return new GetPlatformsRequest().fromJsonString(jsonString, options);
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformSpec {
return new PlatformSpec().fromJsonString(jsonString, options);
}
static equals(a: GetPlatformsRequest | PlainMessage<GetPlatformsRequest> | undefined, b: GetPlatformsRequest | PlainMessage<GetPlatformsRequest> | undefined): boolean {
return proto3.util.equals(GetPlatformsRequest, a, b);
static equals(a: PlatformSpec | PlainMessage<PlatformSpec> | undefined, b: PlatformSpec | PlainMessage<PlatformSpec> | undefined): boolean {
return proto3.util.equals(PlatformSpec, a, b);
}
}
/**
* @generated from message holos.v1alpha1.GetPlatformsResponse
* @generated from message holos.platform.v1alpha1.ListPlatformsRequest
*/
export class GetPlatformsResponse extends Message<GetPlatformsResponse> {
export class ListPlatformsRequest extends Message<ListPlatformsRequest> {
/**
* @generated from field: string org_id = 1;
*/
orgId = "";
constructor(data?: PartialMessage<ListPlatformsRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.ListPlatformsRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ListPlatformsRequest {
return new ListPlatformsRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ListPlatformsRequest {
return new ListPlatformsRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ListPlatformsRequest {
return new ListPlatformsRequest().fromJsonString(jsonString, options);
}
static equals(a: ListPlatformsRequest | PlainMessage<ListPlatformsRequest> | undefined, b: ListPlatformsRequest | PlainMessage<ListPlatformsRequest> | undefined): boolean {
return proto3.util.equals(ListPlatformsRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.ListPlatformsResponse
*/
export class ListPlatformsResponse extends Message<ListPlatformsResponse> {
/**
* @generated from field: repeated holos.v1alpha1.Platform platforms = 2;
* @generated from field: repeated holos.platform.v1alpha1.Platform platforms = 1;
*/
platforms: Platform[] = [];
constructor(data?: PartialMessage<GetPlatformsResponse>) {
constructor(data?: PartialMessage<ListPlatformsResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.GetPlatformsResponse";
static readonly typeName = "holos.platform.v1alpha1.ListPlatformsResponse";
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 {
return new GetPlatformsResponse().fromBinary(bytes, options);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ListPlatformsResponse {
return new ListPlatformsResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetPlatformsResponse {
return new GetPlatformsResponse().fromJson(jsonValue, options);
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ListPlatformsResponse {
return new ListPlatformsResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetPlatformsResponse {
return new GetPlatformsResponse().fromJsonString(jsonString, options);
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ListPlatformsResponse {
return new ListPlatformsResponse().fromJsonString(jsonString, options);
}
static equals(a: GetPlatformsResponse | PlainMessage<GetPlatformsResponse> | undefined, b: GetPlatformsResponse | PlainMessage<GetPlatformsResponse> | undefined): boolean {
return proto3.util.equals(GetPlatformsResponse, a, b);
static equals(a: ListPlatformsResponse | PlainMessage<ListPlatformsResponse> | undefined, b: ListPlatformsResponse | PlainMessage<ListPlatformsResponse> | undefined): boolean {
return proto3.util.equals(ListPlatformsResponse, a, b);
}
}
/**
* @generated from message holos.v1alpha1.AddPlatformRequest
* @generated from message holos.platform.v1alpha1.AddPlatformRequest
*/
export class AddPlatformRequest extends Message<AddPlatformRequest> {
/**
* @generated from field: string org_id = 1;
* @generated from field: holos.platform.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();
@@ -176,11 +201,9 @@ export class AddPlatformRequest extends Message<AddPlatformRequest> {
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.AddPlatformRequest";
static readonly typeName = "holos.platform.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 +223,354 @@ export class AddPlatformRequest extends Message<AddPlatformRequest> {
}
}
/**
* @generated from message holos.platform.v1alpha1.AddPlatformResponse
*/
export class AddPlatformResponse extends Message<AddPlatformResponse> {
/**
* @generated from field: repeated holos.platform.v1alpha1.Platform platform = 1;
*/
platform: Platform[] = [];
constructor(data?: PartialMessage<AddPlatformResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.AddPlatformResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform", kind: "message", T: Platform, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): AddPlatformResponse {
return new AddPlatformResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): AddPlatformResponse {
return new AddPlatformResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): AddPlatformResponse {
return new AddPlatformResponse().fromJsonString(jsonString, options);
}
static equals(a: AddPlatformResponse | PlainMessage<AddPlatformResponse> | undefined, b: AddPlatformResponse | PlainMessage<AddPlatformResponse> | undefined): boolean {
return proto3.util.equals(AddPlatformResponse, a, b);
}
}
/**
* @generated from message holos.platform.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.platform.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.platform.v1alpha1.GetPlatformResponse
*/
export class GetPlatformResponse extends Message<GetPlatformResponse> {
/**
* @generated from field: holos.platform.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.platform.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.platform.v1alpha1.GetFormRequest
*/
export class GetFormRequest extends Message<GetFormRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
constructor(data?: PartialMessage<GetFormRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetFormRequest";
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>): GetFormRequest {
return new GetFormRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetFormRequest {
return new GetFormRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetFormRequest {
return new GetFormRequest().fromJsonString(jsonString, options);
}
static equals(a: GetFormRequest | PlainMessage<GetFormRequest> | undefined, b: GetFormRequest | PlainMessage<GetFormRequest> | undefined): boolean {
return proto3.util.equals(GetFormRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.GetFormResponse
*/
export class GetFormResponse extends Message<GetFormResponse> {
/**
* model represents the persisted state of the form model.
*
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
/**
* fields represents FormlyFieldConfig[] encoded as a JSON array.
*
* @generated from field: repeated google.protobuf.Struct fields = 2;
*/
fields: Struct[] = [];
constructor(data?: PartialMessage<GetFormResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetFormResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
{ no: 2, name: "fields", kind: "message", T: Struct, repeated: true },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetFormResponse {
return new GetFormResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetFormResponse {
return new GetFormResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetFormResponse {
return new GetFormResponse().fromJsonString(jsonString, options);
}
static equals(a: GetFormResponse | PlainMessage<GetFormResponse> | undefined, b: GetFormResponse | PlainMessage<GetFormResponse> | undefined): boolean {
return proto3.util.equals(GetFormResponse, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.GetModelRequest
*/
export class GetModelRequest extends Message<GetModelRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
constructor(data?: PartialMessage<GetModelRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetModelRequest";
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>): GetModelRequest {
return new GetModelRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetModelRequest {
return new GetModelRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetModelRequest {
return new GetModelRequest().fromJsonString(jsonString, options);
}
static equals(a: GetModelRequest | PlainMessage<GetModelRequest> | undefined, b: GetModelRequest | PlainMessage<GetModelRequest> | undefined): boolean {
return proto3.util.equals(GetModelRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.GetModelResponse
*/
export class GetModelResponse extends Message<GetModelResponse> {
/**
* model represents the persisted state of the form model.
*
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
constructor(data?: PartialMessage<GetModelResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.GetModelResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetModelResponse {
return new GetModelResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetModelResponse {
return new GetModelResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetModelResponse {
return new GetModelResponse().fromJsonString(jsonString, options);
}
static equals(a: GetModelResponse | PlainMessage<GetModelResponse> | undefined, b: GetModelResponse | PlainMessage<GetModelResponse> | undefined): boolean {
return proto3.util.equals(GetModelResponse, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PutModelRequest
*/
export class PutModelRequest extends Message<PutModelRequest> {
/**
* @generated from field: string platform_id = 1;
*/
platformId = "";
/**
* @generated from field: google.protobuf.Struct model = 2;
*/
model?: Struct;
constructor(data?: PartialMessage<PutModelRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PutModelRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutModelRequest {
return new PutModelRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutModelRequest {
return new PutModelRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutModelRequest {
return new PutModelRequest().fromJsonString(jsonString, options);
}
static equals(a: PutModelRequest | PlainMessage<PutModelRequest> | undefined, b: PutModelRequest | PlainMessage<PutModelRequest> | undefined): boolean {
return proto3.util.equals(PutModelRequest, a, b);
}
}
/**
* @generated from message holos.platform.v1alpha1.PutModelResponse
*/
export class PutModelResponse extends Message<PutModelResponse> {
/**
* @generated from field: google.protobuf.Struct model = 1;
*/
model?: Struct;
constructor(data?: PartialMessage<PutModelResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.platform.v1alpha1.PutModelResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PutModelResponse {
return new PutModelResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PutModelResponse {
return new PutModelResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PutModelResponse {
return new PutModelResponse().fromJsonString(jsonString, options);
}
static equals(a: PutModelResponse | PlainMessage<PutModelResponse> | undefined, b: PutModelResponse | PlainMessage<PutModelResponse> | undefined): boolean {
return proto3.util.equals(PutModelResponse, a, b);
}
}

View File

@@ -0,0 +1,35 @@
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import { MethodKind } from "@bufbuild/protobuf";
import { EmptyRequest, EmptyResponse } from "./system_pb.js";
/**
* @generated from rpc holos.v1alpha1.SystemService.SeedDatabase
*/
export const seedDatabase = {
localName: "seedDatabase",
name: "SeedDatabase",
kind: MethodKind.Unary,
I: EmptyRequest,
O: EmptyResponse,
service: {
typeName: "holos.v1alpha1.SystemService"
}
} as const;
/**
* @generated from rpc holos.v1alpha1.SystemService.DropTables
*/
export const dropTables = {
localName: "dropTables",
name: "DropTables",
kind: MethodKind.Unary,
I: EmptyRequest,
O: EmptyResponse,
service: {
typeName: "holos.v1alpha1.SystemService"
}
} as const;

View File

@@ -0,0 +1,35 @@
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import { EmptyRequest, EmptyResponse } from "./system_pb.js";
import { MethodKind } from "@bufbuild/protobuf";
/**
* @generated from service holos.v1alpha1.SystemService
*/
export const SystemService = {
typeName: "holos.v1alpha1.SystemService",
methods: {
/**
* @generated from rpc holos.v1alpha1.SystemService.SeedDatabase
*/
seedDatabase: {
name: "SeedDatabase",
I: EmptyRequest,
O: EmptyResponse,
kind: MethodKind.Unary,
},
/**
* @generated from rpc holos.v1alpha1.SystemService.DropTables
*/
dropTables: {
name: "DropTables",
I: EmptyRequest,
O: EmptyResponse,
kind: MethodKind.Unary,
},
}
} as const;

View File

@@ -0,0 +1,70 @@
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
import { Message, proto3 } from "@bufbuild/protobuf";
/**
* @generated from message holos.v1alpha1.EmptyRequest
*/
export class EmptyRequest extends Message<EmptyRequest> {
constructor(data?: PartialMessage<EmptyRequest>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.EmptyRequest";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): EmptyRequest {
return new EmptyRequest().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EmptyRequest {
return new EmptyRequest().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EmptyRequest {
return new EmptyRequest().fromJsonString(jsonString, options);
}
static equals(a: EmptyRequest | PlainMessage<EmptyRequest> | undefined, b: EmptyRequest | PlainMessage<EmptyRequest> | undefined): boolean {
return proto3.util.equals(EmptyRequest, a, b);
}
}
/**
* @generated from message holos.v1alpha1.EmptyResponse
*/
export class EmptyResponse extends Message<EmptyResponse> {
constructor(data?: PartialMessage<EmptyResponse>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.v1alpha1.EmptyResponse";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): EmptyResponse {
return new EmptyResponse().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EmptyResponse {
return new EmptyResponse().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EmptyResponse {
return new EmptyResponse().fromJsonString(jsonString, options);
}
static equals(a: EmptyResponse | PlainMessage<EmptyResponse> | undefined, b: EmptyResponse | PlainMessage<EmptyResponse> | undefined): boolean {
return proto3.util.equals(EmptyResponse, a, b);
}
}

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>
@@ -35,7 +33,8 @@
</span>
<app-profile-button [claims$]="claims$"></app-profile-button>
</mat-toolbar>
<!-- Add Content Here -->
<router-outlet></router-outlet>
<main class="main-content">
<router-outlet></router-outlet>
</main>
</mat-sidenav-content>
</mat-sidenav-container>

View File

@@ -13,7 +13,7 @@
.mat-toolbar.mat-primary {
position: sticky;
top: 0;
z-index: 1;
z-index: 1000;
}
.toolbar-spacer {

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,40 @@
import { Inject, Injectable } from '@angular/core';
import { PlatformService as ConnectPlatformService } from '../gen/holos/v1alpha1/platform_connect';
import { Observable, filter, of, switchMap } from 'rxjs';
import { ObservableClient } from '../../connect/observable-client';
import { Platform, GetFormResponse, ListPlatformsRequest, PutModelRequest, PutModelResponse } from '../gen/holos/v1alpha1/platform_pb';
import { Organization } from '../gen/holos/v1alpha1/organization_pb';
import { JsonValue, Struct, } from '@bufbuild/protobuf';
@Injectable({
providedIn: 'root'
})
export class PlatformService {
listPlatforms(org: Observable<Organization>): Observable<Platform[]> {
return org.pipe(
switchMap(org => {
const req = new ListPlatformsRequest({ orgId: org.id })
return this.client.listPlatforms(req).pipe(
switchMap(resp => { return of(resp.platforms) })
)
})
)
}
getForm(id: string): Observable<GetFormResponse> {
return this.client.getForm({ platformId: id })
}
putModel(id: string, model: JsonValue): Observable<PutModelResponse> {
const req = new PutModelRequest({
platformId: id,
// "We recommend to use fromJson() to construct Struct literals" refer to
// https://github.com/bufbuild/protobuf-es/blob/main/docs/runtime_api.md#struct
model: Struct.fromJson(model),
})
return this.client.putModel(req)
}
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,28 @@
<div class="content-container">
<mat-tab-group>
<mat-tab label="Detail">
<div class="grid-container">
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
<formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
<button type="submit" mat-flat-button color="primary" [disabled]="!form.valid">Submit</button>
</form>
</div>
</mat-tab>
<mat-tab label="Model">
<div class="grid-container">
<pre>{{ model | json }}</pre>
</div>
</mat-tab>
<mat-tab label="Form State">
<div class="grid-container">
<pre>{{ options.formState | json }}</pre>
</div>
</mat-tab>
<mat-tab label="Fields">
<div class="grid-container">
<pre>{{ fields | json }}</pre>
</div>
</mat-tab>
</mat-tab-group>
</div>

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,91 @@
import { Component, Input, OnDestroy, inject } from '@angular/core';
import { Observable, Subject, shareReplay, takeUntil } from 'rxjs';
import { PlatformService } from '../../services/platform.service';
import { GetFormResponse } 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 { FormlyFieldConfig, FormlyFormOptions, FormlyModule } from '@ngx-formly/core';
import { MatButton } from '@angular/material/button';
import { MatDivider } from '@angular/material/divider';
import { JsonObject, JsonValue } from '@bufbuild/protobuf';
@Component({
selector: 'app-platform-detail',
standalone: true,
imports: [
AsyncPipe,
CommonModule,
FormlyMaterialModule,
FormlyModule,
MatButton,
MatDivider,
MatTab,
MatTabGroup,
ReactiveFormsModule,
],
templateUrl: './platform-detail.component.html',
styleUrl: './platform-detail.component.scss'
})
export class PlatformDetailComponent implements OnDestroy {
private platformService = inject(PlatformService);
private platformId: string = "";
private destroy$: Subject<any> = new Subject<any>();
form = new FormGroup({});
fields: FormlyFieldConfig[] = [];
model: JsonValue = {};
// Use form state to store the model for nested forms
// Refer to https://formly.dev/docs/examples/form-options/form-state/
options: FormlyFormOptions = {
formState: {
model: this.model,
},
};
private setModel(model: JsonValue) {
if (model) {
this.model = model
this.options.formState.model = model
}
}
onSubmit(model: JsonValue) {
if (this.form.valid) {
console.log(model)
this.platformService
.putModel(this.platformId, model)
.pipe(takeUntil(this.destroy$))
.subscribe(resp => {
if (resp.model !== undefined) {
this.setModel(resp.model.toJson())
}
})
}
}
@Input()
set id(platformId: string) {
this.platformId = platformId;
this.platformService
.getForm(platformId)
.pipe(takeUntil(this.destroy$))
.subscribe(resp => {
if (resp.model !== undefined) {
this.setModel(resp.model.toJson())
}
if (resp.fields !== undefined) {
// NOTE: We could mix functions into the json data via mapped fields,
// but consider carefully before doing so. Refer to
// https://formly.dev/docs/examples/other/json-powered
this.fields = resp.fields.map(field => field.toJson() as FormlyFieldConfig)
}
})
}
public ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.complete();
}
}

View File

@@ -0,0 +1,11 @@
<div class="grid-container">
<mat-nav-list>
<h3 mat-subheader>Platforms</h3>
<p>Select a platform to manage.</p>
@for (platform of (platforms$ | async); track platform.id) {
<a mat-list-item [routerLink]="['/platform', platform.id]">
{{ platform.displayName ? platform.displayName : platform.name }}
</a>
}
</mat-nav-list>
</div>

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.listPlatforms(this.org$.pipe(
filter((org): org is Organization => org !== undefined)
))
}
}

View File

@@ -0,0 +1,7 @@
<div class="card">
<h2 class="card-header">{{ props.label }}</h2>
<div class="card-body">
<p>{{ props.description }}</p>
<ng-container #fieldComponent></ng-container>
</div>
</div>

View File

@@ -0,0 +1,3 @@
.card {
margin-bottom: 20px;
}

View File

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

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
import { FieldWrapper } from '@ngx-formly/core';
@Component({
selector: 'formly-wrapper-holos-panel',
standalone: true,
imports: [],
templateUrl: './holos-panel-wrapper.component.html',
styleUrl: './holos-panel-wrapper.component.scss'
})
export class HolosPanelWrapperComponent extends FieldWrapper { }

View File

@@ -11,29 +11,335 @@ let Platform = formsv1.#Platform & {
description: "Organization config values are used to derive more specific configuration values throughout the platform."
fieldConfigs: {
// platform.org.name
name: props: {
label: "Name"
placeholder: "example"
description: "DNS label, e.g. 'example'"
// platform.spec.config.user.sections.org.fields.name
name: {
type: "input"
props: {
label: "Name"
// placeholder: "example" placeholder cannot be used with validation?
description: "DNS label, e.g. 'example'"
pattern: "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$"
minLength: 3
maxLength: 30
required: true
}
validation: messages: {
pattern: "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
}
// platform.org.domain
domain: props: {
label: "Domain"
placeholder: "example.com"
description: "DNS domain, e.g. 'example.com'"
// platform.spec.config.user.sections.org.fields.domain
domain: {
type: "input"
props: {
label: "Domain"
placeholder: "example.com"
minLength: 3
maxLength: 100
description: "DNS domain, e.g. 'example.com'"
required: true
}
}
// platform.org.displayName
displayName: props: {
label: "Display Name"
placeholder: "Example Organization"
description: "Display name, e.g. 'Example Organization'"
// platform.spec.config.user.sections.org.fields.displayName
displayName: {
type: "input"
props: {
label: "Display Name"
placeholder: "Example Organization"
description: "Display name, e.g. 'Example Organization'"
maxLength: 100
required: true
}
}
// platform.org.contactEmail
contactEmail: props: {
label: "Contact Email"
placeholder: "platform-team@example.com"
description: "Technical contact email address"
// platform.spec.config.user.sections.org.fields.contactEmail
contactEmail: {
type: "input"
props: {
label: "Contact Email"
placeholder: "platform-team@example.com"
description: "Technical contact email address"
required: true
}
}
}
}
sections: cloud: {
displayName: "Cloud Providers"
description: "Select the services that provide resources for the platform."
fieldConfigs: {
providers: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Providers"
description: "Select the cloud providers the platform builds upon."
multiple: true
selectAllOption: "Select All"
options: [
{value: "aws", label: "Amazon Web Services"},
{value: "gcp", label: "Google Cloud Platform"},
{value: "azure", label: "Microsoft Azure"},
{value: "cloudflare", label: "Cloudflare"},
{value: "github", label: "GitHub"},
{value: "ois", label: "Open Infrastructure Services"},
{value: "onprem", label: "On Premises", disabled: true},
]
}
}
}
}
sections: aws: {
displayName: "Amazon Web Services"
description: "Provide the information necessary for Holos to manage AWS resources to provide the platform."
expressions: hide: "!\(AWSSelected)"
fieldConfigs: {
primaryRoleARN: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Holos Admin Role ARN"
description: "Enter the AWS Role ARN Holos will use to bootstrap resources. For example, arn:aws:iam::123456789012:role/HolosAdminAccess"
pattern: "^arn:.*"
minLength: 4
required: true
}
validation: messages: {
pattern: "Must be a valid ARN. Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html"
}
}
regions: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Regions"
description: "Select the AWS regions this platform operates in."
multiple: true
required: true
selectAllOption: "Select All"
options: AWSRegions
}
}
}
}
sections: gcp: {
displayName: "Google Cloud Platform"
description: "Use this form to configure platform level GCP settings."
expressions: hide: "!\(GCPSelected)"
fieldConfigs: {
regions: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Regions"
description: "Select the GCP regions this platform operates in."
multiple: true
selectAllOption: "Select All"
// gcloud compute regions list --format=json | jq '.[] | {value: .name, label: .description}' regions.json | jq -s | cue export --out cue
options: GCPRegions
}
}
gcpProjectID: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Project ID"
description: "Enter the project id where the provisioner cluster resides."
pattern: "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$"
minLength: 6
maxLength: 30
required: true
}
validation: messages: {
pattern: "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
}
gcpProjectNumber: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Project Number"
// note type number here
type: "number"
description: "Enter the project number where the provisioner cluster resides."
pattern: "^[0-9]+$"
required: true
}
validation: messages: {
pattern: "Must be a valid project number."
}
}
provisionerCABundle: {
type: "input"
props: {
label: "Provisioner CA Bundle"
description: "Enter the provisioner cluster ca bundle. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'"
pattern: "^[0-9a-zA-Z]+=*$"
required: true
}
validation: messages: {
pattern: "Must be a base64 encoded pem encoded certificate bundle."
}
}
provisionerURL: {
type: "input"
props: {
label: "Provisioner URL"
description: "Enter the URL of the provisioner cluster API endpoint. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'"
pattern: "^https://.*$"
required: true
}
validation: messages: {
pattern: "Must be a https:// URL."
}
}
}
}
sections: cloudflare: {
displayName: "Cloudflare"
description: "Cloudflare is primarily used for DNS automation."
expressions: hide: "!" + CloudflareSelected
fieldConfigs: {
email: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Account Email"
description: "Enter the Cloudflare email address to manage DNS"
minLength: 3
required: true
}
}
}
}
sections: github: {
displayName: "GitHub"
description: "GitHub is primarily used to host Git repositories and execute Actions workflows."
expressions: hide: "!\(GitHubSelected)"
fieldConfigs: {
primaryOrg: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Organization"
description: "Enter the primary GitHub organization associed with the platform."
pattern: "^(?!-)(?!.*--)([a-zA-Z0-9]|-){1,39}$"
minLength: 1
maxLength: 39
required: true
}
validation: messages: {
pattern: "All characters must be either a hyphen or alphanumeric. Cannot start with a hyphen. Cannot include consecutive hyphens."
}
}
}
}
sections: backups: {
displayName: "Backups"
description: "Configure platform level data backup settings. Requires AWS."
fieldConfigs: {
s3bucket: {
// https://formly.dev/docs/api/ui/material/input
type: "select"
props: {
label: "S3 Bucket Region"
description: "Select the S3 Bucket Region."
multiple: true
options: AWSRegions
}
expressions: {
// Disable the control if AWS is not selected.
"props.disabled": "!" + AWSSelected
// Required if AWS is selected.
"props.required": AWSSelected
// Change the label depending on AWS
"props.description": AWSSelected + " ? '\(props.description)' : 'Enable AWS in the Cloud Provider section to configure backups.'"
}
}
}
}
_sections: privacy: {
displayName: "Data Privacy"
description: "Configure data privacy aspects of the platform."
fieldConfigs: {
country: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Planet"
description: "Juridiction of applicable data privacy laws."
options: [
{value: "mercury", label: "Mercury"},
{value: "venus", label: "Venus"},
{value: "earth", label: "Earth"},
{value: "mars", label: "Mars"},
{value: "jupiter", label: "Jupiter"},
{value: "saturn", label: "Saturn"},
{value: "uranus", label: "Uranus"},
{value: "neptune", label: "Neptune"},
]
}
}
regions: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Regions"
description: "Select the regions this platform operates in."
multiple: true
selectAllOption: "Select All"
options: [
{value: "us-east-2", label: "Ohio"},
{value: "us-west-2", label: "Oregon"},
{value: "eu-west-1", label: "Ireland"},
{value: "eu-west-2", label: "London", disabled: true},
]
}
}
}
}
_sections: terms: {
displayName: "Terms and Conditions"
description: "Example of a boolean checkbox."
fieldConfigs: {
// platform.spec.config.user.sections.terms.fields.didAgree
didAgree: {
type: "checkbox"
props: {
label: "Accept terms"
description: "In order to proceed, please accept terms"
pattern: "true"
required: true
}
validation: {
messages: {
pattern: "Please accept the terms"
}
}
}
}
}
@@ -41,3 +347,80 @@ let Platform = formsv1.#Platform & {
// Provide the output form fields
Platform.Form
let GCPRegions = [
{value: "africa-south1", label: "africa-south1"},
{value: "asia-east1", label: "asia-east1"},
{value: "asia-east2", label: "asia-east2"},
{value: "asia-northeast1", label: "asia-northeast1"},
{value: "asia-northeast2", label: "asia-northeast2"},
{value: "asia-northeast3", label: "asia-northeast3"},
{value: "asia-south1", label: "asia-south1"},
{value: "asia-south2", label: "asia-south2"},
{value: "asia-southeast1", label: "asia-southeast1"},
{value: "asia-southeast2", label: "asia-southeast2"},
{value: "australia-southeast1", label: "australia-southeast1"},
{value: "australia-southeast2", label: "australia-southeast2"},
{value: "europe-central2", label: "europe-central2"},
{value: "europe-north1", label: "europe-north1"},
{value: "europe-southwest1", label: "europe-southwest1"},
{value: "europe-west1", label: "europe-west1"},
{value: "europe-west10", label: "europe-west10"},
{value: "europe-west12", label: "europe-west12"},
{value: "europe-west2", label: "europe-west2"},
{value: "europe-west3", label: "europe-west3"},
{value: "europe-west4", label: "europe-west4"},
{value: "europe-west6", label: "europe-west6"},
{value: "europe-west8", label: "europe-west8"},
{value: "europe-west9", label: "europe-west9"},
{value: "me-central1", label: "me-central1"},
{value: "me-central2", label: "me-central2"},
{value: "me-west1", label: "me-west1"},
{value: "northamerica-northeast1", label: "northamerica-northeast1"},
{value: "northamerica-northeast2", label: "northamerica-northeast2"},
{value: "southamerica-east1", label: "southamerica-east1"},
{value: "southamerica-west1", label: "southamerica-west1"},
{value: "us-central1", label: "us-central1"},
{value: "us-east1", label: "us-east1"},
{value: "us-east4", label: "us-east4"},
{value: "us-east5", label: "us-east5"},
{value: "us-south1", label: "us-south1"},
{value: "us-west1", label: "us-west1"},
{value: "us-west2", label: "us-west2"},
{value: "us-west3", label: "us-west3"},
{value: "us-west4", label: "us-west4"},
]
let AWSRegions = [
{value: "us-east-1", label: "N. Virginia (us-east-1)"},
{value: "us-east-2", label: "Ohio (us-east-2)"},
{value: "us-west-1", label: "N. California (us-west-1)"},
{value: "us-west-2", label: "Oregon (us-west-2)"},
{value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"},
{value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"},
{value: "ca-central-1", label: "Canada (ca-central-1)"},
{value: "eu-north-1", label: "Stockholm (eu-north-1)"},
{value: "eu-west-1", label: "Ireland (eu-west-1)"},
{value: "eu-west-2", label: "London (eu-west-2)"},
{value: "eu-west-3", label: "Paris (eu-west-3)"},
{value: "eu-central-1", label: "Frankfurt (eu-central-1)"},
{value: "eu-south-1", label: "Milan (eu-south-1)"},
{value: "af-south-1", label: "Cape Town (af-south-1)"},
{value: "ap-northeast-1", label: "Tokyo (ap-northeast-1)"},
{value: "ap-northeast-2", label: "Seoul (ap-northeast-2)"},
{value: "ap-northeast-3", label: "Osaka (ap-northeast-3)"},
{value: "ap-southeast-1", label: "Singapore (ap-southeast-1)"},
{value: "ap-southeast-2", label: "Sydney (ap-southeast-2)"},
{value: "ap-east-1", label: "Hong Kong (ap-east-1)"},
{value: "ap-south-1", label: "Mumbai (ap-south-1)"},
{value: "me-south-1", label: "Bahrain (me-south-1)"},
{value: "sa-east-1", label: "São Paulo (sa-east-1)"},
{value: "cn-north-1", label: "Bejing (cn-north-1)"},
{value: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"},
{value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"},
]
let AWSSelected = "formState.model.cloud?.providers?.includes(\"aws\")"
let GCPSelected = "formState.model.cloud?.providers?.includes(\"gcp\")"
let GitHubSelected = "formState.model.cloud?.providers?.includes(\"github\")"
let CloudflareSelected = "formState.model.cloud?.providers?.includes(\"cloudflare\")"

View File

@@ -22,4 +22,18 @@ package holos
// #PlatformSpec represents configuration values defined by the platform
// designer. Config values are organized by section, then simple strings for
// each section.
#PlatformSpec: {[string]: {[string]: string | bool | [...string]}}
#PlatformSpec: {
config: [string]: _
config: user: #UserDefinedConfig
}
// #PlatformUserConfig represents configuration fields and values defined by the
// user.
#UserDefinedConfig: {
sections: [string]: fields: [string]: _
}
// #PlatformConfig represents the platform config data returned from the Holos API. Useful for cue vet.
#PlatformConfig: {
platform: spec: #PlatformSpec
}

View File

@@ -1 +1,20 @@
{"platform":{"spec":{"org":{"name":"ois"}}}}
{
"platform": {
"spec": {
"config": {
"user": {
"sections": {
"org": {
"fields": {
"contactEmail": "jeff@openinfrastructure.co",
"displayName": "Open Infrastructure Services LLC",
"domain": "ois.run",
"name": "ois"
}
}
}
}
}
}
}
}

View File

@@ -16,35 +16,49 @@ package v1alpha1
}
let Sections = sections
Form: spec: sections: [for s in Sections {s.output}]
// Collapse all sections into one fields list.
// Refer to https://formly.dev/docs/examples/other/nested-formly-forms
Form: spec: fields: [for s in Sections {s.wrapper}]
}
#PlatformFormSpec: {
sections: [...#ConfigSectionOutput]
fields: [...#FieldConfig]
}
// #ConfigSection represents a configuration section of the front end UI. For
// #ConfigSection represents a configuration section of the front end UI. For
// example, Organization config values. The fields of the section map to form
// input fields.
#ConfigSection: {
name: string // e.g. "org"
displayName: string // e.g. "Organization"
description: string
expressions: {[string]: string}
fieldConfigs: {[NAME=string]: #FieldConfig & {key: NAME}}
let Name = name
let DisplayName = displayName
let Description = description
let FieldConfigs = fieldConfigs
let Expressions = expressions
output: #ConfigSectionOutput & {
name: Name
displayName: DisplayName
description: Description
fieldConfigs: [for fc in FieldConfigs {fc}]
// Wrap the fields of the section into one FormlyFieldConfig
wrapper: #FieldConfig & {
key: name
// See our custom wrappers registered in app.config.ts
wrappers: ["holos-panel"]
props: label: displayName
props: description: Description
for k, v in Expressions {
expressions: "\(k)": v
}
// Might need to initialize the default value for a fieldGroup
// https://github.com/ngx-formly/ngx-formly/issues/3667
fieldGroup: [for fc in fieldConfigs {fc}]
}
}
// REMOVE
#ConfigSectionOutput: {
name: string
displayName: string
@@ -53,13 +67,68 @@ package v1alpha1
}
// Refer to https://formly.dev/docs/api/core#formlyfieldconfig
// Refer to https://formly.dev/docs/api/ui/material/select
#FieldConfig: {
key: string
type: "input"
key: string
// type is optional, may be a nested form which has no type field
type?: string | "input" | "select" | "checkbox"
// For nested forms, refer: to https://formly.dev/docs/examples/other/nested-formly-forms
wrappers?: [...string]
// Refer to: https://formly.dev/docs/api/ui/material/select#formlyselectprops
// and other input field select props.
props: {
label: string
placeholder: string
description: string
required: *true | false
#FormlySelectProps
label: string
type?: string
placeholder?: string
description: string
required?: *true | false
pattern?: string
minLength?: number
maxLength?: number
}
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L49-L64
// We support only the string form.
validation?: {
// Note, you can set messages for pattern, minLength, maxLength here.
messages?: [string]: string
}
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L66-L71
// We do not support validators because they must be javascript functions, not data.
validators?: "not supported"
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L115-L120
expressions?: [string]: string
hide?: true | false
// Required to populate protobuf value.
resetOnHide: *true | false
defaultValue?: _
className?: string
fieldGroup?: [...#FieldConfig]
focus?: true | *false
modelOptions?: {
debounce?: {
default: number
}
updateOn?: "change" | "blur" | "submit"
}
}
// Refer to https://formly.dev/docs/api/ui/material/select#formlyselectprops
#FormlySelectProps: {
disableOptionCentering?: true | false
multiple?: true | false
panelClass?: string
selectAllOption?: string
typeaheadDebounceInterval?: number
options?: [...{value: string | number | bool, label: string, disabled?: true | *false}]
// These could be used to set different keys for value and label in the
// options list, but we don't support that level of customization.
// They're here for documentation purposes only.
labelProp?: "label"
valueProp?: "value"
}

View File

@@ -9,32 +9,37 @@ package handler
import (
"context"
"fmt"
"log/slog"
"github.com/holos-run/holos/internal/ent"
"github.com/holos-run/holos/internal/server/middleware/logger"
)
// WithTx runs callbacks in a transaction as described in https://entgo.io/docs/transactions/#best-practices
func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
log := logger.FromContext(ctx)
tx, err := client.Tx(ctx)
if err != nil {
return err
}
defer func() {
if v := recover(); v != nil {
slog.ErrorContext(ctx, "panic", "v", v)
log.ErrorContext(ctx, "panic", "v", v)
_ = tx.Rollback()
panic(v)
}
}()
if err := fn(tx); err != nil {
if rerr := tx.Rollback(); rerr != nil {
err = fmt.Errorf("%w: rolling back transaction: %v", err, rerr)
log.ErrorContext(ctx, "could not roll back tx", "err", rerr)
err = fmt.Errorf("coult not roll back tx: %w: %w", rerr, err)
} else {
log.WarnContext(ctx, "rolled back failed tx", "err", err)
}
return err
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("committing transaction: %w", err)
log.ErrorContext(ctx, "could not commit transaction", "err", err)
return fmt.Errorf("could not commit: %w", err)
}
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/holos-run/holos/internal/ent"
"github.com/holos-run/holos/internal/ent/user"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/internal/logger"
"github.com/holos-run/holos/internal/server/middleware/authn"
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
"google.golang.org/protobuf/types/known/timestamppb"
@@ -66,12 +67,12 @@ func (h *OrganizationHandler) CreateCallerOrganization(
ctx context.Context,
req *connect.Request[holos.CreateCallerOrganizationRequest],
) (*connect.Response[holos.GetCallerOrganizationsResponse], error) {
log := logger.FromContext(ctx)
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
// todo get user by iss, sub
dbUser, err := getUser(ctx, h.db, authnID.Email())
dbUser, err := getUser(ctx, h.db, authnID.Issuer(), authnID.Subject())
if err != nil {
if ent.MaskNotFound(err) == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
@@ -90,14 +91,14 @@ func (h *OrganizationHandler) CreateCallerOrganization(
if err != nil {
return err
}
dbUser, err = dbUser.Update().
AddOrganizations(org).
Save(ctx)
return err
return tx.Organization.UpdateOne(org).AddUsers(dbUser).Exec(ctx)
})
if err != nil {
return nil, connect.NewError(connect.CodeInternal, errors.Wrap(err))
}
log = log.With("organization", org)
log.InfoContext(ctx, "created organization")
// TODO: prefetch organizations
dbOrgs, err := dbUser.QueryOrganizations().All(ctx)

View File

@@ -3,15 +3,17 @@ package handler
import (
"context"
"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"
entplatform "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"
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
"google.golang.org/protobuf/types/known/timestamppb"
psvc "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
// NewPlatformHandler returns a new PlatformService implementation.
@@ -24,23 +26,24 @@ type PlatformHandler struct {
db *ent.Client
}
func (h *PlatformHandler) GetPlatforms(
func (h *PlatformHandler) ListPlatforms(
ctx context.Context,
req *connect.Request[holos.GetPlatformsRequest],
) (*connect.Response[holos.GetPlatformsResponse], error) {
req *connect.Request[psvc.ListPlatformsRequest],
) (*connect.Response[psvc.ListPlatformsResponse], error) {
_, reqDBOrg, err := getAuthnUsersOrg(ctx, req.Msg.OrgId, h.db)
if err != nil {
return nil, errors.Wrap(err)
}
return getPlatformsResponse(reqDBOrg), nil
resp := &psvc.ListPlatformsResponse{Platforms: rpcPlatforms(reqDBOrg)}
return connect.NewResponse(resp), nil
}
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)
req *connect.Request[psvc.AddPlatformRequest],
) (*connect.Response[psvc.AddPlatformResponse], error) {
dbUser, dbOrg, err := getAuthnUsersOrg(ctx, req.Msg.Platform.OrgId, h.db)
if err != nil {
return nil, errors.Wrap(err)
}
@@ -48,31 +51,138 @@ 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).
Save(ctx)
if err != nil {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
}
resp := getPlatformsResponse(dbOrg)
resp.Msg.Platforms = append(resp.Msg.Platforms, PlatformToRPC(platform))
resp := &psvc.AddPlatformResponse{Platforms: rpcPlatforms(dbOrg)}
resp.Platforms = append(resp.Platforms, PlatformToRPC(platform))
return resp, nil
return connect.NewResponse(resp), nil
}
func PlatformToRPC(platform *ent.Platform) *holos.Platform {
return &holos.Platform{
func (h *PlatformHandler) getPlatform(ctx context.Context, id string, uid authn.Identity) (*ent.Platform, error) {
platformID, err := uuid.FromString(id)
if err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
}
p, err := h.db.Platform.Query().
Where(entplatform.ID(platformID)).
Where(entplatform.HasOrganizationWith(
organization.HasUsersWith(
user.Iss(uid.Issuer()),
user.Sub(uid.Subject()),
))).
Only(ctx)
if err != nil {
if ent.MaskNotFound(err) == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
} else {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
}
}
return p, nil
}
func (h *PlatformHandler) GetPlatform(ctx context.Context, req *connect.Request[psvc.GetPlatformRequest]) (*connect.Response[psvc.GetPlatformResponse], error) {
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
p, err := h.getPlatform(ctx, req.Msg.PlatformId, authnID)
if err != nil {
return nil, errors.Wrap(err)
}
return connect.NewResponse(&psvc.GetPlatformResponse{Platform: PlatformToRPC(p)}), nil
}
func (h *PlatformHandler) PutModel(ctx context.Context, req *connect.Request[psvc.PutModelRequest]) (*connect.Response[psvc.PutModelResponse], error) {
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
p, err := h.getPlatform(ctx, req.Msg.GetPlatformId(), authnID)
if err != nil {
return nil, errors.Wrap(err)
}
slog.WarnContext(ctx, "todo: validate the platform config against cue definitions", "action", "todo", "cue", len(p.Cue))
_, err = p.Update().
SetModel(&psvc.Model{Model: req.Msg.GetModel()}).
Save(ctx)
if err != nil {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
}
return connect.NewResponse(&psvc.PutModelResponse{Model: req.Msg.Model}), nil
}
func (h *PlatformHandler) GetModel(ctx context.Context, req *connect.Request[psvc.GetModelRequest]) (*connect.Response[psvc.GetModelResponse], error) {
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
p, err := h.getPlatform(ctx, req.Msg.PlatformId, authnID)
if err != nil {
return nil, errors.Wrap(err)
}
return connect.NewResponse(&psvc.GetModelResponse{Model: p.Model.Model}), nil
}
func (h *PlatformHandler) GetForm(ctx context.Context, req *connect.Request[psvc.GetFormRequest]) (*connect.Response[psvc.GetFormResponse], error) {
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
p, err := h.getPlatform(ctx, req.Msg.GetPlatformId(), authnID)
if err != nil {
return nil, errors.Wrap(err)
}
return connect.NewResponse(&psvc.GetFormResponse{Fields: p.Form.GetFields(), Model: p.Model.GetModel()}), nil
}
func (h *PlatformHandler) PutForm(ctx context.Context, req *connect.Request[psvc.PutFormRequest]) (*connect.Response[psvc.PutFormResponse], error) {
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
p, err := h.getPlatform(ctx, req.Msg.GetPlatformId(), authnID)
if err != nil {
return nil, errors.Wrap(err)
}
_, err = p.Update().
SetForm(&psvc.Form{Fields: req.Msg.GetFields()}).
Save(ctx)
if err != nil {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
}
resp := &psvc.PutFormResponse{Fields: req.Msg.GetFields()}
return connect.NewResponse(resp), nil
}
func PlatformToRPC(platform *ent.Platform) *psvc.Platform {
return &psvc.Platform{
Id: platform.ID.String(),
Name: platform.Name,
DisplayName: platform.DisplayName,
Timestamps: &holos.Timestamps{
CreatedAt: timestamppb.New(platform.CreatedAt),
UpdatedAt: timestamppb.New(platform.UpdatedAt),
},
Creator: &holos.Creator{
Id: platform.CreatorID.String(),
},
OrgId: platform.OrgID.String(),
Spec: &psvc.PlatformSpec{Model: platform.Model.GetModel()},
}
}
@@ -101,6 +211,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 {
@@ -123,15 +234,14 @@ func getAuthnUsersOrg(ctx context.Context, orgID string, db *ent.Client) (*ent.U
return dbUser, reqDBOrg, nil
}
func getPlatformsResponse(reqDBOrg *ent.Organization) *connect.Response[holos.GetPlatformsResponse] {
// one extra in case a new platform is appended.
rpcPlatforms := make([]*holos.Platform, 0, 1+len(reqDBOrg.Edges.Platforms))
for _, platform := range reqDBOrg.Edges.Platforms {
rpcPlatforms = append(rpcPlatforms, PlatformToRPC(platform))
func rpcPlatforms(reqDBOrg *ent.Organization) []*psvc.Platform {
if reqDBOrg == nil {
return nil
}
return connect.NewResponse(&holos.GetPlatformsResponse{
OrgId: reqDBOrg.ID.String(),
Platforms: rpcPlatforms,
})
// one extra in case a new platform is appended.
platforms := make([]*psvc.Platform, 0, 1+len(reqDBOrg.Edges.Platforms))
for _, platform := range reqDBOrg.Edges.Platforms {
platforms = append(platforms, PlatformToRPC(platform))
}
return platforms
}

View File

@@ -0,0 +1,901 @@
package handler
import (
"context"
"encoding/json"
"fmt"
"strings"
"connectrpc.com/connect"
"github.com/gofrs/uuid"
"github.com/holos-run/holos/internal/ent"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/internal/server/middleware/authn"
"github.com/holos-run/holos/internal/server/middleware/logger"
psvc "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
)
const AdminEmail = "jeff@openinfrastructure.co"
// NewSystemHandler returns a new SystemService implementation.
func NewSystemHandler(db *ent.Client) *SystemHandler {
return &SystemHandler{db: db}
}
// SystemHandler implements the PlatformService interface.
type SystemHandler struct {
db *ent.Client
}
func (h *SystemHandler) checkAdmin(ctx context.Context) error {
authnID, err := authn.FromContext(ctx)
if err != nil {
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
if authnID.Email() != AdminEmail {
err := fmt.Errorf("not an admin:\n\thave (%+v)\n\twant (%+v)", authnID.Email(), AdminEmail)
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
return nil
}
func (h *SystemHandler) DropTables(ctx context.Context, req *connect.Request[holos.EmptyRequest]) (*connect.Response[holos.EmptyResponse], error) {
if err := h.checkAdmin(ctx); err != nil {
return nil, err
}
log := logger.FromContext(ctx)
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
var n int
if n, err = tx.Platform.Delete().Exec(ctx); err != nil {
return errors.Wrap(err)
}
log.WarnContext(ctx, "deleted platforms", "count", n)
if n, err = tx.Organization.Delete().Exec(ctx); err != nil {
return errors.Wrap(err)
}
log.WarnContext(ctx, "deleted organizations", "count", n)
if n, err = tx.User.Delete().Exec(ctx); err != nil {
return errors.Wrap(err)
}
log.WarnContext(ctx, "deleted users", "count", n)
return nil
}); err != nil {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
}
return connect.NewResponse(&holos.EmptyResponse{}), nil
}
func (h *SystemHandler) SeedDatabase(ctx context.Context, req *connect.Request[holos.EmptyRequest]) (*connect.Response[holos.EmptyResponse], error) {
if err := h.checkAdmin(ctx); err != nil {
return nil, err
}
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
jeff, err := tx.User.Create().
SetID(uuid.FromStringOrNil("018f36fb-e3f2-7f7f-a72f-ce48eb16c82d")).
SetEmail("jeff@openinfrastructure.co").
SetIss("https://login.ois.run").
SetSub("261773693724656988").
SetName("Jeff McCune").
Save(ctx)
if err != nil {
return errors.Wrap(err)
}
nate, err := tx.User.Create().
SetEmail("nate@openinfrastructure.co").
SetIss("https://login.ois.run").
SetSub("261775487611699776").
SetName("Nate McCurdy").
Save(ctx)
if err != nil {
return errors.Wrap(err)
}
gary, err := tx.User.Create().
SetEmail("gary@openinfrastructure.co").
SetIss("https://login.ois.run").
SetSub("261775531836441152").
SetName("Gary Larizza").
Save(ctx)
if err != nil {
return errors.Wrap(err)
}
// Create the org
org, err := tx.Organization.Create().
SetID(uuid.FromStringOrNil("018f36fb-e3f7-7f7f-a1c5-c85fb735d215")).
SetName("ois").
SetDisplayName("Open Infrastructure Services").
SetCreator(jeff).
Save(ctx)
if err != nil {
return errors.Wrap(err)
}
// Add org memebers
org, err = org.Update().AddUsers(jeff, gary, nate).Save(ctx)
if err != nil {
return errors.Wrap(err)
}
var form psvc.Form
if err := json.Unmarshal([]byte(BareForm), &form); err != nil {
return errors.Wrap(err)
}
var model psvc.Model
if err := json.Unmarshal([]byte(Model), &model); err != nil {
return errors.Wrap(err)
}
// Add a platform
err = tx.Platform.Create().
SetID(uuid.FromStringOrNil("018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94")).
SetName("bare").
SetDisplayName("Bare Platform").
SetForm(&form).
SetModel(&model).
SetCreator(jeff).
SetOrgID(org.ID).
Exec(ctx)
if err != nil {
return errors.Wrap(err)
}
stuff := []string{"Jeff", "Gary", "Nate"}
for _, name := range stuff {
err := tx.Platform.Create().
SetName(strings.ToLower(name)).
SetDisplayName(name + "'s Platform").
SetForm(&form).
SetModel(&model).
SetCreator(jeff).
SetOrgID(org.ID).
Exec(ctx)
if err != nil {
return errors.Wrap(err)
}
}
return nil
}); err != nil {
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
}
return connect.NewResponse(&holos.EmptyResponse{}), nil
}
const Model = `{"model":{}}`
const BareForm = `
{
"fields": [
{
"key": "org",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Organization",
"description": "Organization config values are used to derive more specific configuration values throughout the platform."
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "name",
"type": "input",
"props": {
"label": "Name",
"description": "DNS label, e.g. 'example'",
"pattern": "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$",
"minLength": 3,
"maxLength": 30,
"required": true
},
"validation": {
"messages": {
"pattern": "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
},
"resetOnHide": true
},
{
"key": "domain",
"type": "input",
"props": {
"label": "Domain",
"placeholder": "example.com",
"minLength": 3,
"maxLength": 100,
"description": "DNS domain, e.g. 'example.com'",
"required": true
},
"resetOnHide": true
},
{
"key": "displayName",
"type": "input",
"props": {
"label": "Display Name",
"placeholder": "Example Organization",
"description": "Display name, e.g. 'Example Organization'",
"maxLength": 100,
"required": true
},
"resetOnHide": true
},
{
"key": "contactEmail",
"type": "input",
"props": {
"label": "Contact Email",
"placeholder": "platform-team@example.com",
"description": "Technical contact email address",
"required": true
},
"resetOnHide": true
}
]
},
{
"key": "cloud",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Cloud Providers",
"description": "Select the services that provide resources for the platform."
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "providers",
"type": "select",
"props": {
"label": "Select Providers",
"description": "Select the cloud providers the platform builds upon.",
"multiple": true,
"selectAllOption": "Select All",
"options": [
{
"value": "aws",
"label": "Amazon Web Services"
},
{
"value": "gcp",
"label": "Google Cloud Platform"
},
{
"value": "azure",
"label": "Microsoft Azure"
},
{
"value": "cloudflare",
"label": "Cloudflare"
},
{
"value": "github",
"label": "GitHub"
},
{
"value": "ois",
"label": "Open Infrastructure Services"
},
{
"value": "onprem",
"label": "On Premises",
"disabled": true
}
]
},
"resetOnHide": true
}
]
},
{
"key": "aws",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Amazon Web Services",
"description": "Provide the information necessary for Holos to manage AWS resources to provide the platform."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"aws\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "primaryRoleARN",
"type": "input",
"props": {
"label": "Holos Admin Role ARN",
"description": "Enter the AWS Role ARN Holos will use to bootstrap resources. For example, arn:aws:iam::123456789012:role/HolosAdminAccess",
"pattern": "^arn:.*",
"minLength": 4,
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a valid ARN. Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html"
}
},
"resetOnHide": true
},
{
"key": "regions",
"type": "select",
"props": {
"label": "Select Regions",
"description": "Select the AWS regions this platform operates in.",
"multiple": true,
"required": true,
"selectAllOption": "Select All",
"options": [
{
"value": "us-east-1",
"label": "N. Virginia (us-east-1)"
},
{
"value": "us-east-2",
"label": "Ohio (us-east-2)"
},
{
"value": "us-west-1",
"label": "N. California (us-west-1)"
},
{
"value": "us-west-2",
"label": "Oregon (us-west-2)"
},
{
"value": "us-gov-west1",
"label": "US GovCloud West (us-gov-west1)"
},
{
"value": "us-gov-east1",
"label": "US GovCloud East (us-gov-east1)"
},
{
"value": "ca-central-1",
"label": "Canada (ca-central-1)"
},
{
"value": "eu-north-1",
"label": "Stockholm (eu-north-1)"
},
{
"value": "eu-west-1",
"label": "Ireland (eu-west-1)"
},
{
"value": "eu-west-2",
"label": "London (eu-west-2)"
},
{
"value": "eu-west-3",
"label": "Paris (eu-west-3)"
},
{
"value": "eu-central-1",
"label": "Frankfurt (eu-central-1)"
},
{
"value": "eu-south-1",
"label": "Milan (eu-south-1)"
},
{
"value": "af-south-1",
"label": "Cape Town (af-south-1)"
},
{
"value": "ap-northeast-1",
"label": "Tokyo (ap-northeast-1)"
},
{
"value": "ap-northeast-2",
"label": "Seoul (ap-northeast-2)"
},
{
"value": "ap-northeast-3",
"label": "Osaka (ap-northeast-3)"
},
{
"value": "ap-southeast-1",
"label": "Singapore (ap-southeast-1)"
},
{
"value": "ap-southeast-2",
"label": "Sydney (ap-southeast-2)"
},
{
"value": "ap-east-1",
"label": "Hong Kong (ap-east-1)"
},
{
"value": "ap-south-1",
"label": "Mumbai (ap-south-1)"
},
{
"value": "me-south-1",
"label": "Bahrain (me-south-1)"
},
{
"value": "sa-east-1",
"label": "São Paulo (sa-east-1)"
},
{
"value": "cn-north-1",
"label": "Bejing (cn-north-1)"
},
{
"value": "cn-northwest-1",
"label": "Ningxia (cn-northwest-1)"
},
{
"value": "ap-southeast-3",
"label": "Jakarta (ap-southeast-3)"
}
]
},
"resetOnHide": true
}
]
},
{
"key": "gcp",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Google Cloud Platform",
"description": "Use this form to configure platform level GCP settings."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"gcp\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "regions",
"type": "select",
"props": {
"label": "Select Regions",
"description": "Select the GCP regions this platform operates in.",
"multiple": true,
"selectAllOption": "Select All",
"options": [
{
"value": "africa-south1",
"label": "africa-south1"
},
{
"value": "asia-east1",
"label": "asia-east1"
},
{
"value": "asia-east2",
"label": "asia-east2"
},
{
"value": "asia-northeast1",
"label": "asia-northeast1"
},
{
"value": "asia-northeast2",
"label": "asia-northeast2"
},
{
"value": "asia-northeast3",
"label": "asia-northeast3"
},
{
"value": "asia-south1",
"label": "asia-south1"
},
{
"value": "asia-south2",
"label": "asia-south2"
},
{
"value": "asia-southeast1",
"label": "asia-southeast1"
},
{
"value": "asia-southeast2",
"label": "asia-southeast2"
},
{
"value": "australia-southeast1",
"label": "australia-southeast1"
},
{
"value": "australia-southeast2",
"label": "australia-southeast2"
},
{
"value": "europe-central2",
"label": "europe-central2"
},
{
"value": "europe-north1",
"label": "europe-north1"
},
{
"value": "europe-southwest1",
"label": "europe-southwest1"
},
{
"value": "europe-west1",
"label": "europe-west1"
},
{
"value": "europe-west10",
"label": "europe-west10"
},
{
"value": "europe-west12",
"label": "europe-west12"
},
{
"value": "europe-west2",
"label": "europe-west2"
},
{
"value": "europe-west3",
"label": "europe-west3"
},
{
"value": "europe-west4",
"label": "europe-west4"
},
{
"value": "europe-west6",
"label": "europe-west6"
},
{
"value": "europe-west8",
"label": "europe-west8"
},
{
"value": "europe-west9",
"label": "europe-west9"
},
{
"value": "me-central1",
"label": "me-central1"
},
{
"value": "me-central2",
"label": "me-central2"
},
{
"value": "me-west1",
"label": "me-west1"
},
{
"value": "northamerica-northeast1",
"label": "northamerica-northeast1"
},
{
"value": "northamerica-northeast2",
"label": "northamerica-northeast2"
},
{
"value": "southamerica-east1",
"label": "southamerica-east1"
},
{
"value": "southamerica-west1",
"label": "southamerica-west1"
},
{
"value": "us-central1",
"label": "us-central1"
},
{
"value": "us-east1",
"label": "us-east1"
},
{
"value": "us-east4",
"label": "us-east4"
},
{
"value": "us-east5",
"label": "us-east5"
},
{
"value": "us-south1",
"label": "us-south1"
},
{
"value": "us-west1",
"label": "us-west1"
},
{
"value": "us-west2",
"label": "us-west2"
},
{
"value": "us-west3",
"label": "us-west3"
},
{
"value": "us-west4",
"label": "us-west4"
}
]
},
"resetOnHide": true
},
{
"key": "gcpProjectID",
"type": "input",
"props": {
"label": "Project ID",
"description": "Enter the project id where the provisioner cluster resides.",
"pattern": "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$",
"minLength": 6,
"maxLength": 30,
"required": true
},
"validation": {
"messages": {
"pattern": "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
},
"resetOnHide": true
},
{
"key": "gcpProjectNumber",
"type": "input",
"props": {
"label": "Project Number",
"type": "number",
"description": "Enter the project number where the provisioner cluster resides.",
"pattern": "^[0-9]+$",
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a valid project number."
}
},
"resetOnHide": true
},
{
"key": "provisionerCABundle",
"type": "input",
"props": {
"label": "Provisioner CA Bundle",
"description": "Enter the provisioner cluster ca bundle. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'",
"pattern": "^[0-9a-zA-Z]+=*$",
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a base64 encoded pem encoded certificate bundle."
}
},
"resetOnHide": true
},
{
"key": "provisionerURL",
"type": "input",
"props": {
"label": "Provisioner URL",
"description": "Enter the URL of the provisioner cluster API endpoint. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'",
"pattern": "^https://.*$",
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a https:// URL."
}
},
"resetOnHide": true
}
]
},
{
"key": "cloudflare",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Cloudflare",
"description": "Cloudflare is primarily used for DNS automation."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"cloudflare\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "email",
"type": "input",
"props": {
"label": "Account Email",
"description": "Enter the Cloudflare email address to manage DNS",
"minLength": 3,
"required": true
},
"resetOnHide": true
}
]
},
{
"key": "github",
"wrappers": [
"holos-panel"
],
"props": {
"label": "GitHub",
"description": "GitHub is primarily used to host Git repositories and execute Actions workflows."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"github\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "primaryOrg",
"type": "input",
"props": {
"label": "Organization",
"description": "Enter the primary GitHub organization associed with the platform.",
"pattern": "^(?!-)(?!.*--)([a-zA-Z0-9]|-){1,39}$",
"minLength": 1,
"maxLength": 39,
"required": true
},
"validation": {
"messages": {
"pattern": "All characters must be either a hyphen or alphanumeric. Cannot start with a hyphen. Cannot include consecutive hyphens."
}
},
"resetOnHide": true
}
]
},
{
"key": "backups",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Backups",
"description": "Configure platform level data backup settings. Requires AWS."
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "s3bucket",
"type": "select",
"props": {
"label": "S3 Bucket Region",
"description": "Select the S3 Bucket Region.",
"multiple": true,
"options": [
{
"value": "us-east-1",
"label": "N. Virginia (us-east-1)"
},
{
"value": "us-east-2",
"label": "Ohio (us-east-2)"
},
{
"value": "us-west-1",
"label": "N. California (us-west-1)"
},
{
"value": "us-west-2",
"label": "Oregon (us-west-2)"
},
{
"value": "us-gov-west1",
"label": "US GovCloud West (us-gov-west1)"
},
{
"value": "us-gov-east1",
"label": "US GovCloud East (us-gov-east1)"
},
{
"value": "ca-central-1",
"label": "Canada (ca-central-1)"
},
{
"value": "eu-north-1",
"label": "Stockholm (eu-north-1)"
},
{
"value": "eu-west-1",
"label": "Ireland (eu-west-1)"
},
{
"value": "eu-west-2",
"label": "London (eu-west-2)"
},
{
"value": "eu-west-3",
"label": "Paris (eu-west-3)"
},
{
"value": "eu-central-1",
"label": "Frankfurt (eu-central-1)"
},
{
"value": "eu-south-1",
"label": "Milan (eu-south-1)"
},
{
"value": "af-south-1",
"label": "Cape Town (af-south-1)"
},
{
"value": "ap-northeast-1",
"label": "Tokyo (ap-northeast-1)"
},
{
"value": "ap-northeast-2",
"label": "Seoul (ap-northeast-2)"
},
{
"value": "ap-northeast-3",
"label": "Osaka (ap-northeast-3)"
},
{
"value": "ap-southeast-1",
"label": "Singapore (ap-southeast-1)"
},
{
"value": "ap-southeast-2",
"label": "Sydney (ap-southeast-2)"
},
{
"value": "ap-east-1",
"label": "Hong Kong (ap-east-1)"
},
{
"value": "ap-south-1",
"label": "Mumbai (ap-south-1)"
},
{
"value": "me-south-1",
"label": "Bahrain (me-south-1)"
},
{
"value": "sa-east-1",
"label": "São Paulo (sa-east-1)"
},
{
"value": "cn-north-1",
"label": "Bejing (cn-north-1)"
},
{
"value": "cn-northwest-1",
"label": "Ningxia (cn-northwest-1)"
},
{
"value": "ap-southeast-3",
"label": "Jakarta (ap-southeast-3)"
}
]
},
"expressions": {
"props.disabled": "!formState.model.cloud?.providers?.includes(\"aws\")",
"props.required": "formState.model.cloud?.providers?.includes(\"aws\")",
"props.description": "formState.model.cloud?.providers?.includes(\"aws\") ? 'Select the S3 Bucket Region.' : 'Enable AWS in the Cloud Provider section to configure backups.'"
},
"resetOnHide": true
}
]
}
]
}
`

View File

@@ -56,7 +56,7 @@ func (h *UserHandler) GetCallerUser(
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
}
dbUser, err := getUser(ctx, h.db, authnID.Email())
dbUser, err := getUser(ctx, h.db, authnID.Issuer(), authnID.Subject())
if err != nil {
if ent.MaskNotFound(err) == nil {
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
@@ -68,10 +68,7 @@ func (h *UserHandler) GetCallerUser(
return res, nil
}
func (h *UserHandler) CreateCallerUser(
ctx context.Context,
req *connect.Request[holos.CreateCallerUserRequest],
) (*connect.Response[holos.CreateCallerUserResponse], error) {
func (h *UserHandler) createCallerUser(ctx context.Context) (*ent.User, error) {
authnID, err := authn.FromContext(ctx)
if err != nil {
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
@@ -87,6 +84,18 @@ func (h *UserHandler) CreateCallerUser(
return nil, err
}
return createdUser, nil
}
func (h *UserHandler) CreateCallerUser(
ctx context.Context,
req *connect.Request[holos.CreateCallerUserRequest],
) (*connect.Response[holos.CreateCallerUserResponse], error) {
createdUser, err := h.createCallerUser(ctx)
if err != nil {
return nil, err
}
res := connect.NewResponse(&holos.CreateCallerUserResponse{
User: UserToRPC(createdUser),
})
@@ -107,11 +116,16 @@ func UserToRPC(u *ent.User) *holos.User {
return &iamUser
}
func getUser(ctx context.Context, client *ent.Client, email string) (*ent.User, error) {
func getUser(ctx context.Context, client *ent.Client, iss string, sub string) (*ent.User, error) {
log := logger.FromContext(ctx)
user, err := client.User.Query().Where(user.Email(email)).Only(ctx)
user, err := client.User.Query().
Where(
user.Iss(iss),
user.Sub(sub),
).
Only(ctx)
if err != nil {
log.DebugContext(ctx, "could not get user", "err", err, "email", email)
log.DebugContext(ctx, "could not get user", "err", err, "iss", iss, "sub", sub)
return nil, errors.Wrap(err)
}
return user, nil

View File

@@ -17,6 +17,7 @@ import (
"github.com/holos-run/holos/internal/server/handler"
"github.com/holos-run/holos/internal/server/middleware/authn"
"github.com/holos-run/holos/internal/server/middleware/logger"
"github.com/holos-run/holos/service/gen/holos/platform/v1alpha1/platformconnect"
"github.com/holos-run/holos/service/gen/holos/v1alpha1/holosconnect"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/net/http2"
@@ -114,12 +115,14 @@ func (s *Server) registerConnectRpc() error {
s.handle(holosconnect.NewUserServiceHandler(handler.NewUserHandler(s.db), opts))
s.handle(holosconnect.NewOrganizationServiceHandler(handler.NewOrganizationHandler(s.db), opts))
s.handle(holosconnect.NewPlatformServiceHandler(handler.NewPlatformHandler(s.db), opts))
s.handle(platformconnect.NewPlatformServiceHandler(handler.NewPlatformHandler(s.db), opts))
s.handle(holosconnect.NewSystemServiceHandler(handler.NewSystemHandler(s.db), opts))
reflector := grpcreflect.NewStaticReflector(
holosconnect.UserServiceName,
holosconnect.OrganizationServiceName,
holosconnect.PlatformServiceName,
platformconnect.PlatformServiceName,
holosconnect.SystemServiceName,
)
s.mux.Handle(grpcreflect.NewHandlerV1(reflector))

View File

@@ -0,0 +1,111 @@
syntax = "proto3";
package holos.platform.v1alpha1;
option go_package = "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1;platform";
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
import "buf/validate/validate.proto";
import "google/protobuf/struct.proto";
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
message Platform {
// Unique id assigned by the server.
string id = 1;
// Organization ID resource owner.
string org_id = 2 [(buf.validate.field).string.uuid = true];
// name is the platform short name as a dns label.
string name = 3 [(buf.validate.field).string.max_len = 100];
string display_name = 4 [(buf.validate.field).string.max_len = 100];
PlatformSpec spec = 5;
}
message PlatformSpec {
// model represents the user-defined and user-supplied form field values.
google.protobuf.Struct model = 1;
}
// Form represents the Formly input form.
message Form {
// fields represents FormlyFieldConfig[] encoded as a JSON array.
repeated google.protobuf.Struct fields = 1;
}
// Model represents the values entered into the form, stored in the form's model
// in the web app, and persisted into the backend database. The model is
// ultimately intended as the input to platform rendering.
message Model {
google.protobuf.Struct model = 1;
}
message ListPlatformsRequest {
string org_id = 1 [(buf.validate.field).string.uuid = true];
}
message ListPlatformsResponse {
repeated Platform platforms = 1;
}
message AddPlatformRequest {
Platform platform = 1;
}
message AddPlatformResponse {
repeated Platform platforms = 1;
}
message GetPlatformRequest {
string platform_id = 1 [(buf.validate.field).string.uuid = true];
}
message GetPlatformResponse {
Platform platform = 1;
}
message GetFormRequest {
string platform_id = 1 [(buf.validate.field).string.uuid = true];
}
message GetFormResponse {
repeated google.protobuf.Struct fields = 1;
google.protobuf.Struct model = 2;
}
message GetModelRequest {
string platform_id = 1 [(buf.validate.field).string.uuid = true];
}
message GetModelResponse {
google.protobuf.Struct model = 1;
}
message PutModelRequest {
string platform_id = 1 [(buf.validate.field).string.uuid = true];
google.protobuf.Struct model = 2;
}
message PutModelResponse {
google.protobuf.Struct model = 1;
}
message PutFormRequest {
string platform_id = 1 [(buf.validate.field).string.uuid = true];
repeated google.protobuf.Struct fields = 2;
}
message PutFormResponse {
repeated google.protobuf.Struct fields = 1;
}
service PlatformService {
rpc AddPlatform(AddPlatformRequest) returns (AddPlatformResponse) {}
rpc GetPlatform(GetPlatformRequest) returns (GetPlatformResponse) {}
rpc ListPlatforms(ListPlatformsRequest) returns (ListPlatformsResponse) {}
rpc GetForm(GetFormRequest) returns (GetFormResponse) {}
rpc PutForm(PutFormRequest) returns (PutFormResponse) {}
rpc GetModel(GetModelRequest) returns (GetModelResponse) {}
rpc PutModel(PutModelRequest) returns (PutModelResponse) {}
}

View File

@@ -1,46 +0,0 @@
syntax = "proto3";
package holos.v1alpha1;
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
import "buf/validate/validate.proto";
import "holos/v1alpha1/timestamps.proto";
import "holos/v1alpha1/organization.proto";
import "holos/v1alpha1/user.proto";
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
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];
// platform spec here?
// platform spec form config here?
Timestamps timestamps = 4;
Creator creator = 5;
}
message GetPlatformsRequest {
string org_id = 1 [(buf.validate.field).string.uuid = true];
}
message GetPlatformsResponse {
string org_id = 1 [(buf.validate.field).string.uuid = true];
repeated Platform platforms = 2;
}
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];
}
service PlatformService {
rpc GetPlatforms(GetPlatformsRequest) returns (GetPlatformsResponse) {}
rpc AddPlatform(AddPlatformRequest) returns (GetPlatformsResponse) {}
}

View File

@@ -0,0 +1,20 @@
syntax = "proto3";
package holos.v1alpha1;
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
import "buf/validate/validate.proto";
import "holos/v1alpha1/timestamps.proto";
import "holos/v1alpha1/user.proto";
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
message EmptyRequest {}
message EmptyResponse {}
service SystemService {
rpc SeedDatabase(EmptyRequest) returns (EmptyResponse) {}
rpc DropTables(EmptyRequest) returns (EmptyResponse) {}
}

View File

@@ -1 +1 @@
70
73

View File

@@ -1 +1 @@
2
1