mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 16:54:58 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90f8eab816 | ||
|
|
aee15f95e2 | ||
|
|
1c540ac375 | ||
|
|
5b0e883ac9 | ||
|
|
9a2519af71 | ||
|
|
9b9ff601c0 | ||
|
|
2f798296dc | ||
|
|
2b2ff63cad | ||
|
|
3b135c09f3 | ||
|
|
28813eba5b | ||
|
|
02ff765f54 | ||
|
|
fe8a806132 | ||
|
|
6626d58301 | ||
|
|
cb0911e890 | ||
|
|
3745a68dc5 | ||
|
|
fd64830476 | ||
|
|
1ee0fa9c1f | ||
|
|
8fab325b0a | ||
|
|
858ffad913 | ||
|
|
62735b99e7 | ||
|
|
29ab9c6300 | ||
|
|
debc01c7de | ||
|
|
c07f35ecd6 | ||
|
|
c8f528700c | ||
|
|
896248c237 | ||
|
|
74a181db21 | ||
|
|
ba10113342 | ||
|
|
eb0207c92e | ||
|
|
0fbcee8119 | ||
|
|
ce8bc798f6 | ||
|
|
996195d651 | ||
|
|
f00b29d3a3 |
8
Tiltfile
8
Tiltfile
@@ -99,6 +99,7 @@ docker_build_with_restart(
|
||||
'--listen-port={}'.format(listen_port),
|
||||
'--oidc-issuer=https://login.ois.run',
|
||||
'--oidc-audience=262096764402729854@holos_platform',
|
||||
'--log-level=debug',
|
||||
'--metrics-port={}'.format(metrics_port),
|
||||
],
|
||||
dockerfile='./hack/tilt/Dockerfile',
|
||||
@@ -190,7 +191,7 @@ k8s_resource(
|
||||
],
|
||||
resource_deps=[compile_id],
|
||||
links=[
|
||||
link('https://{}.holos.dev.k2.ois.run/app/'.format(developer), "Holos Web UI")
|
||||
link('https://{}.app.dev.k2.holos.run/ui/'.format(developer), "Holos Web UI")
|
||||
],
|
||||
)
|
||||
|
||||
@@ -200,11 +201,6 @@ k8s_resource(
|
||||
new_name=auth_id,
|
||||
objects=[
|
||||
'{}:virtualservice'.format(holos_server),
|
||||
'{}-allow-groups:authorizationpolicy'.format(holos_server),
|
||||
'{}-allow-nothing:authorizationpolicy'.format(holos_server),
|
||||
'{}-allow-well-known-paths:authorizationpolicy'.format(holos_server),
|
||||
'{}-auth:authorizationpolicy'.format(holos_server),
|
||||
'{}:requestauthentication'.format(holos_server),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ plugins:
|
||||
out: service/gen
|
||||
opt: paths=source_relative
|
||||
- plugin: es
|
||||
out: internal/frontend/holos/gen
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
- plugin: connect-es
|
||||
out: internal/frontend/holos/gen
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
- plugin: connect-query
|
||||
out: internal/frontend/holos/gen
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
|
||||
@@ -2,7 +2,10 @@ package holos
|
||||
|
||||
import ap "security.istio.io/authorizationpolicy/v1"
|
||||
|
||||
// #AuthPolicyRules represents AuthorizationPolicy rules for hosts that need specialized treatment. Entries in this struct are exclused from the blank ingressauth AuthorizationPolicy governing the ingressgateway and included in a spcialized policy
|
||||
// #AuthPolicyRules represents AuthorizationPolicy rules for hosts that need
|
||||
// specialized treatment. Entries in this struct are excluded from
|
||||
// AuthorizationPolicy/authproxy-custom in the istio-ingress namespace. Entries
|
||||
// are added to their own AuthorizationPolicy.
|
||||
#AuthPolicyRules: {
|
||||
// AuthProxySpec represents the identity provider configuration
|
||||
AuthProxySpec: #AuthProxySpec & #Platform.authproxy
|
||||
@@ -14,6 +17,9 @@ import ap "security.istio.io/authorizationpolicy/v1"
|
||||
name: Name
|
||||
// slug is the resource name prefix
|
||||
slug: string
|
||||
// NoAuthorizationPolicy disables an AuthorizationPolicy for the host
|
||||
NoAuthorizationPolicy: true | *false
|
||||
|
||||
// Refer to https://istio.io/latest/docs/reference/config/security/authorization-policy/#Rule
|
||||
spec: ap.#AuthorizationPolicySpec & {
|
||||
action: "CUSTOM"
|
||||
@@ -25,11 +31,13 @@ import ap "security.istio.io/authorizationpolicy/v1"
|
||||
|
||||
objects: #APIObjects & {
|
||||
for Host in hosts {
|
||||
apiObjects: {
|
||||
AuthorizationPolicy: "\(Host.slug)-custom": {
|
||||
metadata: namespace: "istio-ingress"
|
||||
metadata: name: "\(Host.slug)-custom"
|
||||
spec: Host.spec
|
||||
if Host.NoAuthorizationPolicy == false {
|
||||
apiObjects: {
|
||||
AuthorizationPolicy: "\(Host.slug)-custom": {
|
||||
metadata: namespace: "istio-ingress"
|
||||
metadata: name: "\(Host.slug)-custom"
|
||||
spec: Host.spec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package holos
|
||||
|
||||
let Namespace = "jeff-holos"
|
||||
let Broker = "broker"
|
||||
let Broker = "choria-broker"
|
||||
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
_dependsOn: "prod-platform-issuer": _
|
||||
|
||||
metadata: name: "\(Namespace)-broker"
|
||||
metadata: name: "\(Namespace)-\(Broker)"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
@@ -31,9 +31,6 @@ let OBJECTS = #APIObjects & {
|
||||
Broker,
|
||||
"\(Broker).\(Namespace).svc",
|
||||
"\(Broker).\(Namespace).svc.cluster.local",
|
||||
"provision-\(Broker)",
|
||||
"provision-\(Broker).\(Namespace).svc",
|
||||
"provision-\(Broker).\(Namespace).svc.cluster.local",
|
||||
"*.\(Broker)",
|
||||
"*.\(Broker).\(Namespace).svc",
|
||||
"*.\(Broker).\(Namespace).svc.cluster.local",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package holos
|
||||
|
||||
let Namespace = "jeff-holos"
|
||||
let Provisioner = "choria-provisioner"
|
||||
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
_dependsOn: "prod-platform-issuer": _
|
||||
|
||||
metadata: name: "\(Namespace)-\(Provisioner)"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
let SelectorLabels = {
|
||||
"app.kubernetes.io/instance": Provisioner
|
||||
"app.kubernetes.io/name": Provisioner
|
||||
}
|
||||
|
||||
let OBJECTS = #APIObjects & {
|
||||
apiObjects: {
|
||||
Certificate: "\(Provisioner)-tls": #Certificate & {
|
||||
metadata: {
|
||||
name: "\(Provisioner)-tls"
|
||||
namespace: Namespace
|
||||
labels: SelectorLabels
|
||||
}
|
||||
spec: {
|
||||
commonName: "\(Provisioner).\(Namespace).svc.cluster.local"
|
||||
dnsNames: [
|
||||
Provisioner,
|
||||
"\(Provisioner).\(Namespace).svc",
|
||||
"\(Provisioner).\(Namespace).svc.cluster.local",
|
||||
"*.\(Provisioner)",
|
||||
"*.\(Provisioner).\(Namespace).svc",
|
||||
"*.\(Provisioner).\(Namespace).svc.cluster.local",
|
||||
]
|
||||
issuerRef: kind: "ClusterIssuer"
|
||||
issuerRef: name: "platform-issuer"
|
||||
secretName: metadata.name
|
||||
usages: ["signing", "key encipherment", "server auth", "client auth"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
package holos
|
||||
|
||||
let Namespace = "jeff-holos"
|
||||
let Broker = "broker"
|
||||
let Broker = "choria-broker"
|
||||
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
_dependsOn: "prod-secrets-stores": _
|
||||
|
||||
metadata: name: "\(Namespace)-broker"
|
||||
metadata: name: "\(Namespace)-\(Broker)"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
let SelectorLabels = {
|
||||
"app.kubernetes.io/instance": Broker
|
||||
"app.kubernetes.io/name": Broker
|
||||
"app.kubernetes.io/part-of": "choria"
|
||||
"app.kubernetes.io/name": Broker
|
||||
}
|
||||
|
||||
let Metadata = {
|
||||
@@ -29,8 +29,8 @@ let OBJECTS = #APIObjects & {
|
||||
metadata: name: "\(Broker)-tls"
|
||||
metadata: namespace: Namespace
|
||||
}
|
||||
ExternalSecret: "choria-\(Broker)": #ExternalSecret & {
|
||||
metadata: name: "choria-\(Broker)"
|
||||
ExternalSecret: "\(Broker)": #ExternalSecret & {
|
||||
metadata: name: Broker
|
||||
metadata: namespace: Namespace
|
||||
}
|
||||
StatefulSet: "\(Broker)": {
|
||||
@@ -92,7 +92,7 @@ let OBJECTS = #APIObjects & {
|
||||
volumes: [
|
||||
{
|
||||
name: Broker
|
||||
secret: secretName: "choria-\(Broker)"
|
||||
secret: secretName: Broker
|
||||
},
|
||||
{
|
||||
name: "\(Broker)-tls"
|
||||
@@ -137,11 +137,20 @@ let OBJECTS = #APIObjects & {
|
||||
}
|
||||
}
|
||||
DestinationRule: "\(Broker)-wss": #DestinationRule & {
|
||||
metadata: Metadata
|
||||
_decriptions: "Configures Istio to connect to Choria using a cert issued by the Platform Issuer"
|
||||
metadata: Metadata
|
||||
spec: host: "\(Broker).\(Namespace).svc.cluster.local"
|
||||
spec: trafficPolicy: tls: {
|
||||
credentialName: "istio-ingress-mtls-cert"
|
||||
mode: "MUTUAL"
|
||||
// subjectAltNames is important, otherwise istio will fail to verify the
|
||||
// choria broker upstream server. make sure this matches a value
|
||||
// present in the choria broker's cert.
|
||||
//
|
||||
// kubectl get secret choria-broker-tls -o json | jq --exit-status
|
||||
// '.data | map_values(@base64d)' | jq .\"tls.crt\" -r | openssl x509
|
||||
// -text -noout -in -
|
||||
subjectAltNames: [spec.host]
|
||||
}
|
||||
}
|
||||
VirtualService: "\(Broker)-wss": #VirtualService & {
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
# build output from https://github.com/holos-run/holos-infra/blob/main/experiments/components/holos-saas/broker/build
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: broker
|
||||
app.kubernetes.io/version: 0.1.0
|
||||
helm.sh/chart: broker-0.1.0
|
||||
name: broker
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: broker
|
||||
app.kubernetes.io/version: 0.1.0
|
||||
helm.sh/chart: broker-0.1.0
|
||||
name: broker
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- appProtocol: tcp
|
||||
name: tcp-nats
|
||||
port: 4222
|
||||
protocol: TCP
|
||||
targetPort: tcp-nats
|
||||
- appProtocol: tcp
|
||||
name: tcp-cluster
|
||||
port: 5222
|
||||
protocol: TCP
|
||||
targetPort: tcp-cluster
|
||||
- appProtocol: https
|
||||
name: https-wss
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: https-wss
|
||||
selector:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/name: broker
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: broker
|
||||
app.kubernetes.io/version: 0.1.0
|
||||
helm.sh/chart: broker-0.1.0
|
||||
name: broker-lb
|
||||
spec:
|
||||
externalTrafficPolicy: Local
|
||||
loadBalancerIP: 1.2.3.4
|
||||
ports:
|
||||
- appProtocol: tcp
|
||||
name: tcp-nats
|
||||
port: 4222
|
||||
protocol: TCP
|
||||
targetPort: tcp-nats
|
||||
- appProtocol: https
|
||||
name: https-wss
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: https-wss
|
||||
selector:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/name: broker
|
||||
type: LoadBalancer
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: broker
|
||||
app.kubernetes.io/version: 0.1.0
|
||||
helm.sh/chart: broker-0.1.0
|
||||
name: broker
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/name: broker
|
||||
serviceName: broker
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: broker
|
||||
app.kubernetes.io/name: broker
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- choria
|
||||
- broker
|
||||
- run
|
||||
- --config
|
||||
- /etc/choria/broker.conf
|
||||
image: registry.choria.io/choria/choria:latest
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http-stats
|
||||
name: broker
|
||||
ports:
|
||||
- containerPort: 4222
|
||||
name: tcp-nats
|
||||
protocol: TCP
|
||||
- containerPort: 4333
|
||||
name: https-wss
|
||||
protocol: TCP
|
||||
- containerPort: 5222
|
||||
name: tcp-cluster
|
||||
protocol: TCP
|
||||
- containerPort: 8222
|
||||
name: http-stats
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http-stats
|
||||
resources: {}
|
||||
securityContext: {}
|
||||
volumeMounts:
|
||||
- mountPath: /etc/choria
|
||||
name: broker
|
||||
- mountPath: /etc/choria-tls
|
||||
name: broker-tls
|
||||
securityContext: {}
|
||||
serviceAccountName: broker
|
||||
volumes:
|
||||
- name: broker
|
||||
secret:
|
||||
secretName: broker
|
||||
- name: broker-tls
|
||||
secret:
|
||||
secretName: broker-tls
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: broker-tls
|
||||
namespace: holos-dev
|
||||
spec:
|
||||
commonName: broker.holos-dev.svc.cluster.local
|
||||
dnsNames:
|
||||
- broker
|
||||
- broker.holos-dev.svc
|
||||
- broker.holos-dev.svc.cluster.local
|
||||
- provision-broker
|
||||
- provision-broker.holos-dev.svc
|
||||
- provision-broker.holos-dev.svc.cluster.local
|
||||
- '*.broker'
|
||||
- '*.broker.holos-dev.svc'
|
||||
- '*.broker.holos-dev.svc.cluster.local'
|
||||
issuerRef:
|
||||
kind: ClusterIssuer
|
||||
name: cluster-issuer
|
||||
secretName: broker-tls
|
||||
usages:
|
||||
- signing
|
||||
- key encipherment
|
||||
- server auth
|
||||
- client auth
|
||||
---
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: broker
|
||||
spec:
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: kv//kube-namespace/holos-dev/broker
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: SecretStore
|
||||
name: core-vault
|
||||
target:
|
||||
creationPolicy: Owner
|
||||
name: broker
|
||||
---
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: broker-wss
|
||||
namespace: holos-dev
|
||||
spec:
|
||||
host: broker.holos-dev.svc.cluster.local
|
||||
trafficPolicy:
|
||||
tls:
|
||||
credentialName: istio-ingress-mtls-cert
|
||||
mode: MUTUAL
|
||||
---
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: broker-wss
|
||||
namespace: holos-dev
|
||||
spec:
|
||||
gateways:
|
||||
- istio-ingress/wildcard-pub-gw
|
||||
hosts:
|
||||
- provision.pub.k2.holos.run
|
||||
http:
|
||||
- route:
|
||||
- destination:
|
||||
host: broker.holos-dev.svc.cluster.local
|
||||
port:
|
||||
number: 443
|
||||
tls:
|
||||
mode: SIMPLE
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
FROM registry.choria.io/choria/provisioner:latest
|
||||
|
||||
RUN curl -Lo nsc.zip https://github.com/nats-io/nsc/releases/download/v2.8.6/nsc-linux-amd64.zip &&\
|
||||
unzip nsc.zip && \
|
||||
mv nsc /usr/local/bin/nsc && \
|
||||
chmod 755 /usr/local/bin/nsc && \
|
||||
rm -f nsc.zip
|
||||
|
||||
# TODO: Add jwt executable
|
||||
# TODO: Add helper executable
|
||||
|
||||
USER choria
|
||||
ENV USER=choria
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/choria-provisioner"]
|
||||
|
||||
# These two files are expected to be in the provisioner secret.
|
||||
CMD ["--config=/etc/provisioner/provisioner.yaml", "--choria-config=/etc/provisioner/choria.cfg"]
|
||||
@@ -0,0 +1,82 @@
|
||||
package holos
|
||||
|
||||
let Namespace = "jeff-holos"
|
||||
let Provisioner = "choria-provisioner"
|
||||
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
_dependsOn: "prod-secrets-stores": _
|
||||
|
||||
metadata: name: "\(Namespace)-\(Provisioner)"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
let SelectorLabels = {
|
||||
"app.kubernetes.io/instance": Provisioner
|
||||
"app.kubernetes.io/name": Provisioner
|
||||
}
|
||||
|
||||
let Metadata = {
|
||||
name: Provisioner
|
||||
namespace: Namespace
|
||||
labels: SelectorLabels
|
||||
}
|
||||
|
||||
let OBJECTS = #APIObjects & {
|
||||
apiObjects: {
|
||||
ExternalSecret: "\(Provisioner)-tls": #ExternalSecret & {
|
||||
metadata: name: "\(Provisioner)-tls"
|
||||
metadata: namespace: Namespace
|
||||
}
|
||||
ExternalSecret: "\(Provisioner)": #ExternalSecret & {
|
||||
metadata: name: Provisioner
|
||||
metadata: namespace: Namespace
|
||||
}
|
||||
ServiceAccount: "\(Provisioner)": #ServiceAccount & {
|
||||
metadata: Metadata
|
||||
}
|
||||
Deployment: "\(Provisioner)": {
|
||||
metadata: Metadata
|
||||
spec: {
|
||||
selector: matchLabels: SelectorLabels
|
||||
template: metadata: labels: SelectorLabels
|
||||
template: spec: {
|
||||
containers: [
|
||||
{
|
||||
name: Provisioner
|
||||
command: ["bash", "/etc/provisioner/entrypoint"]
|
||||
// skopeo inspect docker://registry.choria.io/choria/provisioner | jq .RepoTags
|
||||
image: "registry.choria.io/choria/provisioner:0.15.1"
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
resources: {}
|
||||
securityContext: {}
|
||||
volumeMounts: [
|
||||
{
|
||||
mountPath: "/etc/provisioner"
|
||||
name: Provisioner
|
||||
},
|
||||
{
|
||||
mountPath: "/etc/provisioner-tls"
|
||||
name: "\(Provisioner)-tls"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
securityContext: {}
|
||||
serviceAccountName: Provisioner
|
||||
volumes: [
|
||||
{
|
||||
name: Provisioner
|
||||
secret: secretName: name
|
||||
},
|
||||
{
|
||||
name: "\(Provisioner)-tls"
|
||||
secret: secretName: name
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,14 +71,15 @@ let IstioInject = [{op: "add", path: "/spec/template/metadata/labels/sidecar.ist
|
||||
}
|
||||
}
|
||||
|
||||
// Probably shouldn't use the authproxy struct and should instead define an identity provider struct.
|
||||
let AuthProxySpec = #AuthProxySpec & #Platform.authproxy
|
||||
let OAuthClient = #Platform.oauthClients.argocd.spec
|
||||
|
||||
let OIDCConfig = {
|
||||
name: "Holos Platform"
|
||||
issuer: AuthProxySpec.issuer
|
||||
clientID: #Platform.argocd.clientID
|
||||
requestedIDTokenClaims: groups: essential: true
|
||||
requestedScopes: ["openid", "profile", "email", "groups", "urn:zitadel:iam:org:domain:primary:\(AuthProxySpec.orgDomain)"]
|
||||
name: "Holos Platform"
|
||||
issuer: OAuthClient.issuer
|
||||
clientID: OAuthClient.clientID
|
||||
requestedScopes: OAuthClient.scopesList
|
||||
// Set redirect uri to https://argocd.example.com/pkce/verify
|
||||
enablePKCEAuthentication: true
|
||||
|
||||
requestedIDTokenClaims: groups: essential: true
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ _IngressAuthProxy: {
|
||||
Domains: (#Platform.org.domain): _
|
||||
Domains: "\(#ClusterName).\(#Platform.org.domain)": _
|
||||
|
||||
// TODO: This should be generated from ProjectHosts
|
||||
Domains: "holos.run": _
|
||||
Domains: "\(#ClusterName).holos.run": _
|
||||
|
||||
let Metadata = {
|
||||
name: string
|
||||
namespace: Namespace
|
||||
@@ -271,11 +275,14 @@ _IngressAuthProxy: {
|
||||
rules: [
|
||||
{
|
||||
to: [{
|
||||
// Refer to https://istio.io/latest/docs/ops/best-practices/security/#writing-host-match-policies
|
||||
operation: notHosts: [
|
||||
// Never send requests for the login service through the authorizer, would block login.
|
||||
AuthProxySpec.issuerHost,
|
||||
"\(AuthProxySpec.issuerHost):*",
|
||||
// Exclude hosts with specialized rules from the catch-all.
|
||||
for x in _AuthPolicyRules.hosts {x.name},
|
||||
for x in _AuthPolicyRules.hosts {"\(x.name):*"},
|
||||
]
|
||||
}]
|
||||
when: [
|
||||
@@ -298,7 +305,7 @@ _IngressAuthProxy: {
|
||||
_AuthPolicyRules: #AuthPolicyRules & {
|
||||
hosts: {
|
||||
let Vault = "vault.core.ois.run"
|
||||
(Vault): {
|
||||
"\(Vault)": {
|
||||
slug: "vault"
|
||||
// Rules for when to route requests through the auth proxy
|
||||
spec: rules: [
|
||||
@@ -321,3 +328,20 @@ _AuthPolicyRules: #AuthPolicyRules & {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude project hosts from the auth proxy if configured to do so. The
|
||||
// intended effect is to exclude the host from the blanket `authproxy-custom`
|
||||
// AuthorizationPolicy rule _without_ adding a specialized AuthorizationPolicy
|
||||
// for the same host. This has the effect of completely excluding the host from
|
||||
// authorization policy.
|
||||
for Project in _Projects {
|
||||
let ProjectHosts = (#ProjectHosts & {project: Project}).Hosts
|
||||
|
||||
for FQDN, Host in ProjectHosts {
|
||||
if Host.NoAuthorizationPolicy {
|
||||
if Host.clusters[#ClusterName] != _|_ {
|
||||
_AuthPolicyRules: hosts: "\(Host.fqdn)": NoAuthorizationPolicy: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ _Projects: #Projects & {
|
||||
// app is the holos web app and grpc api.
|
||||
hosts: app: _
|
||||
// provision is the choria broker provisioning system.
|
||||
hosts: provision: _
|
||||
hosts: provision: NoAuthorizationPolicy: true
|
||||
// nats is the nats service holos controller machine room agents connect after provisioning.
|
||||
hosts: nats: _
|
||||
hosts: nats: NoAuthorizationPolicy: true
|
||||
}
|
||||
|
||||
iam: {
|
||||
|
||||
@@ -29,6 +29,7 @@ let OBJECTS = #APIObjects & {
|
||||
metadata: name: PlatformIssuer
|
||||
metadata: namespace: Namespace
|
||||
spec: {
|
||||
duration: "999999h"
|
||||
isCA: true
|
||||
commonName: PlatformIssuer
|
||||
secretName: PlatformIssuer
|
||||
|
||||
@@ -104,6 +104,8 @@ import "strings"
|
||||
// Host is valid on all project clusters.
|
||||
clusters: clusterMap
|
||||
}
|
||||
|
||||
NoAuthorizationPolicy: host.NoAuthorizationPolicy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +128,10 @@ import "strings"
|
||||
clusters: #ClusterMap
|
||||
// hosts are always valid on the provisioner cluster
|
||||
clusters: provisioner: _
|
||||
|
||||
// NoAuthorizationPolicy excludes the host from the auth proxy integrated with
|
||||
// the default ingress Gateway.
|
||||
NoAuthorizationPolicy: true | *false
|
||||
}
|
||||
|
||||
#ClusterMap: [Name=string]: #Cluster & {name: Name}
|
||||
|
||||
@@ -27,6 +27,9 @@ import (
|
||||
project: #Project
|
||||
let Project = project
|
||||
|
||||
// ProjectHosts represents all of the hosts associated with a project indexed
|
||||
// by FQDN with #CertInfo values. Slice and dice this struct as needed to
|
||||
// work with hosts in the platform.
|
||||
ProjectHosts: (#ProjectHosts & {project: Project}).Hosts
|
||||
|
||||
// GatewayServers maps Gateway spec.servers #GatewayServer values indexed by stage then name.
|
||||
|
||||
@@ -68,8 +68,13 @@ _Projects: #Projects
|
||||
// #Cluster defines a cluster
|
||||
#Cluster: name: string
|
||||
|
||||
// #Host defines a short hostname
|
||||
#Host: name: string
|
||||
#Host: {
|
||||
// #Host defines a short hostname
|
||||
name: string
|
||||
// NoAuthorizationPolicy excludes the host from the auth proxy integrated with
|
||||
// the default ingress Gateway.
|
||||
NoAuthorizationPolicy: true | *false
|
||||
}
|
||||
|
||||
#Environment: {
|
||||
// name uniquely identifies the environment within the scope of the project.
|
||||
|
||||
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.21.5
|
||||
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/validate v0.1.0
|
||||
cuelang.org/go v0.8.0
|
||||
entgo.io/ent v0.13.1
|
||||
|
||||
2
go.sum
2
go.sum
@@ -40,6 +40,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
connectrpc.com/connect v1.16.0 h1:rdtfQjZ0OyFkWPTegBNcH7cwquGAN1WzyJy80oFNibg=
|
||||
connectrpc.com/connect v1.16.0/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw=
|
||||
connectrpc.com/grpcreflect v1.2.0 h1:Q6og1S7HinmtbEuBvARLNwYmTbhEGRpHDhqrPNlmK+U=
|
||||
connectrpc.com/grpcreflect v1.2.0/go.mod h1:nwSOKmE8nU5u/CidgHtPYk1PFI3U9ignz7iDMxOYkSY=
|
||||
connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY=
|
||||
connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc=
|
||||
connectrpc.com/validate v0.1.0 h1:r55jirxMK7HO/xZwVHj3w2XkVFarsUM77ZDy367NtH4=
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
Initialize machine room provisioning credentials
|
||||
|
||||
When you want the holos controller to provision while operating in the current
|
||||
working directory, run:
|
||||
|
||||
1. `init-choria-provisioner-creds` to populate secrets in the Holos
|
||||
Provisioner Cluster (not to be confused with the Choria Provisioner).
|
||||
2. `make-provisioning-jwt` to issue a `provisioning.jwt` file for `holos
|
||||
controller` to use.
|
||||
3. `holos controller --config=agent.cfg` to read `provisioning.jwt` and write
|
||||
the provisioned config file and credentials to the current directory.
|
||||
|
||||
Expect the controller to provision.
|
||||
|
||||
Setup Notes:
|
||||
|
||||
The holos server flag `--provisioner-seed` must match the issuer.seed value.
|
||||
|
||||
@@ -6,6 +6,9 @@ export PROVISIONER_TOKEN="$(LC_ALL=C tr -dc "[:alpha:]" </dev/random | tr '[:upp
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
# Make sure gomplate is available
|
||||
gomplate --version
|
||||
|
||||
PARENT="$(cd $(dirname "$0") && pwd)"
|
||||
TOPLEVEL="$(cd "${PARENT}" && git rev-parse --show-toplevel)"
|
||||
: "${NAMESPACE:=jeff-holos}"
|
||||
@@ -33,7 +36,7 @@ echo -n "${PROVISIONER_TOKEN}" > ./provisioner/token
|
||||
# Provisioner signer
|
||||
choria jwt keys ./provisioner/signer.seed ./provisioner/signer.public
|
||||
choria jwt client ./provisioner/signer.jwt provisioner_signer ./issuer/issuer.seed \
|
||||
--public-key "$(<provisioner/signer.public)" --server-provisioner --validity $((999*365))d --issuer
|
||||
--public-key "$(<provisioner/signer.public)" --server-provisioner --validity $((100*365))d --issuer
|
||||
|
||||
# Provisioner Secret
|
||||
mkdir -p provisioner/secret
|
||||
@@ -57,5 +60,5 @@ choria jwt keys ./agents/signer.seed ./agents/signer.public
|
||||
# Now save the secrets
|
||||
holos create secret --append-hash=false --namespace $NAMESPACE choria-issuer --from-file=issuer
|
||||
holos create secret --append-hash=false --namespace $NAMESPACE choria-broker --from-file=broker
|
||||
holos create secret --append-hash=false --namespace $NAMESPACE choria-provisioner --from-file=provisioner
|
||||
holos create secret --append-hash=false --namespace $NAMESPACE choria-provisioner --from-file=provisioner/secret
|
||||
holos create secret --append-hash=false --namespace $NAMESPACE choria-agents --from-file=agents
|
||||
50
hack/choria/initialize/make-provisioning-jwt
Executable file
50
hack/choria/initialize/make-provisioning-jwt
Executable file
@@ -0,0 +1,50 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# Make a provisioner.jwt and put it in the current directory.
|
||||
#
|
||||
# Use the provisioner.jwt with `holos controller --config=controller.cfg` which
|
||||
# will read the jwt from the same directory as the config file.
|
||||
#
|
||||
# Refer to Arri's
|
||||
# [setup.sh](https://github.com/ripienaar/machine-room-mvp/blob/main/example/setup/setup.sh#L41)
|
||||
# And our own nites at https://github.com/holos-run/holos/issues/142
|
||||
|
||||
PARENT="$(cd $(dirname "$0") && pwd)"
|
||||
OUTDIR="$(pwd)"
|
||||
|
||||
: "${NAMESPACE:=jeff-holos}"
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
finish() {
|
||||
[[ -d "$tmpdir" ]] && rm -rvf "$tmpdir"
|
||||
}
|
||||
trap finish EXIT
|
||||
cd "$tmpdir"
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
# e.g. jeff.provision.dev.k2.holos.run
|
||||
#
|
||||
kubectl -n $NAMESPACE get virtualservice choria-broker-wss -o json > vs.json
|
||||
jq --exit-status -r '.spec.hosts[0]' vs.json > host
|
||||
|
||||
# Get the issuer.seed
|
||||
holos -n $NAMESPACE get secret choria-issuer --to-file issuer.seed
|
||||
|
||||
# Get the provisioner token to embed in the provisioning.jwt file.
|
||||
holos -n $NAMESPACE get secret choria-provisioner --to-file token
|
||||
|
||||
# The --token flag value must be the same value set in the token field of provisioner.yaml
|
||||
# Refer to https://github.com/ripienaar/machine-room-mvp/blob/main/example/setup/setup.sh#L41
|
||||
# Refer to https://github.com/ripienaar/machine-room-mvp/blob/main/example/setup/templates/provisioner/provisioner.yaml#L6
|
||||
choria jwt prov provisioning.jwt "issuer.seed" \
|
||||
--token "$(<token)" \
|
||||
--urls wss://$(<host):443 \
|
||||
--default \
|
||||
--protocol-v2 \
|
||||
--insecure \
|
||||
--update \
|
||||
--validity 30d \
|
||||
--extensions '{}'
|
||||
|
||||
cp provisioning.jwt "${OUTDIR}/"
|
||||
23
hack/choria/initialize/reset-choria-config
Executable file
23
hack/choria/initialize/reset-choria-config
Executable file
@@ -0,0 +1,23 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# This script resets the choria config for a Namespace
|
||||
|
||||
PARENT="$(cd $(dirname "$0") && pwd)"
|
||||
: "${NAMESPACE:=jeff-holos}"
|
||||
export NAMESPACE
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
KUBECONFIG=$HOME/.holos/kubeconfig.provisioner kubectl delete secret -n jeff-holos choria-agents choria-broker choria-provisioner choria-issuer
|
||||
|
||||
"${PARENT}/init-choria-provisioner-creds"
|
||||
|
||||
stamp="$(date)"
|
||||
|
||||
kubectl -n $NAMESPACE annotate externalsecret choria-broker secret.holos.run/refresh="$stamp" --overwrite
|
||||
kubectl -n $NAMESPACE annotate externalsecret choria-provisioner secret.holos.run/refresh="$stamp" --overwrite
|
||||
|
||||
kubectl -n $NAMESPACE wait --for='jsonpath={.status.conditions[?(@.type=="Ready")].status}=True' externalsecret choria-provisioner choria-broker
|
||||
|
||||
kubectl -n $NAMESPACE rollout restart statefulset choria-broker
|
||||
kubectl -n $NAMESPACE rollout restart deployment choria-provisioner
|
||||
@@ -6,11 +6,13 @@ plugin.choria.network.client_port = 4222
|
||||
plugin.choria.network.peer_port = 5222
|
||||
plugin.choria.network.system.user = system
|
||||
plugin.choria.network.system.password = system
|
||||
plugin.choria.network.peers = nats://broker-0.broker:5222,nats://broker-1.broker:5222,nats://broker-2.broker:5222
|
||||
plugin.choria.network.peers = nats://choria-broker-0.choria-broker:5222,nats://choria-broker-1.choria-broker:5222,nats://choria-broker-2.choria-broker:5222
|
||||
plugin.choria.use_srv = false
|
||||
plugin.choria.network.websocket_port = 4333
|
||||
|
||||
plugin.security.provider = choria
|
||||
# NOTE: plugin.security.choria.ca must not be set or provisioning will fail
|
||||
# with a unhandled choria_provisioning purpose token error
|
||||
plugin.security.choria.certificate = /etc/choria-tls/tls.crt
|
||||
plugin.security.choria.key = /etc/choria-tls/tls.key
|
||||
plugin.security.choria.token_file = /etc/choria/broker.jwt
|
||||
|
||||
@@ -4,4 +4,4 @@ plugin.security.choria.seed_file = /etc/provisioner/signer.seed
|
||||
|
||||
identity = provisioner_signer
|
||||
|
||||
plugin.choria.middleware_hosts = broker-0.broker:4222,broker-1.broker:4222,broker-2.broker:4222
|
||||
plugin.choria.middleware_hosts = choria-broker-0.choria-broker:4222,choria-broker-1.choria-broker:4222,choria-broker-2.choria-broker:4222
|
||||
|
||||
9
hack/choria/initialize/templates/provisioner/entrypoint
Normal file
9
hack/choria/initialize/templates/provisioner/entrypoint
Normal file
@@ -0,0 +1,9 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
|
||||
set -xeuo pipefail
|
||||
|
||||
mkdir -p /home/choria/bin
|
||||
install -m 0755 /etc/provisioner/helper.rb /home/choria/bin/helper.rb
|
||||
|
||||
exec /usr/sbin/choria-provisioner --config=/etc/provisioner/provisioner.yaml --choria-config=/etc/provisioner/choria.cfg
|
||||
134
hack/choria/initialize/templates/provisioner/helper.rb
Executable file
134
hack/choria/initialize/templates/provisioner/helper.rb
Executable file
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require "json"
|
||||
require "yaml"
|
||||
require "base64"
|
||||
require "net/http"
|
||||
require "openssl"
|
||||
|
||||
def parse_input
|
||||
input = STDIN.read
|
||||
|
||||
begin
|
||||
File.open("/tmp/request.json", "w") {|f| f.write(input)}
|
||||
rescue Exception
|
||||
end
|
||||
|
||||
request = JSON.parse(input)
|
||||
request["inventory"] = JSON.parse(request["inventory"])
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
def validate!(request, reply)
|
||||
if request["identity"] && request["identity"].length == 0
|
||||
reply["msg"] = "No identity received in request"
|
||||
reply["defer"] = true
|
||||
return false
|
||||
end
|
||||
|
||||
unless request["ed25519_pubkey"]
|
||||
reply["msg"] = "No ed15519 public key received"
|
||||
reply["defer"] = true
|
||||
return false
|
||||
end
|
||||
|
||||
unless request["ed25519_pubkey"]
|
||||
reply["msg"] = "No ed15519 directory received"
|
||||
reply["defer"] = true
|
||||
return false
|
||||
end
|
||||
|
||||
if request["ed25519_pubkey"]["directory"].length == 0
|
||||
reply["msg"] = "No ed15519 directory received"
|
||||
reply["defer"] = true
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def publish_reply(reply)
|
||||
begin
|
||||
File.open("/tmp/reply.json", "w") {|f| f.write(reply.to_json)}
|
||||
rescue Exception
|
||||
end
|
||||
|
||||
puts reply.to_json
|
||||
end
|
||||
|
||||
def publish_reply!(reply)
|
||||
publish_reply(reply)
|
||||
exit
|
||||
end
|
||||
|
||||
def set_config!(request, reply)
|
||||
# stub data the helper will fetch from the saas
|
||||
customers = {
|
||||
"one" => {
|
||||
:brokers => "nats://managed.example.net:9222", # whoever is the leader for this site
|
||||
:site => "customer_one",
|
||||
:source => {
|
||||
:host => "nats://cust_one:s3cret@saas-nats.choria.local",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
customer = request["jwt"]["extensions"]["customer"]
|
||||
brokers = customers[customer][:brokers]
|
||||
source = customers[customer][:source]
|
||||
|
||||
reply["configuration"].merge!(
|
||||
"identity" => request["identity"],
|
||||
"loglevel" => "warn",
|
||||
"plugin.choria.server.provision" => "false",
|
||||
"plugin.choria.middleware_hosts" => brokers,
|
||||
"plugin.security.issuer.names" => "choria",
|
||||
"plugin.security.issuer.choria.public" => "{{ .Env.ISSUER }}",
|
||||
"plugin.security.provider" => "choria",
|
||||
"plugin.security.choria.token_file" => File.join(request["ed25519_pubkey"]["directory"], "server.jwt"),
|
||||
"plugin.security.choria.seed_file" => File.join(request["ed25519_pubkey"]["directory"], "server.seed"),
|
||||
"machine_room.role" => "leader",
|
||||
"machine_room.site" => customers[customer][:site],
|
||||
"machine_room.source.host" => source[:host],
|
||||
)
|
||||
|
||||
reply["server_claims"].merge!(
|
||||
"exp" => 5*60*60*24*365,
|
||||
"pub_subjects" => [">"],
|
||||
"permissions" => {
|
||||
"streams" => true,
|
||||
"submission" => true,
|
||||
"service_host" => true,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
reply = {
|
||||
"defer" => false,
|
||||
"msg" => "",
|
||||
"certificate" => "",
|
||||
"ca" => "",
|
||||
"configuration" => {},
|
||||
"server_claims" => {}
|
||||
}
|
||||
|
||||
begin
|
||||
request = parse_input
|
||||
|
||||
reply["msg"] = "Validating"
|
||||
unless validate!(request, reply)
|
||||
publish_reply!(reply)
|
||||
end
|
||||
|
||||
set_config!(request, reply)
|
||||
|
||||
reply["msg"] = "Done"
|
||||
publish_reply!(reply)
|
||||
rescue SystemExit
|
||||
rescue Exception
|
||||
reply["msg"] = "Unexpected failure during provisioning: %s: %s" % [$!.class, $!.to_s]
|
||||
reply["defer"] = true
|
||||
publish_reply!(reply)
|
||||
end
|
||||
@@ -2,7 +2,8 @@ workers: 4
|
||||
interval: 1m
|
||||
logfile: /dev/stdout
|
||||
loglevel: info
|
||||
helper: /app/.venv/bin/helper
|
||||
# The entrypoint script installs this helper script.
|
||||
helper: /home/choria/bin/helper.rb
|
||||
token: "{{ .Env.PROVISIONER_TOKEN }}"
|
||||
choria_insecure: false
|
||||
site: holos
|
||||
|
||||
1
hack/choria/initialize/templates/provisioner/token
Normal file
1
hack/choria/initialize/templates/provisioner/token
Normal file
@@ -0,0 +1 @@
|
||||
{{ .Env.PROVISIONER_TOKEN -}}
|
||||
@@ -101,15 +101,44 @@ spec:
|
||||
gateways:
|
||||
- istio-ingress/default
|
||||
hosts:
|
||||
- '{developer}.holos.dev.k2.ois.run'
|
||||
- '{developer}.app.dev.k2.holos.run'
|
||||
http:
|
||||
- route:
|
||||
- name: "coffee-ui"
|
||||
match:
|
||||
- uri:
|
||||
prefix: "/ui"
|
||||
route:
|
||||
- destination:
|
||||
host: coffee
|
||||
port:
|
||||
number: 4200
|
||||
- name: "holos-api"
|
||||
route:
|
||||
- destination:
|
||||
host: '{name}'
|
||||
port:
|
||||
number: {listen_port}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: coffee
|
||||
spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 4200
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: coffee
|
||||
subsets:
|
||||
- addresses:
|
||||
- ip: 192.168.2.21
|
||||
ports:
|
||||
- port: 4200
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: holos
|
||||
@@ -120,117 +149,6 @@ metadata:
|
||||
imagePullSecrets:
|
||||
- name: kube-system-ecr-image-pull-creds
|
||||
---
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app: '{name}'
|
||||
holos.run/developer: '{developer}'
|
||||
name: '{name}-allow-groups'
|
||||
namespace: '{namespace}'
|
||||
spec:
|
||||
action: ALLOW
|
||||
rules:
|
||||
- when:
|
||||
- key: request.auth.claims[groups]
|
||||
values:
|
||||
- holos-developer
|
||||
- holos-developer@openinfrastructure.co
|
||||
selector:
|
||||
matchLabels:
|
||||
holos.run/authz: dev-holos-sso
|
||||
---
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: '{name}-allow-nothing'
|
||||
namespace: '{namespace}'
|
||||
labels:
|
||||
app: '{name}'
|
||||
holos.run/developer: '{developer}'
|
||||
spec:
|
||||
action: ALLOW
|
||||
selector:
|
||||
matchLabels:
|
||||
holos.run/authz: dev-holos-sso
|
||||
---
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: '{name}-allow-well-known-paths'
|
||||
namespace: '{namespace}'
|
||||
labels:
|
||||
app: '{name}'
|
||||
holos.run/developer: '{developer}'
|
||||
spec:
|
||||
action: ALLOW
|
||||
rules:
|
||||
- to:
|
||||
- operation:
|
||||
paths:
|
||||
- /healthz
|
||||
- /metrics
|
||||
- /callbacks/github
|
||||
selector:
|
||||
matchLabels:
|
||||
holos.run/authz: dev-holos-sso
|
||||
---
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: '{name}-auth'
|
||||
namespace: '{namespace}'
|
||||
labels:
|
||||
app: '{name}'
|
||||
holos.run/developer: '{developer}'
|
||||
spec:
|
||||
action: CUSTOM
|
||||
provider:
|
||||
name: dev-holos-sso
|
||||
rules:
|
||||
- to:
|
||||
- operation:
|
||||
notPaths:
|
||||
- /healthz
|
||||
- /metrics
|
||||
- /callbacks/github
|
||||
when:
|
||||
- key: request.headers[Authorization]
|
||||
notValues:
|
||||
- Bearer *
|
||||
selector:
|
||||
matchLabels:
|
||||
holos.run/authz: dev-holos-sso
|
||||
---
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: RequestAuthentication
|
||||
metadata:
|
||||
name: '{name}'
|
||||
namespace: '{namespace}'
|
||||
labels:
|
||||
app: '{name}'
|
||||
holos.run/developer: '{developer}'
|
||||
spec:
|
||||
jwtRules:
|
||||
- audiences:
|
||||
- https://sso.dev.holos.run
|
||||
forwardOriginalToken: true
|
||||
fromHeaders:
|
||||
- name: x-auth-request-access-token
|
||||
issuer: https://idex.core.ois.run
|
||||
jwksUri: https://idex.core.ois.run/keys
|
||||
- audiences:
|
||||
- holos-cli
|
||||
forwardOriginalToken: true
|
||||
fromHeaders:
|
||||
- name: authorization
|
||||
prefix: 'Bearer '
|
||||
issuer: https://idex.core.ois.run
|
||||
jwksUri: https://idex.core.ois.run/keys
|
||||
selector:
|
||||
matchLabels:
|
||||
holos.run/authz: dev-holos-sso
|
||||
---
|
||||
apiVersion: postgres-operator.crunchydata.com/v1beta1
|
||||
kind: PGAdmin
|
||||
metadata:
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
// New returns a new login command.
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("login")
|
||||
cmd.Short = "log in by caching credentials"
|
||||
var printClaims bool
|
||||
|
||||
config := token.NewConfig()
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("logout")
|
||||
cmd.Short = "log out by deleting cached credentials"
|
||||
cmd.RunE = func(c *cobra.Command, args []string) error {
|
||||
if err := os.RemoveAll(token.CacheDir); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not logout: %w", err))
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/cli/logout"
|
||||
"github.com/holos-run/holos/internal/cli/preflight"
|
||||
"github.com/holos-run/holos/internal/cli/render"
|
||||
"github.com/holos-run/holos/internal/cli/token"
|
||||
"github.com/holos-run/holos/internal/cli/txtar"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
@@ -61,6 +62,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
rootCmd.AddCommand(preflight.New(cfg))
|
||||
rootCmd.AddCommand(login.New(cfg))
|
||||
rootCmd.AddCommand(logout.New(cfg))
|
||||
rootCmd.AddCommand(token.New(cfg))
|
||||
|
||||
// Maybe not needed?
|
||||
rootCmd.AddCommand(txtar.New(cfg))
|
||||
|
||||
44
internal/cli/token/token.go
Normal file
44
internal/cli/token/token.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/token"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// New returns a new login command.
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("token")
|
||||
cmd.Short = "write id token to stdout"
|
||||
cmd.Long = "Useful with curl / grpcurl -H $(holos token)"
|
||||
|
||||
config := token.NewConfig()
|
||||
cmd.Flags().AddGoFlagSet(config.FlagSet())
|
||||
|
||||
fs := &flag.FlagSet{}
|
||||
cmd.Flags().AddGoFlagSet(fs)
|
||||
|
||||
cmd.RunE = func(c *cobra.Command, args []string) error {
|
||||
ctx := c.Context()
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
token, err := token.Get(ctx, cfg.Logger(), config)
|
||||
if err != nil {
|
||||
slog.Error("could not get token", "err", err)
|
||||
return fmt.Errorf("could not get token: %w", err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), token.Bearer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
"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/user"
|
||||
)
|
||||
@@ -315,6 +316,38 @@ func (c *OrganizationClient) GetX(ctx context.Context, id uuid.UUID) *Organizati
|
||||
return obj
|
||||
}
|
||||
|
||||
// QueryCreator queries the creator edge of a Organization.
|
||||
func (c *OrganizationClient) QueryCreator(o *Organization) *UserQuery {
|
||||
query := (&UserClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := o.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(organization.Table, organization.FieldID, id),
|
||||
sqlgraph.To(user.Table, user.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, organization.CreatorTable, organization.CreatorColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(o.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryUsers queries the users edge of a Organization.
|
||||
func (c *OrganizationClient) QueryUsers(o *Organization) *UserQuery {
|
||||
query := (&UserClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := o.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(organization.Table, organization.FieldID, id),
|
||||
sqlgraph.To(user.Table, user.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, false, organization.UsersTable, organization.UsersPrimaryKey...),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(o.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// Hooks returns the client hooks.
|
||||
func (c *OrganizationClient) Hooks() []Hook {
|
||||
return c.hooks.Organization
|
||||
@@ -448,6 +481,22 @@ func (c *UserClient) GetX(ctx context.Context, id uuid.UUID) *User {
|
||||
return obj
|
||||
}
|
||||
|
||||
// QueryOrganizations queries the organizations edge of a User.
|
||||
func (c *UserClient) QueryOrganizations(u *User) *OrganizationQuery {
|
||||
query := (&OrganizationClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := u.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(user.Table, user.FieldID, id),
|
||||
sqlgraph.To(organization.Table, organization.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, true, user.OrganizationsTable, user.OrganizationsPrimaryKey...),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(u.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// Hooks returns the client hooks.
|
||||
func (c *UserClient) Hooks() []Hook {
|
||||
return c.hooks.User
|
||||
|
||||
@@ -15,12 +15,21 @@ var (
|
||||
{Name: "updated_at", Type: field.TypeTime},
|
||||
{Name: "name", Type: field.TypeString, Unique: true},
|
||||
{Name: "display_name", Type: field.TypeString},
|
||||
{Name: "creator_id", Type: field.TypeUUID},
|
||||
}
|
||||
// OrganizationsTable holds the schema information for the "organizations" table.
|
||||
OrganizationsTable = &schema.Table{
|
||||
Name: "organizations",
|
||||
Columns: OrganizationsColumns,
|
||||
PrimaryKey: []*schema.Column{OrganizationsColumns[0]},
|
||||
ForeignKeys: []*schema.ForeignKey{
|
||||
{
|
||||
Symbol: "organizations_users_creator",
|
||||
Columns: []*schema.Column{OrganizationsColumns[5]},
|
||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||
OnDelete: schema.NoAction,
|
||||
},
|
||||
},
|
||||
}
|
||||
// UsersColumns holds the columns for the "users" table.
|
||||
UsersColumns = []*schema.Column{
|
||||
@@ -37,13 +46,49 @@ var (
|
||||
Name: "users",
|
||||
Columns: UsersColumns,
|
||||
PrimaryKey: []*schema.Column{UsersColumns[0]},
|
||||
Indexes: []*schema.Index{
|
||||
{
|
||||
Name: "user_iss_sub",
|
||||
Unique: true,
|
||||
Columns: []*schema.Column{UsersColumns[4], UsersColumns[5]},
|
||||
},
|
||||
},
|
||||
}
|
||||
// OrganizationUsersColumns holds the columns for the "organization_users" table.
|
||||
OrganizationUsersColumns = []*schema.Column{
|
||||
{Name: "organization_id", Type: field.TypeUUID},
|
||||
{Name: "user_id", Type: field.TypeUUID},
|
||||
}
|
||||
// OrganizationUsersTable holds the schema information for the "organization_users" table.
|
||||
OrganizationUsersTable = &schema.Table{
|
||||
Name: "organization_users",
|
||||
Columns: OrganizationUsersColumns,
|
||||
PrimaryKey: []*schema.Column{OrganizationUsersColumns[0], OrganizationUsersColumns[1]},
|
||||
ForeignKeys: []*schema.ForeignKey{
|
||||
{
|
||||
Symbol: "organization_users_organization_id",
|
||||
Columns: []*schema.Column{OrganizationUsersColumns[0]},
|
||||
RefColumns: []*schema.Column{OrganizationsColumns[0]},
|
||||
OnDelete: schema.Cascade,
|
||||
},
|
||||
{
|
||||
Symbol: "organization_users_user_id",
|
||||
Columns: []*schema.Column{OrganizationUsersColumns[1]},
|
||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||
OnDelete: schema.Cascade,
|
||||
},
|
||||
},
|
||||
}
|
||||
// Tables holds all the tables in the schema.
|
||||
Tables = []*schema.Table{
|
||||
OrganizationsTable,
|
||||
UsersTable,
|
||||
OrganizationUsersTable,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
OrganizationsTable.ForeignKeys[0].RefTable = UsersTable
|
||||
OrganizationUsersTable.ForeignKeys[0].RefTable = OrganizationsTable
|
||||
OrganizationUsersTable.ForeignKeys[1].RefTable = UsersTable
|
||||
}
|
||||
|
||||
@@ -33,17 +33,22 @@ const (
|
||||
// OrganizationMutation represents an operation that mutates the Organization nodes in the graph.
|
||||
type OrganizationMutation struct {
|
||||
config
|
||||
op Op
|
||||
typ string
|
||||
id *uuid.UUID
|
||||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
display_name *string
|
||||
clearedFields map[string]struct{}
|
||||
done bool
|
||||
oldValue func(context.Context) (*Organization, error)
|
||||
predicates []predicate.Organization
|
||||
op Op
|
||||
typ string
|
||||
id *uuid.UUID
|
||||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
display_name *string
|
||||
clearedFields map[string]struct{}
|
||||
creator *uuid.UUID
|
||||
clearedcreator bool
|
||||
users map[uuid.UUID]struct{}
|
||||
removedusers map[uuid.UUID]struct{}
|
||||
clearedusers bool
|
||||
done bool
|
||||
oldValue func(context.Context) (*Organization, error)
|
||||
predicates []predicate.Organization
|
||||
}
|
||||
|
||||
var _ ent.Mutation = (*OrganizationMutation)(nil)
|
||||
@@ -294,6 +299,123 @@ func (m *OrganizationMutation) ResetDisplayName() {
|
||||
m.display_name = nil
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (m *OrganizationMutation) SetCreatorID(u uuid.UUID) {
|
||||
m.creator = &u
|
||||
}
|
||||
|
||||
// CreatorID returns the value of the "creator_id" field in the mutation.
|
||||
func (m *OrganizationMutation) CreatorID() (r uuid.UUID, exists bool) {
|
||||
v := m.creator
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldCreatorID returns the old "creator_id" field's value of the Organization entity.
|
||||
// If the Organization 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 *OrganizationMutation) OldCreatorID(ctx context.Context) (v uuid.UUID, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldCreatorID is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldCreatorID requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldCreatorID: %w", err)
|
||||
}
|
||||
return oldValue.CreatorID, nil
|
||||
}
|
||||
|
||||
// ResetCreatorID resets all changes to the "creator_id" field.
|
||||
func (m *OrganizationMutation) ResetCreatorID() {
|
||||
m.creator = nil
|
||||
}
|
||||
|
||||
// ClearCreator clears the "creator" edge to the User entity.
|
||||
func (m *OrganizationMutation) ClearCreator() {
|
||||
m.clearedcreator = true
|
||||
m.clearedFields[organization.FieldCreatorID] = struct{}{}
|
||||
}
|
||||
|
||||
// CreatorCleared reports if the "creator" edge to the User entity was cleared.
|
||||
func (m *OrganizationMutation) CreatorCleared() bool {
|
||||
return m.clearedcreator
|
||||
}
|
||||
|
||||
// CreatorIDs returns the "creator" edge IDs in the mutation.
|
||||
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
|
||||
// CreatorID instead. It exists only for internal usage by the builders.
|
||||
func (m *OrganizationMutation) CreatorIDs() (ids []uuid.UUID) {
|
||||
if id := m.creator; id != nil {
|
||||
ids = append(ids, *id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResetCreator resets all changes to the "creator" edge.
|
||||
func (m *OrganizationMutation) ResetCreator() {
|
||||
m.creator = nil
|
||||
m.clearedcreator = false
|
||||
}
|
||||
|
||||
// AddUserIDs adds the "users" edge to the User entity by ids.
|
||||
func (m *OrganizationMutation) AddUserIDs(ids ...uuid.UUID) {
|
||||
if m.users == nil {
|
||||
m.users = make(map[uuid.UUID]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
m.users[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearUsers clears the "users" edge to the User entity.
|
||||
func (m *OrganizationMutation) ClearUsers() {
|
||||
m.clearedusers = true
|
||||
}
|
||||
|
||||
// UsersCleared reports if the "users" edge to the User entity was cleared.
|
||||
func (m *OrganizationMutation) UsersCleared() bool {
|
||||
return m.clearedusers
|
||||
}
|
||||
|
||||
// RemoveUserIDs removes the "users" edge to the User entity by IDs.
|
||||
func (m *OrganizationMutation) RemoveUserIDs(ids ...uuid.UUID) {
|
||||
if m.removedusers == nil {
|
||||
m.removedusers = make(map[uuid.UUID]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
delete(m.users, ids[i])
|
||||
m.removedusers[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// RemovedUsers returns the removed IDs of the "users" edge to the User entity.
|
||||
func (m *OrganizationMutation) RemovedUsersIDs() (ids []uuid.UUID) {
|
||||
for id := range m.removedusers {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UsersIDs returns the "users" edge IDs in the mutation.
|
||||
func (m *OrganizationMutation) UsersIDs() (ids []uuid.UUID) {
|
||||
for id := range m.users {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResetUsers resets all changes to the "users" edge.
|
||||
func (m *OrganizationMutation) ResetUsers() {
|
||||
m.users = nil
|
||||
m.clearedusers = false
|
||||
m.removedusers = nil
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the OrganizationMutation builder.
|
||||
func (m *OrganizationMutation) Where(ps ...predicate.Organization) {
|
||||
m.predicates = append(m.predicates, ps...)
|
||||
@@ -328,7 +450,7 @@ func (m *OrganizationMutation) Type() string {
|
||||
// order to get all numeric fields that were incremented/decremented, call
|
||||
// AddedFields().
|
||||
func (m *OrganizationMutation) Fields() []string {
|
||||
fields := make([]string, 0, 4)
|
||||
fields := make([]string, 0, 5)
|
||||
if m.created_at != nil {
|
||||
fields = append(fields, organization.FieldCreatedAt)
|
||||
}
|
||||
@@ -341,6 +463,9 @@ func (m *OrganizationMutation) Fields() []string {
|
||||
if m.display_name != nil {
|
||||
fields = append(fields, organization.FieldDisplayName)
|
||||
}
|
||||
if m.creator != nil {
|
||||
fields = append(fields, organization.FieldCreatorID)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
@@ -357,6 +482,8 @@ func (m *OrganizationMutation) Field(name string) (ent.Value, bool) {
|
||||
return m.Name()
|
||||
case organization.FieldDisplayName:
|
||||
return m.DisplayName()
|
||||
case organization.FieldCreatorID:
|
||||
return m.CreatorID()
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
@@ -374,6 +501,8 @@ func (m *OrganizationMutation) OldField(ctx context.Context, name string) (ent.V
|
||||
return m.OldName(ctx)
|
||||
case organization.FieldDisplayName:
|
||||
return m.OldDisplayName(ctx)
|
||||
case organization.FieldCreatorID:
|
||||
return m.OldCreatorID(ctx)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown Organization field %s", name)
|
||||
}
|
||||
@@ -411,6 +540,13 @@ func (m *OrganizationMutation) SetField(name string, value ent.Value) error {
|
||||
}
|
||||
m.SetDisplayName(v)
|
||||
return nil
|
||||
case organization.FieldCreatorID:
|
||||
v, ok := value.(uuid.UUID)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetCreatorID(v)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Organization field %s", name)
|
||||
}
|
||||
@@ -472,74 +608,134 @@ func (m *OrganizationMutation) ResetField(name string) error {
|
||||
case organization.FieldDisplayName:
|
||||
m.ResetDisplayName()
|
||||
return nil
|
||||
case organization.FieldCreatorID:
|
||||
m.ResetCreatorID()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Organization field %s", name)
|
||||
}
|
||||
|
||||
// AddedEdges returns all edge names that were set/added in this mutation.
|
||||
func (m *OrganizationMutation) AddedEdges() []string {
|
||||
edges := make([]string, 0, 0)
|
||||
edges := make([]string, 0, 2)
|
||||
if m.creator != nil {
|
||||
edges = append(edges, organization.EdgeCreator)
|
||||
}
|
||||
if m.users != nil {
|
||||
edges = append(edges, organization.EdgeUsers)
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
|
||||
// name in this mutation.
|
||||
func (m *OrganizationMutation) AddedIDs(name string) []ent.Value {
|
||||
switch name {
|
||||
case organization.EdgeCreator:
|
||||
if id := m.creator; id != nil {
|
||||
return []ent.Value{*id}
|
||||
}
|
||||
case organization.EdgeUsers:
|
||||
ids := make([]ent.Value, 0, len(m.users))
|
||||
for id := range m.users {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovedEdges returns all edge names that were removed in this mutation.
|
||||
func (m *OrganizationMutation) RemovedEdges() []string {
|
||||
edges := make([]string, 0, 0)
|
||||
edges := make([]string, 0, 2)
|
||||
if m.removedusers != nil {
|
||||
edges = append(edges, organization.EdgeUsers)
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
|
||||
// the given name in this mutation.
|
||||
func (m *OrganizationMutation) RemovedIDs(name string) []ent.Value {
|
||||
switch name {
|
||||
case organization.EdgeUsers:
|
||||
ids := make([]ent.Value, 0, len(m.removedusers))
|
||||
for id := range m.removedusers {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearedEdges returns all edge names that were cleared in this mutation.
|
||||
func (m *OrganizationMutation) ClearedEdges() []string {
|
||||
edges := make([]string, 0, 0)
|
||||
edges := make([]string, 0, 2)
|
||||
if m.clearedcreator {
|
||||
edges = append(edges, organization.EdgeCreator)
|
||||
}
|
||||
if m.clearedusers {
|
||||
edges = append(edges, organization.EdgeUsers)
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// EdgeCleared returns a boolean which indicates if the edge with the given name
|
||||
// was cleared in this mutation.
|
||||
func (m *OrganizationMutation) EdgeCleared(name string) bool {
|
||||
switch name {
|
||||
case organization.EdgeCreator:
|
||||
return m.clearedcreator
|
||||
case organization.EdgeUsers:
|
||||
return m.clearedusers
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ClearEdge clears the value of the edge with the given name. It returns an error
|
||||
// if that edge is not defined in the schema.
|
||||
func (m *OrganizationMutation) ClearEdge(name string) error {
|
||||
switch name {
|
||||
case organization.EdgeCreator:
|
||||
m.ClearCreator()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Organization unique edge %s", name)
|
||||
}
|
||||
|
||||
// ResetEdge resets all changes to the edge with the given name in this mutation.
|
||||
// It returns an error if the edge is not defined in the schema.
|
||||
func (m *OrganizationMutation) ResetEdge(name string) error {
|
||||
switch name {
|
||||
case organization.EdgeCreator:
|
||||
m.ResetCreator()
|
||||
return nil
|
||||
case organization.EdgeUsers:
|
||||
m.ResetUsers()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Organization edge %s", name)
|
||||
}
|
||||
|
||||
// UserMutation represents an operation that mutates the User nodes in the graph.
|
||||
type UserMutation struct {
|
||||
config
|
||||
op Op
|
||||
typ string
|
||||
id *uuid.UUID
|
||||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
email *string
|
||||
iss *string
|
||||
sub *string
|
||||
name *string
|
||||
clearedFields map[string]struct{}
|
||||
done bool
|
||||
oldValue func(context.Context) (*User, error)
|
||||
predicates []predicate.User
|
||||
op Op
|
||||
typ string
|
||||
id *uuid.UUID
|
||||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
email *string
|
||||
iss *string
|
||||
sub *string
|
||||
name *string
|
||||
clearedFields map[string]struct{}
|
||||
organizations map[uuid.UUID]struct{}
|
||||
removedorganizations map[uuid.UUID]struct{}
|
||||
clearedorganizations bool
|
||||
done bool
|
||||
oldValue func(context.Context) (*User, error)
|
||||
predicates []predicate.User
|
||||
}
|
||||
|
||||
var _ ent.Mutation = (*UserMutation)(nil)
|
||||
@@ -862,6 +1058,60 @@ func (m *UserMutation) ResetName() {
|
||||
m.name = nil
|
||||
}
|
||||
|
||||
// AddOrganizationIDs adds the "organizations" edge to the Organization entity by ids.
|
||||
func (m *UserMutation) AddOrganizationIDs(ids ...uuid.UUID) {
|
||||
if m.organizations == nil {
|
||||
m.organizations = make(map[uuid.UUID]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
m.organizations[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearOrganizations clears the "organizations" edge to the Organization entity.
|
||||
func (m *UserMutation) ClearOrganizations() {
|
||||
m.clearedorganizations = true
|
||||
}
|
||||
|
||||
// OrganizationsCleared reports if the "organizations" edge to the Organization entity was cleared.
|
||||
func (m *UserMutation) OrganizationsCleared() bool {
|
||||
return m.clearedorganizations
|
||||
}
|
||||
|
||||
// RemoveOrganizationIDs removes the "organizations" edge to the Organization entity by IDs.
|
||||
func (m *UserMutation) RemoveOrganizationIDs(ids ...uuid.UUID) {
|
||||
if m.removedorganizations == nil {
|
||||
m.removedorganizations = make(map[uuid.UUID]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
delete(m.organizations, ids[i])
|
||||
m.removedorganizations[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// RemovedOrganizations returns the removed IDs of the "organizations" edge to the Organization entity.
|
||||
func (m *UserMutation) RemovedOrganizationsIDs() (ids []uuid.UUID) {
|
||||
for id := range m.removedorganizations {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OrganizationsIDs returns the "organizations" edge IDs in the mutation.
|
||||
func (m *UserMutation) OrganizationsIDs() (ids []uuid.UUID) {
|
||||
for id := range m.organizations {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResetOrganizations resets all changes to the "organizations" edge.
|
||||
func (m *UserMutation) ResetOrganizations() {
|
||||
m.organizations = nil
|
||||
m.clearedorganizations = false
|
||||
m.removedorganizations = nil
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the UserMutation builder.
|
||||
func (m *UserMutation) Where(ps ...predicate.User) {
|
||||
m.predicates = append(m.predicates, ps...)
|
||||
@@ -1080,48 +1330,84 @@ func (m *UserMutation) ResetField(name string) error {
|
||||
|
||||
// AddedEdges returns all edge names that were set/added in this mutation.
|
||||
func (m *UserMutation) AddedEdges() []string {
|
||||
edges := make([]string, 0, 0)
|
||||
edges := make([]string, 0, 1)
|
||||
if m.organizations != nil {
|
||||
edges = append(edges, user.EdgeOrganizations)
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
|
||||
// name in this mutation.
|
||||
func (m *UserMutation) AddedIDs(name string) []ent.Value {
|
||||
switch name {
|
||||
case user.EdgeOrganizations:
|
||||
ids := make([]ent.Value, 0, len(m.organizations))
|
||||
for id := range m.organizations {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovedEdges returns all edge names that were removed in this mutation.
|
||||
func (m *UserMutation) RemovedEdges() []string {
|
||||
edges := make([]string, 0, 0)
|
||||
edges := make([]string, 0, 1)
|
||||
if m.removedorganizations != nil {
|
||||
edges = append(edges, user.EdgeOrganizations)
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
|
||||
// the given name in this mutation.
|
||||
func (m *UserMutation) RemovedIDs(name string) []ent.Value {
|
||||
switch name {
|
||||
case user.EdgeOrganizations:
|
||||
ids := make([]ent.Value, 0, len(m.removedorganizations))
|
||||
for id := range m.removedorganizations {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearedEdges returns all edge names that were cleared in this mutation.
|
||||
func (m *UserMutation) ClearedEdges() []string {
|
||||
edges := make([]string, 0, 0)
|
||||
edges := make([]string, 0, 1)
|
||||
if m.clearedorganizations {
|
||||
edges = append(edges, user.EdgeOrganizations)
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// EdgeCleared returns a boolean which indicates if the edge with the given name
|
||||
// was cleared in this mutation.
|
||||
func (m *UserMutation) EdgeCleared(name string) bool {
|
||||
switch name {
|
||||
case user.EdgeOrganizations:
|
||||
return m.clearedorganizations
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ClearEdge clears the value of the edge with the given name. It returns an error
|
||||
// if that edge is not defined in the schema.
|
||||
func (m *UserMutation) ClearEdge(name string) error {
|
||||
switch name {
|
||||
}
|
||||
return fmt.Errorf("unknown User unique edge %s", name)
|
||||
}
|
||||
|
||||
// ResetEdge resets all changes to the edge with the given name in this mutation.
|
||||
// It returns an error if the edge is not defined in the schema.
|
||||
func (m *UserMutation) ResetEdge(name string) error {
|
||||
switch name {
|
||||
case user.EdgeOrganizations:
|
||||
m.ResetOrganizations()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown User edge %s", name)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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/user"
|
||||
)
|
||||
|
||||
// Organization is the model entity for the Organization schema.
|
||||
@@ -25,10 +26,46 @@ type Organization struct {
|
||||
// Name holds the value of the "name" field.
|
||||
Name string `json:"name,omitempty"`
|
||||
// DisplayName holds the value of the "display_name" field.
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
// CreatorID holds the value of the "creator_id" field.
|
||||
CreatorID uuid.UUID `json:"creator_id,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the OrganizationQuery when eager-loading is set.
|
||||
Edges OrganizationEdges `json:"edges"`
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// OrganizationEdges holds the relations/edges for other nodes in the graph.
|
||||
type OrganizationEdges struct {
|
||||
// Creator holds the value of the creator edge.
|
||||
Creator *User `json:"creator,omitempty"`
|
||||
// Users holds the value of the users edge.
|
||||
Users []*User `json:"users,omitempty"`
|
||||
// loadedTypes holds the information for reporting if a
|
||||
// type was loaded (or requested) in eager-loading or not.
|
||||
loadedTypes [2]bool
|
||||
}
|
||||
|
||||
// CreatorOrErr returns the Creator value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e OrganizationEdges) CreatorOrErr() (*User, error) {
|
||||
if e.Creator != nil {
|
||||
return e.Creator, nil
|
||||
} else if e.loadedTypes[0] {
|
||||
return nil, &NotFoundError{label: user.Label}
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "creator"}
|
||||
}
|
||||
|
||||
// UsersOrErr returns the Users value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e OrganizationEdges) UsersOrErr() ([]*User, error) {
|
||||
if e.loadedTypes[1] {
|
||||
return e.Users, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "users"}
|
||||
}
|
||||
|
||||
// scanValues returns the types for scanning values from sql.Rows.
|
||||
func (*Organization) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
@@ -38,7 +75,7 @@ func (*Organization) scanValues(columns []string) ([]any, error) {
|
||||
values[i] = new(sql.NullString)
|
||||
case organization.FieldCreatedAt, organization.FieldUpdatedAt:
|
||||
values[i] = new(sql.NullTime)
|
||||
case organization.FieldID:
|
||||
case organization.FieldID, organization.FieldCreatorID:
|
||||
values[i] = new(uuid.UUID)
|
||||
default:
|
||||
values[i] = new(sql.UnknownType)
|
||||
@@ -85,6 +122,12 @@ func (o *Organization) assignValues(columns []string, values []any) error {
|
||||
} else if value.Valid {
|
||||
o.DisplayName = value.String
|
||||
}
|
||||
case organization.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 {
|
||||
o.CreatorID = *value
|
||||
}
|
||||
default:
|
||||
o.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
@@ -98,6 +141,16 @@ func (o *Organization) Value(name string) (ent.Value, error) {
|
||||
return o.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryCreator queries the "creator" edge of the Organization entity.
|
||||
func (o *Organization) QueryCreator() *UserQuery {
|
||||
return NewOrganizationClient(o.config).QueryCreator(o)
|
||||
}
|
||||
|
||||
// QueryUsers queries the "users" edge of the Organization entity.
|
||||
func (o *Organization) QueryUsers() *UserQuery {
|
||||
return NewOrganizationClient(o.config).QueryUsers(o)
|
||||
}
|
||||
|
||||
// Update returns a builder for updating this Organization.
|
||||
// Note that you need to call Organization.Unwrap() before calling this method if this Organization
|
||||
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||
@@ -132,6 +185,9 @@ func (o *Organization) String() string {
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("display_name=")
|
||||
builder.WriteString(o.DisplayName)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("creator_id=")
|
||||
builder.WriteString(fmt.Sprintf("%v", o.CreatorID))
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
@@ -22,8 +23,26 @@ const (
|
||||
FieldName = "name"
|
||||
// FieldDisplayName holds the string denoting the display_name field in the database.
|
||||
FieldDisplayName = "display_name"
|
||||
// FieldCreatorID holds the string denoting the creator_id field in the database.
|
||||
FieldCreatorID = "creator_id"
|
||||
// EdgeCreator holds the string denoting the creator edge name in mutations.
|
||||
EdgeCreator = "creator"
|
||||
// EdgeUsers holds the string denoting the users edge name in mutations.
|
||||
EdgeUsers = "users"
|
||||
// Table holds the table name of the organization in the database.
|
||||
Table = "organizations"
|
||||
// CreatorTable is the table that holds the creator relation/edge.
|
||||
CreatorTable = "organizations"
|
||||
// CreatorInverseTable is the table name for the User entity.
|
||||
// It exists in this package in order to avoid circular dependency with the "user" package.
|
||||
CreatorInverseTable = "users"
|
||||
// CreatorColumn is the table column denoting the creator relation/edge.
|
||||
CreatorColumn = "creator_id"
|
||||
// UsersTable is the table that holds the users relation/edge. The primary key declared below.
|
||||
UsersTable = "organization_users"
|
||||
// UsersInverseTable is the table name for the User entity.
|
||||
// It exists in this package in order to avoid circular dependency with the "user" package.
|
||||
UsersInverseTable = "users"
|
||||
)
|
||||
|
||||
// Columns holds all SQL columns for organization fields.
|
||||
@@ -33,8 +52,15 @@ var Columns = []string{
|
||||
FieldUpdatedAt,
|
||||
FieldName,
|
||||
FieldDisplayName,
|
||||
FieldCreatorID,
|
||||
}
|
||||
|
||||
var (
|
||||
// UsersPrimaryKey and UsersColumn2 are the table columns denoting the
|
||||
// primary key for the users relation (M2M).
|
||||
UsersPrimaryKey = []string{"organization_id", "user_id"}
|
||||
)
|
||||
|
||||
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||
func ValidColumn(column string) bool {
|
||||
for i := range Columns {
|
||||
@@ -85,3 +111,43 @@ func ByName(opts ...sql.OrderTermOption) OrderOption {
|
||||
func ByDisplayName(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldDisplayName, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatorID orders the results by the creator_id field.
|
||||
func ByCreatorID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatorID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatorField orders the results by creator field.
|
||||
func ByCreatorField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newCreatorStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
|
||||
// ByUsersCount orders the results by users count.
|
||||
func ByUsersCount(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborsCount(s, newUsersStep(), opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// ByUsers orders the results by users terms.
|
||||
func ByUsers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newUsersStep(), append([]sql.OrderTerm{term}, terms...)...)
|
||||
}
|
||||
}
|
||||
func newCreatorStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(CreatorInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, CreatorTable, CreatorColumn),
|
||||
)
|
||||
}
|
||||
func newUsersStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(UsersInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, false, UsersTable, UsersPrimaryKey...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
)
|
||||
@@ -75,6 +76,11 @@ func DisplayName(v string) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldEQ(FieldDisplayName, v))
|
||||
}
|
||||
|
||||
// CreatorID applies equality check predicate on the "creator_id" field. It's identical to CreatorIDEQ.
|
||||
func CreatorID(v uuid.UUID) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldEQ(FieldCreatorID, v))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
func CreatedAtEQ(v time.Time) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldEQ(FieldCreatedAt, v))
|
||||
@@ -285,6 +291,72 @@ func DisplayNameContainsFold(v string) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldContainsFold(FieldDisplayName, v))
|
||||
}
|
||||
|
||||
// CreatorIDEQ applies the EQ predicate on the "creator_id" field.
|
||||
func CreatorIDEQ(v uuid.UUID) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldEQ(FieldCreatorID, v))
|
||||
}
|
||||
|
||||
// CreatorIDNEQ applies the NEQ predicate on the "creator_id" field.
|
||||
func CreatorIDNEQ(v uuid.UUID) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldNEQ(FieldCreatorID, v))
|
||||
}
|
||||
|
||||
// CreatorIDIn applies the In predicate on the "creator_id" field.
|
||||
func CreatorIDIn(vs ...uuid.UUID) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldIn(FieldCreatorID, vs...))
|
||||
}
|
||||
|
||||
// CreatorIDNotIn applies the NotIn predicate on the "creator_id" field.
|
||||
func CreatorIDNotIn(vs ...uuid.UUID) predicate.Organization {
|
||||
return predicate.Organization(sql.FieldNotIn(FieldCreatorID, vs...))
|
||||
}
|
||||
|
||||
// HasCreator applies the HasEdge predicate on the "creator" edge.
|
||||
func HasCreator() predicate.Organization {
|
||||
return predicate.Organization(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, CreatorTable, CreatorColumn),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
})
|
||||
}
|
||||
|
||||
// HasCreatorWith applies the HasEdge predicate on the "creator" edge with a given conditions (other predicates).
|
||||
func HasCreatorWith(preds ...predicate.User) predicate.Organization {
|
||||
return predicate.Organization(func(s *sql.Selector) {
|
||||
step := newCreatorStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// HasUsers applies the HasEdge predicate on the "users" edge.
|
||||
func HasUsers() predicate.Organization {
|
||||
return predicate.Organization(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, false, UsersTable, UsersPrimaryKey...),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
})
|
||||
}
|
||||
|
||||
// HasUsersWith applies the HasEdge predicate on the "users" edge with a given conditions (other predicates).
|
||||
func HasUsersWith(preds ...predicate.User) predicate.Organization {
|
||||
return predicate.Organization(func(s *sql.Selector) {
|
||||
step := newUsersStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.Organization) predicate.Organization {
|
||||
return predicate.Organization(sql.AndPredicates(predicates...))
|
||||
|
||||
@@ -14,6 +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/user"
|
||||
)
|
||||
|
||||
// OrganizationCreate is the builder for creating a Organization entity.
|
||||
@@ -64,6 +65,12 @@ func (oc *OrganizationCreate) SetDisplayName(s string) *OrganizationCreate {
|
||||
return oc
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (oc *OrganizationCreate) SetCreatorID(u uuid.UUID) *OrganizationCreate {
|
||||
oc.mutation.SetCreatorID(u)
|
||||
return oc
|
||||
}
|
||||
|
||||
// SetID sets the "id" field.
|
||||
func (oc *OrganizationCreate) SetID(u uuid.UUID) *OrganizationCreate {
|
||||
oc.mutation.SetID(u)
|
||||
@@ -78,6 +85,26 @@ func (oc *OrganizationCreate) SetNillableID(u *uuid.UUID) *OrganizationCreate {
|
||||
return oc
|
||||
}
|
||||
|
||||
// SetCreator sets the "creator" edge to the User entity.
|
||||
func (oc *OrganizationCreate) SetCreator(u *User) *OrganizationCreate {
|
||||
return oc.SetCreatorID(u.ID)
|
||||
}
|
||||
|
||||
// AddUserIDs adds the "users" edge to the User entity by IDs.
|
||||
func (oc *OrganizationCreate) AddUserIDs(ids ...uuid.UUID) *OrganizationCreate {
|
||||
oc.mutation.AddUserIDs(ids...)
|
||||
return oc
|
||||
}
|
||||
|
||||
// AddUsers adds the "users" edges to the User entity.
|
||||
func (oc *OrganizationCreate) AddUsers(u ...*User) *OrganizationCreate {
|
||||
ids := make([]uuid.UUID, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].ID
|
||||
}
|
||||
return oc.AddUserIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the OrganizationMutation object of the builder.
|
||||
func (oc *OrganizationCreate) Mutation() *OrganizationMutation {
|
||||
return oc.mutation
|
||||
@@ -146,6 +173,12 @@ func (oc *OrganizationCreate) check() error {
|
||||
if _, ok := oc.mutation.DisplayName(); !ok {
|
||||
return &ValidationError{Name: "display_name", err: errors.New(`ent: missing required field "Organization.display_name"`)}
|
||||
}
|
||||
if _, ok := oc.mutation.CreatorID(); !ok {
|
||||
return &ValidationError{Name: "creator_id", err: errors.New(`ent: missing required field "Organization.creator_id"`)}
|
||||
}
|
||||
if _, ok := oc.mutation.CreatorID(); !ok {
|
||||
return &ValidationError{Name: "creator", err: errors.New(`ent: missing required edge "Organization.creator"`)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -198,6 +231,39 @@ func (oc *OrganizationCreate) createSpec() (*Organization, *sqlgraph.CreateSpec)
|
||||
_spec.SetField(organization.FieldDisplayName, field.TypeString, value)
|
||||
_node.DisplayName = value
|
||||
}
|
||||
if nodes := oc.mutation.CreatorIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: organization.CreatorTable,
|
||||
Columns: []string{organization.CreatorColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_node.CreatorID = nodes[0]
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
if nodes := oc.mutation.UsersIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
return _node, _spec
|
||||
}
|
||||
|
||||
@@ -286,6 +352,18 @@ func (u *OrganizationUpsert) UpdateDisplayName() *OrganizationUpsert {
|
||||
return u
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (u *OrganizationUpsert) SetCreatorID(v uuid.UUID) *OrganizationUpsert {
|
||||
u.Set(organization.FieldCreatorID, v)
|
||||
return u
|
||||
}
|
||||
|
||||
// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
|
||||
func (u *OrganizationUpsert) UpdateCreatorID() *OrganizationUpsert {
|
||||
u.SetExcluded(organization.FieldCreatorID)
|
||||
return u
|
||||
}
|
||||
|
||||
// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field.
|
||||
// Using this option is equivalent to using:
|
||||
//
|
||||
@@ -379,6 +457,20 @@ func (u *OrganizationUpsertOne) UpdateDisplayName() *OrganizationUpsertOne {
|
||||
})
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (u *OrganizationUpsertOne) SetCreatorID(v uuid.UUID) *OrganizationUpsertOne {
|
||||
return u.Update(func(s *OrganizationUpsert) {
|
||||
s.SetCreatorID(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
|
||||
func (u *OrganizationUpsertOne) UpdateCreatorID() *OrganizationUpsertOne {
|
||||
return u.Update(func(s *OrganizationUpsert) {
|
||||
s.UpdateCreatorID()
|
||||
})
|
||||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (u *OrganizationUpsertOne) Exec(ctx context.Context) error {
|
||||
if len(u.create.conflict) == 0 {
|
||||
@@ -639,6 +731,20 @@ func (u *OrganizationUpsertBulk) UpdateDisplayName() *OrganizationUpsertBulk {
|
||||
})
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (u *OrganizationUpsertBulk) SetCreatorID(v uuid.UUID) *OrganizationUpsertBulk {
|
||||
return u.Update(func(s *OrganizationUpsert) {
|
||||
s.SetCreatorID(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateCreatorID sets the "creator_id" field to the value that was provided on create.
|
||||
func (u *OrganizationUpsertBulk) UpdateCreatorID() *OrganizationUpsertBulk {
|
||||
return u.Update(func(s *OrganizationUpsert) {
|
||||
s.UpdateCreatorID()
|
||||
})
|
||||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (u *OrganizationUpsertBulk) Exec(ctx context.Context) error {
|
||||
if u.create.err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
@@ -13,15 +14,18 @@ import (
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
)
|
||||
|
||||
// OrganizationQuery is the builder for querying Organization entities.
|
||||
type OrganizationQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []organization.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.Organization
|
||||
ctx *QueryContext
|
||||
order []organization.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.Organization
|
||||
withCreator *UserQuery
|
||||
withUsers *UserQuery
|
||||
// intermediate query (i.e. traversal path).
|
||||
sql *sql.Selector
|
||||
path func(context.Context) (*sql.Selector, error)
|
||||
@@ -58,6 +62,50 @@ func (oq *OrganizationQuery) Order(o ...organization.OrderOption) *OrganizationQ
|
||||
return oq
|
||||
}
|
||||
|
||||
// QueryCreator chains the current query on the "creator" edge.
|
||||
func (oq *OrganizationQuery) QueryCreator() *UserQuery {
|
||||
query := (&UserClient{config: oq.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := oq.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector := oq.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(organization.Table, organization.FieldID, selector),
|
||||
sqlgraph.To(user.Table, user.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, organization.CreatorTable, organization.CreatorColumn),
|
||||
)
|
||||
fromU = sqlgraph.SetNeighbors(oq.driver.Dialect(), step)
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryUsers chains the current query on the "users" edge.
|
||||
func (oq *OrganizationQuery) QueryUsers() *UserQuery {
|
||||
query := (&UserClient{config: oq.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := oq.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector := oq.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(organization.Table, organization.FieldID, selector),
|
||||
sqlgraph.To(user.Table, user.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, false, organization.UsersTable, organization.UsersPrimaryKey...),
|
||||
)
|
||||
fromU = sqlgraph.SetNeighbors(oq.driver.Dialect(), step)
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// First returns the first Organization entity from the query.
|
||||
// Returns a *NotFoundError when no Organization was found.
|
||||
func (oq *OrganizationQuery) First(ctx context.Context) (*Organization, error) {
|
||||
@@ -245,17 +293,41 @@ func (oq *OrganizationQuery) Clone() *OrganizationQuery {
|
||||
return nil
|
||||
}
|
||||
return &OrganizationQuery{
|
||||
config: oq.config,
|
||||
ctx: oq.ctx.Clone(),
|
||||
order: append([]organization.OrderOption{}, oq.order...),
|
||||
inters: append([]Interceptor{}, oq.inters...),
|
||||
predicates: append([]predicate.Organization{}, oq.predicates...),
|
||||
config: oq.config,
|
||||
ctx: oq.ctx.Clone(),
|
||||
order: append([]organization.OrderOption{}, oq.order...),
|
||||
inters: append([]Interceptor{}, oq.inters...),
|
||||
predicates: append([]predicate.Organization{}, oq.predicates...),
|
||||
withCreator: oq.withCreator.Clone(),
|
||||
withUsers: oq.withUsers.Clone(),
|
||||
// clone intermediate query.
|
||||
sql: oq.sql.Clone(),
|
||||
path: oq.path,
|
||||
}
|
||||
}
|
||||
|
||||
// WithCreator tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "creator" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (oq *OrganizationQuery) WithCreator(opts ...func(*UserQuery)) *OrganizationQuery {
|
||||
query := (&UserClient{config: oq.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
oq.withCreator = query
|
||||
return oq
|
||||
}
|
||||
|
||||
// WithUsers tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "users" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (oq *OrganizationQuery) WithUsers(opts ...func(*UserQuery)) *OrganizationQuery {
|
||||
query := (&UserClient{config: oq.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
oq.withUsers = query
|
||||
return oq
|
||||
}
|
||||
|
||||
// GroupBy is used to group vertices by one or more fields/columns.
|
||||
// It is often used with aggregate functions, like: count, max, mean, min, sum.
|
||||
//
|
||||
@@ -332,8 +404,12 @@ func (oq *OrganizationQuery) prepareQuery(ctx context.Context) error {
|
||||
|
||||
func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Organization, error) {
|
||||
var (
|
||||
nodes = []*Organization{}
|
||||
_spec = oq.querySpec()
|
||||
nodes = []*Organization{}
|
||||
_spec = oq.querySpec()
|
||||
loadedTypes = [2]bool{
|
||||
oq.withCreator != nil,
|
||||
oq.withUsers != nil,
|
||||
}
|
||||
)
|
||||
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
return (*Organization).scanValues(nil, columns)
|
||||
@@ -341,6 +417,7 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]
|
||||
_spec.Assign = func(columns []string, values []any) error {
|
||||
node := &Organization{config: oq.config}
|
||||
nodes = append(nodes, node)
|
||||
node.Edges.loadedTypes = loadedTypes
|
||||
return node.assignValues(columns, values)
|
||||
}
|
||||
for i := range hooks {
|
||||
@@ -352,9 +429,113 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]
|
||||
if len(nodes) == 0 {
|
||||
return nodes, nil
|
||||
}
|
||||
if query := oq.withCreator; query != nil {
|
||||
if err := oq.loadCreator(ctx, query, nodes, nil,
|
||||
func(n *Organization, e *User) { n.Edges.Creator = e }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if query := oq.withUsers; query != nil {
|
||||
if err := oq.loadUsers(ctx, query, nodes,
|
||||
func(n *Organization) { n.Edges.Users = []*User{} },
|
||||
func(n *Organization, e *User) { n.Edges.Users = append(n.Edges.Users, e) }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func (oq *OrganizationQuery) loadCreator(ctx context.Context, query *UserQuery, nodes []*Organization, init func(*Organization), assign func(*Organization, *User)) error {
|
||||
ids := make([]uuid.UUID, 0, len(nodes))
|
||||
nodeids := make(map[uuid.UUID][]*Organization)
|
||||
for i := range nodes {
|
||||
fk := nodes[i].CreatorID
|
||||
if _, ok := nodeids[fk]; !ok {
|
||||
ids = append(ids, fk)
|
||||
}
|
||||
nodeids[fk] = append(nodeids[fk], nodes[i])
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
query.Where(user.IDIn(ids...))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nodeids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected foreign-key "creator_id" returned %v`, n.ID)
|
||||
}
|
||||
for i := range nodes {
|
||||
assign(nodes[i], n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (oq *OrganizationQuery) loadUsers(ctx context.Context, query *UserQuery, nodes []*Organization, init func(*Organization), assign func(*Organization, *User)) error {
|
||||
edgeIDs := make([]driver.Value, len(nodes))
|
||||
byID := make(map[uuid.UUID]*Organization)
|
||||
nids := make(map[uuid.UUID]map[*Organization]struct{})
|
||||
for i, node := range nodes {
|
||||
edgeIDs[i] = node.ID
|
||||
byID[node.ID] = node
|
||||
if init != nil {
|
||||
init(node)
|
||||
}
|
||||
}
|
||||
query.Where(func(s *sql.Selector) {
|
||||
joinT := sql.Table(organization.UsersTable)
|
||||
s.Join(joinT).On(s.C(user.FieldID), joinT.C(organization.UsersPrimaryKey[1]))
|
||||
s.Where(sql.InValues(joinT.C(organization.UsersPrimaryKey[0]), edgeIDs...))
|
||||
columns := s.SelectedColumns()
|
||||
s.Select(joinT.C(organization.UsersPrimaryKey[0]))
|
||||
s.AppendSelect(columns...)
|
||||
s.SetDistinct(false)
|
||||
})
|
||||
if err := query.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) {
|
||||
assign := spec.Assign
|
||||
values := spec.ScanValues
|
||||
spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
values, err := values(columns[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append([]any{new(uuid.UUID)}, values...), nil
|
||||
}
|
||||
spec.Assign = func(columns []string, values []any) error {
|
||||
outValue := *values[0].(*uuid.UUID)
|
||||
inValue := *values[1].(*uuid.UUID)
|
||||
if nids[inValue] == nil {
|
||||
nids[inValue] = map[*Organization]struct{}{byID[outValue]: {}}
|
||||
return assign(columns[1:], values[1:])
|
||||
}
|
||||
nids[inValue][byID[outValue]] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
})
|
||||
neighbors, err := withInterceptors[[]*User](ctx, query, qr, query.inters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected "users" node returned %v`, n.ID)
|
||||
}
|
||||
for kn := range nodes {
|
||||
assign(kn, n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (oq *OrganizationQuery) sqlCount(ctx context.Context) (int, error) {
|
||||
_spec := oq.querySpec()
|
||||
_spec.Node.Columns = oq.ctx.Fields
|
||||
@@ -380,6 +561,9 @@ func (oq *OrganizationQuery) querySpec() *sqlgraph.QuerySpec {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
|
||||
}
|
||||
}
|
||||
if oq.withCreator != nil {
|
||||
_spec.Node.AddColumnOnce(organization.FieldCreatorID)
|
||||
}
|
||||
}
|
||||
if ps := oq.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
|
||||
@@ -11,8 +11,10 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
)
|
||||
|
||||
// OrganizationUpdate is the builder for updating Organization entities.
|
||||
@@ -62,11 +64,72 @@ func (ou *OrganizationUpdate) SetNillableDisplayName(s *string) *OrganizationUpd
|
||||
return ou
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (ou *OrganizationUpdate) SetCreatorID(u uuid.UUID) *OrganizationUpdate {
|
||||
ou.mutation.SetCreatorID(u)
|
||||
return ou
|
||||
}
|
||||
|
||||
// SetNillableCreatorID sets the "creator_id" field if the given value is not nil.
|
||||
func (ou *OrganizationUpdate) SetNillableCreatorID(u *uuid.UUID) *OrganizationUpdate {
|
||||
if u != nil {
|
||||
ou.SetCreatorID(*u)
|
||||
}
|
||||
return ou
|
||||
}
|
||||
|
||||
// SetCreator sets the "creator" edge to the User entity.
|
||||
func (ou *OrganizationUpdate) SetCreator(u *User) *OrganizationUpdate {
|
||||
return ou.SetCreatorID(u.ID)
|
||||
}
|
||||
|
||||
// AddUserIDs adds the "users" edge to the User entity by IDs.
|
||||
func (ou *OrganizationUpdate) AddUserIDs(ids ...uuid.UUID) *OrganizationUpdate {
|
||||
ou.mutation.AddUserIDs(ids...)
|
||||
return ou
|
||||
}
|
||||
|
||||
// AddUsers adds the "users" edges to the User entity.
|
||||
func (ou *OrganizationUpdate) AddUsers(u ...*User) *OrganizationUpdate {
|
||||
ids := make([]uuid.UUID, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].ID
|
||||
}
|
||||
return ou.AddUserIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the OrganizationMutation object of the builder.
|
||||
func (ou *OrganizationUpdate) Mutation() *OrganizationMutation {
|
||||
return ou.mutation
|
||||
}
|
||||
|
||||
// ClearCreator clears the "creator" edge to the User entity.
|
||||
func (ou *OrganizationUpdate) ClearCreator() *OrganizationUpdate {
|
||||
ou.mutation.ClearCreator()
|
||||
return ou
|
||||
}
|
||||
|
||||
// ClearUsers clears all "users" edges to the User entity.
|
||||
func (ou *OrganizationUpdate) ClearUsers() *OrganizationUpdate {
|
||||
ou.mutation.ClearUsers()
|
||||
return ou
|
||||
}
|
||||
|
||||
// RemoveUserIDs removes the "users" edge to User entities by IDs.
|
||||
func (ou *OrganizationUpdate) RemoveUserIDs(ids ...uuid.UUID) *OrganizationUpdate {
|
||||
ou.mutation.RemoveUserIDs(ids...)
|
||||
return ou
|
||||
}
|
||||
|
||||
// RemoveUsers removes "users" edges to User entities.
|
||||
func (ou *OrganizationUpdate) RemoveUsers(u ...*User) *OrganizationUpdate {
|
||||
ids := make([]uuid.UUID, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].ID
|
||||
}
|
||||
return ou.RemoveUserIDs(ids...)
|
||||
}
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (ou *OrganizationUpdate) Save(ctx context.Context) (int, error) {
|
||||
ou.defaults()
|
||||
@@ -110,6 +173,9 @@ func (ou *OrganizationUpdate) check() error {
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Organization.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := ou.mutation.CreatorID(); ou.mutation.CreatorCleared() && !ok {
|
||||
return errors.New(`ent: clearing a required unique edge "Organization.creator"`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -134,6 +200,80 @@ func (ou *OrganizationUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
if value, ok := ou.mutation.DisplayName(); ok {
|
||||
_spec.SetField(organization.FieldDisplayName, field.TypeString, value)
|
||||
}
|
||||
if ou.mutation.CreatorCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: organization.CreatorTable,
|
||||
Columns: []string{organization.CreatorColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ou.mutation.CreatorIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: organization.CreatorTable,
|
||||
Columns: []string{organization.CreatorColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if ou.mutation.UsersCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ou.mutation.RemovedUsersIDs(); len(nodes) > 0 && !ou.mutation.UsersCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ou.mutation.UsersIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if n, err = sqlgraph.UpdateNodes(ctx, ou.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{organization.Label}
|
||||
@@ -188,11 +328,72 @@ func (ouo *OrganizationUpdateOne) SetNillableDisplayName(s *string) *Organizatio
|
||||
return ouo
|
||||
}
|
||||
|
||||
// SetCreatorID sets the "creator_id" field.
|
||||
func (ouo *OrganizationUpdateOne) SetCreatorID(u uuid.UUID) *OrganizationUpdateOne {
|
||||
ouo.mutation.SetCreatorID(u)
|
||||
return ouo
|
||||
}
|
||||
|
||||
// SetNillableCreatorID sets the "creator_id" field if the given value is not nil.
|
||||
func (ouo *OrganizationUpdateOne) SetNillableCreatorID(u *uuid.UUID) *OrganizationUpdateOne {
|
||||
if u != nil {
|
||||
ouo.SetCreatorID(*u)
|
||||
}
|
||||
return ouo
|
||||
}
|
||||
|
||||
// SetCreator sets the "creator" edge to the User entity.
|
||||
func (ouo *OrganizationUpdateOne) SetCreator(u *User) *OrganizationUpdateOne {
|
||||
return ouo.SetCreatorID(u.ID)
|
||||
}
|
||||
|
||||
// AddUserIDs adds the "users" edge to the User entity by IDs.
|
||||
func (ouo *OrganizationUpdateOne) AddUserIDs(ids ...uuid.UUID) *OrganizationUpdateOne {
|
||||
ouo.mutation.AddUserIDs(ids...)
|
||||
return ouo
|
||||
}
|
||||
|
||||
// AddUsers adds the "users" edges to the User entity.
|
||||
func (ouo *OrganizationUpdateOne) AddUsers(u ...*User) *OrganizationUpdateOne {
|
||||
ids := make([]uuid.UUID, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].ID
|
||||
}
|
||||
return ouo.AddUserIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the OrganizationMutation object of the builder.
|
||||
func (ouo *OrganizationUpdateOne) Mutation() *OrganizationMutation {
|
||||
return ouo.mutation
|
||||
}
|
||||
|
||||
// ClearCreator clears the "creator" edge to the User entity.
|
||||
func (ouo *OrganizationUpdateOne) ClearCreator() *OrganizationUpdateOne {
|
||||
ouo.mutation.ClearCreator()
|
||||
return ouo
|
||||
}
|
||||
|
||||
// ClearUsers clears all "users" edges to the User entity.
|
||||
func (ouo *OrganizationUpdateOne) ClearUsers() *OrganizationUpdateOne {
|
||||
ouo.mutation.ClearUsers()
|
||||
return ouo
|
||||
}
|
||||
|
||||
// RemoveUserIDs removes the "users" edge to User entities by IDs.
|
||||
func (ouo *OrganizationUpdateOne) RemoveUserIDs(ids ...uuid.UUID) *OrganizationUpdateOne {
|
||||
ouo.mutation.RemoveUserIDs(ids...)
|
||||
return ouo
|
||||
}
|
||||
|
||||
// RemoveUsers removes "users" edges to User entities.
|
||||
func (ouo *OrganizationUpdateOne) RemoveUsers(u ...*User) *OrganizationUpdateOne {
|
||||
ids := make([]uuid.UUID, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].ID
|
||||
}
|
||||
return ouo.RemoveUserIDs(ids...)
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the OrganizationUpdate builder.
|
||||
func (ouo *OrganizationUpdateOne) Where(ps ...predicate.Organization) *OrganizationUpdateOne {
|
||||
ouo.mutation.Where(ps...)
|
||||
@@ -249,6 +450,9 @@ func (ouo *OrganizationUpdateOne) check() error {
|
||||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Organization.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := ouo.mutation.CreatorID(); ouo.mutation.CreatorCleared() && !ok {
|
||||
return errors.New(`ent: clearing a required unique edge "Organization.creator"`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -290,6 +494,80 @@ func (ouo *OrganizationUpdateOne) sqlSave(ctx context.Context) (_node *Organizat
|
||||
if value, ok := ouo.mutation.DisplayName(); ok {
|
||||
_spec.SetField(organization.FieldDisplayName, field.TypeString, value)
|
||||
}
|
||||
if ouo.mutation.CreatorCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: organization.CreatorTable,
|
||||
Columns: []string{organization.CreatorColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ouo.mutation.CreatorIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: organization.CreatorTable,
|
||||
Columns: []string{organization.CreatorColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if ouo.mutation.UsersCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ouo.mutation.RemovedUsersIDs(); len(nodes) > 0 && !ouo.mutation.UsersCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := ouo.mutation.UsersIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: false,
|
||||
Table: organization.UsersTable,
|
||||
Columns: organization.UsersPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
_node = &Organization{config: ouo.config}
|
||||
_spec.Assign = _node.assignValues
|
||||
_spec.ScanValues = _node.scanValues
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/mixin"
|
||||
"github.com/gofrs/uuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newUUID() uuid.UUID {
|
||||
@@ -18,6 +19,7 @@ type BaseMixin struct {
|
||||
|
||||
func (BaseMixin) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
// id represents the identity of the entity.
|
||||
field.UUID("id", uuid.UUID{}).Default(newUUID),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,37 @@ package schema
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/edge"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
// User holds the schema definition for the User entity, the internal
|
||||
// representation and identity of a single human user. Users are scoped
|
||||
// globally.
|
||||
// Organization represents an organization account.
|
||||
type Organization struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
func (Organization) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("name").NotEmpty().Unique(),
|
||||
field.String("display_name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (Organization) Mixin() []ent.Mixin {
|
||||
return []ent.Mixin{
|
||||
BaseMixin{},
|
||||
TimeMixin{},
|
||||
}
|
||||
}
|
||||
|
||||
func (Organization) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("name").NotEmpty().Unique(),
|
||||
field.String("display_name"),
|
||||
field.UUID("creator_id", uuid.UUID{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (Organization) Edges() []ent.Edge {
|
||||
return []ent.Edge{
|
||||
edge.To("creator", User.Type).
|
||||
Field("creator_id").
|
||||
Unique().
|
||||
Required(),
|
||||
edge.To("users", User.Type),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package schema
|
||||
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/edge"
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// User holds the schema definition for the User entity, the internal
|
||||
@@ -28,3 +30,16 @@ func (User) Fields() []ent.Field {
|
||||
field.String("name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (User) Edges() []ent.Edge {
|
||||
return []ent.Edge{
|
||||
edge.From("organizations", Organization.Type).
|
||||
Ref("users"),
|
||||
}
|
||||
}
|
||||
|
||||
func (User) Indexes() []ent.Index {
|
||||
return []ent.Index{
|
||||
index.Fields("iss", "sub").Unique(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,31 @@ type User struct {
|
||||
// Sub holds the value of the "sub" field.
|
||||
Sub string `json:"sub,omitempty"`
|
||||
// Name holds the value of the "name" field.
|
||||
Name string `json:"name,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the UserQuery when eager-loading is set.
|
||||
Edges UserEdges `json:"edges"`
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// UserEdges holds the relations/edges for other nodes in the graph.
|
||||
type UserEdges struct {
|
||||
// Organizations holds the value of the organizations edge.
|
||||
Organizations []*Organization `json:"organizations,omitempty"`
|
||||
// loadedTypes holds the information for reporting if a
|
||||
// type was loaded (or requested) in eager-loading or not.
|
||||
loadedTypes [1]bool
|
||||
}
|
||||
|
||||
// OrganizationsOrErr returns the Organizations value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e UserEdges) OrganizationsOrErr() ([]*Organization, error) {
|
||||
if e.loadedTypes[0] {
|
||||
return e.Organizations, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "organizations"}
|
||||
}
|
||||
|
||||
// scanValues returns the types for scanning values from sql.Rows.
|
||||
func (*User) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
@@ -114,6 +135,11 @@ func (u *User) Value(name string) (ent.Value, error) {
|
||||
return u.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryOrganizations queries the "organizations" edge of the User entity.
|
||||
func (u *User) QueryOrganizations() *OrganizationQuery {
|
||||
return NewUserClient(u.config).QueryOrganizations(u)
|
||||
}
|
||||
|
||||
// Update returns a builder for updating this User.
|
||||
// Note that you need to call User.Unwrap() before calling this method if this User
|
||||
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
@@ -26,8 +27,15 @@ const (
|
||||
FieldSub = "sub"
|
||||
// FieldName holds the string denoting the name field in the database.
|
||||
FieldName = "name"
|
||||
// EdgeOrganizations holds the string denoting the organizations edge name in mutations.
|
||||
EdgeOrganizations = "organizations"
|
||||
// Table holds the table name of the user in the database.
|
||||
Table = "users"
|
||||
// OrganizationsTable is the table that holds the organizations relation/edge. The primary key declared below.
|
||||
OrganizationsTable = "organization_users"
|
||||
// OrganizationsInverseTable is the table name for the Organization entity.
|
||||
// It exists in this package in order to avoid circular dependency with the "organization" package.
|
||||
OrganizationsInverseTable = "organizations"
|
||||
)
|
||||
|
||||
// Columns holds all SQL columns for user fields.
|
||||
@@ -41,6 +49,12 @@ var Columns = []string{
|
||||
FieldName,
|
||||
}
|
||||
|
||||
var (
|
||||
// OrganizationsPrimaryKey and OrganizationsColumn2 are the table columns denoting the
|
||||
// primary key for the organizations relation (M2M).
|
||||
OrganizationsPrimaryKey = []string{"organization_id", "user_id"}
|
||||
)
|
||||
|
||||
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||
func ValidColumn(column string) bool {
|
||||
for i := range Columns {
|
||||
@@ -101,3 +115,24 @@ func BySub(opts ...sql.OrderTermOption) OrderOption {
|
||||
func ByName(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldName, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByOrganizationsCount orders the results by organizations count.
|
||||
func ByOrganizationsCount(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborsCount(s, newOrganizationsStep(), opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// ByOrganizations orders the results by organizations terms.
|
||||
func ByOrganizations(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newOrganizationsStep(), append([]sql.OrderTerm{term}, terms...)...)
|
||||
}
|
||||
}
|
||||
func newOrganizationsStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(OrganizationsInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, true, OrganizationsTable, OrganizationsPrimaryKey...),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
)
|
||||
@@ -425,6 +426,29 @@ func NameContainsFold(v string) predicate.User {
|
||||
return predicate.User(sql.FieldContainsFold(FieldName, v))
|
||||
}
|
||||
|
||||
// HasOrganizations applies the HasEdge predicate on the "organizations" edge.
|
||||
func HasOrganizations() predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, true, OrganizationsTable, OrganizationsPrimaryKey...),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
})
|
||||
}
|
||||
|
||||
// HasOrganizationsWith applies the HasEdge predicate on the "organizations" edge with a given conditions (other predicates).
|
||||
func HasOrganizationsWith(preds ...predicate.Organization) predicate.User {
|
||||
return predicate.User(func(s *sql.Selector) {
|
||||
step := newOrganizationsStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.User) predicate.User {
|
||||
return predicate.User(sql.AndPredicates(predicates...))
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
)
|
||||
|
||||
@@ -90,6 +91,21 @@ func (uc *UserCreate) SetNillableID(u *uuid.UUID) *UserCreate {
|
||||
return uc
|
||||
}
|
||||
|
||||
// AddOrganizationIDs adds the "organizations" edge to the Organization entity by IDs.
|
||||
func (uc *UserCreate) AddOrganizationIDs(ids ...uuid.UUID) *UserCreate {
|
||||
uc.mutation.AddOrganizationIDs(ids...)
|
||||
return uc
|
||||
}
|
||||
|
||||
// AddOrganizations adds the "organizations" edges to the Organization entity.
|
||||
func (uc *UserCreate) AddOrganizations(o ...*Organization) *UserCreate {
|
||||
ids := make([]uuid.UUID, len(o))
|
||||
for i := range o {
|
||||
ids[i] = o[i].ID
|
||||
}
|
||||
return uc.AddOrganizationIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the UserMutation object of the builder.
|
||||
func (uc *UserCreate) Mutation() *UserMutation {
|
||||
return uc.mutation
|
||||
@@ -224,6 +240,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
|
||||
_spec.SetField(user.FieldName, field.TypeString, value)
|
||||
_node.Name = value
|
||||
}
|
||||
if nodes := uc.mutation.OrganizationsIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
return _node, _spec
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
)
|
||||
@@ -18,10 +20,11 @@ import (
|
||||
// UserQuery is the builder for querying User entities.
|
||||
type UserQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []user.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.User
|
||||
ctx *QueryContext
|
||||
order []user.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.User
|
||||
withOrganizations *OrganizationQuery
|
||||
// intermediate query (i.e. traversal path).
|
||||
sql *sql.Selector
|
||||
path func(context.Context) (*sql.Selector, error)
|
||||
@@ -58,6 +61,28 @@ func (uq *UserQuery) Order(o ...user.OrderOption) *UserQuery {
|
||||
return uq
|
||||
}
|
||||
|
||||
// QueryOrganizations chains the current query on the "organizations" edge.
|
||||
func (uq *UserQuery) QueryOrganizations() *OrganizationQuery {
|
||||
query := (&OrganizationClient{config: uq.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := uq.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector := uq.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(user.Table, user.FieldID, selector),
|
||||
sqlgraph.To(organization.Table, organization.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2M, true, user.OrganizationsTable, user.OrganizationsPrimaryKey...),
|
||||
)
|
||||
fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step)
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// First returns the first User entity from the query.
|
||||
// Returns a *NotFoundError when no User was found.
|
||||
func (uq *UserQuery) First(ctx context.Context) (*User, error) {
|
||||
@@ -245,17 +270,29 @@ func (uq *UserQuery) Clone() *UserQuery {
|
||||
return nil
|
||||
}
|
||||
return &UserQuery{
|
||||
config: uq.config,
|
||||
ctx: uq.ctx.Clone(),
|
||||
order: append([]user.OrderOption{}, uq.order...),
|
||||
inters: append([]Interceptor{}, uq.inters...),
|
||||
predicates: append([]predicate.User{}, uq.predicates...),
|
||||
config: uq.config,
|
||||
ctx: uq.ctx.Clone(),
|
||||
order: append([]user.OrderOption{}, uq.order...),
|
||||
inters: append([]Interceptor{}, uq.inters...),
|
||||
predicates: append([]predicate.User{}, uq.predicates...),
|
||||
withOrganizations: uq.withOrganizations.Clone(),
|
||||
// clone intermediate query.
|
||||
sql: uq.sql.Clone(),
|
||||
path: uq.path,
|
||||
}
|
||||
}
|
||||
|
||||
// WithOrganizations tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "organizations" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (uq *UserQuery) WithOrganizations(opts ...func(*OrganizationQuery)) *UserQuery {
|
||||
query := (&OrganizationClient{config: uq.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
uq.withOrganizations = query
|
||||
return uq
|
||||
}
|
||||
|
||||
// GroupBy is used to group vertices by one or more fields/columns.
|
||||
// It is often used with aggregate functions, like: count, max, mean, min, sum.
|
||||
//
|
||||
@@ -332,8 +369,11 @@ func (uq *UserQuery) prepareQuery(ctx context.Context) error {
|
||||
|
||||
func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, error) {
|
||||
var (
|
||||
nodes = []*User{}
|
||||
_spec = uq.querySpec()
|
||||
nodes = []*User{}
|
||||
_spec = uq.querySpec()
|
||||
loadedTypes = [1]bool{
|
||||
uq.withOrganizations != nil,
|
||||
}
|
||||
)
|
||||
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
return (*User).scanValues(nil, columns)
|
||||
@@ -341,6 +381,7 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
|
||||
_spec.Assign = func(columns []string, values []any) error {
|
||||
node := &User{config: uq.config}
|
||||
nodes = append(nodes, node)
|
||||
node.Edges.loadedTypes = loadedTypes
|
||||
return node.assignValues(columns, values)
|
||||
}
|
||||
for i := range hooks {
|
||||
@@ -352,9 +393,78 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
|
||||
if len(nodes) == 0 {
|
||||
return nodes, nil
|
||||
}
|
||||
if query := uq.withOrganizations; query != nil {
|
||||
if err := uq.loadOrganizations(ctx, query, nodes,
|
||||
func(n *User) { n.Edges.Organizations = []*Organization{} },
|
||||
func(n *User, e *Organization) { n.Edges.Organizations = append(n.Edges.Organizations, e) }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func (uq *UserQuery) loadOrganizations(ctx context.Context, query *OrganizationQuery, nodes []*User, init func(*User), assign func(*User, *Organization)) error {
|
||||
edgeIDs := make([]driver.Value, len(nodes))
|
||||
byID := make(map[uuid.UUID]*User)
|
||||
nids := make(map[uuid.UUID]map[*User]struct{})
|
||||
for i, node := range nodes {
|
||||
edgeIDs[i] = node.ID
|
||||
byID[node.ID] = node
|
||||
if init != nil {
|
||||
init(node)
|
||||
}
|
||||
}
|
||||
query.Where(func(s *sql.Selector) {
|
||||
joinT := sql.Table(user.OrganizationsTable)
|
||||
s.Join(joinT).On(s.C(organization.FieldID), joinT.C(user.OrganizationsPrimaryKey[0]))
|
||||
s.Where(sql.InValues(joinT.C(user.OrganizationsPrimaryKey[1]), edgeIDs...))
|
||||
columns := s.SelectedColumns()
|
||||
s.Select(joinT.C(user.OrganizationsPrimaryKey[1]))
|
||||
s.AppendSelect(columns...)
|
||||
s.SetDistinct(false)
|
||||
})
|
||||
if err := query.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) {
|
||||
assign := spec.Assign
|
||||
values := spec.ScanValues
|
||||
spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
values, err := values(columns[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append([]any{new(uuid.UUID)}, values...), nil
|
||||
}
|
||||
spec.Assign = func(columns []string, values []any) error {
|
||||
outValue := *values[0].(*uuid.UUID)
|
||||
inValue := *values[1].(*uuid.UUID)
|
||||
if nids[inValue] == nil {
|
||||
nids[inValue] = map[*User]struct{}{byID[outValue]: {}}
|
||||
return assign(columns[1:], values[1:])
|
||||
}
|
||||
nids[inValue][byID[outValue]] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
})
|
||||
neighbors, err := withInterceptors[[]*Organization](ctx, query, qr, query.inters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected "organizations" node returned %v`, n.ID)
|
||||
}
|
||||
for kn := range nodes {
|
||||
assign(kn, n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) {
|
||||
_spec := uq.querySpec()
|
||||
_spec.Node.Columns = uq.ctx.Fields
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
)
|
||||
@@ -90,11 +92,47 @@ func (uu *UserUpdate) SetNillableName(s *string) *UserUpdate {
|
||||
return uu
|
||||
}
|
||||
|
||||
// AddOrganizationIDs adds the "organizations" edge to the Organization entity by IDs.
|
||||
func (uu *UserUpdate) AddOrganizationIDs(ids ...uuid.UUID) *UserUpdate {
|
||||
uu.mutation.AddOrganizationIDs(ids...)
|
||||
return uu
|
||||
}
|
||||
|
||||
// AddOrganizations adds the "organizations" edges to the Organization entity.
|
||||
func (uu *UserUpdate) AddOrganizations(o ...*Organization) *UserUpdate {
|
||||
ids := make([]uuid.UUID, len(o))
|
||||
for i := range o {
|
||||
ids[i] = o[i].ID
|
||||
}
|
||||
return uu.AddOrganizationIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the UserMutation object of the builder.
|
||||
func (uu *UserUpdate) Mutation() *UserMutation {
|
||||
return uu.mutation
|
||||
}
|
||||
|
||||
// ClearOrganizations clears all "organizations" edges to the Organization entity.
|
||||
func (uu *UserUpdate) ClearOrganizations() *UserUpdate {
|
||||
uu.mutation.ClearOrganizations()
|
||||
return uu
|
||||
}
|
||||
|
||||
// RemoveOrganizationIDs removes the "organizations" edge to Organization entities by IDs.
|
||||
func (uu *UserUpdate) RemoveOrganizationIDs(ids ...uuid.UUID) *UserUpdate {
|
||||
uu.mutation.RemoveOrganizationIDs(ids...)
|
||||
return uu
|
||||
}
|
||||
|
||||
// RemoveOrganizations removes "organizations" edges to Organization entities.
|
||||
func (uu *UserUpdate) RemoveOrganizations(o ...*Organization) *UserUpdate {
|
||||
ids := make([]uuid.UUID, len(o))
|
||||
for i := range o {
|
||||
ids[i] = o[i].ID
|
||||
}
|
||||
return uu.RemoveOrganizationIDs(ids...)
|
||||
}
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (uu *UserUpdate) Save(ctx context.Context) (int, error) {
|
||||
uu.defaults()
|
||||
@@ -168,6 +206,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
if value, ok := uu.mutation.Name(); ok {
|
||||
_spec.SetField(user.FieldName, field.TypeString, value)
|
||||
}
|
||||
if uu.mutation.OrganizationsCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uu.mutation.RemovedOrganizationsIDs(); len(nodes) > 0 && !uu.mutation.OrganizationsCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uu.mutation.OrganizationsIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{user.Label}
|
||||
@@ -250,11 +333,47 @@ func (uuo *UserUpdateOne) SetNillableName(s *string) *UserUpdateOne {
|
||||
return uuo
|
||||
}
|
||||
|
||||
// AddOrganizationIDs adds the "organizations" edge to the Organization entity by IDs.
|
||||
func (uuo *UserUpdateOne) AddOrganizationIDs(ids ...uuid.UUID) *UserUpdateOne {
|
||||
uuo.mutation.AddOrganizationIDs(ids...)
|
||||
return uuo
|
||||
}
|
||||
|
||||
// AddOrganizations adds the "organizations" edges to the Organization entity.
|
||||
func (uuo *UserUpdateOne) AddOrganizations(o ...*Organization) *UserUpdateOne {
|
||||
ids := make([]uuid.UUID, len(o))
|
||||
for i := range o {
|
||||
ids[i] = o[i].ID
|
||||
}
|
||||
return uuo.AddOrganizationIDs(ids...)
|
||||
}
|
||||
|
||||
// Mutation returns the UserMutation object of the builder.
|
||||
func (uuo *UserUpdateOne) Mutation() *UserMutation {
|
||||
return uuo.mutation
|
||||
}
|
||||
|
||||
// ClearOrganizations clears all "organizations" edges to the Organization entity.
|
||||
func (uuo *UserUpdateOne) ClearOrganizations() *UserUpdateOne {
|
||||
uuo.mutation.ClearOrganizations()
|
||||
return uuo
|
||||
}
|
||||
|
||||
// RemoveOrganizationIDs removes the "organizations" edge to Organization entities by IDs.
|
||||
func (uuo *UserUpdateOne) RemoveOrganizationIDs(ids ...uuid.UUID) *UserUpdateOne {
|
||||
uuo.mutation.RemoveOrganizationIDs(ids...)
|
||||
return uuo
|
||||
}
|
||||
|
||||
// RemoveOrganizations removes "organizations" edges to Organization entities.
|
||||
func (uuo *UserUpdateOne) RemoveOrganizations(o ...*Organization) *UserUpdateOne {
|
||||
ids := make([]uuid.UUID, len(o))
|
||||
for i := range o {
|
||||
ids[i] = o[i].ID
|
||||
}
|
||||
return uuo.RemoveOrganizationIDs(ids...)
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the UserUpdate builder.
|
||||
func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne {
|
||||
uuo.mutation.Where(ps...)
|
||||
@@ -358,6 +477,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
|
||||
if value, ok := uuo.mutation.Name(); ok {
|
||||
_spec.SetField(user.FieldName, field.TypeString, value)
|
||||
}
|
||||
if uuo.mutation.OrganizationsCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uuo.mutation.RemovedOrganizationsIDs(); len(nodes) > 0 && !uuo.mutation.OrganizationsCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := uuo.mutation.OrganizationsIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2M,
|
||||
Inverse: true,
|
||||
Table: user.OrganizationsTable,
|
||||
Columns: user.OrganizationsPrimaryKey,
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
_node = &User{config: uuo.config}
|
||||
_spec.Assign = _node.assignValues
|
||||
_spec.ScanValues = _node.scanValues
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/holos.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { GetUserClaimsRequest, GetUserClaimsResponse, RegisterUserRequest, RegisterUserResponse } from "./holos_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.HolosService.GetUserClaims
|
||||
*/
|
||||
export const getUserClaims = {
|
||||
localName: "getUserClaims",
|
||||
name: "GetUserClaims",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetUserClaimsRequest,
|
||||
O: GetUserClaimsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.HolosService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.HolosService.RegisterUser
|
||||
*/
|
||||
export const registerUser = {
|
||||
localName: "registerUser",
|
||||
name: "RegisterUser",
|
||||
kind: MethodKind.Unary,
|
||||
I: RegisterUserRequest,
|
||||
O: RegisterUserResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.HolosService"
|
||||
}
|
||||
} as const;
|
||||
@@ -1,35 +0,0 @@
|
||||
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/holos.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { GetUserClaimsRequest, GetUserClaimsResponse, RegisterUserRequest, RegisterUserResponse } from "./holos_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from service holos.v1alpha1.HolosService
|
||||
*/
|
||||
export const HolosService = {
|
||||
typeName: "holos.v1alpha1.HolosService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.HolosService.GetUserClaims
|
||||
*/
|
||||
getUserClaims: {
|
||||
name: "GetUserClaims",
|
||||
I: GetUserClaimsRequest,
|
||||
O: GetUserClaimsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.HolosService.RegisterUser
|
||||
*/
|
||||
registerUser: {
|
||||
name: "RegisterUser",
|
||||
I: RegisterUserRequest,
|
||||
O: RegisterUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
// @generated by protoc-gen-es v1.8.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/holos.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3, Timestamp } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.Timestamps
|
||||
*/
|
||||
export class Timestamps extends Message<Timestamps> {
|
||||
/**
|
||||
* Created at timestamp
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp created_at = 1;
|
||||
*/
|
||||
createdAt?: Timestamp;
|
||||
|
||||
/**
|
||||
* Updated at timestamp
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp updated_at = 2;
|
||||
*/
|
||||
updatedAt?: Timestamp;
|
||||
|
||||
constructor(data?: PartialMessage<Timestamps>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.Timestamps";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "created_at", kind: "message", T: Timestamp },
|
||||
{ no: 2, name: "updated_at", kind: "message", T: Timestamp },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Timestamps {
|
||||
return new Timestamps().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Timestamps {
|
||||
return new Timestamps().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Timestamps {
|
||||
return new Timestamps().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Timestamps | PlainMessage<Timestamps> | undefined, b: Timestamps | PlainMessage<Timestamps> | undefined): boolean {
|
||||
return proto3.util.equals(Timestamps, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty request, claims are pulled from the id token
|
||||
*
|
||||
* @generated from message holos.v1alpha1.GetUserClaimsRequest
|
||||
*/
|
||||
export class GetUserClaimsRequest extends Message<GetUserClaimsRequest> {
|
||||
constructor(data?: PartialMessage<GetUserClaimsRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetUserClaimsRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetUserClaimsRequest {
|
||||
return new GetUserClaimsRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetUserClaimsRequest {
|
||||
return new GetUserClaimsRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetUserClaimsRequest {
|
||||
return new GetUserClaimsRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetUserClaimsRequest | PlainMessage<GetUserClaimsRequest> | undefined, b: GetUserClaimsRequest | PlainMessage<GetUserClaimsRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetUserClaimsRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UserClaims represents id token claims
|
||||
*
|
||||
* @generated from message holos.v1alpha1.GetUserClaimsResponse
|
||||
*/
|
||||
export class GetUserClaimsResponse extends Message<GetUserClaimsResponse> {
|
||||
/**
|
||||
* @generated from field: string iss = 1;
|
||||
*/
|
||||
iss = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string sub = 2;
|
||||
*/
|
||||
sub = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string email = 3;
|
||||
*/
|
||||
email = "";
|
||||
|
||||
/**
|
||||
* @generated from field: bool email_verified = 4;
|
||||
*/
|
||||
emailVerified = false;
|
||||
|
||||
/**
|
||||
* @generated from field: string name = 5;
|
||||
*/
|
||||
name = "";
|
||||
|
||||
constructor(data?: PartialMessage<GetUserClaimsResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetUserClaimsResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "iss", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "sub", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 3, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 4, name: "email_verified", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
||||
{ no: 5, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetUserClaimsResponse {
|
||||
return new GetUserClaimsResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetUserClaimsResponse {
|
||||
return new GetUserClaimsResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetUserClaimsResponse {
|
||||
return new GetUserClaimsResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetUserClaimsResponse | PlainMessage<GetUserClaimsResponse> | undefined, b: GetUserClaimsResponse | PlainMessage<GetUserClaimsResponse> | undefined): boolean {
|
||||
return proto3.util.equals(GetUserClaimsResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User represents a human user in the system. See db schema in ent/schema/user.go
|
||||
*
|
||||
* @generated from message holos.v1alpha1.User
|
||||
*/
|
||||
export class User extends Message<User> {
|
||||
/**
|
||||
* Unique id assigned by the server.
|
||||
*
|
||||
* @generated from field: string id = 1;
|
||||
*/
|
||||
id = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string email = 2;
|
||||
*/
|
||||
email = "";
|
||||
|
||||
/**
|
||||
* @generated from field: bool email_verified = 3;
|
||||
*/
|
||||
emailVerified = false;
|
||||
|
||||
/**
|
||||
* @generated from field: string name = 4;
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.Timestamps timestamps = 5;
|
||||
*/
|
||||
timestamps?: Timestamps;
|
||||
|
||||
constructor(data?: PartialMessage<User>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.User";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 3, name: "email_verified", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
||||
{ no: 4, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 5, name: "timestamps", kind: "message", T: Timestamps },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): User {
|
||||
return new User().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): User {
|
||||
return new User().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): User {
|
||||
return new User().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: User | PlainMessage<User> | undefined, b: User | PlainMessage<User> | undefined): boolean {
|
||||
return proto3.util.equals(User, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.RegisterUserRequest
|
||||
*/
|
||||
export class RegisterUserRequest extends Message<RegisterUserRequest> {
|
||||
/**
|
||||
* @generated from field: optional string name = 1;
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
constructor(data?: PartialMessage<RegisterUserRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.RegisterUserRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */, 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.v1alpha1.RegisterUserResponse
|
||||
*/
|
||||
export class RegisterUserResponse extends Message<RegisterUserResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* @generated from field: bool already_exists = 2;
|
||||
*/
|
||||
alreadyExists = false;
|
||||
|
||||
constructor(data?: PartialMessage<RegisterUserResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.RegisterUserResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
{ no: 2, name: "already_exists", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
||||
]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
86
internal/frontend/holos/package-lock.json
generated
86
internal/frontend/holos/package-lock.json
generated
@@ -18,7 +18,7 @@
|
||||
"@angular/platform-browser": "^17.3.0",
|
||||
"@angular/platform-browser-dynamic": "^17.3.0",
|
||||
"@angular/router": "^17.3.0",
|
||||
"@bufbuild/protobuf": "^1.8.0",
|
||||
"@bufbuild/protobuf": "^1.9.0",
|
||||
"@connectrpc/connect": "^1.4.0",
|
||||
"@connectrpc/connect-query": "^1.3.1",
|
||||
"@connectrpc/connect-web": "^1.4.0",
|
||||
@@ -30,8 +30,8 @@
|
||||
"@angular-devkit/build-angular": "^17.3.4",
|
||||
"@angular/cli": "^17.3.4",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@bufbuild/buf": "^1.30.1",
|
||||
"@bufbuild/protoc-gen-es": "^1.8.0",
|
||||
"@bufbuild/buf": "^1.31.0",
|
||||
"@bufbuild/protoc-gen-es": "^1.9.0",
|
||||
"@connectrpc/protoc-gen-connect-es": "^1.4.0",
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.3.1",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
@@ -2337,9 +2337,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.30.1.tgz",
|
||||
"integrity": "sha512-9VVvrXBCWUiH8ToccqDfPRuTiPXSbHmSkL8XPlMpUhpJIlm01m4/Vzbc5FJL1yuk3e1rdBGCF6I9Obs9NsILzg==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.31.0.tgz",
|
||||
"integrity": "sha512-kM/eueGkp0NDo8p8B6GXr1MdCzf4w8zEV1gbEiDlaLYDoyeHGLtlf5jF/hrb6MsvCccy3x7cc+cj4Wn/DmoR2g==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
@@ -2351,18 +2351,18 @@
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@bufbuild/buf-darwin-arm64": "1.30.1",
|
||||
"@bufbuild/buf-darwin-x64": "1.30.1",
|
||||
"@bufbuild/buf-linux-aarch64": "1.30.1",
|
||||
"@bufbuild/buf-linux-x64": "1.30.1",
|
||||
"@bufbuild/buf-win32-arm64": "1.30.1",
|
||||
"@bufbuild/buf-win32-x64": "1.30.1"
|
||||
"@bufbuild/buf-darwin-arm64": "1.31.0",
|
||||
"@bufbuild/buf-darwin-x64": "1.31.0",
|
||||
"@bufbuild/buf-linux-aarch64": "1.31.0",
|
||||
"@bufbuild/buf-linux-x64": "1.31.0",
|
||||
"@bufbuild/buf-win32-arm64": "1.31.0",
|
||||
"@bufbuild/buf-win32-x64": "1.31.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf-darwin-arm64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.30.1.tgz",
|
||||
"integrity": "sha512-FRgf+x4V4s9Z1wH2xHdP8+1AYtil1GCmMjzKf/4AQ+eaUpoLfipSIsVYiBrnpcRxEPe9UMVzwNjKtPak/szwPw==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.31.0.tgz",
|
||||
"integrity": "sha512-C0jArGS/SW0jfpBBmG6xEhUBWQTsGInnPr7y44WYWNS/U5OnnWPJtYQ7xbH0mzYDMx7sZVRV0FKvPO0FPKS+hA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2376,9 +2376,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf-darwin-x64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.30.1.tgz",
|
||||
"integrity": "sha512-kE0ne45zE7lSdv9WxPVhapwu627WMbWmWCzqSxzYr8sWDLqiAuw+XvO9/mHGdPWcMhV4lMX6tutitd9PPVxK8A==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.31.0.tgz",
|
||||
"integrity": "sha512-oBTe1T4l2WSukAG+9YS7VHID4N1CuSvAxGBfuzpFQrjjiQZaaTfYuLqqVP6408MyCN7X/LOjfCekR1QToVweNw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2392,9 +2392,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf-linux-aarch64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.30.1.tgz",
|
||||
"integrity": "sha512-kVV9Sl0GwZiQkMOXJiuwuU+gIHe6AWcYBMRMmuW55sY0ePZNXBmRGt4k5W4ijy98O6pnY3ao+n9ne0KwiD9MVA==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.31.0.tgz",
|
||||
"integrity": "sha512-UN9KsTuO9YS5Vefj/CaqX1wO+hvc3AyGElxzOHMc7S3MWEuqSAFOhxu5I7CyOr2/yoZO2qZPPR29HuzmQsb2+w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2408,9 +2408,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf-linux-x64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.30.1.tgz",
|
||||
"integrity": "sha512-RacDbQJYNwqRlMESa/rLHprfUVa8Wu1/cmcqS29Fyt/cGzs0G8sNcQzQ87HYFIS9cSlSPl6vWL0x8JqQRp68lQ==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.31.0.tgz",
|
||||
"integrity": "sha512-4uKq5Iu5tNEFE3Mz9a52KFCaywg5xLqwhN6Kf4kAk34kxWJgQ8D3WFe9ZpXHzH7Lj00u3Q+V/3vKCVATHR3tkw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2424,9 +2424,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf-win32-arm64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.30.1.tgz",
|
||||
"integrity": "sha512-ndp/qb5M6yrSzcnMI0j4jjAuDKa7zHBFc187FwyDb3v63rvyQeYqncHb0leT5ZWqfNggJT4vXIH6QnH82PfDQw==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.31.0.tgz",
|
||||
"integrity": "sha512-jz7GenlNsqwbC3qaHcBHBO35MycZ1gV8OUSRp/wTXGgZsEZzAyw335JA2NWL+5LaI8cF+CsYd6/uuWzKkdCKTQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2440,9 +2440,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/buf-win32-x64": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.30.1.tgz",
|
||||
"integrity": "sha512-1kmIY6oKLKZ4zIQVNG60GRDp+vKSZdaim7wRejOtgEDuWXhIuErlnGbpstypU8FO+OV3SeFUJNOJ8tLOYd3PvQ==",
|
||||
"version": "1.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.31.0.tgz",
|
||||
"integrity": "sha512-gw7p3PYam0g7hNwIhNphua5P8GBZczginoWNK3jk5sGVv0TzWOdHrjkVVhkc3DJbRZEg10ExGI3qRfCqiX1IHw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2456,18 +2456,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/protobuf": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.8.0.tgz",
|
||||
"integrity": "sha512-qR9FwI8QKIveDnUYutvfzbC21UZJJryYrLuZGjeZ/VGz+vXelUkK+xgkOHsvPEdYEdxtgUUq4313N8QtOehJ1Q=="
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.9.0.tgz",
|
||||
"integrity": "sha512-W7gp8Q/v1NlCZLsv8pQ3Y0uCu/SHgXOVFK+eUluUKWXmsb6VHkpNx0apdOWWcDbB9sJoKeP8uPrjmehJz6xETQ=="
|
||||
},
|
||||
"node_modules/@bufbuild/protoc-gen-es": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.8.0.tgz",
|
||||
"integrity": "sha512-jnvBKwHq3o/iOgfKxaxn5Za7ay4oAs8KWgoHiDc9Fsb0g+/d1z+mHlHvmevOiCPcVZsnH6V3LImOJvGStPONpA==",
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.9.0.tgz",
|
||||
"integrity": "sha512-LJy1nC3Jsfdhs9v48P7qF6YXIqh+usFhXSVzJDTmw0yKjxQ3CKBNISRtaMql/g9hb1MLRU6unHCcFfdz4HSO/Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^1.8.0",
|
||||
"@bufbuild/protoplugin": "1.8.0"
|
||||
"@bufbuild/protobuf": "^1.9.0",
|
||||
"@bufbuild/protoplugin": "1.9.0"
|
||||
},
|
||||
"bin": {
|
||||
"protoc-gen-es": "bin/protoc-gen-es"
|
||||
@@ -2476,7 +2476,7 @@
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@bufbuild/protobuf": "1.8.0"
|
||||
"@bufbuild/protobuf": "1.9.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@bufbuild/protobuf": {
|
||||
@@ -2485,12 +2485,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/protoplugin": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.8.0.tgz",
|
||||
"integrity": "sha512-Pb89cTshW+I577qh27VvxGYvZEvQ3zJ8La1OfzPCKugP9d4A4P65WStkAY+aSCiDHk68m1/+mtBb6elfiLPuFg==",
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.9.0.tgz",
|
||||
"integrity": "sha512-/mxMiGs5h78RUHT7v4+mv0Wt0gyRf/SOS5PLzKEg2sclEAlFPbXfZ8HjlvxJpXZP/YpP3HvsW/mil3E69G0mXg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "1.8.0",
|
||||
"@bufbuild/protobuf": "1.9.0",
|
||||
"@typescript/vfs": "^1.4.0",
|
||||
"typescript": "4.5.2"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"@angular/platform-browser": "^17.3.0",
|
||||
"@angular/platform-browser-dynamic": "^17.3.0",
|
||||
"@angular/router": "^17.3.0",
|
||||
"@bufbuild/protobuf": "^1.8.0",
|
||||
"@bufbuild/protobuf": "^1.9.0",
|
||||
"@connectrpc/connect": "^1.4.0",
|
||||
"@connectrpc/connect-query": "^1.3.1",
|
||||
"@connectrpc/connect-web": "^1.4.0",
|
||||
@@ -32,8 +32,8 @@
|
||||
"@angular-devkit/build-angular": "^17.3.4",
|
||||
"@angular/cli": "^17.3.4",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@bufbuild/buf": "^1.30.1",
|
||||
"@bufbuild/protoc-gen-es": "^1.8.0",
|
||||
"@bufbuild/buf": "^1.31.0",
|
||||
"@bufbuild/protoc-gen-es": "^1.9.0",
|
||||
"@connectrpc/protoc-gen-connect-es": "^1.4.0",
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.3.1",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
@@ -45,4 +45,4 @@
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
import { ApplicationConfig } from '@angular/core';
|
||||
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
// import { provideHttpClient, withFetch } from '@angular/common/http';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||
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';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(routes), provideAnimationsAsync()]
|
||||
providers: [
|
||||
provideRouter(routes),
|
||||
provideAnimationsAsync(),
|
||||
// provideHttpClient(withFetch()),
|
||||
provideClient(UserService),
|
||||
provideClient(OrganizationService),
|
||||
importProvidersFrom(
|
||||
ConnectModule.forRoot({
|
||||
baseUrl: window.location.origin
|
||||
}),
|
||||
),
|
||||
]
|
||||
};
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/organization.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { CreateCallerOrganizationRequest, GetCallerOrganizationsRequest, GetCallerOrganizationsResponse } from "./organization_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.GetCallerOrganizations
|
||||
*/
|
||||
export const getCallerOrganizations = {
|
||||
localName: "getCallerOrganizations",
|
||||
name: "GetCallerOrganizations",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetCallerOrganizationsRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.OrganizationService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.CreateCallerOrganization
|
||||
*/
|
||||
export const createCallerOrganization = {
|
||||
localName: "createCallerOrganization",
|
||||
name: "CreateCallerOrganization",
|
||||
kind: MethodKind.Unary,
|
||||
I: CreateCallerOrganizationRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.OrganizationService"
|
||||
}
|
||||
} as const;
|
||||
@@ -0,0 +1,35 @@
|
||||
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/organization.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { CreateCallerOrganizationRequest, GetCallerOrganizationsRequest, GetCallerOrganizationsResponse } from "./organization_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from service holos.v1alpha1.OrganizationService
|
||||
*/
|
||||
export const OrganizationService = {
|
||||
typeName: "holos.v1alpha1.OrganizationService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.GetCallerOrganizations
|
||||
*/
|
||||
getCallerOrganizations: {
|
||||
name: "GetCallerOrganizations",
|
||||
I: GetCallerOrganizationsRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.CreateCallerOrganization
|
||||
*/
|
||||
createCallerOrganization: {
|
||||
name: "CreateCallerOrganization",
|
||||
I: CreateCallerOrganizationRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/organization.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";
|
||||
import { Timestamps } from "./timestamps_pb.js";
|
||||
import { User } from "./user_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.Organization
|
||||
*/
|
||||
export class Organization extends Message<Organization> {
|
||||
/**
|
||||
* Unique id assigned by the server.
|
||||
*
|
||||
* @generated from field: string id = 1;
|
||||
*/
|
||||
id = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string name = 2;
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string display_name = 3;
|
||||
*/
|
||||
displayName = "";
|
||||
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.Timestamps timestamps = 4;
|
||||
*/
|
||||
timestamps?: Timestamps;
|
||||
|
||||
constructor(data?: PartialMessage<Organization>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.Organization";
|
||||
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 },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Organization {
|
||||
return new Organization().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Organization {
|
||||
return new Organization().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Organization {
|
||||
return new Organization().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Organization | PlainMessage<Organization> | undefined, b: Organization | PlainMessage<Organization> | undefined): boolean {
|
||||
return proto3.util.equals(Organization, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetCallerOrganizationsRequest
|
||||
*/
|
||||
export class GetCallerOrganizationsRequest extends Message<GetCallerOrganizationsRequest> {
|
||||
constructor(data?: PartialMessage<GetCallerOrganizationsRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerOrganizationsRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerOrganizationsRequest {
|
||||
return new GetCallerOrganizationsRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerOrganizationsRequest {
|
||||
return new GetCallerOrganizationsRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerOrganizationsRequest {
|
||||
return new GetCallerOrganizationsRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerOrganizationsRequest | PlainMessage<GetCallerOrganizationsRequest> | undefined, b: GetCallerOrganizationsRequest | PlainMessage<GetCallerOrganizationsRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerOrganizationsRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetCallerOrganizationsResponse
|
||||
*/
|
||||
export class GetCallerOrganizationsResponse extends Message<GetCallerOrganizationsResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated holos.v1alpha1.Organization organizations = 2;
|
||||
*/
|
||||
organizations: Organization[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<GetCallerOrganizationsResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerOrganizationsResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
{ no: 2, name: "organizations", kind: "message", T: Organization, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerOrganizationsResponse {
|
||||
return new GetCallerOrganizationsResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerOrganizationsResponse {
|
||||
return new GetCallerOrganizationsResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerOrganizationsResponse {
|
||||
return new GetCallerOrganizationsResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerOrganizationsResponse | PlainMessage<GetCallerOrganizationsResponse> | undefined, b: GetCallerOrganizationsResponse | PlainMessage<GetCallerOrganizationsResponse> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerOrganizationsResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.CreateCallerOrganizationRequest
|
||||
*/
|
||||
export class CreateCallerOrganizationRequest extends Message<CreateCallerOrganizationRequest> {
|
||||
constructor(data?: PartialMessage<CreateCallerOrganizationRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.CreateCallerOrganizationRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateCallerOrganizationRequest {
|
||||
return new CreateCallerOrganizationRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateCallerOrganizationRequest {
|
||||
return new CreateCallerOrganizationRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateCallerOrganizationRequest {
|
||||
return new CreateCallerOrganizationRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: CreateCallerOrganizationRequest | PlainMessage<CreateCallerOrganizationRequest> | undefined, b: CreateCallerOrganizationRequest | PlainMessage<CreateCallerOrganizationRequest> | undefined): boolean {
|
||||
return proto3.util.equals(CreateCallerOrganizationRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/timestamps.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3, Timestamp } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.Timestamps
|
||||
*/
|
||||
export class Timestamps extends Message<Timestamps> {
|
||||
/**
|
||||
* Created at timestamp
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp created_at = 1;
|
||||
*/
|
||||
createdAt?: Timestamp;
|
||||
|
||||
/**
|
||||
* Updated at timestamp
|
||||
*
|
||||
* @generated from field: google.protobuf.Timestamp updated_at = 2;
|
||||
*/
|
||||
updatedAt?: Timestamp;
|
||||
|
||||
constructor(data?: PartialMessage<Timestamps>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.Timestamps";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "created_at", kind: "message", T: Timestamp },
|
||||
{ no: 2, name: "updated_at", kind: "message", T: Timestamp },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Timestamps {
|
||||
return new Timestamps().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Timestamps {
|
||||
return new Timestamps().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Timestamps {
|
||||
return new Timestamps().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Timestamps | PlainMessage<Timestamps> | undefined, b: Timestamps | PlainMessage<Timestamps> | undefined): boolean {
|
||||
return proto3.util.equals(Timestamps, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/user.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { CreateCallerUserRequest, CreateCallerUserResponse, GetCallerClaimsRequest, GetCallerClaimsResponse, GetCallerUserRequest, GetCallerUserResponse } from "./user_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.GetCallerClaims
|
||||
*/
|
||||
export const getCallerClaims = {
|
||||
localName: "getCallerClaims",
|
||||
name: "GetCallerClaims",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetCallerClaimsRequest,
|
||||
O: GetCallerClaimsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.UserService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.GetCallerUser
|
||||
*/
|
||||
export const getCallerUser = {
|
||||
localName: "getCallerUser",
|
||||
name: "GetCallerUser",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetCallerUserRequest,
|
||||
O: GetCallerUserResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.UserService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.CreateCallerUser
|
||||
*/
|
||||
export const createCallerUser = {
|
||||
localName: "createCallerUser",
|
||||
name: "CreateCallerUser",
|
||||
kind: MethodKind.Unary,
|
||||
I: CreateCallerUserRequest,
|
||||
O: CreateCallerUserResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.UserService"
|
||||
}
|
||||
} as const;
|
||||
@@ -0,0 +1,44 @@
|
||||
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/user.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { CreateCallerUserRequest, CreateCallerUserResponse, GetCallerClaimsRequest, GetCallerClaimsResponse, GetCallerUserRequest, GetCallerUserResponse } from "./user_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from service holos.v1alpha1.UserService
|
||||
*/
|
||||
export const UserService = {
|
||||
typeName: "holos.v1alpha1.UserService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.GetCallerClaims
|
||||
*/
|
||||
getCallerClaims: {
|
||||
name: "GetCallerClaims",
|
||||
I: GetCallerClaimsRequest,
|
||||
O: GetCallerClaimsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.GetCallerUser
|
||||
*/
|
||||
getCallerUser: {
|
||||
name: "GetCallerUser",
|
||||
I: GetCallerUserRequest,
|
||||
O: GetCallerUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.CreateCallerUser
|
||||
*/
|
||||
createCallerUser: {
|
||||
name: "CreateCallerUser",
|
||||
I: CreateCallerUserRequest,
|
||||
O: CreateCallerUserResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
367
internal/frontend/holos/src/app/gen/holos/v1alpha1/user_pb.ts
Normal file
367
internal/frontend/holos/src/app/gen/holos/v1alpha1/user_pb.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/user.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";
|
||||
import { Timestamps } from "./timestamps_pb.js";
|
||||
|
||||
/**
|
||||
* User represents a human user in the system. See db schema in ent/schema/user.go
|
||||
*
|
||||
* @generated from message holos.v1alpha1.User
|
||||
*/
|
||||
export class User extends Message<User> {
|
||||
/**
|
||||
* Unique id assigned by the server.
|
||||
*
|
||||
* @generated from field: string id = 1;
|
||||
*/
|
||||
id = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string email = 2;
|
||||
*/
|
||||
email = "";
|
||||
|
||||
/**
|
||||
* @generated from field: bool email_verified = 3;
|
||||
*/
|
||||
emailVerified = false;
|
||||
|
||||
/**
|
||||
* @generated from field: string name = 4;
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.Timestamps timestamps = 5;
|
||||
*/
|
||||
timestamps?: Timestamps;
|
||||
|
||||
constructor(data?: PartialMessage<User>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.User";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 3, name: "email_verified", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
||||
{ no: 4, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 5, name: "timestamps", kind: "message", T: Timestamps },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): User {
|
||||
return new User().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): User {
|
||||
return new User().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): User {
|
||||
return new User().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: User | PlainMessage<User> | undefined, b: User | PlainMessage<User> | undefined): boolean {
|
||||
return proto3.util.equals(User, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.CreateCallerUserRequest
|
||||
*/
|
||||
export class CreateCallerUserRequest extends Message<CreateCallerUserRequest> {
|
||||
constructor(data?: PartialMessage<CreateCallerUserRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.CreateCallerUserRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateCallerUserRequest {
|
||||
return new CreateCallerUserRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateCallerUserRequest {
|
||||
return new CreateCallerUserRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateCallerUserRequest {
|
||||
return new CreateCallerUserRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: CreateCallerUserRequest | PlainMessage<CreateCallerUserRequest> | undefined, b: CreateCallerUserRequest | PlainMessage<CreateCallerUserRequest> | undefined): boolean {
|
||||
return proto3.util.equals(CreateCallerUserRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.CreateCallerUserResponse
|
||||
*/
|
||||
export class CreateCallerUserResponse extends Message<CreateCallerUserResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
constructor(data?: PartialMessage<CreateCallerUserResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.CreateCallerUserResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateCallerUserResponse {
|
||||
return new CreateCallerUserResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateCallerUserResponse {
|
||||
return new CreateCallerUserResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateCallerUserResponse {
|
||||
return new CreateCallerUserResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: CreateCallerUserResponse | PlainMessage<CreateCallerUserResponse> | undefined, b: CreateCallerUserResponse | PlainMessage<CreateCallerUserResponse> | undefined): boolean {
|
||||
return proto3.util.equals(CreateCallerUserResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetCallerClaimsRequest
|
||||
*/
|
||||
export class GetCallerClaimsRequest extends Message<GetCallerClaimsRequest> {
|
||||
constructor(data?: PartialMessage<GetCallerClaimsRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerClaimsRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerClaimsRequest {
|
||||
return new GetCallerClaimsRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerClaimsRequest {
|
||||
return new GetCallerClaimsRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerClaimsRequest {
|
||||
return new GetCallerClaimsRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerClaimsRequest | PlainMessage<GetCallerClaimsRequest> | undefined, b: GetCallerClaimsRequest | PlainMessage<GetCallerClaimsRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerClaimsRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.Claims
|
||||
*/
|
||||
export class Claims extends Message<Claims> {
|
||||
/**
|
||||
* @generated from field: string iss = 1;
|
||||
*/
|
||||
iss = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string sub = 2;
|
||||
*/
|
||||
sub = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string email = 3;
|
||||
*/
|
||||
email = "";
|
||||
|
||||
/**
|
||||
* @generated from field: bool email_verified = 4;
|
||||
*/
|
||||
emailVerified = false;
|
||||
|
||||
/**
|
||||
* @generated from field: string name = 5;
|
||||
*/
|
||||
name = "";
|
||||
|
||||
/**
|
||||
* @generated from field: repeated string groups = 6;
|
||||
*/
|
||||
groups: string[] = [];
|
||||
|
||||
/**
|
||||
* @generated from field: string given_name = 7;
|
||||
*/
|
||||
givenName = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string family_name = 8;
|
||||
*/
|
||||
familyName = "";
|
||||
|
||||
/**
|
||||
* @generated from field: string picture = 9;
|
||||
*/
|
||||
picture = "";
|
||||
|
||||
constructor(data?: PartialMessage<Claims>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.Claims";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "iss", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "sub", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 3, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 4, name: "email_verified", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
||||
{ no: 5, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 6, name: "groups", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true },
|
||||
{ no: 7, name: "given_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 8, name: "family_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 9, name: "picture", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Claims {
|
||||
return new Claims().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Claims {
|
||||
return new Claims().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Claims {
|
||||
return new Claims().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Claims | PlainMessage<Claims> | undefined, b: Claims | PlainMessage<Claims> | undefined): boolean {
|
||||
return proto3.util.equals(Claims, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UserClaims represents id token claims
|
||||
*
|
||||
* @generated from message holos.v1alpha1.GetCallerClaimsResponse
|
||||
*/
|
||||
export class GetCallerClaimsResponse extends Message<GetCallerClaimsResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.Claims claims = 1;
|
||||
*/
|
||||
claims?: Claims;
|
||||
|
||||
constructor(data?: PartialMessage<GetCallerClaimsResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerClaimsResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "claims", kind: "message", T: Claims },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerClaimsResponse {
|
||||
return new GetCallerClaimsResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerClaimsResponse {
|
||||
return new GetCallerClaimsResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerClaimsResponse {
|
||||
return new GetCallerClaimsResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerClaimsResponse | PlainMessage<GetCallerClaimsResponse> | undefined, b: GetCallerClaimsResponse | PlainMessage<GetCallerClaimsResponse> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerClaimsResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty request, claims are pulled from the id token
|
||||
*
|
||||
* @generated from message holos.v1alpha1.GetCallerUserRequest
|
||||
*/
|
||||
export class GetCallerUserRequest extends Message<GetCallerUserRequest> {
|
||||
constructor(data?: PartialMessage<GetCallerUserRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerUserRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerUserRequest {
|
||||
return new GetCallerUserRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerUserRequest {
|
||||
return new GetCallerUserRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerUserRequest {
|
||||
return new GetCallerUserRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerUserRequest | PlainMessage<GetCallerUserRequest> | undefined, b: GetCallerUserRequest | PlainMessage<GetCallerUserRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerUserRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetCallerUserResponse
|
||||
*/
|
||||
export class GetCallerUserResponse extends Message<GetCallerUserResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
constructor(data?: PartialMessage<GetCallerUserResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerUserResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerUserResponse {
|
||||
return new GetCallerUserResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerUserResponse {
|
||||
return new GetCallerUserResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerUserResponse {
|
||||
return new GetCallerUserResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerUserResponse | PlainMessage<GetCallerUserResponse> | undefined, b: GetCallerUserResponse | PlainMessage<GetCallerUserResponse> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerUserResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
|
||||
[mode]="(isHandset$ | async) ? 'over' : 'side'"
|
||||
[opened]="(isHandset$ | async) === false">
|
||||
<mat-toolbar>Menu</mat-toolbar>
|
||||
<mat-toolbar>
|
||||
<span>Menu</span>
|
||||
</mat-toolbar>
|
||||
<mat-nav-list>
|
||||
<a mat-list-item routerLink="/home" routerLinkActive="active-link">Home</a>
|
||||
<a mat-list-item routerLink="/clusters" routerLinkActive="active-link">Clusters</a>
|
||||
@@ -21,6 +23,15 @@
|
||||
</button>
|
||||
}
|
||||
<span>Holos</span>
|
||||
<span class="toolbar-spacer"></span>
|
||||
<span>
|
||||
@if (org$ | async; as org) {
|
||||
<button mat-button (click)="refreshOrg()">
|
||||
{{ org.displayName }}
|
||||
</button>
|
||||
}
|
||||
</span>
|
||||
<app-profile-button [claims$]="claims$"></app-profile-button>
|
||||
</mat-toolbar>
|
||||
<!-- Add Content Here -->
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@@ -15,3 +15,22 @@
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.toolbar-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
button {
|
||||
&.image {
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
@@ -9,6 +9,12 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { Claims } from '../gen/holos/v1alpha1/user_pb';
|
||||
import { ProfileButtonComponent } from '../profile-button/profile-button.component';
|
||||
import { UserService } from '../services/user.service';
|
||||
import { Organization } from '../gen/holos/v1alpha1/organization_pb';
|
||||
import { OrganizationService } from '../services/organization.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav',
|
||||
@@ -21,18 +27,35 @@ import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
MatSidenavModule,
|
||||
MatListModule,
|
||||
MatIconModule,
|
||||
NgIf,
|
||||
AsyncPipe,
|
||||
RouterLink,
|
||||
RouterLinkActive,
|
||||
RouterOutlet,
|
||||
MatCardModule,
|
||||
ProfileButtonComponent,
|
||||
]
|
||||
})
|
||||
export class NavComponent {
|
||||
export class NavComponent implements OnInit {
|
||||
private breakpointObserver = inject(BreakpointObserver);
|
||||
private userService = inject(UserService);
|
||||
private orgService = inject(OrganizationService);
|
||||
|
||||
claims$!: Observable<Claims | null>;
|
||||
org$!: Observable<Organization | undefined>;
|
||||
|
||||
refreshOrg(): void {
|
||||
this.orgService.refreshOrganizations()
|
||||
}
|
||||
|
||||
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
|
||||
.pipe(
|
||||
map(result => result.matches),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
ngOnInit(): void {
|
||||
this.claims$ = this.userService.getClaims();
|
||||
this.org$ = this.orgService.activeOrg();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
@if (claims$ | async; as claims) {
|
||||
<button mat-icon-button [matMenuTriggerFor]="menu">
|
||||
@if (claims.picture) {
|
||||
<img class="profile-picture" [src]="claims.picture" alt="Profile"/>
|
||||
} @else {
|
||||
<mat-icon>account_circle</mat-icon>
|
||||
}
|
||||
</button>
|
||||
|
||||
<mat-menu class="accounts-menu" #menu="matMenu">
|
||||
<mat-card class="accounts-card">
|
||||
<mat-card-header>
|
||||
<div mat-card-avatar class="accounts-header-image" [ngStyle]="{'background-image': claims.picture ? 'url(' + claims.picture +')' : 'url(/ui/assets/img/account_circle.svg)'}">
|
||||
</div>
|
||||
<mat-card-title>{{ claims.name }}</mat-card-title>
|
||||
<mat-card-subtitle>{{ claims.email }}</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
<mat-card-actions>
|
||||
<a mat-menu-item href="{{claims.iss}}/ui/console/users/me?id=general">
|
||||
Profile
|
||||
</a>
|
||||
<a mat-menu-item href="{{claims.iss}}/oidc/v1/end_session">
|
||||
Logout
|
||||
</a>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</mat-menu>
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
.profile-picture {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.accounts-card {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.accounts-header-image {
|
||||
background-image: url('/ui/assets/img/account_circle.svg');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
::ng-deep .mat-mdc-menu-content {
|
||||
padding: 0px !important;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProfileButtonComponent } from './profile-button.component';
|
||||
|
||||
describe('ProfileButtonComponent', () => {
|
||||
let component: ProfileButtonComponent;
|
||||
let fixture: ComponentFixture<ProfileButtonComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ProfileButtonComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ProfileButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Claims } from '../gen/holos/v1alpha1/user_pb';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AsyncPipe, NgIf, NgStyle } from '@angular/common';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile-button',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
AsyncPipe,
|
||||
NgIf,
|
||||
NgStyle,
|
||||
],
|
||||
templateUrl: './profile-button.component.html',
|
||||
styleUrl: './profile-button.component.scss'
|
||||
})
|
||||
export class ProfileButtonComponent {
|
||||
@Input({ required: true }) claims$!: Observable<Claims | null>;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { OrganizationService } from './organization.service';
|
||||
|
||||
describe('OrganizationService', () => {
|
||||
let service: OrganizationService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(OrganizationService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { OrganizationService as ConnectOrganizationService } from '../gen/holos/v1alpha1/organization_connect';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { Observable, switchMap, of, shareReplay, catchError, BehaviorSubject } from 'rxjs';
|
||||
import { GetCallerOrganizationsResponse, Organization } from '../gen/holos/v1alpha1/organization_pb';
|
||||
import { UserService } from './user.service';
|
||||
import { Code, ConnectError } from '@connectrpc/connect';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class OrganizationService {
|
||||
private callerOrganizationsTrigger$ = new BehaviorSubject<void>(undefined);
|
||||
private callerOrganizations$: Observable<GetCallerOrganizationsResponse>;
|
||||
|
||||
private fetchCallerOrganizations(): Observable<GetCallerOrganizationsResponse> {
|
||||
return this.client.getCallerOrganizations({ request: {} }).pipe(
|
||||
switchMap(resp => {
|
||||
if (resp && resp.organizations.length > 0) {
|
||||
return of(resp)
|
||||
}
|
||||
return this.client.createCallerOrganization({ request: {} })
|
||||
}),
|
||||
catchError(err => {
|
||||
if (err instanceof ConnectError) {
|
||||
if (err.code == Code.NotFound) {
|
||||
return this.userService.createUser().pipe(
|
||||
switchMap(user => this.client.createCallerOrganization({ request: {} }))
|
||||
)
|
||||
}
|
||||
}
|
||||
console.error('Error fetching data:', err);
|
||||
throw err;
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
getOrganizations(): Observable<Organization[]> {
|
||||
return this.callerOrganizations$.pipe(
|
||||
switchMap(resp => of(resp.organizations))
|
||||
)
|
||||
}
|
||||
|
||||
activeOrg(): Observable<Organization | undefined> {
|
||||
return this.callerOrganizations$.pipe(
|
||||
switchMap(resp => of(resp.organizations.at(-1)))
|
||||
)
|
||||
}
|
||||
|
||||
refreshOrganizations(): void {
|
||||
this.callerOrganizationsTrigger$.next()
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Inject(ConnectOrganizationService) private client: ObservableClient<typeof ConnectOrganizationService>,
|
||||
private userService: UserService,
|
||||
) {
|
||||
this.callerOrganizations$ = this.callerOrganizationsTrigger$.pipe(
|
||||
switchMap(() => this.fetchCallerOrganizations()),
|
||||
shareReplay(1),
|
||||
catchError(err => {
|
||||
console.error('Error fetching data:', err);
|
||||
throw err;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
|
||||
describe('UserService', () => {
|
||||
let service: UserService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(UserService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
42
internal/frontend/holos/src/app/services/user.service.ts
Normal file
42
internal/frontend/holos/src/app/services/user.service.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Observable, switchMap, of, shareReplay } from 'rxjs';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { Claims, User } from '../gen/holos/v1alpha1/user_pb';
|
||||
import { UserService as ConnectUserService } from '../gen/holos/v1alpha1/user_connect';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserService {
|
||||
|
||||
getClaims(): Observable<Claims | null> {
|
||||
return this.client.getCallerClaims({ request: {} }).pipe(
|
||||
switchMap(getCallerClaimsResponse => {
|
||||
if (getCallerClaimsResponse && getCallerClaimsResponse.claims) {
|
||||
return of(getCallerClaimsResponse.claims)
|
||||
} else {
|
||||
return of(null)
|
||||
}
|
||||
}),
|
||||
// Consolidate to one api call for all subscribers
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
|
||||
createUser(): Observable<User | null> {
|
||||
return this.client.createCallerUser({ request: {} }).pipe(
|
||||
switchMap(resp => {
|
||||
if (resp && resp.user) {
|
||||
return of(resp.user)
|
||||
} else {
|
||||
return of(null)
|
||||
}
|
||||
}),
|
||||
shareReplay(1)
|
||||
)
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Inject(ConnectUserService) private client: ObservableClient<typeof ConnectUserService>,
|
||||
) { }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/></svg>
|
||||
|
After Width: | Height: | Size: 365 B |
15
internal/frontend/holos/src/connect/client.provider.ts
Normal file
15
internal/frontend/holos/src/connect/client.provider.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Provider } from "@angular/core";
|
||||
import { Transport } from "@connectrpc/connect";
|
||||
import { ServiceType } from "@bufbuild/protobuf";
|
||||
import { createObservableClient } from "./observable-client";
|
||||
import { TRANSPORT } from "./transport.token";
|
||||
|
||||
export function provideClient<T extends ServiceType>(service: T): Provider {
|
||||
return {
|
||||
provide: service,
|
||||
useFactory: (transport: Transport) => {
|
||||
return createObservableClient(service, transport);
|
||||
},
|
||||
deps: [TRANSPORT],
|
||||
};
|
||||
}
|
||||
30
internal/frontend/holos/src/connect/connect.module.ts
Normal file
30
internal/frontend/holos/src/connect/connect.module.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core'
|
||||
import { Interceptor } from '@connectrpc/connect'
|
||||
import { createConnectTransport } from '@connectrpc/connect-web'
|
||||
import { INTERCEPTORS } from './interceptor.token'
|
||||
import { TRANSPORT } from './transport.token'
|
||||
|
||||
@NgModule()
|
||||
export class ConnectModule {
|
||||
public static forRoot(
|
||||
connectOptions: Omit<
|
||||
Parameters<typeof createConnectTransport>[0],
|
||||
'interceptors'
|
||||
>
|
||||
): ModuleWithProviders<ConnectModule> {
|
||||
return {
|
||||
ngModule: ConnectModule,
|
||||
providers: [
|
||||
{
|
||||
provide: TRANSPORT,
|
||||
useFactory: (interceptors: Interceptor[]) =>
|
||||
createConnectTransport({
|
||||
...connectOptions,
|
||||
interceptors: interceptors,
|
||||
}),
|
||||
deps: [INTERCEPTORS],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
30
internal/frontend/holos/src/connect/grpc-web.module.ts
Normal file
30
internal/frontend/holos/src/connect/grpc-web.module.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core'
|
||||
import { Interceptor } from '@connectrpc/connect'
|
||||
import { createGrpcWebTransport } from '@connectrpc/connect-web'
|
||||
import { INTERCEPTORS } from './interceptor.token'
|
||||
import { TRANSPORT } from './transport.token'
|
||||
|
||||
@NgModule()
|
||||
export class GrpcWebModule {
|
||||
public static forRoot(
|
||||
grpcWebOptions: Omit<
|
||||
Parameters<typeof createGrpcWebTransport>[0],
|
||||
'interceptors'
|
||||
>
|
||||
): ModuleWithProviders<GrpcWebModule> {
|
||||
return {
|
||||
ngModule: GrpcWebModule,
|
||||
providers: [
|
||||
{
|
||||
provide: TRANSPORT,
|
||||
useFactory: (interceptors: Interceptor[]) =>
|
||||
createGrpcWebTransport({
|
||||
...grpcWebOptions,
|
||||
interceptors: interceptors,
|
||||
}),
|
||||
deps: [INTERCEPTORS],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
9
internal/frontend/holos/src/connect/interceptor.token.ts
Normal file
9
internal/frontend/holos/src/connect/interceptor.token.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { InjectionToken } from '@angular/core'
|
||||
import type { Interceptor } from '@connectrpc/connect'
|
||||
|
||||
export const INTERCEPTORS = new InjectionToken<Interceptor[]>(
|
||||
'connect.interceptors',
|
||||
{
|
||||
factory: () => [],
|
||||
}
|
||||
)
|
||||
120
internal/frontend/holos/src/connect/observable-client.ts
Normal file
120
internal/frontend/holos/src/connect/observable-client.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { makeAnyClient, CallOptions, Transport } from '@connectrpc/connect'
|
||||
import { createAsyncIterable } from '@connectrpc/connect/protocol'
|
||||
import {
|
||||
ServiceType,
|
||||
PartialMessage,
|
||||
MethodInfoServerStreaming,
|
||||
MethodInfo,
|
||||
MethodInfoUnary,
|
||||
MethodKind,
|
||||
Message,
|
||||
} from '@bufbuild/protobuf'
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
export type ObservableClient<T extends ServiceType> = {
|
||||
[P in keyof T['methods']]: T['methods'][P] extends MethodInfoUnary<
|
||||
infer I,
|
||||
infer O
|
||||
>
|
||||
? UnaryFn<I, O>
|
||||
: T['methods'][P] extends MethodInfoServerStreaming<infer I, infer O>
|
||||
? ServerStreamingFn<I, O>
|
||||
: never
|
||||
}
|
||||
|
||||
export function createObservableClient<T extends ServiceType>(
|
||||
service: T,
|
||||
transport: Transport
|
||||
) {
|
||||
return makeAnyClient(service, (method) => {
|
||||
switch (method.kind) {
|
||||
case MethodKind.Unary:
|
||||
return createUnaryFn(transport, service, method)
|
||||
case MethodKind.ServerStreaming:
|
||||
return createServerStreamingFn(transport, service, method)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}) as ObservableClient<T>
|
||||
}
|
||||
|
||||
type UnaryFn<I extends Message<I>, O extends Message<O>> = (
|
||||
request: PartialMessage<I>,
|
||||
options?: CallOptions
|
||||
) => Observable<O>
|
||||
|
||||
function createUnaryFn<I extends Message<I>, O extends Message<O>>(
|
||||
transport: Transport,
|
||||
service: ServiceType,
|
||||
method: MethodInfo<I, O>
|
||||
): UnaryFn<I, O> {
|
||||
return function (requestMessage, options) {
|
||||
return new Observable<O>((subscriber) => {
|
||||
transport
|
||||
.unary(
|
||||
service,
|
||||
method,
|
||||
options?.signal,
|
||||
options?.timeoutMs,
|
||||
options?.headers,
|
||||
requestMessage
|
||||
)
|
||||
.then(
|
||||
(response) => {
|
||||
options?.onHeader?.(response.header)
|
||||
subscriber.next(response.message)
|
||||
options?.onTrailer?.(response.trailer)
|
||||
},
|
||||
(err) => {
|
||||
subscriber.error(err)
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
subscriber.complete()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type ServerStreamingFn<I extends Message<I>, O extends Message<O>> = (
|
||||
request: PartialMessage<I>,
|
||||
options?: CallOptions
|
||||
) => Observable<O>
|
||||
|
||||
export function createServerStreamingFn<
|
||||
I extends Message<I>,
|
||||
O extends Message<O>
|
||||
>(
|
||||
transport: Transport,
|
||||
service: ServiceType,
|
||||
method: MethodInfo<I, O>
|
||||
): ServerStreamingFn<I, O> {
|
||||
return function (input, options) {
|
||||
return new Observable<O>((subscriber) => {
|
||||
transport
|
||||
.stream<I, O>(
|
||||
service,
|
||||
method,
|
||||
options?.signal,
|
||||
options?.timeoutMs,
|
||||
options?.headers,
|
||||
createAsyncIterable([input])
|
||||
)
|
||||
.then(
|
||||
async (streamResponse) => {
|
||||
options?.onHeader?.(streamResponse.header)
|
||||
for await (const response of streamResponse.message) {
|
||||
subscriber.next(response)
|
||||
}
|
||||
options?.onTrailer?.(streamResponse.trailer)
|
||||
},
|
||||
(err) => {
|
||||
subscriber.error(err)
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
subscriber.complete()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
4
internal/frontend/holos/src/connect/transport.token.ts
Normal file
4
internal/frontend/holos/src/connect/transport.token.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { InjectionToken } from "@angular/core";
|
||||
import type { Transport } from "@connectrpc/connect";
|
||||
|
||||
export const TRANSPORT = new InjectionToken<Transport>("connect.transport");
|
||||
@@ -3,7 +3,7 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
"types": [],
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
@@ -282,6 +283,7 @@ func getenv(key, defaultValue string) string {
|
||||
type ServerConfig struct {
|
||||
oidcIssuer string // --oidc-issuer
|
||||
oidcAudiences stringSlice // --oidc-audience
|
||||
authHeader string // --auth-header
|
||||
listenAndServe bool // --no-serve
|
||||
listenPort int // --listen-port
|
||||
metricsPort int // --metrics-port
|
||||
@@ -299,6 +301,10 @@ func (c *ServerConfig) OIDCAudiences() []string {
|
||||
return c.oidcAudiences
|
||||
}
|
||||
|
||||
func (c *ServerConfig) AuthHeader() string {
|
||||
return c.authHeader
|
||||
}
|
||||
|
||||
// DatabaseURI represents the database connection uri.
|
||||
func (c *ServerConfig) DatabaseURI() string {
|
||||
return c.databaseURI
|
||||
@@ -326,6 +332,7 @@ func (c *ServerConfig) FlagSet() *flag.FlagSet {
|
||||
f := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
f.StringVar(&c.oidcIssuer, "oidc-issuer", c.oidcIssuer, "oidc issuer url.")
|
||||
f.Var(&c.oidcAudiences, "oidc-audience", "allowed oidc audiences.")
|
||||
f.StringVar(&c.authHeader, "auth-header", authn.Header, "bearer token header.")
|
||||
f.BoolVar(&c.listenAndServe, "serve", true, "listen and serve requests.")
|
||||
f.IntVar(&c.listenPort, "listen-port", 3000, "service listen port.")
|
||||
f.IntVar(&c.metricsPort, "metrics-port", 9090, "metrics listen port.")
|
||||
|
||||
@@ -1,53 +1 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func NewHolosHandler(db *ent.Client) *HolosHandler {
|
||||
return &HolosHandler{db: db}
|
||||
}
|
||||
|
||||
// HolosHandler implements the connect service handler interface.
|
||||
type HolosHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *HolosHandler) GetUserClaims(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.GetUserClaimsRequest],
|
||||
) (*connect.Response[holos.GetUserClaimsResponse], error) {
|
||||
authnIdentity, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
res := connect.NewResponse(&holos.GetUserClaimsResponse{
|
||||
Iss: authnIdentity.Issuer(),
|
||||
Sub: authnIdentity.Subject(),
|
||||
Email: authnIdentity.Email(),
|
||||
EmailVerified: authnIdentity.Verified(),
|
||||
Name: authnIdentity.Name(),
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// UserToRPC returns an *holos.User adapted from *ent.User u.
|
||||
func UserToRPC(u *ent.User) *holos.User {
|
||||
iamUser := holos.User{
|
||||
Id: u.ID.String(),
|
||||
Email: u.Email,
|
||||
Name: u.Name,
|
||||
Timestamps: &holos.Timestamps{
|
||||
CreatedAt: timestamppb.New(u.CreatedAt),
|
||||
UpdatedAt: timestamppb.New(u.UpdatedAt),
|
||||
},
|
||||
}
|
||||
return &iamUser
|
||||
}
|
||||
|
||||
143
internal/server/handler/organization.go
Normal file
143
internal/server/handler/organization.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"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/server/middleware/authn"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// NewOrganizationHandler returns a new OrganizationService implementation.
|
||||
func NewOrganizationHandler(db *ent.Client) *OrganizationHandler {
|
||||
return &OrganizationHandler{db: db}
|
||||
}
|
||||
|
||||
// OrganizationHandler implements the OrganizationService interface.
|
||||
type OrganizationHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *OrganizationHandler) GetCallerOrganizations(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.GetCallerOrganizationsRequest],
|
||||
) (*connect.Response[holos.GetCallerOrganizationsResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
dbUser, err := h.db.User.Query().
|
||||
Where(
|
||||
user.Iss(authnID.Issuer()),
|
||||
user.Sub(authnID.Subject()),
|
||||
).
|
||||
WithOrganizations().
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
rpcOrgs := make([]*holos.Organization, 0, len(dbUser.Edges.Organizations))
|
||||
for _, dbOrg := range dbUser.Edges.Organizations {
|
||||
rpcOrgs = append(rpcOrgs, OrganizationToRPC(dbOrg))
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.GetCallerOrganizationsResponse{
|
||||
User: UserToRPC(dbUser),
|
||||
Organizations: rpcOrgs,
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *OrganizationHandler) CreateCallerOrganization(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerOrganizationRequest],
|
||||
) (*connect.Response[holos.GetCallerOrganizationsResponse], error) {
|
||||
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())
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
var org *ent.Organization
|
||||
err = WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
|
||||
org, err = h.db.Organization.Create().
|
||||
SetName(cleanAndAppendRandom(authnID.Name())).
|
||||
SetDisplayName(authnID.GivenName() + "'s Org").
|
||||
SetCreatorID(dbUser.ID).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbUser, err = dbUser.Update().
|
||||
AddOrganizations(org).
|
||||
Save(ctx)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, errors.Wrap(err))
|
||||
}
|
||||
|
||||
// TODO: prefetch organizations
|
||||
dbOrgs, err := dbUser.QueryOrganizations().All(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, errors.Wrap(err))
|
||||
}
|
||||
rpcOrgs := make([]*holos.Organization, 0, len(dbOrgs))
|
||||
for _, dbOrg := range dbOrgs {
|
||||
rpcOrgs = append(rpcOrgs, OrganizationToRPC(dbOrg))
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.GetCallerOrganizationsResponse{
|
||||
User: UserToRPC(dbUser),
|
||||
Organizations: rpcOrgs,
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func cleanAndAppendRandom(s string) string {
|
||||
mapping := func(r rune) rune {
|
||||
if unicode.IsLetter(r) {
|
||||
return unicode.ToLower(r)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
cleaned := strings.Map(mapping, s)
|
||||
randNum := rand.Intn(900_000) + 100_000
|
||||
return fmt.Sprintf("%s-%06d", cleaned, randNum)
|
||||
}
|
||||
|
||||
// OrganizationToRPC returns an *holos.Organization adapted from *ent.Organization u.
|
||||
func OrganizationToRPC(org *ent.Organization) *holos.Organization {
|
||||
rpcEntity := holos.Organization{
|
||||
Id: org.ID.String(),
|
||||
Name: org.Name,
|
||||
DisplayName: org.DisplayName,
|
||||
Timestamps: &holos.Timestamps{
|
||||
CreatedAt: timestamppb.New(org.CreatedAt),
|
||||
UpdatedAt: timestamppb.New(org.UpdatedAt),
|
||||
},
|
||||
}
|
||||
return &rpcEntity
|
||||
}
|
||||
@@ -6,12 +6,117 @@ import (
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"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/server/middleware/authn"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// NewUserHandler returns a new UserService implementation.
|
||||
func NewUserHandler(db *ent.Client) *UserHandler {
|
||||
return &UserHandler{db: db}
|
||||
}
|
||||
|
||||
// UserHandler implements the UserService interface.
|
||||
type UserHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetCallerClaims(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.GetCallerClaimsRequest],
|
||||
) (*connect.Response[holos.GetCallerClaimsResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
res := connect.NewResponse(&holos.GetCallerClaimsResponse{
|
||||
Claims: &holos.Claims{
|
||||
Iss: authnID.Issuer(),
|
||||
Sub: authnID.Subject(),
|
||||
Email: authnID.Email(),
|
||||
EmailVerified: authnID.Verified(),
|
||||
Name: authnID.Name(),
|
||||
Groups: authnID.Groups(),
|
||||
GivenName: authnID.GivenName(),
|
||||
FamilyName: authnID.FamilyName(),
|
||||
Picture: authnID.Picture(),
|
||||
},
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetCallerUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.GetCallerUserRequest],
|
||||
) (*connect.Response[holos.GetCallerUserResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Email())
|
||||
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))
|
||||
}
|
||||
}
|
||||
res := connect.NewResponse(&holos.GetCallerUserResponse{User: UserToRPC(dbUser)})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateCallerUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerUserRequest],
|
||||
) (*connect.Response[holos.CreateCallerUserResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
var createdUser *ent.User
|
||||
err = WithTx(ctx, h.db, func(tx *ent.Tx) error {
|
||||
createdUser, err = createUser(ctx, tx.Client(), authnID.Name(), authnID)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "could not save transaction", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.CreateCallerUserResponse{
|
||||
User: UserToRPC(createdUser),
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// UserToRPC returns an *holos.User adapted from *ent.User u.
|
||||
func UserToRPC(u *ent.User) *holos.User {
|
||||
iamUser := holos.User{
|
||||
Id: u.ID.String(),
|
||||
Email: u.Email,
|
||||
Name: u.Name,
|
||||
Timestamps: &holos.Timestamps{
|
||||
CreatedAt: timestamppb.New(u.CreatedAt),
|
||||
UpdatedAt: timestamppb.New(u.UpdatedAt),
|
||||
},
|
||||
}
|
||||
return &iamUser
|
||||
}
|
||||
|
||||
func getUser(ctx context.Context, client *ent.Client, email string) (*ent.User, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
user, err := client.User.Query().Where(user.Email(email)).Only(ctx)
|
||||
if err != nil {
|
||||
log.DebugContext(ctx, "could not get user", "err", err, "email", email)
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func createUser(ctx context.Context, client *ent.Client, name string, claims authn.Identity) (*ent.User, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
// Create the user, error if it already exists
|
||||
@@ -20,7 +125,7 @@ func createUser(ctx context.Context, client *ent.Client, name string, claims aut
|
||||
SetEmail(claims.Email()).
|
||||
SetIss(claims.Issuer()).
|
||||
SetSub(claims.Subject()).
|
||||
SetName(claims.Name()).
|
||||
SetName(name).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
err = connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
@@ -29,43 +134,7 @@ func createUser(ctx context.Context, client *ent.Client, name string, claims aut
|
||||
}
|
||||
|
||||
log = log.With("user", user)
|
||||
log.DebugContext(ctx, "created user")
|
||||
log.InfoContext(ctx, "created user")
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (h *HolosHandler) RegisterUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.RegisterUserRequest],
|
||||
) (*connect.Response[holos.RegisterUserResponse], error) {
|
||||
log := logger.FromContext(ctx).With("issue", 127)
|
||||
oidc, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
var name string
|
||||
if req.Msg.Name != nil {
|
||||
name = req.Msg.GetName()
|
||||
} else {
|
||||
name = oidc.Name()
|
||||
}
|
||||
|
||||
var createdUser *ent.User
|
||||
err = WithTx(ctx, h.db, func(tx *ent.Tx) error {
|
||||
createdUser, err = createUser(ctx, tx.Client(), name, oidc)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "could not save transaction", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log = log.With("user.id", createdUser.ID, "user.name", createdUser.Name)
|
||||
log.InfoContext(ctx, "registered user", "event", "registration")
|
||||
|
||||
res := connect.NewResponse(&holos.RegisterUserResponse{
|
||||
User: UserToRPC(createdUser),
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
const Header = "x-oidc-id-token"
|
||||
|
||||
// Verifier is the interface that wraps the basic Verify method to verify an
|
||||
// oidc id token is authentic. Intended for use in request handlers.
|
||||
type Verifier interface {
|
||||
@@ -45,6 +47,14 @@ type Identity interface {
|
||||
Verified() bool
|
||||
// Name is usually set on the initial id token, often omitted by google in refreshed id tokens.
|
||||
Name() string
|
||||
// Groups is the groups claim.
|
||||
Groups() []string
|
||||
// GivenName is the given name of the user.
|
||||
GivenName() string
|
||||
// FamilyName is the family name of the user.
|
||||
FamilyName() string
|
||||
// Picture is an optional avatar image url for the user.
|
||||
Picture() string
|
||||
}
|
||||
|
||||
// key is an unexported type for keys defined in this package to prevent
|
||||
@@ -100,11 +110,15 @@ func NewVerifier(ctx context.Context, log *slog.Logger, issuer string) (*oidc.ID
|
||||
}
|
||||
|
||||
type claims struct {
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"email_verified"`
|
||||
Name string `json:"name"`
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"email_verified"`
|
||||
Name string `json:"name"`
|
||||
Groups []string `json:"groups"`
|
||||
GivenName string `json:"given_name"`
|
||||
FamilyName string `json:"family_name"`
|
||||
Picture string `json:"picture"`
|
||||
}
|
||||
|
||||
type user struct {
|
||||
@@ -127,13 +141,29 @@ func (u user) Email() string {
|
||||
return u.claims.Email
|
||||
}
|
||||
|
||||
func (u user) Groups() []string {
|
||||
return u.claims.Groups
|
||||
}
|
||||
|
||||
func (u user) GivenName() string {
|
||||
return u.claims.GivenName
|
||||
}
|
||||
|
||||
func (u user) FamilyName() string {
|
||||
return u.claims.FamilyName
|
||||
}
|
||||
|
||||
func (u user) Picture() string {
|
||||
return u.claims.Picture
|
||||
}
|
||||
|
||||
func (u user) Verified() bool {
|
||||
return u.claims.Verified
|
||||
}
|
||||
|
||||
// Handler returns a handler that verifies the request is authentic and adds a
|
||||
// Identity to the request context.
|
||||
func Handler(v Verifier, allowedAudiences []string, next http.Handler) http.Handler {
|
||||
func Handler(v Verifier, allowedAudiences []string, header string, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var rawIDToken string
|
||||
start := time.Now()
|
||||
@@ -142,7 +172,7 @@ func Handler(v Verifier, allowedAudiences []string, next http.Handler) http.Hand
|
||||
|
||||
// Check the X-Auth-Request-Access-Token header set by Istio ExternalAuthorization
|
||||
if rawIDToken == "" {
|
||||
rawIDToken = r.Header.Get("X-Auth-Request-Access-Token")
|
||||
rawIDToken = r.Header.Get(header)
|
||||
}
|
||||
|
||||
// Validate the authorization bearer token
|
||||
|
||||
@@ -145,7 +145,7 @@ func (hf *handlerFactory) NewHandler(t testing.TB) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := logger.NewContext(r.Context(), testutils.TestLogger(t))
|
||||
r = r.WithContext(ctx)
|
||||
authn.Handler(hf.verifier, []string{clientID}, http.HandlerFunc(myHandler)).ServeHTTP(w, r)
|
||||
authn.Handler(hf.verifier, []string{clientID}, authn.Header, http.HandlerFunc(myHandler)).ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/grpcreflect"
|
||||
"connectrpc.com/validate"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
@@ -91,6 +92,17 @@ func (s *Server) registerHandlers() {
|
||||
s.mux.Handle("/", s.middlewares(s.notFoundHandler()))
|
||||
}
|
||||
|
||||
// handle wraps handler with holos authentication and registers the handler with the server mux.
|
||||
func (s *Server) handle(pattern string, handler http.Handler) {
|
||||
authenticatingHandler := authn.Handler(
|
||||
s.authenticator,
|
||||
s.cfg.ServerConfig.OIDCAudiences(),
|
||||
s.cfg.ServerConfig.AuthHeader(),
|
||||
handler,
|
||||
)
|
||||
s.mux.Handle(pattern, authenticatingHandler)
|
||||
}
|
||||
|
||||
func (s *Server) registerConnectRpc() error {
|
||||
// Validator for all rpc messages
|
||||
validator, err := validate.NewInterceptor()
|
||||
@@ -98,10 +110,18 @@ func (s *Server) registerConnectRpc() error {
|
||||
return errors.Wrap(fmt.Errorf("could not initialize proto validation interceptor: %w", err))
|
||||
}
|
||||
|
||||
h := handler.NewHolosHandler(s.db)
|
||||
holosPath, holosHandler := holosconnect.NewHolosServiceHandler(h, connect.WithInterceptors(validator))
|
||||
authenticatingHandler := authn.Handler(s.authenticator, s.cfg.ServerConfig.OIDCAudiences(), holosHandler)
|
||||
s.mux.Handle(holosPath, s.middlewares(authenticatingHandler))
|
||||
opts := connect.WithInterceptors(validator)
|
||||
|
||||
s.handle(holosconnect.NewUserServiceHandler(handler.NewUserHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewOrganizationServiceHandler(handler.NewOrganizationHandler(s.db), opts))
|
||||
|
||||
reflector := grpcreflect.NewStaticReflector(
|
||||
holosconnect.UserServiceName,
|
||||
holosconnect.OrganizationServiceName,
|
||||
)
|
||||
|
||||
s.mux.Handle(grpcreflect.NewHandlerV1(reflector))
|
||||
s.mux.Handle(grpcreflect.NewHandlerV1Alpha(reflector))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
34
service/holos/v1alpha1/organization.proto
Normal file
34
service/holos/v1alpha1/organization.proto
Normal file
@@ -0,0 +1,34 @@
|
||||
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 Organization {
|
||||
// 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];
|
||||
Timestamps timestamps = 4;
|
||||
}
|
||||
|
||||
message GetCallerOrganizationsRequest {}
|
||||
|
||||
message GetCallerOrganizationsResponse {
|
||||
User user = 1;
|
||||
repeated Organization organizations = 2;
|
||||
}
|
||||
|
||||
message CreateCallerOrganizationRequest {}
|
||||
|
||||
service OrganizationService {
|
||||
rpc GetCallerOrganizations(GetCallerOrganizationsRequest) returns (GetCallerOrganizationsResponse) {}
|
||||
rpc CreateCallerOrganization(CreateCallerOrganizationRequest) returns (GetCallerOrganizationsResponse) {}
|
||||
}
|
||||
18
service/holos/v1alpha1/timestamps.proto
Normal file
18
service/holos/v1alpha1/timestamps.proto
Normal file
@@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package holos.v1alpha1;
|
||||
|
||||
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
|
||||
import "buf/validate/validate.proto";
|
||||
|
||||
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
|
||||
|
||||
message Timestamps {
|
||||
// Created at timestamp
|
||||
google.protobuf.Timestamp created_at = 1 [(buf.validate.field).timestamp.lt_now = true];
|
||||
// Updated at timestamp
|
||||
google.protobuf.Timestamp updated_at = 2 [(buf.validate.field).timestamp.lt_now = true];
|
||||
}
|
||||
@@ -7,28 +7,10 @@ option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos
|
||||
import "google/protobuf/timestamp.proto";
|
||||
// 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";
|
||||
|
||||
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
|
||||
|
||||
message Timestamps {
|
||||
// Created at timestamp
|
||||
google.protobuf.Timestamp created_at = 1 [(buf.validate.field).timestamp.lt_now = true];
|
||||
// Updated at timestamp
|
||||
google.protobuf.Timestamp updated_at = 2 [(buf.validate.field).timestamp.lt_now = true];
|
||||
}
|
||||
|
||||
// Empty request, claims are pulled from the id token
|
||||
message GetUserClaimsRequest {}
|
||||
|
||||
// UserClaims represents id token claims
|
||||
message GetUserClaimsResponse {
|
||||
string iss = 1;
|
||||
string sub = 2;
|
||||
string email = 3 [(buf.validate.field).string.email = true];
|
||||
bool email_verified = 4;
|
||||
string name = 5 [(buf.validate.field).string.max_len = 100];
|
||||
}
|
||||
|
||||
// User represents a human user in the system. See db schema in ent/schema/user.go
|
||||
message User {
|
||||
// Unique id assigned by the server.
|
||||
@@ -39,16 +21,40 @@ message User {
|
||||
Timestamps timestamps = 5;
|
||||
}
|
||||
|
||||
message RegisterUserRequest {
|
||||
optional string name = 1 [(buf.validate.field).string.max_len = 100];
|
||||
}
|
||||
message CreateCallerUserRequest {}
|
||||
|
||||
message RegisterUserResponse {
|
||||
message CreateCallerUserResponse {
|
||||
User user = 1;
|
||||
bool already_exists = 2;
|
||||
}
|
||||
|
||||
service HolosService {
|
||||
rpc GetUserClaims(GetUserClaimsRequest) returns (GetUserClaimsResponse) {}
|
||||
rpc RegisterUser(RegisterUserRequest) returns (RegisterUserResponse) {}
|
||||
message GetCallerClaimsRequest {}
|
||||
|
||||
message Claims {
|
||||
string iss = 1;
|
||||
string sub = 2;
|
||||
string email = 3 [(buf.validate.field).string.email = true];
|
||||
bool email_verified = 4;
|
||||
string name = 5 [(buf.validate.field).string.max_len = 100];
|
||||
repeated string groups = 6;
|
||||
string given_name = 7;
|
||||
string family_name = 8;
|
||||
string picture = 9;
|
||||
}
|
||||
|
||||
// UserClaims represents id token claims
|
||||
message GetCallerClaimsResponse {
|
||||
Claims claims = 1;
|
||||
}
|
||||
|
||||
// Empty request, claims are pulled from the id token
|
||||
message GetCallerUserRequest {}
|
||||
|
||||
message GetCallerUserResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
service UserService {
|
||||
rpc GetCallerClaims(GetCallerClaimsRequest) returns (GetCallerClaimsResponse) {}
|
||||
rpc GetCallerUser(GetCallerUserRequest) returns (GetCallerUserResponse) {}
|
||||
rpc CreateCallerUser(CreateCallerUserRequest) returns (CreateCallerUserResponse) {}
|
||||
}
|
||||
4
tidy.go
4
tidy.go
@@ -2,6 +2,6 @@ package holos
|
||||
|
||||
// These imports are here to keep go mod tidy from constantly creating a dirty git state which makes goreleaser fail.
|
||||
import (
|
||||
_ "github.com/olekukonko/tablewriter"
|
||||
_ "github.com/mattn/go-runewidth"
|
||||
_ "github.com/mattn/go-runewidth"
|
||||
_ "github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
@@ -1 +1 @@
|
||||
68
|
||||
70
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user