diff --git a/applications/accounts-api/api/openapi.yaml b/applications/accounts-api/api/openapi.yaml
index 455422a7..c2d7ab18 100644
--- a/applications/accounts-api/api/openapi.yaml
+++ b/applications/accounts-api/api/openapi.yaml
@@ -1,199 +1,181 @@
----
openapi: 3.0.2
info:
- title: accounts_api
- version: 0.1.0
- description: Accounts rest api
- contact:
- email: cloudharness@metacell.us
- license:
- name: UNLICENSED
+ title: accounts_api
+ version: 0.1.0
+ description: Accounts rest api
+ contact:
+ email: cloudharness@metacell.us
+ license:
+ name: UNLICENSED
servers:
-- url: /api
+ -
+ url: /api
paths:
- /live:
- get:
- tags:
- - infrastructure
- responses:
- "200":
- content:
- application/json:
- schema:
- type: string
- description: Healthy
- "500":
- description: Application is not healthy
- operationId: live
- summary: Test if application is healthy
- /ready:
- get:
- tags:
- - infrastructure
- responses:
- "200":
- content:
- application/json:
- schema:
- type: string
- description: Ready
- "500":
- description: Application is not ready yet
- operationId: ready
- summary: Test if application is ready to take requests
- /users:
- get:
- summary: Get list of users based on query
- operationId: get_users
- parameters:
- - description: query filter
- in: query
- name: query_string
- required: false
- schema:
- type: string
- security:
- - bearerAuth: []
- responses:
- "200":
- content:
- application/json:
- schema:
- type: object
- properties:
- users:
- type: array
- items:
- $ref: '#/components/schemas/User'
- description: Get list of users
- tags:
- - users
- x-openapi-router-controller: accounts_api.controllers.users_controller
- post:
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/User'
- required: true
- tags:
- - users
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/User'
- description: The user as just saved
- security:
- - bearerAuth: []
- operationId: createUser
- /users/{userid}:
- get:
- tags:
- - users
- parameters:
- - name: userid
- description: user id
- schema:
- type: string
- in: path
- required: true
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/User'
- description: Get a user's public information
- operationId: getUser
- put:
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/User'
- required: true
- tags:
- - users
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/User'
- description: The user as just saved
- security:
- - bearerAuth: []
- operationId: updateUser
- parameters:
- - name: userid
- description: user id
- schema:
- type: string
- in: path
- required: true
+ /live:
+ get:
+ tags:
+ - infrastructure
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ type: string
+ description: Healthy
+ '500':
+ description: Application is not healthy
+ operationId: live
+ summary: Test if application is healthy
+ /ready:
+ get:
+ tags:
+ - infrastructure
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ type: string
+ description: Ready
+ '500':
+ description: Application is not ready yet
+ operationId: ready
+ summary: Test if application is ready to take requests
+ /users:
+ post:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ required: true
+ tags:
+ - users
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ description: The user as just saved
+ security:
+ -
+ bearerAuth: []
+ operationId: createUser
+ '/users/{userid}':
+ get:
+ tags:
+ - users
+ parameters:
+ -
+ name: userid
+ description: user id
+ schema:
+ type: string
+ in: path
+ required: true
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ description: Get a user's public information
+ operationId: getUser
+ put:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ required: true
+ tags:
+ - users
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/User'
+ description: The user as just saved
+ security:
+ -
+ bearerAuth: []
+ operationId: updateUser
+ parameters:
+ -
+ name: userid
+ description: user id
+ schema:
+ type: string
+ in: path
+ required: true
components:
- schemas:
- Valid:
- type: object
- properties:
- response:
- type: string
- Url:
- description: ""
- type: string
- User:
- description: Keycloak User
- type: object
- properties:
- id:
- description: user id
- type: string
- username:
- description: Username of the keycloak user
- type: string
- email:
- description: Email address of the keycloak user
- type: string
- firstName:
- description: First Name of the keycloak user
- type: string
- lastName:
- description: Last Name of the keycloak user
- type: string
- groups:
- description: ""
- type: array
- items:
+ schemas:
+ Valid:
+ type: object
+ properties:
+ response:
+ type: string
+ Url:
+ description: ''
type: string
- readOnly: true
- profiles:
- $ref: '#/components/schemas/Profiles'
- description: "External links to user profiles (e.g. GitHub, Twitter, etc.)"
- registrationDate:
- format: date
- description: ""
- type: string
- avatar:
- $ref: '#/components/schemas/Url'
- description: ""
- website:
- $ref: '#/components/schemas/Url'
- description: ""
- additionalProperties: true
- Profiles:
- description: ""
- type: object
- additionalProperties: true
- securitySchemes:
- bearerAuth:
- scheme: bearer
- bearerFormat: JWT
- type: http
- x-bearerInfoFunc: cloudharness.auth.decode_token
+ Profiles:
+ description: ''
+ type: object
+ additionalProperties: true
+ User:
+ description: Keycloak User
+ type: object
+ properties:
+ id:
+ description: user id
+ type: string
+ username:
+ description: Username of the keycloak user
+ type: string
+ email:
+ description: Email address of the keycloak user
+ type: string
+ firstName:
+ description: First Name of the keycloak user
+ type: string
+ lastName:
+ description: Last Name of the keycloak user
+ type: string
+ groups:
+ description: ''
+ type: array
+ items:
+ type: string
+ readOnly: true
+ profiles:
+ $ref: '#/components/schemas/Profiles'
+ description: 'External links to user profiles (e.g. GitHub, Twitter, etc.)'
+ registrationDate:
+ format: date
+ description: ''
+ type: string
+ avatar:
+ $ref: '#/components/schemas/Url'
+ description: ''
+ website:
+ $ref: '#/components/schemas/Url'
+ description: ''
+ quotas:
+ $ref: '#/components/schemas/Profiles'
+ description: ''
+ additionalProperties: true
+ securitySchemes:
+ bearerAuth:
+ scheme: bearer
+ bearerFormat: JWT
+ type: http
+ x-bearerInfoFunc: cloudharness.auth.decode_token
tags:
-- name: infrastructure
- description: ""
-- name: users
- description: ""
+ -
+ name: infrastructure
+ description: ''
+ -
+ name: users
+ description: ''
diff --git a/applications/accounts-api/backend/accounts_api/models/base_model_.py b/applications/accounts-api/backend/accounts_api/models/base_model_.py
index b027aebf..48b2ff79 100644
--- a/applications/accounts-api/backend/accounts_api/models/base_model_.py
+++ b/applications/accounts-api/backend/accounts_api/models/base_model_.py
@@ -11,11 +11,11 @@
class Model(object):
# openapiTypes: The key is attribute name and the
# value is attribute type.
- openapi_types = {}
+ openapi_types: typing.Dict[str, type] = {}
# attributeMap: The key is attribute name and the
# value is json key in definition.
- attribute_map = {}
+ attribute_map: typing.Dict[str, str] = {}
@classmethod
def from_dict(cls: typing.Type[T], dikt) -> T:
diff --git a/applications/accounts-api/backend/accounts_api/models/user.py b/applications/accounts-api/backend/accounts_api/models/user.py
index d1220bb9..35013b1b 100644
--- a/applications/accounts-api/backend/accounts_api/models/user.py
+++ b/applications/accounts-api/backend/accounts_api/models/user.py
@@ -15,7 +15,7 @@ class User(Model):
Do not edit the class manually.
"""
- def __init__(self, id=None, username=None, email=None, first_name=None, last_name=None, groups=None, profiles=None, registration_date=None, avatar=None, website=None): # noqa: E501
+ def __init__(self, id=None, username=None, email=None, first_name=None, last_name=None, groups=None, profiles=None, registration_date=None, avatar=None, website=None, quotas=None): # noqa: E501
"""User - a model defined in OpenAPI
:param id: The id of this User. # noqa: E501
@@ -38,6 +38,8 @@ def __init__(self, id=None, username=None, email=None, first_name=None, last_nam
:type avatar: str
:param website: The website of this User. # noqa: E501
:type website: str
+ :param quotas: The quotas of this User. # noqa: E501
+ :type quotas: Dict[str, object]
"""
self.openapi_types = {
'id': str,
@@ -49,7 +51,8 @@ def __init__(self, id=None, username=None, email=None, first_name=None, last_nam
'profiles': Dict[str, object],
'registration_date': date,
'avatar': str,
- 'website': str
+ 'website': str,
+ 'quotas': Dict[str, object]
}
self.attribute_map = {
@@ -62,7 +65,8 @@ def __init__(self, id=None, username=None, email=None, first_name=None, last_nam
'profiles': 'profiles',
'registration_date': 'registrationDate',
'avatar': 'avatar',
- 'website': 'website'
+ 'website': 'website',
+ 'quotas': 'quotas'
}
self._id = id
@@ -75,6 +79,7 @@ def __init__(self, id=None, username=None, email=None, first_name=None, last_nam
self._registration_date = registration_date
self._avatar = avatar
self._website = website
+ self._quotas = quotas
@classmethod
def from_dict(cls, dikt) -> 'User':
@@ -206,6 +211,7 @@ def last_name(self, last_name):
def groups(self):
"""Gets the groups of this User.
+ # noqa: E501
:return: The groups of this User.
:rtype: List[str]
@@ -216,6 +222,7 @@ def groups(self):
def groups(self, groups):
"""Sets the groups of this User.
+ # noqa: E501
:param groups: The groups of this User.
:type groups: List[str]
@@ -227,7 +234,7 @@ def groups(self, groups):
def profiles(self):
"""Gets the profiles of this User.
- sddssd # noqa: E501
+ # noqa: E501
:return: The profiles of this User.
:rtype: Dict[str, object]
@@ -238,7 +245,7 @@ def profiles(self):
def profiles(self, profiles):
"""Sets the profiles of this User.
- sddssd # noqa: E501
+ # noqa: E501
:param profiles: The profiles of this User.
:type profiles: Dict[str, object]
@@ -250,6 +257,7 @@ def profiles(self, profiles):
def registration_date(self):
"""Gets the registration_date of this User.
+ # noqa: E501
:return: The registration_date of this User.
:rtype: date
@@ -260,6 +268,7 @@ def registration_date(self):
def registration_date(self, registration_date):
"""Sets the registration_date of this User.
+ # noqa: E501
:param registration_date: The registration_date of this User.
:type registration_date: date
@@ -271,6 +280,7 @@ def registration_date(self, registration_date):
def avatar(self):
"""Gets the avatar of this User.
+ # noqa: E501
:return: The avatar of this User.
:rtype: str
@@ -281,6 +291,7 @@ def avatar(self):
def avatar(self, avatar):
"""Sets the avatar of this User.
+ # noqa: E501
:param avatar: The avatar of this User.
:type avatar: str
@@ -292,6 +303,7 @@ def avatar(self, avatar):
def website(self):
"""Gets the website of this User.
+ # noqa: E501
:return: The website of this User.
:rtype: str
@@ -302,9 +314,33 @@ def website(self):
def website(self, website):
"""Sets the website of this User.
+ # noqa: E501
:param website: The website of this User.
:type website: str
"""
self._website = website
+
+ @property
+ def quotas(self):
+ """Gets the quotas of this User.
+
+ # noqa: E501
+
+ :return: The quotas of this User.
+ :rtype: Dict[str, object]
+ """
+ return self._quotas
+
+ @quotas.setter
+ def quotas(self, quotas):
+ """Sets the quotas of this User.
+
+ # noqa: E501
+
+ :param quotas: The quotas of this User.
+ :type quotas: Dict[str, object]
+ """
+
+ self._quotas = quotas
diff --git a/applications/accounts-api/backend/accounts_api/openapi/openapi.yaml b/applications/accounts-api/backend/accounts_api/openapi/openapi.yaml
index 1f5a38b5..d7407cdc 100644
--- a/applications/accounts-api/backend/accounts_api/openapi/openapi.yaml
+++ b/applications/accounts-api/backend/accounts_api/openapi/openapi.yaml
@@ -10,8 +10,10 @@ info:
servers:
- url: /api
tags:
-- name: infrastructure
-- name: users
+- description: ""
+ name: infrastructure
+- description: ""
+ name: users
paths:
/live:
get:
@@ -46,33 +48,6 @@ paths:
- infrastructure
x-openapi-router-controller: accounts_api.controllers.infrastructure_controller
/users:
- get:
- summary: Get list of users based on query
- operationId: get_users
- parameters:
- - description: query filter
- in: query
- name: query_string
- required: false
- schema:
- type: string
- security:
- - bearerAuth: []
- responses:
- "200":
- content:
- application/json:
- schema:
- type: object
- properties:
- users:
- type: array
- items:
- $ref: '#/components/schemas/User'
- description: Get list of users
- tags:
- - users
- x-openapi-router-controller: accounts_api.controllers.users_controller
post:
operationId: create_user
requestBody:
@@ -152,7 +127,12 @@ components:
type: string
type: object
Url:
+ description: ""
type: string
+ Profiles:
+ additionalProperties: true
+ description: ""
+ type: object
User:
additionalProperties: true
description: Keycloak User
@@ -160,8 +140,10 @@ components:
firstName: firstName
lastName: lastName
website: website
+ quotas:
+ key: ""
profiles:
- key: '{}'
+ key: ""
registrationDate: 2000-01-23
groups:
- groups
@@ -187,27 +169,31 @@ components:
description: Last Name of the keycloak user
type: string
groups:
+ description: ""
items:
type: string
readOnly: true
type: array
profiles:
additionalProperties: true
- description: sddssd
+ description: ""
type: object
registrationDate:
+ description: ""
format: date
type: string
avatar:
+ description: ""
type: string
website:
+ description: ""
type: string
+ quotas:
+ additionalProperties: true
+ description: ""
+ type: object
title: User
type: object
- Profiles:
- additionalProperties: true
- description: sddssd
- type: object
securitySchemes:
bearerAuth:
bearerFormat: JWT
diff --git a/applications/accounts-api/backend/accounts_api/services/user_service.py b/applications/accounts-api/backend/accounts_api/services/user_service.py
index 9ed39824..f63c38aa 100644
--- a/applications/accounts-api/backend/accounts_api/services/user_service.py
+++ b/applications/accounts-api/backend/accounts_api/services/user_service.py
@@ -1,4 +1,7 @@
from datetime import datetime
+import os
+from cloudharness.applications import get_configuration
+from cloudharness.auth.quota import get_user_quotas
from keycloak.exceptions import KeycloakGetError, KeycloakError
from accounts_api.models import User
from cloudharness.auth import AuthClient
@@ -13,7 +16,7 @@ class UserNotAuthorized(Exception): pass
def get_user(username_or_id: str) -> User:
- client = AuthClient()
+ client = AuthClient(username=os.getenv('ACCOUNTS_ADMIN_USERNAME', None), password=os.getenv('ACCOUNTS_ADMIN_PASSWORD', None))
try:
kc_user = client.get_user(username_or_id)
@@ -35,6 +38,11 @@ def get_user(username_or_id: str) -> User:
except: # user not provided
log.error("Error checking user", exc_info=True)
user.email = None
+
+
+ ws_quotas = get_user_quotas(get_configuration('workspaces'), user_id=user.id)
+ hub_quotas = get_user_quotas(get_configuration('jupyterhub'), user_id=user.id)
+ user.quotas = {**ws_quotas, **hub_quotas}
return user
diff --git a/applications/accounts/Dockerfile b/applications/accounts/Dockerfile
index c14b452f..58495c81 100644
--- a/applications/accounts/Dockerfile
+++ b/applications/accounts/Dockerfile
@@ -8,5 +8,5 @@ USER jboss
# Customize keycloak look
COPY themes/custom /opt/jboss/keycloak/themes/custom
-# keycloak kafka listener plugin
-COPY plugins/metacell-admin-event-listener-bundle-1.0.0.ear /opt/jboss/keycloak/standalone/deployments/
+# plugins
+COPY plugins/* /opt/jboss/keycloak/standalone/deployments/
diff --git a/applications/accounts/deploy/resources/realm.json b/applications/accounts/deploy/resources/realm.json
index 2a2b4647..d3e9e060 100644
--- a/applications/accounts/deploy/resources/realm.json
+++ b/applications/accounts/deploy/resources/realm.json
@@ -43,6 +43,46 @@
"duplicateEmailsAllowed": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": true,
+ "identityProviders": [
+ {
+ "alias": "orcid",
+ "internalId": "2ab5a52e-60f2-411b-86af-ae58224155e3",
+ "providerId": "orcid",
+ "enabled": true,
+ "updateProfileFirstLoginMode": "on",
+ "trustEmail": false,
+ "storeToken": false,
+ "addReadTokenRoleOnCreate": false,
+ "authenticateByDefault": false,
+ "linkOnly": false,
+ "firstBrokerLoginFlowAlias": "first broker login",
+ "config": {
+ "syncMode": "IMPORT",
+ "clientSecret": "**********",
+ "clientId": "APP-ZBK69Z38RMGG6G0R",
+ "useJwksUrl": "true"
+ }
+ },
+ {
+ "alias": "github",
+ "internalId": "a22eb498-74dc-4d64-9bcd-38e2623c1af2",
+ "providerId": "github",
+ "enabled": true,
+ "updateProfileFirstLoginMode": "on",
+ "trustEmail": false,
+ "storeToken": false,
+ "addReadTokenRoleOnCreate": false,
+ "authenticateByDefault": false,
+ "linkOnly": false,
+ "firstBrokerLoginFlowAlias": "first broker login",
+ "config": {
+ "syncMode": "IMPORT",
+ "clientSecret": "**********",
+ "clientId": "97a77871b8005be03669",
+ "useJwksUrl": "true"
+ }
+ }
+ ],
"users": [
{{- range $app := .Values.apps }}
{{- if (hasKey $app.harness "accounts") }}
diff --git a/applications/accounts/docker-compose.yaml b/applications/accounts/docker-compose.yaml
index d30fe03b..3ca0a1ba 100644
--- a/applications/accounts/docker-compose.yaml
+++ b/applications/accounts/docker-compose.yaml
@@ -8,7 +8,7 @@ services:
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
keycloak:
- image: quay.io/keycloak/keycloak:11.0.2
+ image: quay.io/keycloak/keycloak:16.1.0
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres
diff --git a/applications/accounts/plugins/keycloak-orcid-1.0.0.jar b/applications/accounts/plugins/keycloak-orcid-1.0.0.jar
new file mode 100644
index 00000000..fd7c6c2b
Binary files /dev/null and b/applications/accounts/plugins/keycloak-orcid-1.0.0.jar differ
diff --git a/applications/jupyterhub/deploy/values-minimal.yaml b/applications/jupyterhub/deploy/values-minimal.yaml
new file mode 100644
index 00000000..9054ed69
--- /dev/null
+++ b/applications/jupyterhub/deploy/values-minimal.yaml
@@ -0,0 +1,4 @@
+harness:
+ dependencies:
+ soft: [nfsserver, accounts]
+ hard: []
diff --git a/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py b/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py
index 3f32dac6..cea91077 100644
--- a/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py
+++ b/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py
@@ -13,11 +13,13 @@
handler.setLevel(logging.DEBUG)
logging.getLogger().addHandler(handler)
+
class CloudHarnessAuthenticateHandler(BaseHandler):
"""
Handler for /chkclogin
Creates a new user based on the keycloak user, and auto starts their server
"""
+
def initialize(self, force_new_server, process_user):
super().initialize()
self.force_new_server = force_new_server
@@ -28,28 +30,36 @@ def get(self):
self.clear_login_cookie()
try:
-
- accessToken = self.request.cookies.get(
- 'kc-access', None) or self.request.cookies.get('accessToken', None)
- print("Token", accessToken)
- if accessToken == '-1' or not accessToken:
- import socket
- raw_user = self.user_from_username("a-%s-%0.5x" % (socket.inet_aton(self.request.remote_ip).hex(), random.randint(0, 99999)))
- else:
- accessToken = accessToken.value
- user_data = AuthClient.decode_token(accessToken)
- username = user_data['sub']
- print("Username", username, "-",user_data['preferred_username'])
- raw_user = self.user_from_username(username)
- print("JH user: ", raw_user.__dict__)
- self.set_login_cookie(raw_user)
+
+ accessToken = self.request.cookies.get(
+ 'kc-access', None) or self.request.cookies.get('accessToken', None)
+ print("Token", accessToken)
+ if accessToken == '-1' or not accessToken:
+
+ raw_user = self.get_anonymous_user()
+ else:
+ accessToken = accessToken.value
+ user_data = AuthClient.decode_token(accessToken)
+ username = user_data['sub']
+ print("Username", username, "-",
+ user_data['preferred_username'])
+ raw_user = self.user_from_username(username)
+
except Exception as e:
- logging.error("Error getting user from session", exc_info=True)
- raise
+ logging.info("Error getting user from session", exc_info=True)
+ self.request.cookies.clear()
+ raw_user = self.get_anonymous_user()
+ print("JH user: ", raw_user.__dict__)
+ self.set_login_cookie(raw_user)
user = yield gen.maybe_future(self.process_user(raw_user, self))
self.redirect(self.get_next_url(user))
+ def get_anonymous_user(self):
+ import socket
+ print("Anonymous connection: ", self.request.remote_ip)
+ return self.user_from_username("a-%s-%0.5x" % (socket.inet_aton(self.request.remote_ip).hex(), random.randint(0, 99999)))
+
class CloudHarnessAuthenticator(Authenticator):
"""
diff --git a/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py b/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py
index 423e5c50..414e94b9 100755
--- a/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py
+++ b/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py
@@ -113,14 +113,11 @@ def workspace_volume_is_legacy(workspace_id):
})
except CookieNotFound:
# Setup a readonly default session
- self.pod_name = f'anonymous-{self.user.username}-{appname}'
- from pprint import pprint
- pprint(self.volumes)
-
+ self.pod_name = f'anonymous-{self.user.name}-{appname}'
+ self.storage_pvc_ensure = False
self.volumes = []
- pprint(self.volume_mounts)
self.volume_mounts = []
- self.maxAge
+ print("Starting anonymoous session with no volumes")
except Exception as e:
log.error('Change pod manifest failed due to an error.', exc_info=True)
diff --git a/applications/jupyterlab-minimal/Dockerfile b/applications/jupyterlab-minimal/Dockerfile
index c055ce77..0c0770c9 100644
--- a/applications/jupyterlab-minimal/Dockerfile
+++ b/applications/jupyterlab-minimal/Dockerfile
@@ -4,4 +4,5 @@ COPY hub/jupyter_notebook_config.py /etc/jupyter/jupyter_notebook_config.py
USER root
RUN mkdir /opt/workspace
RUN chown -R jovyan:users /opt/workspace
-USER jovyan
\ No newline at end of file
+COPY --chown=jovyan:users overrides/* /opt/conda/share/jupyter/lab/static/
+USER jovyan
diff --git a/applications/jupyterlab-minimal/deploy/values.yaml b/applications/jupyterlab-minimal/deploy/values.yaml
index d5264ee5..fc99dbec 100644
--- a/applications/jupyterlab-minimal/deploy/values.yaml
+++ b/applications/jupyterlab-minimal/deploy/values.yaml
@@ -20,5 +20,5 @@ harness:
c.Spawner.http_timeout = 300
c.Spawner.start_timeout = 300
- c.JupyterHub.tornado_settings = { "headers": { "Content-Security-Policy": "frame-ancestors 'self' localhost:3000 *.osb.local osb.local localhost *.metacell.us *.opensourcebrain.org"}}
+ c.JupyterHub.tornado_settings = { "headers": { "Content-Security-Policy": "frame-ancestors *"}}
diff --git a/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py b/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py
index c3cbd116..a23e00ad 100644
--- a/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py
+++ b/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py
@@ -20,7 +20,7 @@
print(c.NotebookApp.tornado_settings)
c.NotebookApp.tornado_settings = {
'headers': {
- 'Content-Security-Policy': "frame-ancestors 'self' localhost:3000 localhost *.osb.local *.opensourcebrain.org",
+ 'Content-Security-Policy': "frame-ancestors *",
}
}
print(c.NotebookApp.tornado_settings)
diff --git a/applications/jupyterlab-minimal/overrides/index.html b/applications/jupyterlab-minimal/overrides/index.html
new file mode 100644
index 00000000..0a0a9542
--- /dev/null
+++ b/applications/jupyterlab-minimal/overrides/index.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ JupyterLab
+ {# Copy so we do not modify the page_config with
+ updates. #} {% set page_config_full = page_config.copy() %} {# Set a dummy variable - we just want the side effect
+ of the update. #} {% set _ = page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %}
+ {% block favicon %}
+
+
+ {% endblock %}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/applications/jupyterlab/Dockerfile b/applications/jupyterlab/Dockerfile
index b3313c70..7f8f4a16 100644
--- a/applications/jupyterlab/Dockerfile
+++ b/applications/jupyterlab/Dockerfile
@@ -92,5 +92,5 @@ RUN pip install -r requirements.txt --upgrade --no-cache-dir
#########################################################################
-
+COPY --chown=jovyan:users overrides/* /opt/conda/share/jupyter/lab/static/
WORKDIR /opt/workspace
diff --git a/applications/jupyterlab/overrides/index.html b/applications/jupyterlab/overrides/index.html
new file mode 100644
index 00000000..bf3a6746
--- /dev/null
+++ b/applications/jupyterlab/overrides/index.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ JupyterLab
+ {# Copy so we do not modify the page_config with
+ updates. #} {% set page_config_full = page_config.copy() %} {# Set a dummy variable - we just want the side effect
+ of the update. #} {% set _ = page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %}
+ {% block favicon %}
+
+
+ {% endblock %}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/applications/netpyne/Dockerfile b/applications/netpyne/Dockerfile
index 3e58e618..f7ea8b2d 100644
--- a/applications/netpyne/Dockerfile
+++ b/applications/netpyne/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:13.14 as jsbuild
+FROM node:14 as jsbuild
ENV REPO=https://github.com/MetaCell/NetPyNE-UI.git
ENV BRANCH_TAG=release/1.0.0
ENV FOLDER=netpyne
diff --git a/applications/netpyne/overrides/requirements.txt b/applications/netpyne/overrides/requirements.txt
index b1236668..f1b26169 100644
--- a/applications/netpyne/overrides/requirements.txt
+++ b/applications/netpyne/overrides/requirements.txt
@@ -1,2 +1,3 @@
lfpykit==0.5.1
+pyNeuroML>=0.7.5
git+https://github.com/Neurosim-lab/netpyne.git@osbv2
\ No newline at end of file
diff --git a/applications/nfsserver/deploy/values-dev.yaml b/applications/nfsserver/deploy/values-dev.yaml
index c0ec34ae..a2b66b9e 100644
--- a/applications/nfsserver/deploy/values-dev.yaml
+++ b/applications/nfsserver/deploy/values-dev.yaml
@@ -1,2 +1,4 @@
nfs:
- useDNS: true
\ No newline at end of file
+ useDNS: true
+server:
+ diskSize: 20Gi
\ No newline at end of file
diff --git a/applications/nfsserver/deploy/values.yaml b/applications/nfsserver/deploy/values.yaml
index 42607a99..be457168 100644
--- a/applications/nfsserver/deploy/values.yaml
+++ b/applications/nfsserver/deploy/values.yaml
@@ -1,4 +1,4 @@
# nfs server pvc disk size (/exports)
server:
- diskSize: 20Gi
+ diskSize: 40Gi
diff --git a/applications/osb-portal/src/components/repository/RepositoryPageDetails.tsx b/applications/osb-portal/src/components/repository/RepositoryPageDetails.tsx
index 150ab0db..18e0e9a9 100644
--- a/applications/osb-portal/src/components/repository/RepositoryPageDetails.tsx
+++ b/applications/osb-portal/src/components/repository/RepositoryPageDetails.tsx
@@ -227,7 +227,7 @@ const RepositoryPageDetails = ({
overflow: "auto",
}}
>
-
+
({
iframe: {
flex: 1,
+ border: "none",
},
}));
@@ -117,7 +118,6 @@ export const WorkspaceFrame = (props: {
return (
diff --git a/applications/osb-portal/src/components/workspace/drawer/WorkspaceInteractions.tsx b/applications/osb-portal/src/components/workspace/drawer/WorkspaceInteractions.tsx
index 92ba3163..2379cac5 100644
--- a/applications/osb-portal/src/components/workspace/drawer/WorkspaceInteractions.tsx
+++ b/applications/osb-portal/src/components/workspace/drawer/WorkspaceInteractions.tsx
@@ -20,6 +20,7 @@ import Tooltip from "@mui/material/Tooltip";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import AddOutlinedIcon from "@mui/icons-material/AddOutlined";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
+import CachedIcon from "@mui/icons-material/Cached";
import Box from "@mui/material/Box";
import WorkspaceResourceBrowser from "./WorkspaceResourceBrowser";
@@ -29,9 +30,9 @@ import AddResourceForm from "../AddResourceForm";
import { canEditWorkspace } from "../../../service/UserService";
import OSBDialog from "../../common/OSBDialog";
import { bgRegular as borderColor, paragraph } from "../../../theme";
-import WorkspaceActionsMenu from "../WorkspaceActionsMenu";
import { UserInfo } from "../../../types/user";
import { Typography } from "@mui/material";
+import { update } from "lodash";
const useStyles = makeStyles((theme) => ({
drawerContent: {
@@ -101,6 +102,7 @@ interface WorkspaceProps {
workspace: Workspace;
open?: boolean;
refreshWorkspace?: () => void;
+ refreshWorkspaceResources?: () => void;
updateWorkspace: (ws: Workspace) => null;
deleteWorkspace: (wsId: number) => null;
user: UserInfo;
@@ -144,7 +146,7 @@ const SidebarIconButton = styled(IconButton)(({ theme }) => ({
}));
export default (props: WorkspaceProps | any) => {
- const { workspace, refreshWorkspace, hideTabs } = props;
+ const { workspace, refreshWorkspace, hideTabs, refreshWorkspaceResources } = props;
const classes = useStyles();
const [tabValue, setTabValue] = React.useState(0);
@@ -165,6 +167,10 @@ export default (props: WorkspaceProps | any) => {
refreshWorkspace();
};
+ const handleRefreshResources = () => {
+ refreshWorkspaceResources();
+ }
+
if (!workspace) {
return null;
}
@@ -185,24 +191,24 @@ export default (props: WorkspaceProps | any) => {
{props.open ? (
- {!hideTabs &&
-
-
- }
+ {!hideTabs && (
+
+
+
+
+ )}
-
-
{
+
{canEdit ? (
-
-
-
- ) :
+ <>
+
+
+
+
+
+
+
+
+ >
+ ) : (
+
- }
+ )}
diff --git a/applications/osb-portal/src/middleware/osbbackend.ts b/applications/osb-portal/src/middleware/osbbackend.ts
index cd8347f6..1597959b 100644
--- a/applications/osb-portal/src/middleware/osbbackend.ts
+++ b/applications/osb-portal/src/middleware/osbbackend.ts
@@ -18,7 +18,7 @@ let refreshPending = false;
* @private
*/
const callAPIMiddlewareFn: Middleware =
- ({ getState }: { getState: () => RootState }) =>
+ ({ getState, dispatch }: { getState: () => RootState, dispatch: any }) =>
(next) =>
async (action: AnyAction) => {
switch (action.type) {
@@ -46,13 +46,21 @@ const callAPIMiddlewareFn: Middleware =
next(Tags.loadTags(tagDetails.tags));
});
break;
- case Workspaces.selectWorkspace.toString():
+ case Workspaces.refreshWorkspaceResources.toString():
+ const selectedWorkspaceId =
+ action.payload || getState().workspaces.selectedWorkspace?.id;
+ workspaceService.refreshResources(selectedWorkspaceId).then(dispatch(Workspaces.refreshWorkspace(selectedWorkspaceId)));
+ break;
+
+ case Workspaces.selectWorkspace.toString():
case Workspaces.refreshWorkspace.toString():
- function refreshWorkspace(callback: (workspace: Workspace) => any) {
+ async function refreshWorkspace(callback: (workspace: Workspace) => any) {
const selectedWorkspaceId =
- action.payload || getState().workspaces.selectedWorkspace.id;
+ action.payload || getState().workspaces.selectedWorkspace?.id;
refreshPending = true;
+
+
workspaceService.getWorkspace(selectedWorkspaceId).then(
(workspace: Workspace) => {
callback(workspace);
@@ -76,7 +84,7 @@ const callAPIMiddlewareFn: Middleware =
)
) {
// update state only if something happened in resources
- next({ ...action, payload: workspaceUpdated });
+ next({ ...action, payload: workspaceUpdated });
}
});
}
diff --git a/applications/osb-portal/src/pages/WorkspacePage.tsx b/applications/osb-portal/src/pages/WorkspacePage.tsx
index 5ebc265d..37bb502a 100644
--- a/applications/osb-portal/src/pages/WorkspacePage.tsx
+++ b/applications/osb-portal/src/pages/WorkspacePage.tsx
@@ -39,7 +39,6 @@ import { Workspace, OSBApplication } from "../types/workspace";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import MoreVertIcon from "@mui/icons-material/MoreVert";
-import WorkspaceResourceBrowser from "../components/workspace/drawer/WorkspaceResourceBrowser";
const NavbarButton = styled(Button)(({ theme }) => ({
diff --git a/applications/osb-portal/src/service/WorkspaceService.tsx b/applications/osb-portal/src/service/WorkspaceService.tsx
index d200edc0..4907f58a 100644
--- a/applications/osb-portal/src/service/WorkspaceService.tsx
+++ b/applications/osb-portal/src/service/WorkspaceService.tsx
@@ -34,6 +34,7 @@ const PER_PAGE_DEFAULT = 10;
const workspacesApiUri = "/proxy/workspaces/api";
class WorkspaceService {
+
workspacesApi: workspaceApi.RestApi = null;
accessToken: string = null;
@@ -62,6 +63,12 @@ class WorkspaceService {
return ws;
}
+ async refreshResources(selectedWorkspaceId: any) {
+ return this.workspacesApi.workspacesControllersWorkspaceControllerImportResources(
+ {id: selectedWorkspaceId, inlineObject: {}}
+ );
+ }
+
async fetchWorkspaces(
isPublic = false,
isFeatured = false,
@@ -211,7 +218,7 @@ class WorkspaceService {
inlineObject: { resourceorigins: resources },
};
- await this.workspacesApi.workspacesControllersWorkspaceControllerImportResources(
+ return this.workspacesApi.workspacesControllersWorkspaceControllerImportResources(
requestObject
);
}
diff --git a/applications/osb-portal/src/store/actions/workspaces.ts b/applications/osb-portal/src/store/actions/workspaces.ts
index 2ebf72b3..584fc515 100644
--- a/applications/osb-portal/src/store/actions/workspaces.ts
+++ b/applications/osb-portal/src/store/actions/workspaces.ts
@@ -4,6 +4,7 @@ export const {
selectWorkspace,
refreshWorkspace,
refreshWorkspaces,
+ refreshWorkspaceResources,
deleteWorkspace,
updateWorkspace,
resourceAdded,
diff --git a/applications/osb-portal/src/store/reducers/workspaces.ts b/applications/osb-portal/src/store/reducers/workspaces.ts
index 1db55e68..6c2ffe9a 100644
--- a/applications/osb-portal/src/store/reducers/workspaces.ts
+++ b/applications/osb-portal/src/store/reducers/workspaces.ts
@@ -25,13 +25,16 @@ const workspaceSlice = createSlice({
selectWorkspace(state, action: PayloadAction) {
return { ...state, selectedWorkspace: action.payload };
},
- refreshWorkspace(state, action: AnyAction) {
+ refreshWorkspace(state, action: PayloadAction) {
return {
...state,
selectedWorkspace: action.payload,
counter: state.counter + 1,
};
},
+ refreshWorkspaceResources(state, action: AnyAction) {
+ return state;
+ },
refreshWorkspaces(state, action: AnyAction) {
console.log(action.payload, action, "action");
diff --git a/applications/workspaces/server/workspaces/controllers/workspace_controller.py b/applications/workspaces/server/workspaces/controllers/workspace_controller.py
index 50e6d674..2674a176 100644
--- a/applications/workspaces/server/workspaces/controllers/workspace_controller.py
+++ b/applications/workspaces/server/workspaces/controllers/workspace_controller.py
@@ -88,11 +88,18 @@ def delimage(id_=None, image_id=None, **kwargs):
def import_resources(id_, body, **kwargs):
+ workspace = WorkspaceRepository().get(id=id_)
+ if workspace is None:
+ return f"Workspace with id {id_} not found.", 404
+
resource_origins = body.get("resourceorigins", [])
copy_origins(id_, resource_origins)
return "Scheduled", 200
def workspace_clone(id_, body=None):
+ workspace = WorkspaceRepository().get(id=id_)
+ if workspace is None:
+ return f"Workspace with id {id_} not found.", 404
try:
ws = WorkspaceService().clone(id_)
if ws is None:
diff --git a/applications/workspaces/server/workspaces/repository/models.py b/applications/workspaces/server/workspaces/repository/models.py
index 8a3d66c5..29c929c4 100644
--- a/applications/workspaces/server/workspaces/repository/models.py
+++ b/applications/workspaces/server/workspaces/repository/models.py
@@ -9,8 +9,6 @@
from open_alchemy import models
-Base = models.Base # type: ignore
-
class _WorkspaceEntityDictBase(typing.TypedDict, total=True):
"""TypedDict for properties that are required."""
@@ -22,10 +20,9 @@ class _WorkspaceEntityDictBase(typing.TypedDict, total=True):
class WorkspaceEntityDict(_WorkspaceEntityDictBase, total=False):
"""TypedDict for properties that are not required."""
- resources: typing.Sequence["WorkspaceResourceEntityDict"]
id: int
- timestamp_created: typing.Optional[str]
- timestamp_updated: typing.Optional[str]
+ timestamp_created: typing.Optional[datetime.datetime]
+ timestamp_updated: typing.Optional[datetime.datetime]
last_opened_resource_id: typing.Optional[int]
thumbnail: typing.Optional[str]
gallery: typing.Sequence["WorkspaceImageDict"]
@@ -36,6 +33,7 @@ class WorkspaceEntityDict(_WorkspaceEntityDictBase, total=False):
collaborators: typing.Sequence["WorkspaceCollaboratorDict"]
storage: typing.Optional["VolumeStorageDict"]
tags: typing.Sequence["TagDict"]
+ resources: typing.Sequence["WorkspaceResourceEntityDict"]
class TWorkspaceEntity(typing.Protocol):
@@ -45,7 +43,6 @@ class TWorkspaceEntity(typing.Protocol):
Workspace item
Attrs:
- resources: Resources of the workspace
id: The id of the WorkspaceEntity.
name: Workspace name.
description: Workspace description.
@@ -61,8 +58,9 @@ class TWorkspaceEntity(typing.Protocol):
featured: Is this a featured workspace? Default false
license: Workspace license
collaborators: Collaborators who work on the workspace
- storage: Storage of the workspace
+ storage: The storage of the WorkspaceEntity.
tags: The tags of the WorkspaceEntity.
+ resources: Resources of the workspace
"""
@@ -72,29 +70,48 @@ class TWorkspaceEntity(typing.Protocol):
query: orm.Query
# Model properties
- resources: 'sqlalchemy.Column[typing.Sequence["TWorkspaceResourceEntity"]]'
- id: 'sqlalchemy.Column[int]'
- name: 'sqlalchemy.Column[str]'
- description: 'sqlalchemy.Column[str]'
- timestamp_created: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- timestamp_updated: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- last_opened_resource_id: 'sqlalchemy.Column[typing.Optional[int]]'
- thumbnail: 'sqlalchemy.Column[typing.Optional[str]]'
- gallery: 'sqlalchemy.Column[typing.Sequence["TWorkspaceImage"]]'
- user_id: 'sqlalchemy.Column[typing.Optional[str]]'
- publicable: 'sqlalchemy.Column[bool]'
- featured: 'sqlalchemy.Column[bool]'
- license: 'sqlalchemy.Column[typing.Optional[str]]'
- collaborators: 'sqlalchemy.Column[typing.Sequence["TWorkspaceCollaborator"]]'
- storage: 'sqlalchemy.Column[typing.Optional["TVolumeStorage"]]'
- tags: 'sqlalchemy.Column[typing.Sequence["TTag"]]'
-
- def __init__(self, name: str, description: str, resources: typing.Optional[typing.Sequence["TWorkspaceResourceEntity"]] = None, id: typing.Optional[int] = None, timestamp_created: typing.Optional[datetime.datetime] = None, timestamp_updated: typing.Optional[datetime.datetime] = None, last_opened_resource_id: typing.Optional[int] = None, thumbnail: typing.Optional[str] = None, gallery: typing.Optional[typing.Sequence["TWorkspaceImage"]] = None, user_id: typing.Optional[str] = None, publicable: bool = False, featured: bool = False, license: typing.Optional[str] = None, collaborators: typing.Optional[typing.Sequence["TWorkspaceCollaborator"]] = None, storage: typing.Optional["TVolumeStorage"] = None, tags: typing.Optional[typing.Sequence["TTag"]] = None) -> None:
+ id: int
+ name: str
+ description: str
+ timestamp_created: typing.Optional[datetime.datetime]
+ timestamp_updated: typing.Optional[datetime.datetime]
+ last_opened_resource_id: typing.Optional[int]
+ thumbnail: typing.Optional[str]
+ gallery: typing.Sequence["TWorkspaceImage"]
+ user_id: typing.Optional[str]
+ publicable: bool
+ featured: bool
+ license: typing.Optional[str]
+ collaborators: typing.Sequence["TWorkspaceCollaborator"]
+ storage: typing.Optional["TVolumeStorage"]
+ tags: typing.Sequence["TTag"]
+ resources: typing.Sequence["TWorkspaceResourceEntity"]
+
+ def __init__(
+ self,
+ name: str,
+ description: str,
+ id: typing.Optional[int] = None,
+ timestamp_created: typing.Optional[datetime.datetime] = None,
+ timestamp_updated: typing.Optional[datetime.datetime] = None,
+ last_opened_resource_id: typing.Optional[int] = None,
+ thumbnail: typing.Optional[str] = None,
+ gallery: typing.Optional[typing.Sequence["TWorkspaceImage"]] = None,
+ user_id: typing.Optional[str] = None,
+ publicable: bool = False,
+ featured: bool = False,
+ license: typing.Optional[str] = None,
+ collaborators: typing.Optional[
+ typing.Sequence["TWorkspaceCollaborator"]
+ ] = None,
+ storage: typing.Optional["TVolumeStorage"] = None,
+ tags: typing.Optional[typing.Sequence["TTag"]] = None,
+ resources: typing.Optional[typing.Sequence["TWorkspaceResourceEntity"]] = None,
+ ) -> None:
"""
Construct.
Args:
- resources: Resources of the workspace
id: The id of the WorkspaceEntity.
name: Workspace name.
description: Workspace description.
@@ -110,19 +127,41 @@ def __init__(self, name: str, description: str, resources: typing.Optional[typin
featured: Is this a featured workspace? Default false
license: Workspace license
collaborators: Collaborators who work on the workspace
- storage: Storage of the workspace
+ storage: The storage of the WorkspaceEntity.
tags: The tags of the WorkspaceEntity.
+ resources: Resources of the workspace
"""
...
@classmethod
- def from_dict(cls, name: str, description: str, resources: typing.Optional[typing.Sequence["WorkspaceResourceEntityDict"]] = None, id: typing.Optional[int] = None, timestamp_created: typing.Optional[datetime.datetime] = None, timestamp_updated: typing.Optional[datetime.datetime] = None, last_opened_resource_id: typing.Optional[int] = None, thumbnail: typing.Optional[str] = None, gallery: typing.Optional[typing.Sequence["WorkspaceImageDict"]] = None, user_id: typing.Optional[str] = None, publicable: bool = False, featured: bool = False, license: typing.Optional[str] = None, collaborators: typing.Optional[typing.Sequence["WorkspaceCollaboratorDict"]] = None, storage: typing.Optional["VolumeStorageDict"] = None, tags: typing.Optional[typing.Sequence["TagDict"]] = None) -> "TWorkspaceEntity":
+ def from_dict(
+ cls,
+ name: str,
+ description: str,
+ id: typing.Optional[int] = None,
+ timestamp_created: typing.Optional[datetime.datetime] = None,
+ timestamp_updated: typing.Optional[datetime.datetime] = None,
+ last_opened_resource_id: typing.Optional[int] = None,
+ thumbnail: typing.Optional[str] = None,
+ gallery: typing.Optional[typing.Sequence["WorkspaceImageDict"]] = None,
+ user_id: typing.Optional[str] = None,
+ publicable: bool = False,
+ featured: bool = False,
+ license: typing.Optional[str] = None,
+ collaborators: typing.Optional[
+ typing.Sequence["WorkspaceCollaboratorDict"]
+ ] = None,
+ storage: typing.Optional["VolumeStorageDict"] = None,
+ tags: typing.Optional[typing.Sequence["TagDict"]] = None,
+ resources: typing.Optional[
+ typing.Sequence["WorkspaceResourceEntityDict"]
+ ] = None,
+ ) -> "TWorkspaceEntity":
"""
Construct from a dictionary (eg. a POST payload).
Args:
- resources: Resources of the workspace
id: The id of the WorkspaceEntity.
name: Workspace name.
description: Workspace description.
@@ -138,8 +177,9 @@ def from_dict(cls, name: str, description: str, resources: typing.Optional[typin
featured: Is this a featured workspace? Default false
license: Workspace license
collaborators: Collaborators who work on the workspace
- storage: Storage of the workspace
+ storage: The storage of the WorkspaceEntity.
tags: The tags of the WorkspaceEntity.
+ resources: Resources of the workspace
Returns:
Model instance based on the dictionary.
@@ -179,7 +219,7 @@ def to_str(self) -> str:
...
-WorkspaceEntity: typing.Type[TWorkspaceEntity] = models.WorkspaceEntity # type: ignore
+WorkspaceEntity: TWorkspaceEntity = models.WorkspaceEntity # type: ignore
class _WorkspaceCollaboratorDictBase(typing.TypedDict, total=True):
@@ -212,8 +252,8 @@ class TWorkspaceCollaborator(typing.Protocol):
query: orm.Query
# Model properties
- id: 'sqlalchemy.Column[int]'
- user_id: 'sqlalchemy.Column[str]'
+ id: int
+ user_id: str
def __init__(self, user_id: str, id: typing.Optional[int] = None) -> None:
"""
@@ -227,7 +267,9 @@ def __init__(self, user_id: str, id: typing.Optional[int] = None) -> None:
...
@classmethod
- def from_dict(cls, user_id: str, id: typing.Optional[int] = None) -> "TWorkspaceCollaborator":
+ def from_dict(
+ cls, user_id: str, id: typing.Optional[int] = None
+ ) -> "TWorkspaceCollaborator":
"""
Construct from a dictionary (eg. a POST payload).
@@ -273,7 +315,7 @@ def to_str(self) -> str:
...
-WorkspaceCollaborator: typing.Type[TWorkspaceCollaborator] = models.WorkspaceCollaborator # type: ignore
+WorkspaceCollaborator: TWorkspaceCollaborator = models.WorkspaceCollaborator # type: ignore
class _WorkspaceImageDictBase(typing.TypedDict, total=True):
@@ -306,8 +348,8 @@ class TWorkspaceImage(typing.Protocol):
query: orm.Query
# Model properties
- id: 'sqlalchemy.Column[int]'
- image: 'sqlalchemy.Column[str]'
+ id: int
+ image: str
def __init__(self, image: str, id: typing.Optional[int] = None) -> None:
"""
@@ -321,7 +363,9 @@ def __init__(self, image: str, id: typing.Optional[int] = None) -> None:
...
@classmethod
- def from_dict(cls, image: str, id: typing.Optional[int] = None) -> "TWorkspaceImage":
+ def from_dict(
+ cls, image: str, id: typing.Optional[int] = None
+ ) -> "TWorkspaceImage":
"""
Construct from a dictionary (eg. a POST payload).
@@ -367,7 +411,7 @@ def to_str(self) -> str:
...
-WorkspaceImage: typing.Type[TWorkspaceImage] = models.WorkspaceImage # type: ignore
+WorkspaceImage: TWorkspaceImage = models.WorkspaceImage # type: ignore
class _WorkspaceResourceEntityDictBase(typing.TypedDict, total=True):
@@ -380,14 +424,14 @@ class _WorkspaceResourceEntityDictBase(typing.TypedDict, total=True):
class WorkspaceResourceEntityDict(_WorkspaceResourceEntityDictBase, total=False):
"""TypedDict for properties that are not required."""
- origin: typing.Optional[str]
- workspace_id: typing.Optional[int]
id: int
folder: typing.Optional[str]
status: str
- timestamp_created: typing.Optional[str]
- timestamp_updated: typing.Optional[str]
- timestamp_last_opened: typing.Optional[str]
+ timestamp_created: typing.Optional[datetime.datetime]
+ timestamp_updated: typing.Optional[datetime.datetime]
+ timestamp_last_opened: typing.Optional[datetime.datetime]
+ origin: typing.Optional[str]
+ workspace_id: typing.Optional[int]
class TWorkspaceResourceEntity(typing.Protocol):
@@ -395,8 +439,6 @@ class TWorkspaceResourceEntity(typing.Protocol):
SQLAlchemy model protocol.
Attrs:
- origin: Origin data JSON formatted of the WorkspaceResource
- workspace_id: workspace_id
id: The id of the WorkspaceResourceEntity.
name: WorkspaceResource name
folder: WorkspaceResource folder where the resource will stored in the
@@ -409,6 +451,8 @@ class TWorkspaceResourceEntity(typing.Protocol):
WorkspaceResource
resource_type: Resource type: * e - Experimental * m - Model * g -
Generic * u - Unknown (to be defined)
+ origin: Origin data JSON formatted of the WorkspaceResource
+ workspace_id: workspace_id
"""
@@ -418,24 +462,34 @@ class TWorkspaceResourceEntity(typing.Protocol):
query: orm.Query
# Model properties
- origin: 'sqlalchemy.Column[typing.Optional[str]]'
- workspace_id: 'sqlalchemy.Column[typing.Optional[int]]'
- id: 'sqlalchemy.Column[int]'
- name: 'sqlalchemy.Column[str]'
- folder: 'sqlalchemy.Column[typing.Optional[str]]'
- status: 'sqlalchemy.Column[str]'
- timestamp_created: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- timestamp_updated: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- timestamp_last_opened: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- resource_type: 'sqlalchemy.Column[str]'
-
- def __init__(self, name: str, resource_type: str, origin: typing.Optional[str] = None, workspace_id: typing.Optional[int] = None, id: typing.Optional[int] = None, folder: typing.Optional[str] = None, status: str = "p", timestamp_created: typing.Optional[datetime.datetime] = None, timestamp_updated: typing.Optional[datetime.datetime] = None, timestamp_last_opened: typing.Optional[datetime.datetime] = None) -> None:
+ id: int
+ name: str
+ folder: typing.Optional[str]
+ status: str
+ timestamp_created: typing.Optional[datetime.datetime]
+ timestamp_updated: typing.Optional[datetime.datetime]
+ timestamp_last_opened: typing.Optional[datetime.datetime]
+ resource_type: str
+ origin: typing.Optional[str]
+ workspace_id: typing.Optional[int]
+
+ def __init__(
+ self,
+ name: str,
+ resource_type: str,
+ id: typing.Optional[int] = None,
+ folder: typing.Optional[str] = None,
+ status: str = "p",
+ timestamp_created: typing.Optional[datetime.datetime] = None,
+ timestamp_updated: typing.Optional[datetime.datetime] = None,
+ timestamp_last_opened: typing.Optional[datetime.datetime] = None,
+ origin: typing.Optional[str] = None,
+ workspace_id: typing.Optional[int] = None,
+ ) -> None:
"""
Construct.
Args:
- origin: Origin data JSON formatted of the WorkspaceResource
- workspace_id: workspace_id
id: The id of the WorkspaceResourceEntity.
name: WorkspaceResource name
folder: WorkspaceResource folder where the resource will stored in
@@ -449,18 +503,30 @@ def __init__(self, name: str, resource_type: str, origin: typing.Optional[str] =
WorkspaceResource
resource_type: Resource type: * e - Experimental * m - Model * g
- Generic * u - Unknown (to be defined)
+ origin: Origin data JSON formatted of the WorkspaceResource
+ workspace_id: workspace_id
"""
...
@classmethod
- def from_dict(cls, name: str, resource_type: str, origin: typing.Optional[str] = None, workspace_id: typing.Optional[int] = None, id: typing.Optional[int] = None, folder: typing.Optional[str] = None, status: str = "p", timestamp_created: typing.Optional[datetime.datetime] = None, timestamp_updated: typing.Optional[datetime.datetime] = None, timestamp_last_opened: typing.Optional[datetime.datetime] = None) -> "TWorkspaceResourceEntity":
+ def from_dict(
+ cls,
+ name: str,
+ resource_type: str,
+ id: typing.Optional[int] = None,
+ folder: typing.Optional[str] = None,
+ status: str = "p",
+ timestamp_created: typing.Optional[datetime.datetime] = None,
+ timestamp_updated: typing.Optional[datetime.datetime] = None,
+ timestamp_last_opened: typing.Optional[datetime.datetime] = None,
+ origin: typing.Optional[str] = None,
+ workspace_id: typing.Optional[int] = None,
+ ) -> "TWorkspaceResourceEntity":
"""
Construct from a dictionary (eg. a POST payload).
Args:
- origin: Origin data JSON formatted of the WorkspaceResource
- workspace_id: workspace_id
id: The id of the WorkspaceResourceEntity.
name: WorkspaceResource name
folder: WorkspaceResource folder where the resource will stored in
@@ -474,6 +540,8 @@ def from_dict(cls, name: str, resource_type: str, origin: typing.Optional[str] =
WorkspaceResource
resource_type: Resource type: * e - Experimental * m - Model * g
- Generic * u - Unknown (to be defined)
+ origin: Origin data JSON formatted of the WorkspaceResource
+ workspace_id: workspace_id
Returns:
Model instance based on the dictionary.
@@ -513,7 +581,7 @@ def to_str(self) -> str:
...
-WorkspaceResourceEntity: typing.Type[TWorkspaceResourceEntity] = models.WorkspaceResourceEntity # type: ignore
+WorkspaceResourceEntity: TWorkspaceResourceEntity = models.WorkspaceResourceEntity # type: ignore
class _VolumeStorageDictBase(typing.TypedDict, total=True):
@@ -546,8 +614,8 @@ class TVolumeStorage(typing.Protocol):
query: orm.Query
# Model properties
- id: 'sqlalchemy.Column[int]'
- name: 'sqlalchemy.Column[str]'
+ id: int
+ name: str
def __init__(self, name: str, id: typing.Optional[int] = None) -> None:
"""
@@ -607,7 +675,7 @@ def to_str(self) -> str:
...
-VolumeStorage: typing.Type[TVolumeStorage] = models.VolumeStorage # type: ignore
+VolumeStorage: TVolumeStorage = models.VolumeStorage # type: ignore
class _OSBRepositoryEntityDictBase(typing.TypedDict, total=True):
@@ -627,8 +695,8 @@ class OSBRepositoryEntityDict(_OSBRepositoryEntityDictBase, total=False):
auto_sync: bool
default_context: typing.Optional[str]
user_id: typing.Optional[str]
- timestamp_created: typing.Optional[str]
- timestamp_updated: typing.Optional[str]
+ timestamp_created: typing.Optional[datetime.datetime]
+ timestamp_updated: typing.Optional[datetime.datetime]
tags: typing.Sequence["TagDict"]
@@ -662,20 +730,34 @@ class TOSBRepositoryEntity(typing.Protocol):
query: orm.Query
# Model properties
- id: 'sqlalchemy.Column[int]'
- name: 'sqlalchemy.Column[str]'
- summary: 'sqlalchemy.Column[typing.Optional[str]]'
- repository_type: 'sqlalchemy.Column[str]'
- content_types: 'sqlalchemy.Column[str]'
- auto_sync: 'sqlalchemy.Column[bool]'
- uri: 'sqlalchemy.Column[str]'
- default_context: 'sqlalchemy.Column[typing.Optional[str]]'
- user_id: 'sqlalchemy.Column[typing.Optional[str]]'
- timestamp_created: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- timestamp_updated: 'sqlalchemy.Column[typing.Optional[datetime.datetime]]'
- tags: 'sqlalchemy.Column[typing.Sequence["TTag"]]'
-
- def __init__(self, name: str, repository_type: str, content_types: str, uri: str, id: typing.Optional[int] = None, summary: typing.Optional[str] = None, auto_sync: bool = True, default_context: typing.Optional[str] = None, user_id: typing.Optional[str] = None, timestamp_created: typing.Optional[datetime.datetime] = None, timestamp_updated: typing.Optional[datetime.datetime] = None, tags: typing.Optional[typing.Sequence["TTag"]] = None) -> None:
+ id: int
+ name: str
+ summary: typing.Optional[str]
+ repository_type: str
+ content_types: str
+ auto_sync: bool
+ uri: str
+ default_context: typing.Optional[str]
+ user_id: typing.Optional[str]
+ timestamp_created: typing.Optional[datetime.datetime]
+ timestamp_updated: typing.Optional[datetime.datetime]
+ tags: typing.Sequence["TTag"]
+
+ def __init__(
+ self,
+ name: str,
+ repository_type: str,
+ content_types: str,
+ uri: str,
+ id: typing.Optional[int] = None,
+ summary: typing.Optional[str] = None,
+ auto_sync: bool = True,
+ default_context: typing.Optional[str] = None,
+ user_id: typing.Optional[str] = None,
+ timestamp_created: typing.Optional[datetime.datetime] = None,
+ timestamp_updated: typing.Optional[datetime.datetime] = None,
+ tags: typing.Optional[typing.Sequence["TTag"]] = None,
+ ) -> None:
"""
Construct.
@@ -699,7 +781,21 @@ def __init__(self, name: str, repository_type: str, content_types: str, uri: str
...
@classmethod
- def from_dict(cls, name: str, repository_type: str, content_types: str, uri: str, id: typing.Optional[int] = None, summary: typing.Optional[str] = None, auto_sync: bool = True, default_context: typing.Optional[str] = None, user_id: typing.Optional[str] = None, timestamp_created: typing.Optional[datetime.datetime] = None, timestamp_updated: typing.Optional[datetime.datetime] = None, tags: typing.Optional[typing.Sequence["TagDict"]] = None) -> "TOSBRepositoryEntity":
+ def from_dict(
+ cls,
+ name: str,
+ repository_type: str,
+ content_types: str,
+ uri: str,
+ id: typing.Optional[int] = None,
+ summary: typing.Optional[str] = None,
+ auto_sync: bool = True,
+ default_context: typing.Optional[str] = None,
+ user_id: typing.Optional[str] = None,
+ timestamp_created: typing.Optional[datetime.datetime] = None,
+ timestamp_updated: typing.Optional[datetime.datetime] = None,
+ tags: typing.Optional[typing.Sequence["TagDict"]] = None,
+ ) -> "TOSBRepositoryEntity":
"""
Construct from a dictionary (eg. a POST payload).
@@ -757,7 +853,7 @@ def to_str(self) -> str:
...
-OSBRepositoryEntity: typing.Type[TOSBRepositoryEntity] = models.OSBRepositoryEntity # type: ignore
+OSBRepositoryEntity: TOSBRepositoryEntity = models.OSBRepositoryEntity # type: ignore
class TagDict(typing.TypedDict, total=False):
@@ -785,10 +881,12 @@ class TTag(typing.Protocol):
query: orm.Query
# Model properties
- id: 'sqlalchemy.Column[int]'
- tag: 'sqlalchemy.Column[typing.Optional[str]]'
+ id: int
+ tag: typing.Optional[str]
- def __init__(self, id: typing.Optional[int] = None, tag: typing.Optional[str] = None) -> None:
+ def __init__(
+ self, id: typing.Optional[int] = None, tag: typing.Optional[str] = None
+ ) -> None:
"""
Construct.
@@ -800,7 +898,9 @@ def __init__(self, id: typing.Optional[int] = None, tag: typing.Optional[str] =
...
@classmethod
- def from_dict(cls, id: typing.Optional[int] = None, tag: typing.Optional[str] = None) -> "TTag":
+ def from_dict(
+ cls, id: typing.Optional[int] = None, tag: typing.Optional[str] = None
+ ) -> "TTag":
"""
Construct from a dictionary (eg. a POST payload).
@@ -846,178 +946,4 @@ def to_str(self) -> str:
...
-Tag: typing.Type[TTag] = models.Tag # type: ignore
-
-
-class WorkspaceTagDict(typing.TypedDict, total=True):
- """TypedDict for properties that are required."""
-
- workspace_id: int
- tag_id: int
-
-
-class TWorkspaceTag(typing.Protocol):
- """
- SQLAlchemy model protocol.
-
- Attrs:
- workspace_id: The workspace_id of the WorkspaceTag.
- tag_id: The tag_id of the WorkspaceTag.
-
- """
-
- # SQLAlchemy properties
- __table__: sqlalchemy.Table
- __tablename__: str
- query: orm.Query
-
- # Model properties
- workspace_id: 'sqlalchemy.Column[int]'
- tag_id: 'sqlalchemy.Column[int]'
-
- def __init__(self, workspace_id: int, tag_id: int) -> None:
- """
- Construct.
-
- Args:
- workspace_id: The workspace_id of the WorkspaceTag.
- tag_id: The tag_id of the WorkspaceTag.
-
- """
- ...
-
- @classmethod
- def from_dict(cls, workspace_id: int, tag_id: int) -> "TWorkspaceTag":
- """
- Construct from a dictionary (eg. a POST payload).
-
- Args:
- workspace_id: The workspace_id of the WorkspaceTag.
- tag_id: The tag_id of the WorkspaceTag.
-
- Returns:
- Model instance based on the dictionary.
-
- """
- ...
-
- @classmethod
- def from_str(cls, value: str) -> "TWorkspaceTag":
- """
- Construct from a JSON string (eg. a POST payload).
-
- Returns:
- Model instance based on the JSON string.
-
- """
- ...
-
- def to_dict(self) -> WorkspaceTagDict:
- """
- Convert to a dictionary (eg. to send back for a GET request).
-
- Returns:
- Dictionary based on the model instance.
-
- """
- ...
-
- def to_str(self) -> str:
- """
- Convert to a JSON string (eg. to send back for a GET request).
-
- Returns:
- JSON string based on the model instance.
-
- """
- ...
-
-
-WorkspaceTag: typing.Type[TWorkspaceTag] = models.WorkspaceTag # type: ignore
-
-
-class OsbrepositoryTagDict(typing.TypedDict, total=True):
- """TypedDict for properties that are required."""
-
- osbrepository_id: int
- tag_id: int
-
-
-class TOsbrepositoryTag(typing.Protocol):
- """
- SQLAlchemy model protocol.
-
- Attrs:
- osbrepository_id: The osbrepository_id of the OsbrepositoryTag.
- tag_id: The tag_id of the OsbrepositoryTag.
-
- """
-
- # SQLAlchemy properties
- __table__: sqlalchemy.Table
- __tablename__: str
- query: orm.Query
-
- # Model properties
- osbrepository_id: 'sqlalchemy.Column[int]'
- tag_id: 'sqlalchemy.Column[int]'
-
- def __init__(self, osbrepository_id: int, tag_id: int) -> None:
- """
- Construct.
-
- Args:
- osbrepository_id: The osbrepository_id of the OsbrepositoryTag.
- tag_id: The tag_id of the OsbrepositoryTag.
-
- """
- ...
-
- @classmethod
- def from_dict(cls, osbrepository_id: int, tag_id: int) -> "TOsbrepositoryTag":
- """
- Construct from a dictionary (eg. a POST payload).
-
- Args:
- osbrepository_id: The osbrepository_id of the OsbrepositoryTag.
- tag_id: The tag_id of the OsbrepositoryTag.
-
- Returns:
- Model instance based on the dictionary.
-
- """
- ...
-
- @classmethod
- def from_str(cls, value: str) -> "TOsbrepositoryTag":
- """
- Construct from a JSON string (eg. a POST payload).
-
- Returns:
- Model instance based on the JSON string.
-
- """
- ...
-
- def to_dict(self) -> OsbrepositoryTagDict:
- """
- Convert to a dictionary (eg. to send back for a GET request).
-
- Returns:
- Dictionary based on the model instance.
-
- """
- ...
-
- def to_str(self) -> str:
- """
- Convert to a JSON string (eg. to send back for a GET request).
-
- Returns:
- JSON string based on the model instance.
-
- """
- ...
-
-
-OsbrepositoryTag: typing.Type[TOsbrepositoryTag] = models.OsbrepositoryTag # type: ignore
+Tag: TTag = models.Tag # type: ignore
diff --git a/port-forward.sh b/port-forward.sh
index e6dbad71..17346f52 100644
--- a/port-forward.sh
+++ b/port-forward.sh
@@ -1,5 +1,5 @@
killall -9 kubectl
-kubectl port-forward --namespace osb2dev $(kubectl get po -n osb2dev --field-selector=status.phase==Running | grep workspaces-postgres-host | \awk '{print $1;}') 5432:5432 &
-kubectl port-forward --namespace osb2dev $(kubectl get po -n osb2dev | grep accounts | \awk '{print $1;}') 8080:8080 &
-kubectl port-forward --namespace osb2dev $(kubectl get po -n osb2dev | grep kafka | \awk '{print $1;}') 9092:9092 &
-kubectl port-forward --namespace osb2dev $(kubectl get po -n osb2dev | grep argo-osb2dev-server | \awk '{print $1;}') 2746:2746 &
+kubectl port-forward --namespace $1 $(kubectl get po -n $1 --field-selector=status.phase==Running | grep workspaces-postgres-host | \awk '{print $1;}') 5432:5432 &
+kubectl port-forward --namespace $1 $(kubectl get po -n $1 | grep accounts | \awk '{print $1;}') 8080:8080 &
+kubectl port-forward --namespace $1 $(kubectl get po -n $1 | grep kafka | \awk '{print $1;}') 9092:9092 &
+kubectl port-forward --namespace $1 $(kubectl get po -n $1 | grep argo-$1-server | \awk '{print $1;}') 2746:2746 &