mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 08:44:58 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2df843bc98 | ||
|
|
be4d2c29a5 | ||
|
|
8ce88bf491 | ||
|
|
b05571a595 | ||
|
|
4edfc71d68 | ||
|
|
3049694a0a |
3
.github/workflows/release.yaml
vendored
3
.github/workflows/release.yaml
vendored
@@ -54,6 +54,9 @@ jobs:
|
||||
- name: List keys
|
||||
run: gpg -K
|
||||
|
||||
- name: Git diff
|
||||
run: git diff
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
|
||||
2
Makefile
2
Makefile
@@ -113,7 +113,7 @@ snapshot: ## Go release snapshot
|
||||
|
||||
.PHONY: buf
|
||||
buf: ## buf generate
|
||||
cd service && buf mod update
|
||||
cd service && buf dep update
|
||||
buf generate
|
||||
|
||||
.PHONY: tools
|
||||
|
||||
4
go.mod
4
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240401165935-b983156c5e99.1
|
||||
connectrpc.com/connect v1.16.0
|
||||
connectrpc.com/grpcreflect v1.2.0
|
||||
connectrpc.com/otelconnect v0.7.0
|
||||
connectrpc.com/validate v0.1.0
|
||||
cuelang.org/go v0.8.0
|
||||
entgo.io/ent v0.13.1
|
||||
@@ -30,6 +31,7 @@ require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/tools v0.20.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa
|
||||
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002
|
||||
honnef.co/go/tools v0.4.7
|
||||
k8s.io/api v0.29.2
|
||||
@@ -44,7 +46,6 @@ require (
|
||||
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
connectrpc.com/otelconnect v0.7.0 // indirect
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
@@ -248,7 +249,6 @@ require (
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/generate"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
@@ -31,8 +32,11 @@ func NewPlatform(cfg *holos.Config) *cobra.Command {
|
||||
cmd.Args = cobra.ExactArgs(1)
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
clientContext := holos.NewClientContext(ctx)
|
||||
client := client.New(client.NewConfig(cfg))
|
||||
|
||||
for _, name := range args {
|
||||
if err := generate.GeneratePlatform(ctx, name); err != nil {
|
||||
if err := generate.GeneratePlatform(ctx, client, clientContext.OrgID, name); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
cue "cuelang.org/go/cue/errors"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
)
|
||||
|
||||
// MakeMain makes a main function for the cli or tests.
|
||||
@@ -25,7 +27,8 @@ func MakeMain(options ...holos.Option) func() int {
|
||||
|
||||
// HandleError is the top level error handler that unwraps and logs errors.
|
||||
func HandleError(ctx context.Context, err error, hc *holos.Config) (exitCode int) {
|
||||
log := hc.NewTopLevelLogger()
|
||||
// Connect errors have codes, log them.
|
||||
log := hc.NewTopLevelLogger().With("code", connect.CodeOf(err))
|
||||
var cueErr cue.Error
|
||||
var errAt *errors.ErrorAt
|
||||
const msg = "could not execute"
|
||||
@@ -39,5 +42,24 @@ func HandleError(ctx context.Context, err error, hc *holos.Config) (exitCode int
|
||||
msg := cue.Details(cueErr, nil)
|
||||
_, _ = fmt.Fprint(hc.Stderr(), msg)
|
||||
}
|
||||
// connect errors have details and codes.
|
||||
// Refer to https://connectrpc.com/docs/go/errors
|
||||
if connectErr := new(connect.Error); errors.As(err, &connectErr) {
|
||||
for _, detail := range connectErr.Details() {
|
||||
msg, valueErr := detail.Value()
|
||||
if valueErr != nil {
|
||||
log.WarnContext(ctx, "could not decode error detail", "err", err, "type", detail.Type(), "note", "this usually means we don't have the schema for the protobuf message type")
|
||||
continue
|
||||
}
|
||||
if info, ok := msg.(*errdetails.ErrorInfo); ok {
|
||||
logDetail := log.With("reason", info.GetReason(), "domain", info.GetDomain())
|
||||
for k, v := range info.GetMetadata() {
|
||||
logDetail = logDetail.With(k, v)
|
||||
}
|
||||
logDetail.ErrorContext(ctx, info.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
58
internal/cli/push/push.go
Normal file
58
internal/cli/push/push.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Package push pushes resources to the holos api server.
|
||||
package push
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/push"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("push")
|
||||
cmd.Short = "push resources to holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
cmd.AddCommand(NewPlatform(config))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatform(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("platform")
|
||||
|
||||
cmd.Short = "push platform resources to holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
cmd.AddCommand(NewPlatformForm(cfg))
|
||||
// cmd.AddCommand(NewPlatformModel(cfg))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatformForm(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("form")
|
||||
cmd.Short = "push platform form to holos server"
|
||||
cmd.Args = cobra.MinimumNArgs(1)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
if ctx == nil {
|
||||
return errors.Wrap(errors.New("cannot execute: no context"))
|
||||
}
|
||||
for _, name := range args {
|
||||
if err := push.PlatformForm(ctx, name); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
36
internal/cli/register/register.go
Normal file
36
internal/cli/register/register.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Package register provides user registration via the command line.
|
||||
package register
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/register"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// New returns a new register command.
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("register")
|
||||
cmd.Short = "register with holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
cmd.AddCommand(NewUser(config))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewUser returns a command to register a user with holos server.
|
||||
func NewUser(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("user")
|
||||
cmd.Short = "user registration workflow"
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
return register.User(ctx, cfg)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"github.com/holos-run/holos/internal/cli/login"
|
||||
"github.com/holos-run/holos/internal/cli/logout"
|
||||
"github.com/holos-run/holos/internal/cli/preflight"
|
||||
"github.com/holos-run/holos/internal/cli/push"
|
||||
"github.com/holos-run/holos/internal/cli/register"
|
||||
"github.com/holos-run/holos/internal/cli/render"
|
||||
"github.com/holos-run/holos/internal/cli/rpc"
|
||||
"github.com/holos-run/holos/internal/cli/token"
|
||||
@@ -68,6 +70,8 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
rootCmd.AddCommand(token.New(cfg))
|
||||
rootCmd.AddCommand(rpc.New(cfg))
|
||||
rootCmd.AddCommand(generate.New(cfg))
|
||||
rootCmd.AddCommand(register.New(cfg))
|
||||
rootCmd.AddCommand(push.New(cfg))
|
||||
|
||||
// Maybe not needed?
|
||||
rootCmd.AddCommand(txtar.New(cfg))
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewPlatformModel(cfg *Config) *cobra.Command {
|
||||
cmd := command.New("platform-model")
|
||||
cmd.Short = "get the platform model"
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
ctx := cmd.Root().Context()
|
||||
log := logger.FromContext(ctx)
|
||||
// client := platformconnect.NewPlatformServiceClient(token.NewClient(cfg.token), cfg.client.Server())
|
||||
client := platformconnect.NewPlatformServiceClient(token.NewClient(cfg.token), cfg.client.Server())
|
||||
|
||||
51
internal/client/client.go
Normal file
51
internal/client/client.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Package client provides configuration and convenience methods for making API calls to the holos server.
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/holos-run/holos/internal/token"
|
||||
"github.com/holos-run/holos/service/gen/holos/organization/v1alpha1/organizationconnect"
|
||||
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
|
||||
"github.com/holos-run/holos/service/gen/holos/platform/v1alpha1/platformconnect"
|
||||
"github.com/holos-run/holos/service/gen/holos/user/v1alpha1/userconnect"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
)
|
||||
|
||||
func New(cfg *Config) *Client {
|
||||
t := token.NewClient(cfg.Token())
|
||||
s := cfg.Client().Server()
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
usrSvc: userconnect.NewUserServiceClient(t, s),
|
||||
orgSvc: organizationconnect.NewOrganizationServiceClient(t, s),
|
||||
pltSvc: platformconnect.NewPlatformServiceClient(t, s),
|
||||
}
|
||||
}
|
||||
|
||||
// Client provides convenience methods for making API calls to the holos server.
|
||||
type Client struct {
|
||||
cfg *Config
|
||||
usrSvc userconnect.UserServiceClient
|
||||
pltSvc platformconnect.PlatformServiceClient
|
||||
orgSvc organizationconnect.OrganizationServiceClient
|
||||
}
|
||||
|
||||
func (c *Client) Platforms(ctx context.Context, orgID string) ([]*platform.Platform, error) {
|
||||
if c == nil {
|
||||
return nil, errors.New("no service client")
|
||||
}
|
||||
req := &platform.ListPlatformsRequest{
|
||||
OrgId: orgID,
|
||||
FieldMask: &fieldmaskpb.FieldMask{
|
||||
Paths: []string{"id", "name", "displayName"},
|
||||
},
|
||||
}
|
||||
resp, err := c.pltSvc.ListPlatforms(ctx, connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Msg.GetPlatforms(), nil
|
||||
}
|
||||
51
internal/client/config.go
Normal file
51
internal/client/config.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Package client provides client configuration for the holos cli.
|
||||
package client
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/token"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
holos *holos.Config
|
||||
client *holos.ClientConfig
|
||||
token *token.Config
|
||||
}
|
||||
|
||||
func (c *Config) ClientFlagSet() *flag.FlagSet {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.client.FlagSet()
|
||||
}
|
||||
|
||||
func (c *Config) TokenFlagSet() *flag.FlagSet {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.token.FlagSet()
|
||||
}
|
||||
|
||||
func (c *Config) Token() *token.Config {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.token
|
||||
}
|
||||
|
||||
func (c *Config) Client() *holos.ClientConfig {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.client
|
||||
}
|
||||
|
||||
func NewConfig(cfg *holos.Config) *Config {
|
||||
return &Config{
|
||||
holos: cfg,
|
||||
client: holos.NewClientConfig(),
|
||||
token: token.NewConfig(),
|
||||
}
|
||||
}
|
||||
2027
internal/frontend/holos/package-lock.json
generated
2027
internal/frontend/holos/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "^17.3.4",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@bufbuild/buf": "^1.31.0",
|
||||
"@bufbuild/buf": "^1.32.0",
|
||||
"@bufbuild/protoc-gen-es": "^1.9.0",
|
||||
"@connectrpc/protoc-gen-connect-es": "^1.4.0",
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.4.0",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { CreateUserRequest, CreateUserResponse, GetUserRequest, GetUserResponse } from "./user_service_pb.js";
|
||||
import { CreateUserRequest, CreateUserResponse, GetUserRequest, GetUserResponse, RegisterUserRequest, RegisterUserResponse } from "./user_service_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
@@ -36,6 +36,17 @@ export const UserService = {
|
||||
O: GetUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* Register an user and initialize an organization, bare platform, and reference platform.
|
||||
*
|
||||
* @generated from rpc holos.user.v1alpha1.UserService.RegisterUser
|
||||
*/
|
||||
registerUser: {
|
||||
name: "RegisterUser",
|
||||
I: RegisterUserRequest,
|
||||
O: RegisterUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialM
|
||||
import { FieldMask, Message, proto3 } from "@bufbuild/protobuf";
|
||||
import { User } from "./user_pb.js";
|
||||
import { UserRef } from "../../object/v1alpha1/object_pb.js";
|
||||
import { Organization } from "../../organization/v1alpha1/organization_pb.js";
|
||||
|
||||
/**
|
||||
* Create a User from the oidc id token claims or the provided user. Each one
|
||||
@@ -172,3 +173,118 @@ export class GetUserResponse extends Message<GetUserResponse> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a User from the oidc id token claims or the provided user. Each one
|
||||
* of subject, email, and user id must be globally unique.
|
||||
*
|
||||
* @generated from message holos.user.v1alpha1.RegisterUserRequest
|
||||
*/
|
||||
export class RegisterUserRequest extends Message<RegisterUserRequest> {
|
||||
/**
|
||||
* User resource to create. If absent, the server populates User fields with
|
||||
* the oidc id token claims of the authenticated request.
|
||||
* NOTE: The server may ignore this request field and register the user solely
|
||||
* from authenticated identity claims.
|
||||
*
|
||||
* @generated from field: optional holos.user.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* Mask of the user fields to include in the response.
|
||||
*
|
||||
* @generated from field: optional google.protobuf.FieldMask user_mask = 2;
|
||||
*/
|
||||
userMask?: FieldMask;
|
||||
|
||||
/**
|
||||
* Organization resource to create. If absent, the server generates an
|
||||
* organization based on the user fields.
|
||||
* NOTE: The server may ignore this request field and register the
|
||||
* organization solely from authenticated identity claims.
|
||||
*
|
||||
* @generated from field: optional holos.organization.v1alpha1.Organization organization = 3;
|
||||
*/
|
||||
organization?: Organization;
|
||||
|
||||
/**
|
||||
* Mask of the organization fields to include in the response.
|
||||
*
|
||||
* @generated from field: optional google.protobuf.FieldMask organization_mask = 4;
|
||||
*/
|
||||
organizationMask?: FieldMask;
|
||||
|
||||
constructor(data?: PartialMessage<RegisterUserRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.user.v1alpha1.RegisterUserRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User, opt: true },
|
||||
{ no: 2, name: "user_mask", kind: "message", T: FieldMask, opt: true },
|
||||
{ no: 3, name: "organization", kind: "message", T: Organization, opt: true },
|
||||
{ no: 4, name: "organization_mask", kind: "message", T: FieldMask, opt: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RegisterUserRequest {
|
||||
return new RegisterUserRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RegisterUserRequest {
|
||||
return new RegisterUserRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RegisterUserRequest {
|
||||
return new RegisterUserRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: RegisterUserRequest | PlainMessage<RegisterUserRequest> | undefined, b: RegisterUserRequest | PlainMessage<RegisterUserRequest> | undefined): boolean {
|
||||
return proto3.util.equals(RegisterUserRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.user.v1alpha1.RegisterUserResponse
|
||||
*/
|
||||
export class RegisterUserResponse extends Message<RegisterUserResponse> {
|
||||
/**
|
||||
* @generated from field: holos.user.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* @generated from field: holos.organization.v1alpha1.Organization organization = 2;
|
||||
*/
|
||||
organization?: Organization;
|
||||
|
||||
constructor(data?: PartialMessage<RegisterUserResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.user.v1alpha1.RegisterUserResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
{ no: 2, name: "organization", kind: "message", T: Organization },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RegisterUserResponse {
|
||||
return new RegisterUserResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RegisterUserResponse {
|
||||
return new RegisterUserResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RegisterUserResponse {
|
||||
return new RegisterUserResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: RegisterUserResponse | PlainMessage<RegisterUserResponse> | undefined, b: RegisterUserResponse | PlainMessage<RegisterUserResponse> | undefined): boolean {
|
||||
return proto3.util.equals(RegisterUserResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,16 @@ package generate
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
|
||||
)
|
||||
|
||||
//go:embed all:platforms
|
||||
@@ -35,13 +38,46 @@ func Platforms() []string {
|
||||
|
||||
// GeneratePlatform writes the cue code for a platform to the local working
|
||||
// directory.
|
||||
func GeneratePlatform(ctx context.Context, name string) error {
|
||||
func GeneratePlatform(ctx context.Context, rpc *client.Client, orgID string, name string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
// Check for a valid platform
|
||||
platformPath := filepath.Join(root, name)
|
||||
if !dirExists(platforms, platformPath) {
|
||||
return errors.Wrap(fmt.Errorf("cannot generate: have: [%s] want: %+v", name, Platforms()))
|
||||
}
|
||||
|
||||
// Link the local platform the SaaS platform ID.
|
||||
rpcPlatforms, err := rpc.Platforms(ctx, orgID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
var rpcPlatform *platform.Platform
|
||||
for _, p := range rpcPlatforms {
|
||||
if p.GetName() == name {
|
||||
rpcPlatform = p
|
||||
break
|
||||
}
|
||||
}
|
||||
if rpcPlatform == nil {
|
||||
return errors.Wrap(errors.New("cannot generate: platform not found in the holos server"))
|
||||
}
|
||||
|
||||
// Write the platform data.
|
||||
data, err := json.MarshalIndent(rpcPlatform, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if len(data) > 0 {
|
||||
data = append(data, '\n')
|
||||
}
|
||||
log = log.With("platform_id", rpcPlatform.GetId())
|
||||
path := "platform.metadata.json"
|
||||
if err := os.WriteFile(path, data, 0644); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not write platform metadata: %w", err))
|
||||
}
|
||||
log.InfoContext(ctx, "wrote "+path, "path", filepath.Join(getCwd(ctx), path))
|
||||
|
||||
// Copy the cue.mod directory
|
||||
if err := copyEmbedFS(ctx, platforms, filepath.Join(root, "cue.mod"), "cue.mod"); err != nil {
|
||||
return errors.Wrap(err)
|
||||
@@ -52,6 +88,8 @@ func GeneratePlatform(ctx context.Context, name string) error {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "generated platform "+name, "path", getCwd(ctx))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -95,3 +133,17 @@ func copyEmbedFS(ctx context.Context, srcFS embed.FS, srcPath, dstPath string) e
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getCwd(ctx context.Context) string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WarnContext(ctx, "could not get working directory", "err", err)
|
||||
return "."
|
||||
}
|
||||
abs, err := filepath.Abs(cwd)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WarnContext(ctx, "could not get absolute path", "err", err)
|
||||
return cwd
|
||||
}
|
||||
return abs
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forms
|
||||
|
||||
import v1 "github.com/holos-run/holos/v1alpha1"
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
// Provides a concrete v1.#Form
|
||||
FormBuilder.Output
|
||||
@@ -24,22 +24,12 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
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."
|
||||
pattern: "It must be \(props.minLength) to \(props.maxLength) lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
minLength: "Must be at least \(props.minLength) characters"
|
||||
maxLength: "Must be at most \(props.maxLength) characters"
|
||||
}
|
||||
}
|
||||
|
||||
// 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.spec.config.user.sections.org.fields.displayName
|
||||
displayName: {
|
||||
type: "input"
|
||||
@@ -51,16 +41,6 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +140,9 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
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."
|
||||
pattern: "It must be \(props.minLength) to \(props.maxLength) lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
minLength: "Must be at least \(props.minLength) characters."
|
||||
maxLength: "Must be at most \(props.maxLength) characters."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,32 +234,6 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let GCPRegions = [
|
||||
|
||||
@@ -1 +1 @@
|
||||
module: "github.com/holos-run/holos/internal/platforms/bare"
|
||||
module: "user.holos.run/platform"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package holos
|
||||
|
||||
// #ClusterName is the --cluster-name flag value provided by the holos cli.
|
||||
#ClusterName: string @tag(cluster, type=string)
|
||||
@@ -0,0 +1,25 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
let PLATFORM = {message: "TODO: Load the platform from the API."}
|
||||
|
||||
// Provide a BuildPlan to the holos cli to render k8s api objects.
|
||||
v1.#BuildPlan & {
|
||||
spec: components: resources: platformConfigmap: {
|
||||
metadata: name: "platform-configmap"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
}
|
||||
}
|
||||
|
||||
// OBJECTS represents the kubernetes api objects to manage.
|
||||
let OBJECTS = v1.#APIObjects & {
|
||||
apiObjects: ConfigMap: platform: {
|
||||
metadata: {
|
||||
name: "platform"
|
||||
namespace: "default"
|
||||
}
|
||||
data: platform: yaml.Marshal(PLATFORM)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
package forms
|
||||
|
||||
import v1 "github.com/holos-run/holos/v1alpha1"
|
||||
|
||||
// Provides a concrete v1.#Form
|
||||
FormBuilder.Output
|
||||
|
||||
let FormBuilder = v1.#FormBuilder & {
|
||||
Sections: org: {
|
||||
displayName: "Organization"
|
||||
description: "Organization config values are used to derive more specific configuration values throughout the platform."
|
||||
|
||||
fieldConfigs: {
|
||||
// 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.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.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.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.'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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\")"
|
||||
7
internal/generate/platforms/example/readme.md
Normal file
7
internal/generate/platforms/example/readme.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Example Platform
|
||||
|
||||
| Folder | Description |
|
||||
| - | - |
|
||||
| forms | Contains Platform and Project form and model definitions |
|
||||
| platform | Contains the Platform resource that defines how to render the configuration for all Platform Components |
|
||||
| components | Contains BuildPlan resources which define how to render individual Platform Components |
|
||||
81
internal/holos/clientcontext.go
Normal file
81
internal/holos/clientcontext.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package holos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
func NewClientContext(ctx context.Context) *ClientContext {
|
||||
cc := &ClientContext{}
|
||||
if cc.Exists() {
|
||||
if err := cc.Load(ctx); err != nil {
|
||||
logger.FromContext(ctx).WarnContext(ctx, "could not load client context", "err", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return cc
|
||||
}
|
||||
|
||||
// ClientContext represents the context the holos api is working in. Used to
|
||||
// store and recall values from the filesystem.
|
||||
type ClientContext struct {
|
||||
// OrgID is the organization id of the current context.
|
||||
OrgID string `json:"org_id"`
|
||||
// UserID is the user id of the current context.
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
func (cc *ClientContext) Save(ctx context.Context) error {
|
||||
log := logger.FromContext(ctx)
|
||||
config := cc.configFile()
|
||||
data, err := json.MarshalIndent(cc, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(config, data, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
log.DebugContext(ctx, "saved", "path", config, "bytes", len(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cc *ClientContext) Load(ctx context.Context) error {
|
||||
log := logger.FromContext(ctx)
|
||||
config := cc.configFile()
|
||||
data, err := os.ReadFile(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, cc); err != nil {
|
||||
return err
|
||||
}
|
||||
log.DebugContext(ctx, "loaded", "path", config, "bytes", len(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists returns true if the client context file exists.
|
||||
func (cc *ClientContext) Exists() bool {
|
||||
_, err := os.Stat(cc.configFile())
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (cc *ClientContext) configFile() string {
|
||||
config := "client-context.json"
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
dir := filepath.Join(home, ".holos")
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
slog.Warn("could not mkdir", "path", dir, "err", err)
|
||||
}
|
||||
config = filepath.Join(home, ".holos", config)
|
||||
}
|
||||
return config
|
||||
}
|
||||
8
internal/push/push.go
Normal file
8
internal/push/push.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Package push pushes resources to the holos api server.
|
||||
package push
|
||||
|
||||
import "context"
|
||||
|
||||
func PlatformForm(ctx context.Context, path string) error {
|
||||
return nil
|
||||
}
|
||||
106
internal/register/register.go
Normal file
106
internal/register/register.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package register
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
"github.com/holos-run/holos/internal/token"
|
||||
org "github.com/holos-run/holos/service/gen/holos/organization/v1alpha1"
|
||||
"github.com/holos-run/holos/service/gen/holos/organization/v1alpha1/organizationconnect"
|
||||
user "github.com/holos-run/holos/service/gen/holos/user/v1alpha1"
|
||||
"github.com/holos-run/holos/service/gen/holos/user/v1alpha1/userconnect"
|
||||
)
|
||||
|
||||
// User registers the user with the holos server.
|
||||
func User(ctx context.Context, cfg *client.Config) error {
|
||||
log := logger.FromContext(ctx)
|
||||
client := userconnect.NewUserServiceClient(token.NewClient(cfg.Token()), cfg.Client().Server())
|
||||
|
||||
var err error
|
||||
var u *user.User
|
||||
var o *org.Organization
|
||||
cc := &holos.ClientContext{}
|
||||
|
||||
u, err = getUser(ctx, client)
|
||||
if err != nil {
|
||||
if connect.CodeOf(err) != connect.CodeNotFound {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if u, o, err = registerUser(ctx, client); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Save the registration context
|
||||
cc.OrgID = o.GetOrgId()
|
||||
cc.UserID = u.GetId()
|
||||
if err := cc.Save(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.InfoContext(ctx, "created user", "email", u.GetEmail(), "id", u.GetId())
|
||||
}
|
||||
|
||||
if cc.Exists() {
|
||||
if err := cc.Load(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the current user id gets saved.
|
||||
cc.UserID = u.GetId()
|
||||
|
||||
// Ensure an org ID gets saved.
|
||||
if cc.OrgID == "" {
|
||||
org, err := getOrg(ctx, cfg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
cc.OrgID = org.GetOrgId()
|
||||
}
|
||||
|
||||
// One last save, we know we have the user id and org id at this point.
|
||||
if err := cc.Save(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "user", "email", u.GetEmail(), "user_id", cc.UserID, "org_id", cc.OrgID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUser(ctx context.Context, client userconnect.UserServiceClient) (*user.User, error) {
|
||||
req := connect.NewRequest(&user.GetUserRequest{})
|
||||
resp, err := client.GetUser(ctx, req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return resp.Msg.GetUser(), nil
|
||||
}
|
||||
|
||||
// getOrg returns the first organization returned from the ListOrganizations rpc
|
||||
// method.
|
||||
func getOrg(ctx context.Context, cfg *client.Config) (*org.Organization, error) {
|
||||
client := organizationconnect.NewOrganizationServiceClient(token.NewClient(cfg.Token()), cfg.Client().Server())
|
||||
req := connect.NewRequest(&org.ListOrganizationsRequest{})
|
||||
resp, err := client.ListOrganizations(ctx, req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
orgs := resp.Msg.GetOrganizations()
|
||||
if len(orgs) == 0 {
|
||||
return nil, nil
|
||||
} else {
|
||||
return orgs[0], nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func registerUser(ctx context.Context, client userconnect.UserServiceClient) (*user.User, *org.Organization, error) {
|
||||
req := connect.NewRequest(&user.RegisterUserRequest{})
|
||||
resp, err := client.RegisterUser(ctx, req)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
return resp.Msg.GetUser(), resp.Msg.GetOrganization(), nil
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
@@ -20,7 +21,7 @@ import (
|
||||
fieldmask_utils "github.com/mennanov/fieldmask-utils"
|
||||
)
|
||||
|
||||
const AdminEmail = "jeff@openinfrastructure.co"
|
||||
var adminEmails = []string{"jeff@openinfrastructure.co", "jeff@ois.run"}
|
||||
|
||||
// NewSystemHandler returns a new SystemService implementation.
|
||||
func NewSystemHandler(db *ent.Client) *SystemHandler {
|
||||
@@ -37,8 +38,8 @@ func (h *SystemHandler) checkAdmin(ctx context.Context) error {
|
||||
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)
|
||||
if !slices.Contains(adminEmails, authnID.Email()) {
|
||||
err := fmt.Errorf("not an admin:\n\thave (%+v)\n\twant (%+v)", authnID.Email(), strings.Join(adminEmails, ","))
|
||||
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
"github.com/holos-run/holos/internal/strings"
|
||||
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
org "github.com/holos-run/holos/service/gen/holos/organization/v1alpha1"
|
||||
storage "github.com/holos-run/holos/service/gen/holos/storage/v1alpha1"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/user/v1alpha1"
|
||||
fieldmask_utils "github.com/mennanov/fieldmask-utils"
|
||||
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
@@ -50,8 +58,12 @@ func (h *UserHandler) GetUser(ctx context.Context, req *connect.Request[holos.Ge
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateUser(ctx context.Context, req *connect.Request[holos.CreateUserRequest]) (*connect.Response[holos.CreateUserResponse], error) {
|
||||
_, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
var createdUser *ent.User
|
||||
var err error
|
||||
if rpcUser := req.Msg.GetUser(); rpcUser != nil {
|
||||
createdUser, err = h.createUser(ctx, h.db, rpcUser)
|
||||
} else {
|
||||
@@ -67,6 +79,145 @@ func (h *UserHandler) CreateUser(ctx context.Context, req *connect.Request[holos
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) RegisterUser(ctx context.Context, req *connect.Request[holos.RegisterUserRequest]) (*connect.Response[holos.RegisterUserResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
var dbUser *ent.User
|
||||
var dbOrg *ent.Organization
|
||||
var rpcUser holos.User
|
||||
var rpcOrg org.Organization
|
||||
|
||||
userMask, err := fieldmask_utils.MaskFromProtoFieldMask(req.Msg.GetUserMask(), strings.PascalCase)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
orgMask, err := fieldmask_utils.MaskFromProtoFieldMask(req.Msg.GetOrganizationMask(), strings.PascalCase)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
// Server assigns IDs.
|
||||
userID, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(connect.NewError(connect.CodeInternal, err))
|
||||
}
|
||||
orgID, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(connect.NewError(connect.CodeInternal, err))
|
||||
}
|
||||
bareID, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(connect.NewError(connect.CodeInternal, err))
|
||||
}
|
||||
refID, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(connect.NewError(connect.CodeInternal, err))
|
||||
}
|
||||
|
||||
// Perform registration in a single transaction.
|
||||
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
|
||||
// Create the user
|
||||
dbUser, err = tx.User.Create().
|
||||
SetID(userID).
|
||||
SetEmail(authnID.Email()).
|
||||
SetIss(authnID.Issuer()).
|
||||
SetSub(authnID.Subject()).
|
||||
SetName(authnID.Name()).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
if ent.IsConstraintError(err) {
|
||||
rpcErr := connect.NewError(connect.CodeAlreadyExists, errors.New("user already registered"))
|
||||
errInfo := &errdetails.ErrorInfo{
|
||||
Reason: "USER_EXISTS",
|
||||
Domain: "user.holos.run",
|
||||
Metadata: map[string]string{"email": authnID.Email()},
|
||||
}
|
||||
if detail, detailErr := connect.NewErrorDetail(errInfo); detailErr != nil {
|
||||
logger.FromContext(ctx).ErrorContext(ctx, detailErr.Error(), "err", detailErr)
|
||||
} else {
|
||||
rpcErr.AddDetail(detail)
|
||||
}
|
||||
return errors.Wrap(rpcErr)
|
||||
}
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Create the org
|
||||
dbOrg, err = tx.Organization.Create().
|
||||
SetID(orgID).
|
||||
SetName(cleanAndAppendRandom(authnID.Name())).
|
||||
SetDisplayName(authnID.GivenName() + "'s Org").
|
||||
SetCreatorID(userID).
|
||||
SetEditorID(userID).
|
||||
AddUserIDs(userID).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Create the platforms.
|
||||
decoder := json.NewDecoder(bytes.NewReader([]byte(BareForm)))
|
||||
decoder.DisallowUnknownFields()
|
||||
var form storage.Form
|
||||
if err := decoder.Decode(&form); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
decoder = json.NewDecoder(bytes.NewReader([]byte(Model)))
|
||||
decoder.DisallowUnknownFields()
|
||||
var model storage.Model
|
||||
if err := decoder.Decode(&model); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Add the platforms.
|
||||
err = tx.Platform.Create().
|
||||
SetID(bareID).
|
||||
SetName("bare").
|
||||
SetDisplayName("Bare Platform").
|
||||
SetForm(&form).
|
||||
SetModel(&model).
|
||||
SetCreatorID(userID).
|
||||
SetEditorID(userID).
|
||||
SetOrgID(orgID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
err = tx.Platform.Create().
|
||||
SetID(refID).
|
||||
SetName("reference").
|
||||
SetDisplayName("Holos Reference Platform").
|
||||
SetForm(&form).
|
||||
SetModel(&model).
|
||||
SetCreatorID(userID).
|
||||
SetEditorID(userID).
|
||||
SetOrgID(orgID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, errors.Wrap(connect.NewError(connect.CodeFailedPrecondition, err))
|
||||
}
|
||||
|
||||
if err = fieldmask_utils.StructToStruct(userMask, UserToRPC(dbUser), &rpcUser); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
if err = fieldmask_utils.StructToStruct(orgMask, OrganizationToRPC(dbOrg), &rpcOrg); err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
msg := holos.RegisterUserResponse{
|
||||
User: &rpcUser,
|
||||
Organization: &rpcOrg,
|
||||
}
|
||||
|
||||
return connect.NewResponse(&msg), nil
|
||||
}
|
||||
|
||||
// UserToRPC returns an *holos.User adapted from *ent.User u.
|
||||
func UserToRPC(entity *ent.User) *holos.User {
|
||||
uid := entity.ID.String()
|
||||
|
||||
33
internal/server/interceptor/logger.go
Normal file
33
internal/server/interceptor/logger.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
func NewLogger() connect.UnaryInterceptorFunc {
|
||||
interceptor := func(next connect.UnaryFunc) connect.UnaryFunc {
|
||||
return connect.UnaryFunc(func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
|
||||
start := time.Now()
|
||||
rpcLogger := logger.FromContext(ctx).With("procedure", req.Spec().Procedure)
|
||||
ctx = logger.NewContext(ctx, rpcLogger)
|
||||
resp, err := next(ctx, req)
|
||||
go emitLog(ctx, start, err)
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
return connect.UnaryInterceptorFunc(interceptor)
|
||||
}
|
||||
|
||||
func emitLog(ctx context.Context, start time.Time, err error) {
|
||||
log := logger.FromContext(ctx)
|
||||
if err == nil {
|
||||
log = log.With("ok", true)
|
||||
} else {
|
||||
log = log.With("ok", false, "code", connect.CodeOf(err), "err", err)
|
||||
}
|
||||
log.InfoContext(ctx, "response", "duration", time.Since(start))
|
||||
}
|
||||
@@ -9,12 +9,14 @@ import (
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/grpcreflect"
|
||||
"connectrpc.com/otelconnect"
|
||||
"connectrpc.com/validate"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/frontend"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/server/handler"
|
||||
"github.com/holos-run/holos/internal/server/interceptor"
|
||||
"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/organization/v1alpha1/organizationconnect"
|
||||
@@ -113,7 +115,12 @@ func (s *Server) registerConnectRpc() error {
|
||||
return errors.Wrap(fmt.Errorf("could not initialize proto validation interceptor: %w", err))
|
||||
}
|
||||
|
||||
opts := connect.WithInterceptors(validator)
|
||||
otel, err := otelconnect.NewInterceptor()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
opts := connect.WithInterceptors(interceptor.NewLogger(), otel, validator)
|
||||
|
||||
s.handle(userconnect.NewUserServiceHandler(handler.NewUserHandler(s.db), opts))
|
||||
s.handle(organizationconnect.NewOrganizationServiceHandler(handler.NewOrganizationHandler(s.db), opts))
|
||||
|
||||
@@ -8,6 +8,7 @@ package user
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
v1alpha11 "github.com/holos-run/holos/service/gen/holos/organization/v1alpha1"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
@@ -224,6 +225,144 @@ func (x *GetUserResponse) GetUser() *User {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Register a User from the oidc id token claims or the provided user. Each one
|
||||
// of subject, email, and user id must be globally unique.
|
||||
type RegisterUserRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// User resource to create. If absent, the server populates User fields with
|
||||
// the oidc id token claims of the authenticated request.
|
||||
// NOTE: The server may ignore this request field and register the user solely
|
||||
// from authenticated identity claims.
|
||||
User *User `protobuf:"bytes,1,opt,name=user,proto3,oneof" json:"user,omitempty"`
|
||||
// Mask of the user fields to include in the response.
|
||||
UserMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=user_mask,json=userMask,proto3,oneof" json:"user_mask,omitempty"`
|
||||
// Organization resource to create. If absent, the server generates an
|
||||
// organization based on the user fields.
|
||||
// NOTE: The server may ignore this request field and register the
|
||||
// organization solely from authenticated identity claims.
|
||||
Organization *v1alpha11.Organization `protobuf:"bytes,3,opt,name=organization,proto3,oneof" json:"organization,omitempty"`
|
||||
// Mask of the organization fields to include in the response.
|
||||
OrganizationMask *fieldmaskpb.FieldMask `protobuf:"bytes,4,opt,name=organization_mask,json=organizationMask,proto3,oneof" json:"organization_mask,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) Reset() {
|
||||
*x = RegisterUserRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_holos_user_v1alpha1_user_service_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RegisterUserRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_holos_user_v1alpha1_user_service_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RegisterUserRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RegisterUserRequest) Descriptor() ([]byte, []int) {
|
||||
return file_holos_user_v1alpha1_user_service_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) GetUser() *User {
|
||||
if x != nil {
|
||||
return x.User
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) GetUserMask() *fieldmaskpb.FieldMask {
|
||||
if x != nil {
|
||||
return x.UserMask
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) GetOrganization() *v1alpha11.Organization {
|
||||
if x != nil {
|
||||
return x.Organization
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RegisterUserRequest) GetOrganizationMask() *fieldmaskpb.FieldMask {
|
||||
if x != nil {
|
||||
return x.OrganizationMask
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RegisterUserResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
|
||||
Organization *v1alpha11.Organization `protobuf:"bytes,2,opt,name=organization,proto3" json:"organization,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RegisterUserResponse) Reset() {
|
||||
*x = RegisterUserResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_holos_user_v1alpha1_user_service_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RegisterUserResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RegisterUserResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_holos_user_v1alpha1_user_service_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RegisterUserResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RegisterUserResponse) Descriptor() ([]byte, []int) {
|
||||
return file_holos_user_v1alpha1_user_service_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *RegisterUserResponse) GetUser() *User {
|
||||
if x != nil {
|
||||
return x.User
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RegisterUserResponse) GetOrganization() *v1alpha11.Organization {
|
||||
if x != nil {
|
||||
return x.Organization
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_holos_user_v1alpha1_user_service_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_holos_user_v1alpha1_user_service_proto_rawDesc = []byte{
|
||||
@@ -232,7 +371,10 @@ var file_holos_user_v1alpha1_user_service_proto_rawDesc = []byte{
|
||||
0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e,
|
||||
0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1e, 0x68,
|
||||
0x6f, 0x6c, 0x6f, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67,
|
||||
0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x68,
|
||||
0x6f, 0x6c, 0x6f, 0x73, 0x2f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x61, 0x6e,
|
||||
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66,
|
||||
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
|
||||
0x22, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x76, 0x31,
|
||||
@@ -260,24 +402,62 @@ var file_holos_user_v1alpha1_user_service_proto_rawDesc = []byte{
|
||||
0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x19, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73,
|
||||
0x65, 0x72, 0x32, 0xc6, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x12, 0x5f, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||
0x12, 0x26, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
|
||||
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65,
|
||||
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73,
|
||||
0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x23,
|
||||
0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c,
|
||||
0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
|
||||
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x41, 0x5a, 0x3f, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2d,
|
||||
0x72, 0x75, 0x6e, 0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x72, 0x22, 0xe7, 0x02, 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55,
|
||||
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x75, 0x73,
|
||||
0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73,
|
||||
0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55,
|
||||
0x73, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x3c,
|
||||
0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x48, 0x01, 0x52,
|
||||
0x08, 0x75, 0x73, 0x65, 0x72, 0x4d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x52, 0x0a, 0x0c,
|
||||
0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x29, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e,
|
||||
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
|
||||
0x2e, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x02, 0x52,
|
||||
0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01,
|
||||
0x12, 0x4c, 0x0a, 0x11, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69,
|
||||
0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x48, 0x03, 0x52, 0x10, 0x6f, 0x72, 0x67, 0x61, 0x6e,
|
||||
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x07,
|
||||
0x0a, 0x05, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x75, 0x73, 0x65, 0x72,
|
||||
0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69,
|
||||
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6f, 0x72, 0x67, 0x61, 0x6e,
|
||||
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x94, 0x01, 0x0a,
|
||||
0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04,
|
||||
0x75, 0x73, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x68, 0x6f, 0x6c,
|
||||
0x6f, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
|
||||
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x32, 0xad, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65,
|
||||
0x72, 0x12, 0x26, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
|
||||
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73,
|
||||
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x68, 0x6f, 0x6c, 0x6f,
|
||||
0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12,
|
||||
0x23, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61,
|
||||
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65,
|
||||
0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73,
|
||||
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0c,
|
||||
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x68,
|
||||
0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
|
||||
0x61, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2e, 0x75,
|
||||
0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x67,
|
||||
0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2d, 0x72, 0x75, 0x6e, 0x2f, 0x68, 0x6f, 0x6c, 0x6f,
|
||||
0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x68, 0x6f,
|
||||
0x6c, 0x6f, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
|
||||
0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -292,31 +472,42 @@ func file_holos_user_v1alpha1_user_service_proto_rawDescGZIP() []byte {
|
||||
return file_holos_user_v1alpha1_user_service_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_holos_user_v1alpha1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_holos_user_v1alpha1_user_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_holos_user_v1alpha1_user_service_proto_goTypes = []interface{}{
|
||||
(*CreateUserRequest)(nil), // 0: holos.user.v1alpha1.CreateUserRequest
|
||||
(*CreateUserResponse)(nil), // 1: holos.user.v1alpha1.CreateUserResponse
|
||||
(*GetUserRequest)(nil), // 2: holos.user.v1alpha1.GetUserRequest
|
||||
(*GetUserResponse)(nil), // 3: holos.user.v1alpha1.GetUserResponse
|
||||
(*User)(nil), // 4: holos.user.v1alpha1.User
|
||||
(*v1alpha1.UserRef)(nil), // 5: holos.object.v1alpha1.UserRef
|
||||
(*fieldmaskpb.FieldMask)(nil), // 6: google.protobuf.FieldMask
|
||||
(*CreateUserRequest)(nil), // 0: holos.user.v1alpha1.CreateUserRequest
|
||||
(*CreateUserResponse)(nil), // 1: holos.user.v1alpha1.CreateUserResponse
|
||||
(*GetUserRequest)(nil), // 2: holos.user.v1alpha1.GetUserRequest
|
||||
(*GetUserResponse)(nil), // 3: holos.user.v1alpha1.GetUserResponse
|
||||
(*RegisterUserRequest)(nil), // 4: holos.user.v1alpha1.RegisterUserRequest
|
||||
(*RegisterUserResponse)(nil), // 5: holos.user.v1alpha1.RegisterUserResponse
|
||||
(*User)(nil), // 6: holos.user.v1alpha1.User
|
||||
(*v1alpha1.UserRef)(nil), // 7: holos.object.v1alpha1.UserRef
|
||||
(*fieldmaskpb.FieldMask)(nil), // 8: google.protobuf.FieldMask
|
||||
(*v1alpha11.Organization)(nil), // 9: holos.organization.v1alpha1.Organization
|
||||
}
|
||||
var file_holos_user_v1alpha1_user_service_proto_depIdxs = []int32{
|
||||
4, // 0: holos.user.v1alpha1.CreateUserRequest.user:type_name -> holos.user.v1alpha1.User
|
||||
4, // 1: holos.user.v1alpha1.CreateUserResponse.user:type_name -> holos.user.v1alpha1.User
|
||||
5, // 2: holos.user.v1alpha1.GetUserRequest.user:type_name -> holos.object.v1alpha1.UserRef
|
||||
6, // 3: holos.user.v1alpha1.GetUserRequest.field_mask:type_name -> google.protobuf.FieldMask
|
||||
4, // 4: holos.user.v1alpha1.GetUserResponse.user:type_name -> holos.user.v1alpha1.User
|
||||
0, // 5: holos.user.v1alpha1.UserService.CreateUser:input_type -> holos.user.v1alpha1.CreateUserRequest
|
||||
2, // 6: holos.user.v1alpha1.UserService.GetUser:input_type -> holos.user.v1alpha1.GetUserRequest
|
||||
1, // 7: holos.user.v1alpha1.UserService.CreateUser:output_type -> holos.user.v1alpha1.CreateUserResponse
|
||||
3, // 8: holos.user.v1alpha1.UserService.GetUser:output_type -> holos.user.v1alpha1.GetUserResponse
|
||||
7, // [7:9] is the sub-list for method output_type
|
||||
5, // [5:7] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
6, // 0: holos.user.v1alpha1.CreateUserRequest.user:type_name -> holos.user.v1alpha1.User
|
||||
6, // 1: holos.user.v1alpha1.CreateUserResponse.user:type_name -> holos.user.v1alpha1.User
|
||||
7, // 2: holos.user.v1alpha1.GetUserRequest.user:type_name -> holos.object.v1alpha1.UserRef
|
||||
8, // 3: holos.user.v1alpha1.GetUserRequest.field_mask:type_name -> google.protobuf.FieldMask
|
||||
6, // 4: holos.user.v1alpha1.GetUserResponse.user:type_name -> holos.user.v1alpha1.User
|
||||
6, // 5: holos.user.v1alpha1.RegisterUserRequest.user:type_name -> holos.user.v1alpha1.User
|
||||
8, // 6: holos.user.v1alpha1.RegisterUserRequest.user_mask:type_name -> google.protobuf.FieldMask
|
||||
9, // 7: holos.user.v1alpha1.RegisterUserRequest.organization:type_name -> holos.organization.v1alpha1.Organization
|
||||
8, // 8: holos.user.v1alpha1.RegisterUserRequest.organization_mask:type_name -> google.protobuf.FieldMask
|
||||
6, // 9: holos.user.v1alpha1.RegisterUserResponse.user:type_name -> holos.user.v1alpha1.User
|
||||
9, // 10: holos.user.v1alpha1.RegisterUserResponse.organization:type_name -> holos.organization.v1alpha1.Organization
|
||||
0, // 11: holos.user.v1alpha1.UserService.CreateUser:input_type -> holos.user.v1alpha1.CreateUserRequest
|
||||
2, // 12: holos.user.v1alpha1.UserService.GetUser:input_type -> holos.user.v1alpha1.GetUserRequest
|
||||
4, // 13: holos.user.v1alpha1.UserService.RegisterUser:input_type -> holos.user.v1alpha1.RegisterUserRequest
|
||||
1, // 14: holos.user.v1alpha1.UserService.CreateUser:output_type -> holos.user.v1alpha1.CreateUserResponse
|
||||
3, // 15: holos.user.v1alpha1.UserService.GetUser:output_type -> holos.user.v1alpha1.GetUserResponse
|
||||
5, // 16: holos.user.v1alpha1.UserService.RegisterUser:output_type -> holos.user.v1alpha1.RegisterUserResponse
|
||||
14, // [14:17] is the sub-list for method output_type
|
||||
11, // [11:14] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_holos_user_v1alpha1_user_service_proto_init() }
|
||||
@@ -374,16 +565,41 @@ func file_holos_user_v1alpha1_user_service_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_holos_user_v1alpha1_user_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RegisterUserRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_holos_user_v1alpha1_user_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RegisterUserResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_holos_user_v1alpha1_user_service_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
file_holos_user_v1alpha1_user_service_proto_msgTypes[2].OneofWrappers = []interface{}{}
|
||||
file_holos_user_v1alpha1_user_service_proto_msgTypes[4].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_holos_user_v1alpha1_user_service_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -37,13 +37,17 @@ const (
|
||||
UserServiceCreateUserProcedure = "/holos.user.v1alpha1.UserService/CreateUser"
|
||||
// UserServiceGetUserProcedure is the fully-qualified name of the UserService's GetUser RPC.
|
||||
UserServiceGetUserProcedure = "/holos.user.v1alpha1.UserService/GetUser"
|
||||
// UserServiceRegisterUserProcedure is the fully-qualified name of the UserService's RegisterUser
|
||||
// RPC.
|
||||
UserServiceRegisterUserProcedure = "/holos.user.v1alpha1.UserService/RegisterUser"
|
||||
)
|
||||
|
||||
// These variables are the protoreflect.Descriptor objects for the RPCs defined in this package.
|
||||
var (
|
||||
userServiceServiceDescriptor = v1alpha1.File_holos_user_v1alpha1_user_service_proto.Services().ByName("UserService")
|
||||
userServiceCreateUserMethodDescriptor = userServiceServiceDescriptor.Methods().ByName("CreateUser")
|
||||
userServiceGetUserMethodDescriptor = userServiceServiceDescriptor.Methods().ByName("GetUser")
|
||||
userServiceServiceDescriptor = v1alpha1.File_holos_user_v1alpha1_user_service_proto.Services().ByName("UserService")
|
||||
userServiceCreateUserMethodDescriptor = userServiceServiceDescriptor.Methods().ByName("CreateUser")
|
||||
userServiceGetUserMethodDescriptor = userServiceServiceDescriptor.Methods().ByName("GetUser")
|
||||
userServiceRegisterUserMethodDescriptor = userServiceServiceDescriptor.Methods().ByName("RegisterUser")
|
||||
)
|
||||
|
||||
// UserServiceClient is a client for the holos.user.v1alpha1.UserService service.
|
||||
@@ -52,6 +56,8 @@ type UserServiceClient interface {
|
||||
CreateUser(context.Context, *connect.Request[v1alpha1.CreateUserRequest]) (*connect.Response[v1alpha1.CreateUserResponse], error)
|
||||
// Get an existing user by id, email, or subject.
|
||||
GetUser(context.Context, *connect.Request[v1alpha1.GetUserRequest]) (*connect.Response[v1alpha1.GetUserResponse], error)
|
||||
// Register an user and initialize an organization, bare platform, and reference platform.
|
||||
RegisterUser(context.Context, *connect.Request[v1alpha1.RegisterUserRequest]) (*connect.Response[v1alpha1.RegisterUserResponse], error)
|
||||
}
|
||||
|
||||
// NewUserServiceClient constructs a client for the holos.user.v1alpha1.UserService service. By
|
||||
@@ -76,13 +82,20 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
||||
connect.WithSchema(userServiceGetUserMethodDescriptor),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
registerUser: connect.NewClient[v1alpha1.RegisterUserRequest, v1alpha1.RegisterUserResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceRegisterUserProcedure,
|
||||
connect.WithSchema(userServiceRegisterUserMethodDescriptor),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// userServiceClient implements UserServiceClient.
|
||||
type userServiceClient struct {
|
||||
createUser *connect.Client[v1alpha1.CreateUserRequest, v1alpha1.CreateUserResponse]
|
||||
getUser *connect.Client[v1alpha1.GetUserRequest, v1alpha1.GetUserResponse]
|
||||
createUser *connect.Client[v1alpha1.CreateUserRequest, v1alpha1.CreateUserResponse]
|
||||
getUser *connect.Client[v1alpha1.GetUserRequest, v1alpha1.GetUserResponse]
|
||||
registerUser *connect.Client[v1alpha1.RegisterUserRequest, v1alpha1.RegisterUserResponse]
|
||||
}
|
||||
|
||||
// CreateUser calls holos.user.v1alpha1.UserService.CreateUser.
|
||||
@@ -95,12 +108,19 @@ func (c *userServiceClient) GetUser(ctx context.Context, req *connect.Request[v1
|
||||
return c.getUser.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// RegisterUser calls holos.user.v1alpha1.UserService.RegisterUser.
|
||||
func (c *userServiceClient) RegisterUser(ctx context.Context, req *connect.Request[v1alpha1.RegisterUserRequest]) (*connect.Response[v1alpha1.RegisterUserResponse], error) {
|
||||
return c.registerUser.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// UserServiceHandler is an implementation of the holos.user.v1alpha1.UserService service.
|
||||
type UserServiceHandler interface {
|
||||
// Create a new user from authenticated claims or the provided User resource.
|
||||
CreateUser(context.Context, *connect.Request[v1alpha1.CreateUserRequest]) (*connect.Response[v1alpha1.CreateUserResponse], error)
|
||||
// Get an existing user by id, email, or subject.
|
||||
GetUser(context.Context, *connect.Request[v1alpha1.GetUserRequest]) (*connect.Response[v1alpha1.GetUserResponse], error)
|
||||
// Register an user and initialize an organization, bare platform, and reference platform.
|
||||
RegisterUser(context.Context, *connect.Request[v1alpha1.RegisterUserRequest]) (*connect.Response[v1alpha1.RegisterUserResponse], error)
|
||||
}
|
||||
|
||||
// NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path
|
||||
@@ -121,12 +141,20 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
||||
connect.WithSchema(userServiceGetUserMethodDescriptor),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceRegisterUserHandler := connect.NewUnaryHandler(
|
||||
UserServiceRegisterUserProcedure,
|
||||
svc.RegisterUser,
|
||||
connect.WithSchema(userServiceRegisterUserMethodDescriptor),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
return "/holos.user.v1alpha1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case UserServiceCreateUserProcedure:
|
||||
userServiceCreateUserHandler.ServeHTTP(w, r)
|
||||
case UserServiceGetUserProcedure:
|
||||
userServiceGetUserHandler.ServeHTTP(w, r)
|
||||
case UserServiceRegisterUserProcedure:
|
||||
userServiceRegisterUserHandler.ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@@ -143,3 +171,7 @@ func (UnimplementedUserServiceHandler) CreateUser(context.Context, *connect.Requ
|
||||
func (UnimplementedUserServiceHandler) GetUser(context.Context, *connect.Request[v1alpha1.GetUserRequest]) (*connect.Response[v1alpha1.GetUserResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("holos.user.v1alpha1.UserService.GetUser is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) RegisterUser(context.Context, *connect.Request[v1alpha1.RegisterUserRequest]) (*connect.Response[v1alpha1.RegisterUserResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("holos.user.v1alpha1.UserService.RegisterUser is not implemented"))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ option go_package = "github.com/holos-run/holos/service/gen/holos/user/v1alpha1;
|
||||
|
||||
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
|
||||
import "holos/user/v1alpha1/user.proto";
|
||||
import "holos/organization/v1alpha1/organization.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "holos/object/v1alpha1/object.proto";
|
||||
|
||||
@@ -33,10 +34,36 @@ message GetUserResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
// Register a User from the oidc id token claims or the provided user. Each one
|
||||
// of subject, email, and user id must be globally unique.
|
||||
message RegisterUserRequest {
|
||||
// User resource to create. If absent, the server populates User fields with
|
||||
// the oidc id token claims of the authenticated request.
|
||||
// NOTE: The server may ignore this request field and register the user solely
|
||||
// from authenticated identity claims.
|
||||
optional User user = 1;
|
||||
// Mask of the user fields to include in the response.
|
||||
optional google.protobuf.FieldMask user_mask = 2;
|
||||
// Organization resource to create. If absent, the server generates an
|
||||
// organization based on the user fields.
|
||||
// NOTE: The server may ignore this request field and register the
|
||||
// organization solely from authenticated identity claims.
|
||||
optional holos.organization.v1alpha1.Organization organization = 3;
|
||||
// Mask of the organization fields to include in the response.
|
||||
optional google.protobuf.FieldMask organization_mask = 4;
|
||||
}
|
||||
|
||||
message RegisterUserResponse {
|
||||
User user = 1;
|
||||
holos.organization.v1alpha1.Organization organization = 2;
|
||||
}
|
||||
|
||||
// UserService provides CRUD methods for User resources in the system.
|
||||
service UserService {
|
||||
// Create a new user from authenticated claims or the provided User resource.
|
||||
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse) {}
|
||||
// Get an existing user by id, email, or subject.
|
||||
rpc GetUser(GetUserRequest) returns (GetUserResponse) {}
|
||||
// Register an user and initialize an organization, bare platform, and reference platform.
|
||||
rpc RegisterUser(RegisterUserRequest) returns (RegisterUserResponse) {}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
76
|
||||
78
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user