From 81c7d4214e291f01cba039a0f04b40b88cb94de5 Mon Sep 17 00:00:00 2001 From: YolanFery Date: Mon, 19 Jan 2026 16:03:21 +0100 Subject: [PATCH 1/4] Remove always True --- openhexa/cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhexa/cli/cli.py b/openhexa/cli/cli.py index b3866c29..97df4206 100644 --- a/openhexa/cli/cli.py +++ b/openhexa/cli/cli.py @@ -79,7 +79,7 @@ def app(ctx): if settings.last_version_check is None or now_timestamp - settings.last_version_check > ONE_HOUR: installed_version, latest_version = get_library_versions() settings.last_version_check = now_timestamp - if installed_version != latest_version or True: + if installed_version != latest_version: click.secho( "\n".join( ( From 308fc68dccb735b9dc1bb5cdba8f2dfb680a37dd Mon Sep 17 00:00:00 2001 From: YolanFery Date: Mon, 19 Jan 2026 16:04:43 +0100 Subject: [PATCH 2/4] New schema + codegen --- openhexa/graphql/graphql_client/__init__.py | 34 ++ openhexa/graphql/graphql_client/enums.py | 64 +++ .../graphql/graphql_client/input_types.py | 55 +++ openhexa/graphql/schema.generated.graphql | 403 +++++++++++++++++- 4 files changed, 553 insertions(+), 3 deletions(-) diff --git a/openhexa/graphql/graphql_client/__init__.py b/openhexa/graphql/graphql_client/__init__.py index 6e258c77..8c3a772c 100644 --- a/openhexa/graphql/graphql_client/__init__.py +++ b/openhexa/graphql/graphql_client/__init__.py @@ -89,6 +89,7 @@ AccessmodProjectOrder, AddOrganizationMemberError, AddToFavoritesError, + AddWebappToShortcutsError, ApproveAccessmodAccessRequestError, ArchiveWorkspaceError, BucketObjectType, @@ -105,6 +106,7 @@ CreateDatasetVersionError, CreateDatasetVersionFileError, CreateMembershipError, + CreateOrganizationError, CreatePipelineFromTemplateVersionError, CreatePipelineTemplateVersionError, CreateTeamError, @@ -126,6 +128,7 @@ DeleteDatasetVersionError, DeleteMembershipError, DeleteMetadataAttributeError, + DeleteOrganizationError, DeleteOrganizationInvitationError, DeleteOrganizationMemberError, DeletePipelineVersionError, @@ -152,6 +155,7 @@ IASOMetadataType, InviteOrganizationMemberError, InviteWorkspaceMembershipError, + IssueWorkspaceTokenError, JoinWorkspaceError, LaunchAccessmodAnalysisError, LaunchNotebookServerError, @@ -174,12 +178,14 @@ PipelineRunStatus, PipelineRunTrigger, PipelineTemplateError, + PipelineTemplateOrderBy, PipelineType, PrepareObjectDownloadError, PrepareObjectUploadError, PrepareVersionFileDownloadError, RegisterError, RemoveFromFavoritesError, + RemoveWebappFromShortcutsError, RequestAccessmodAccessError, ResendOrganizationInvitationError, ResendWorkspaceInvitationError, @@ -197,7 +203,9 @@ UpdateDatasetError, UpdateDatasetVersionError, UpdateMembershipError, + UpdateOrganizationError, UpdateOrganizationMemberError, + UpdateOrganizationSubscriptionError, UpdatePipelineError, UpdatePipelineVersionError, UpdateTeamError, @@ -209,6 +217,7 @@ UpdateWorkspaceMemberError, UpgradePipelineVersionFromTemplateError, VerifyDeviceError, + WebappType, WorkspaceInvitationStatus, WorkspaceMembershipRole, ) @@ -237,6 +246,7 @@ AddOrganizationMemberInput, AddPipelineOutputInput, AddToFavoritesInput, + AddWebappToShortcutsInput, ApproveAccessmodAccessRequestInput, ArchiveWorkspaceInput, ConnectionFieldInput, @@ -253,6 +263,7 @@ CreateDatasetVersionFileInput, CreateDatasetVersionInput, CreateMembershipInput, + CreateOrganizationInput, CreatePipelineFromTemplateVersionInput, CreatePipelineInput, CreatePipelineRecipientInput, @@ -272,6 +283,7 @@ DeleteDatasetVersionInput, DeleteMembershipInput, DeleteMetadataAttributeInput, + DeleteOrganizationInput, DeleteOrganizationInvitationInput, DeleteOrganizationMemberInput, DeletePipelineInput, @@ -295,6 +307,7 @@ IASOQueryFilterInput, InviteOrganizationMemberInput, InviteWorkspaceMemberInput, + IssueWorkspaceTokenInput, JoinWorkspaceInput, LaunchAccessmodAnalysisInput, LaunchNotebookServerInput, @@ -314,10 +327,12 @@ PrepareVersionFileDownloadInput, RegisterInput, RemoveFromFavoritesInput, + RemoveWebappFromShortcutsInput, RequestAccessmodAccessInput, ResendOrganizationInvitationInput, ResendWorkspaceInvitationInput, ResetPasswordInput, + ResourceCountsInput, RunDAGInput, RunPipelineInput, SetDAGRunFavoriteInput, @@ -334,7 +349,9 @@ UpdateDatasetInput, UpdateDatasetVersionInput, UpdateMembershipInput, + UpdateOrganizationInput, UpdateOrganizationMemberInput, + UpdateOrganizationSubscriptionInput, UpdatePipelineInput, UpdatePipelineProgressInput, UpdatePipelineRecipientInput, @@ -443,6 +460,8 @@ "AddToFavoritesInput", "AddWebappToFavorites", "AddWebappToFavoritesAddToFavorites", + "AddWebappToShortcutsError", + "AddWebappToShortcutsInput", "ApproveAccessmodAccessRequestError", "ApproveAccessmodAccessRequestInput", "ArchiveWorkspace", @@ -486,6 +505,8 @@ "CreateDatasetVersionInput", "CreateMembershipError", "CreateMembershipInput", + "CreateOrganizationError", + "CreateOrganizationInput", "CreatePipeline", "CreatePipelineCreatePipeline", "CreatePipelineCreatePipelinePipeline", @@ -562,6 +583,8 @@ "DeleteMembershipInput", "DeleteMetadataAttributeError", "DeleteMetadataAttributeInput", + "DeleteOrganizationError", + "DeleteOrganizationInput", "DeleteOrganizationInvitationError", "DeleteOrganizationInvitationInput", "DeleteOrganizationMemberError", @@ -637,6 +660,8 @@ "InviteWorkspaceMemberInviteWorkspaceMember", "InviteWorkspaceMemberInviteWorkspaceMemberWorkspaceMembership", "InviteWorkspaceMembershipError", + "IssueWorkspaceTokenError", + "IssueWorkspaceTokenInput", "JoinWorkspaceError", "JoinWorkspaceInput", "LaunchAccessmodAnalysisError", @@ -680,6 +705,7 @@ "PipelineRunStatus", "PipelineRunTrigger", "PipelineTemplateError", + "PipelineTemplateOrderBy", "PipelineTokenInput", "PipelineType", "Pipelines", @@ -704,6 +730,8 @@ "RemoveFromFavoritesInput", "RemoveWebappFromFavorites", "RemoveWebappFromFavoritesRemoveFromFavorites", + "RemoveWebappFromShortcutsError", + "RemoveWebappFromShortcutsInput", "RequestAccessmodAccessError", "RequestAccessmodAccessInput", "ResendOrganizationInvitationError", @@ -711,6 +739,7 @@ "ResendWorkspaceInvitationError", "ResendWorkspaceInvitationInput", "ResetPasswordInput", + "ResourceCountsInput", "RunDAGError", "RunDAGInput", "RunPipelineInput", @@ -750,8 +779,12 @@ "UpdateDatasetVersionInput", "UpdateMembershipError", "UpdateMembershipInput", + "UpdateOrganizationError", + "UpdateOrganizationInput", "UpdateOrganizationMemberError", "UpdateOrganizationMemberInput", + "UpdateOrganizationSubscriptionError", + "UpdateOrganizationSubscriptionInput", "UpdatePipelineError", "UpdatePipelineHeartbeat", "UpdatePipelineHeartbeatUpdatePipelineHeartbeat", @@ -790,6 +823,7 @@ "UploadPipelineUploadPipeline", "VerifyDeviceError", "VerifyDeviceInput", + "WebappType", "Workspace", "WorkspaceInvitationInput", "WorkspaceInvitationStatus", diff --git a/openhexa/graphql/graphql_client/enums.py b/openhexa/graphql/graphql_client/enums.py index 9a0de24e..bb8c9974 100644 --- a/openhexa/graphql/graphql_client/enums.py +++ b/openhexa/graphql/graphql_client/enums.py @@ -82,6 +82,11 @@ class AddToFavoritesError(str, Enum): WEBAPP_NOT_FOUND = "WEBAPP_NOT_FOUND" +class AddWebappToShortcutsError(str, Enum): + ITEM_ALREADY_EXISTS = "ITEM_ALREADY_EXISTS" + ITEM_NOT_FOUND = "ITEM_NOT_FOUND" + + class ApproveAccessmodAccessRequestError(str, Enum): INVALID = "INVALID" @@ -171,6 +176,12 @@ class CreateMembershipError(str, Enum): PERMISSION_DENIED = "PERMISSION_DENIED" +class CreateOrganizationError(str, Enum): + INVALID_EMAIL = "INVALID_EMAIL" + NAME_DUPLICATE = "NAME_DUPLICATE" + PERMISSION_DENIED = "PERMISSION_DENIED" + + class CreatePipelineFromTemplateVersionError(str, Enum): PERMISSION_DENIED = "PERMISSION_DENIED" PIPELINE_TEMPLATE_VERSION_NOT_FOUND = "PIPELINE_TEMPLATE_VERSION_NOT_FOUND" @@ -208,6 +219,7 @@ class CreateWorkspaceError(str, Enum): INVALID_SLUG = "INVALID_SLUG" ORGANIZATION_NOT_FOUND = "ORGANIZATION_NOT_FOUND" PERMISSION_DENIED = "PERMISSION_DENIED" + WORKSPACES_LIMIT_REACHED = "WORKSPACES_LIMIT_REACHED" class DAGRunOrderBy(str, Enum): @@ -313,6 +325,11 @@ class DeleteMetadataAttributeError(str, Enum): TARGET_NOT_FOUND = "TARGET_NOT_FOUND" +class DeleteOrganizationError(str, Enum): + NOT_FOUND = "NOT_FOUND" + PERMISSION_DENIED = "PERMISSION_DENIED" + + class DeleteOrganizationInvitationError(str, Enum): INVITATION_NOT_FOUND = "INVITATION_NOT_FOUND" PERMISSION_DENIED = "PERMISSION_DENIED" @@ -427,6 +444,7 @@ class InviteOrganizationMemberError(str, Enum): ALREADY_MEMBER = "ALREADY_MEMBER" ORGANIZATION_NOT_FOUND = "ORGANIZATION_NOT_FOUND" PERMISSION_DENIED = "PERMISSION_DENIED" + USERS_LIMIT_REACHED = "USERS_LIMIT_REACHED" WORKSPACE_NOT_FOUND = "WORKSPACE_NOT_FOUND" @@ -437,6 +455,16 @@ class InviteWorkspaceMembershipError(str, Enum): WORKSPACE_NOT_FOUND = "WORKSPACE_NOT_FOUND" +class IssueWorkspaceTokenError(str, Enum): + AUTH_UNAUTHENTICATED = "AUTH_UNAUTHENTICATED" + CLOCK_ERROR = "CLOCK_ERROR" + CONFIG_MISSING_PRIVATE_KEY = "CONFIG_MISSING_PRIVATE_KEY" + INPUT_INVALID = "INPUT_INVALID" + MEMBERSHIP_REQUIRED = "MEMBERSHIP_REQUIRED" + ROLE_UNRESOLVED = "ROLE_UNRESOLVED" + WORKSPACE_NOT_FOUND = "WORKSPACE_NOT_FOUND" + + class JoinWorkspaceError(str, Enum): ALREADY_ACCEPTED = "ALREADY_ACCEPTED" ALREADY_EXISTS = "ALREADY_EXISTS" @@ -549,6 +577,7 @@ class PipelineError(str, Enum): PIPELINE_CODE_PARSING_ERROR = "PIPELINE_CODE_PARSING_ERROR" PIPELINE_DOES_NOT_SUPPORT_PARAMETERS = "PIPELINE_DOES_NOT_SUPPORT_PARAMETERS" PIPELINE_NOT_FOUND = "PIPELINE_NOT_FOUND" + PIPELINE_RUNS_LIMIT_REACHED = "PIPELINE_RUNS_LIMIT_REACHED" PIPELINE_VERSION_NOT_FOUND = "PIPELINE_VERSION_NOT_FOUND" TABLE_NOT_FOUND = "TABLE_NOT_FOUND" WORKSPACE_NOT_FOUND = "WORKSPACE_NOT_FOUND" @@ -583,6 +612,7 @@ class PipelineRunStatus(str, Enum): failed = "failed" queued = "queued" running = "running" + skipped = "skipped" stopped = "stopped" success = "success" terminating = "terminating" @@ -599,6 +629,15 @@ class PipelineTemplateError(str, Enum): PIPELINE_TEMPLATE_NOT_FOUND = "PIPELINE_TEMPLATE_NOT_FOUND" +class PipelineTemplateOrderBy(str, Enum): + CREATED_AT_ASC = "CREATED_AT_ASC" + CREATED_AT_DESC = "CREATED_AT_DESC" + NAME_ASC = "NAME_ASC" + NAME_DESC = "NAME_DESC" + PIPELINES_COUNT_ASC = "PIPELINES_COUNT_ASC" + PIPELINES_COUNT_DESC = "PIPELINES_COUNT_DESC" + + class PipelineType(str, Enum): notebook = "notebook" zipFile = "zipFile" @@ -631,6 +670,10 @@ class RemoveFromFavoritesError(str, Enum): WEBAPP_NOT_FOUND = "WEBAPP_NOT_FOUND" +class RemoveWebappFromShortcutsError(str, Enum): + ITEM_NOT_FOUND = "ITEM_NOT_FOUND" + + class RequestAccessmodAccessError(str, Enum): ALREADY_EXISTS = "ALREADY_EXISTS" INVALID = "INVALID" @@ -725,12 +768,26 @@ class UpdateMembershipError(str, Enum): PERMISSION_DENIED = "PERMISSION_DENIED" +class UpdateOrganizationError(str, Enum): + INVALID_LOGO = "INVALID_LOGO" + INVALID_SHORT_NAME = "INVALID_SHORT_NAME" + NAME_DUPLICATE = "NAME_DUPLICATE" + NOT_FOUND = "NOT_FOUND" + PERMISSION_DENIED = "PERMISSION_DENIED" + SHORT_NAME_DUPLICATE = "SHORT_NAME_DUPLICATE" + + class UpdateOrganizationMemberError(str, Enum): INVALID_ROLE = "INVALID_ROLE" NOT_FOUND = "NOT_FOUND" PERMISSION_DENIED = "PERMISSION_DENIED" +class UpdateOrganizationSubscriptionError(str, Enum): + NOT_FOUND = "NOT_FOUND" + PERMISSION_DENIED = "PERMISSION_DENIED" + + class UpdatePipelineError(str, Enum): INVALID_CONFIG = "INVALID_CONFIG" MISSING_VERSION_CONFIG = "MISSING_VERSION_CONFIG" @@ -791,6 +848,13 @@ class VerifyDeviceError(str, Enum): NO_DEVICE = "NO_DEVICE" +class WebappType(str, Enum): + BUNDLE = "BUNDLE" + HTML = "HTML" + IFRAME = "IFRAME" + SUPERSET = "SUPERSET" + + class WorkspaceInvitationStatus(str, Enum): ACCEPTED = "ACCEPTED" DECLINED = "DECLINED" diff --git a/openhexa/graphql/graphql_client/input_types.py b/openhexa/graphql/graphql_client/input_types.py index e2436eef..15ae281e 100644 --- a/openhexa/graphql/graphql_client/input_types.py +++ b/openhexa/graphql/graphql_client/input_types.py @@ -16,6 +16,7 @@ PermissionMode, PipelineFunctionalType, PipelineNotificationLevel, + WebappType, WorkspaceMembershipRole, ) @@ -36,6 +37,10 @@ class AddToFavoritesInput(BaseModel): webapp_id: str = Field(alias="webappId") +class AddWebappToShortcutsInput(BaseModel): + webapp_id: Any = Field(alias="webappId") + + class ApproveAccessmodAccessRequestInput(BaseModel): id: str @@ -135,6 +140,16 @@ class CreateMembershipInput(BaseModel): user_email: str = Field(alias="userEmail") +class CreateOrganizationInput(BaseModel): + limits: "ResourceCountsInput" + name: str + owner_email: str = Field(alias="ownerEmail") + plan_code: str = Field(alias="planCode") + subscription_end_date: Any = Field(alias="subscriptionEndDate") + subscription_id: Any = Field(alias="subscriptionId") + subscription_start_date: Any = Field(alias="subscriptionStartDate") + + class CreatePipelineFromTemplateVersionInput(BaseModel): pipeline_template_version_id: Any = Field(alias="pipelineTemplateVersionId") workspace_slug: str = Field(alias="workspaceSlug") @@ -176,6 +191,7 @@ class CreateWebappInput(BaseModel): description: Optional[str] = None icon: Optional[str] = None name: str + type: WebappType url: str workspace_slug: str = Field(alias="workspaceSlug") @@ -240,6 +256,10 @@ class DeleteMetadataAttributeInput(BaseModel): target_id: Any = Field(alias="targetId") +class DeleteOrganizationInput(BaseModel): + id: Any + + class DeleteOrganizationInvitationInput(BaseModel): id: Any @@ -343,6 +363,11 @@ class InviteWorkspaceMemberInput(BaseModel): workspace_slug: str = Field(alias="workspaceSlug") +class IssueWorkspaceTokenInput(BaseModel): + workspace_id: Optional[Any] = Field(alias="workspaceId", default=None) + workspace_slug: Optional[str] = Field(alias="workspaceSlug", default=None) + + class JoinWorkspaceInput(BaseModel): invitation_id: Any = Field(alias="invitationId") @@ -421,6 +446,7 @@ class PrepareDownloadURLInput(BaseModel): class PrepareObjectDownloadInput(BaseModel): + force_attachment: Optional[bool] = Field(alias="forceAttachment", default=True) object_key: str = Field(alias="objectKey") workspace_slug: str = Field(alias="workspaceSlug") @@ -447,6 +473,10 @@ class RemoveFromFavoritesInput(BaseModel): webapp_id: str = Field(alias="webappId") +class RemoveWebappFromShortcutsInput(BaseModel): + webapp_id: Any = Field(alias="webappId") + + class RequestAccessmodAccessInput(BaseModel): accept_tos: bool = Field(alias="acceptTos") email: str @@ -466,6 +496,12 @@ class ResetPasswordInput(BaseModel): email: str +class ResourceCountsInput(BaseModel): + pipeline_runs: int = Field(alias="pipelineRuns") + users: int + workspaces: int + + class RunDAGInput(BaseModel): config: Any dag_id: Any = Field(alias="dagId") @@ -590,6 +626,13 @@ class UpdateMembershipInput(BaseModel): role: MembershipRole +class UpdateOrganizationInput(BaseModel): + id: Any + logo: Optional[str] = None + name: Optional[str] = None + short_name: Optional[str] = Field(alias="shortName", default=None) + + class UpdateOrganizationMemberInput(BaseModel): id: Any role: OrganizationMembershipRole @@ -598,6 +641,15 @@ class UpdateOrganizationMemberInput(BaseModel): ) +class UpdateOrganizationSubscriptionInput(BaseModel): + limits: "ResourceCountsInput" + organization_id: Any = Field(alias="organizationId") + plan_code: str = Field(alias="planCode") + subscription_end_date: Any = Field(alias="subscriptionEndDate") + subscription_id: Any = Field(alias="subscriptionId") + subscription_start_date: Any = Field(alias="subscriptionStartDate") + + class UpdatePipelineInput(BaseModel): auto_update_from_template: Optional[bool] = Field( alias="autoUpdateFromTemplate", default=None @@ -663,6 +715,7 @@ class UpdateWebappInput(BaseModel): icon: Optional[str] = None id: Any name: Optional[str] = None + type: Optional[WebappType] = None url: Optional[str] = None @@ -718,10 +771,12 @@ class WorkspacePermissionInput(BaseModel): CreateAccessmodProjectInput.model_rebuild() CreateConnectionInput.model_rebuild() +CreateOrganizationInput.model_rebuild() CreateWorkspaceInput.model_rebuild() InviteOrganizationMemberInput.model_rebuild() UpdateConnectionInput.model_rebuild() UpdateDAGInput.model_rebuild() UpdateOrganizationMemberInput.model_rebuild() +UpdateOrganizationSubscriptionInput.model_rebuild() UpdateWorkspaceInput.model_rebuild() UploadPipelineInput.model_rebuild() diff --git a/openhexa/graphql/schema.generated.graphql b/openhexa/graphql/schema.generated.graphql index 221c3ad3..4de9060e 100644 --- a/openhexa/graphql/schema.generated.graphql +++ b/openhexa/graphql/schema.generated.graphql @@ -358,6 +358,23 @@ type AddToFavoritesResult { success: Boolean! } +"""Represents the error message for adding a webapp to shortcuts.""" +enum AddWebappToShortcutsError { + ITEM_ALREADY_EXISTS + ITEM_NOT_FOUND +} + +"""Represents the input for adding a webapp to shortcuts.""" +input AddWebappToShortcutsInput { + webappId: UUID! +} + +"""Represents the result of adding a webapp to shortcuts.""" +type AddWebappToShortcutsResult { + errors: [AddWebappToShortcutsError!]! + success: Boolean! +} + enum ApproveAccessmodAccessRequestError { INVALID } @@ -765,6 +782,70 @@ type CreateMembershipResult { success: Boolean! } +""" +The CreateOrganizationError enum represents the possible errors that can occur during the createOrganization mutation. +""" +enum CreateOrganizationError { + """Indicates that the provided email is invalid.""" + INVALID_EMAIL + + """Indicates that an organization with the same name already exists.""" + NAME_DUPLICATE + + """ + Indicates that the user does not have permission to create an organization. + """ + PERMISSION_DENIED +} + +""" +The CreateOrganizationInput type represents the input for the createOrganization mutation. +Used by the Bluesquare Console to provision new organizations after Stripe payment. +""" +input CreateOrganizationInput { + """The resource limits for the organization.""" + limits: ResourceCountsInput! + + """The name of the organization.""" + name: String! + + """ + The email address of the organization owner. Creates user if doesn't exist. + """ + ownerEmail: String! + + """The subscription plan code (e.g., "openhexa_starter").""" + planCode: String! + + """The date when the subscription ends.""" + subscriptionEndDate: Date! + + """The external subscription ID from the Bluesquare Console.""" + subscriptionId: UUID! + + """The date when the subscription starts.""" + subscriptionStartDate: Date! +} + +""" +The CreateOrganizationResult type represents the result of the createOrganization mutation. +""" +type CreateOrganizationResult { + """ + The list of errors that occurred during the createOrganization mutation. + """ + errors: [CreateOrganizationError!]! + + """The created organization object.""" + organization: Organization + + """Indicates whether the createOrganization mutation was successful.""" + success: Boolean! + + """The owner user object (created or existing).""" + user: User +} + """ Enum representing the possible errors that can occur when creating a pipeline from a template version. """ @@ -907,6 +988,7 @@ input CreateWebappInput { description: String icon: String name: String! + type: WebappType! url: String! workspaceSlug: String! } @@ -925,6 +1007,7 @@ enum CreateWorkspaceError { INVALID_SLUG ORGANIZATION_NOT_FOUND PERMISSION_DENIED + WORKSPACES_LIMIT_REACHED } """Represents the input for creating a workspace.""" @@ -1102,6 +1185,7 @@ type DHIS2QueryResultPage { type Database { credentials: DatabaseCredentials + readOnlyCredentials: DatabaseCredentials table(name: String!): DatabaseTable tables(page: Int, perPage: Int): DatabaseTablePage! } @@ -1541,6 +1625,27 @@ type DeleteMetadataAttributeResult { success: Boolean! } +""" +The DeleteOrganizationError enum represents the possible errors that can occur during the deleteOrganization mutation. +""" +enum DeleteOrganizationError { + """Indicates that the organization was not found.""" + NOT_FOUND + + """ + Indicates that the user does not have permission to delete the organization. + """ + PERMISSION_DENIED +} + +""" +The DeleteOrganizationInput type represents the input for the deleteOrganization mutation. +""" +input DeleteOrganizationInput { + """The unique identifier of the organization.""" + id: UUID! +} + """ The DeleteOrganizationInvitationError enum represents the possible errors that can occur during the deleteOrganizationInvitation mutation. """ @@ -1604,6 +1709,19 @@ type DeleteOrganizationMemberResult { success: Boolean! } +""" +The DeleteOrganizationResult type represents the result of the deleteOrganization mutation. +""" +type DeleteOrganizationResult { + """ + The list of errors that occurred during the deleteOrganization mutation. + """ + errors: [DeleteOrganizationError!]! + + """Indicates whether the deleteOrganization mutation was successful.""" + success: Boolean! +} + """Represents the input for deleting a pipeline.""" input DeletePipelineInput { id: UUID! @@ -2084,6 +2202,11 @@ enum InviteOrganizationMemberError { """ PERMISSION_DENIED + """ + Indicates that the organization has reached its user limit (SaaS only). + """ + USERS_LIMIT_REACHED + """Indicates that one or more workspaces were not found.""" WORKSPACE_NOT_FOUND } @@ -2144,6 +2267,33 @@ enum InviteWorkspaceMembershipError { WORKSPACE_NOT_FOUND } +"""Represents the error types for issuing a workspace token.""" +enum IssueWorkspaceTokenError { + AUTH_UNAUTHENTICATED + CLOCK_ERROR + CONFIG_MISSING_PRIVATE_KEY + INPUT_INVALID + MEMBERSHIP_REQUIRED + ROLE_UNRESOLVED + WORKSPACE_NOT_FOUND +} + +"""Represents the input for issuing a workspace JWT token.""" +input IssueWorkspaceTokenInput { + workspaceId: UUID + workspaceSlug: String +} + +"""Represents the result of issuing a workspace JWT token.""" +type IssueWorkspaceTokenPayload { + errors: [IssueWorkspaceTokenError!]! + expiresAt: DateTime + role: WorkspaceMembershipRole + success: Boolean! + token: String + workspace: WorkspaceRef +} + scalar JSON """Represents the error types for joining a workspace.""" @@ -2396,6 +2546,7 @@ type Mutation { """Adds a recipient to a pipeline.""" addPipelineRecipient(input: CreatePipelineRecipientInput!): AddPipelineRecipientResult! addToFavorites(input: AddToFavoritesInput!): AddToFavoritesResult! + addWebappToShortcuts(input: AddWebappToShortcutsInput!): AddWebappToShortcutsResult! approveAccessmodAccessRequest(input: ApproveAccessmodAccessRequestInput!): ApproveAccessmodAccessRequestResult! archiveWorkspace(input: ArchiveWorkspaceInput!): ArchiveWorkspaceResult! createAccessmodAccessibilityAnalysis(input: CreateAccessmodAccessibilityAnalysisInput): CreateAccessmodAccessibilityAnalysisResult! @@ -2418,6 +2569,7 @@ type Mutation { """Create a new file in a dataset version.""" createDatasetVersionFile(input: CreateDatasetVersionFileInput!): CreateDatasetVersionFileResult! createMembership(input: CreateMembershipInput!): CreateMembershipResult! + createOrganization(input: CreateOrganizationInput!): CreateOrganizationResult! """Creates a new pipeline.""" createPipeline(input: CreatePipelineInput!): CreatePipelineResult! @@ -2450,6 +2602,7 @@ type Mutation { """Delete an metadata attribute from an object instance""" deleteMetadataAttribute(input: DeleteMetadataAttributeInput!): DeleteMetadataAttributeResult! + deleteOrganization(input: DeleteOrganizationInput!): DeleteOrganizationResult! deleteOrganizationInvitation(input: DeleteOrganizationInvitationInput!): DeleteOrganizationInvitationResult! deleteOrganizationMember(input: DeleteOrganizationMemberInput!): DeleteOrganizationMemberResult! @@ -2494,11 +2647,15 @@ type Mutation { """Generates a new password for a database.""" generateNewDatabasePassword(input: GenerateNewDatabasePasswordInput!): GenerateNewDatabasePasswordResult! + """Generates a new password for the read-only database user.""" + generateNewDatabaseRoPassword(input: GenerateNewDatabasePasswordInput!): GenerateNewDatabasePasswordResult! + """Generates a webhook URL for a pipeline.""" generatePipelineWebhookUrl(input: GeneratePipelineWebhookUrlInput!): GeneratePipelineWebhookUrlResult! generateWorkspaceToken(input: GenerateWorkspaceTokenInput!): GenerateWorkspaceTokenResult! inviteOrganizationMember(input: InviteOrganizationMemberInput!): InviteOrganizationMemberResult! inviteWorkspaceMember(input: InviteWorkspaceMemberInput!): InviteWorkspaceMemberResult! + issueWorkspaceToken(input: IssueWorkspaceTokenInput!): IssueWorkspaceTokenPayload! joinWorkspace(input: JoinWorkspaceInput!): JoinWorkspaceResult! launchAccessmodAnalysis(input: LaunchAccessmodAnalysisInput): LaunchAccessmodAnalysisResult! launchNotebookServer(input: LaunchNotebookServerInput!): LaunchNotebookServerResult! @@ -2537,6 +2694,7 @@ type Mutation { """Registers a new user.""" register(input: RegisterInput!): RegisterResult! removeFromFavorites(input: RemoveFromFavoritesInput!): RemoveFromFavoritesResult! + removeWebappFromShortcuts(input: RemoveWebappFromShortcutsInput!): RemoveWebappFromShortcutsResult! requestAccessmodAccess(input: RequestAccessmodAccessInput!): RequestAccessmodAccessInputResult! resendOrganizationInvitation(input: ResendOrganizationInvitationInput!): ResendOrganizationInvitationResult! resendWorkspaceInvitation(input: ResendWorkspaceInvitationInput!): ResendWorkspaceInvitationResult! @@ -2571,7 +2729,9 @@ type Mutation { """Update a dataset version.""" updateDatasetVersion(input: UpdateDatasetVersionInput!): UpdateDatasetVersionResult! updateMembership(input: UpdateMembershipInput!): UpdateMembershipResult! + updateOrganization(input: UpdateOrganizationInput!): UpdateOrganizationResult! updateOrganizationMember(input: UpdateOrganizationMemberInput!): UpdateOrganizationMemberResult! + updateOrganizationSubscription(input: UpdateOrganizationSubscriptionInput!): UpdateOrganizationSubscriptionResult! """Updates an existing pipeline.""" updatePipeline(input: UpdatePipelineInput!): UpdatePipelineResult! @@ -2645,6 +2805,9 @@ type Organization { """The invitations sent to join the organization.""" invitations(includeAccepted: Boolean, page: Int, perPage: Int): OrganizationInvitationPage! + """The logo of the organization (base64 encoded).""" + logo: String + """The members of the organization.""" members(page: Int, perPage: Int, role: OrganizationMembershipRole, term: String): OrganizationMembershipPage! @@ -2668,12 +2831,18 @@ type Organization { """The short name of the organization.""" shortName: String + """Subscription details with limits. Null for self-hosted deployments.""" + subscription: Subscription + """The type of the organization.""" type: String! """The URL of the organization.""" url: String! + """Current resource usage counts.""" + usage: ResourceCounts! + """The workspaces associated with the organization.""" workspaces(page: Int, perPage: Int): WorkspacePage! } @@ -2755,8 +2924,10 @@ enum OrganizationMembershipRole { type OrganizationPermissions { archiveWorkspace: Boolean! createWorkspace: Boolean! + delete: Boolean! manageMembers: Boolean! manageOwners: Boolean! + update: Boolean! } """Represents a workspace invitation within an organization invitation.""" @@ -2886,6 +3057,7 @@ enum PipelineError { PIPELINE_CODE_PARSING_ERROR PIPELINE_DOES_NOT_SUPPORT_PARAMETERS PIPELINE_NOT_FOUND + PIPELINE_RUNS_LIMIT_REACHED PIPELINE_VERSION_NOT_FOUND TABLE_NOT_FOUND WORKSPACE_NOT_FOUND @@ -2977,6 +3149,7 @@ type PipelineRun { duration: Int enableDebugLogs: Boolean! executionDate: DateTime + hasErrorMessages: Boolean! id: UUID! logs: String messages: [PipelineRunMessage!]! @@ -3024,6 +3197,7 @@ enum PipelineRunStatus { failed queued running + skipped stopped success terminating @@ -3044,10 +3218,13 @@ type PipelineTemplate { functionalType: PipelineFunctionalType id: UUID! name: String! + organization: Organization permissions: PipelineTemplatePermissions! + pipelinesCount: Int! sourcePipeline: Pipeline tags: [Tag!]! updatedAt: DateTime! + validatedAt: DateTime versions(page: Int, perPage: Int): TemplateVersionPage! workspace: Workspace } @@ -3057,6 +3234,16 @@ enum PipelineTemplateError { PIPELINE_TEMPLATE_NOT_FOUND } +"""Enum representing the possible orderings for pipeline templates.""" +enum PipelineTemplateOrderBy { + CREATED_AT_ASC + CREATED_AT_DESC + NAME_ASC + NAME_DESC + PIPELINES_COUNT_ASC + PIPELINES_COUNT_DESC +} + """Represents paged result of fetching pipeline templates.""" type PipelineTemplatePage { items: [PipelineTemplate!]! @@ -3232,6 +3419,7 @@ enum PrepareObjectDownloadError { } input PrepareObjectDownloadInput { + forceAttachment: Boolean = true objectKey: String! workspaceSlug: String! } @@ -3361,13 +3549,13 @@ type Query { pipelineTemplateVersion(id: UUID!): PipelineTemplateVersion """Retrieves a page of pipeline templates.""" - pipelineTemplates(functionalType: PipelineFunctionalType, page: Int = 1, perPage: Int = 15, search: String, tags: [String!], workspaceSlug: String): PipelineTemplatePage! + pipelineTemplates(functionalType: PipelineFunctionalType, isValidated: Boolean, orderBy: PipelineTemplateOrderBy, page: Int = 1, perPage: Int = 15, search: String, tags: [String!], workspaceSlug: String): PipelineTemplatePage! """Retrieves a pipeline version by ID.""" pipelineVersion(id: UUID!): PipelineVersion """Retrieves a page of pipelines ordered by relevant name.""" - pipelines(functionalType: PipelineFunctionalType, name: String, page: Int, perPage: Int, search: String, tags: [String!], workspaceSlug: String): PipelinesPage! + pipelines(functionalType: PipelineFunctionalType, lastRunStates: [PipelineRunStatus!], name: String, page: Int, perPage: Int, search: String, tags: [String!], workspaceSlug: String): PipelinesPage! searchDatabaseTables(organizationId: UUID, page: Int = 1, perPage: Int = 15, query: String!, workspaceSlugs: [String]): DatabaseTableResultPage! searchDatasets(organizationId: UUID, page: Int = 1, perPage: Int = 15, query: String!, workspaceSlugs: [String]): DatasetResultPage! searchFiles(organizationId: UUID, page: Int = 1, perPage: Int = 15, prefix: String, query: String!, workspaceSlugs: [String]): FileResultPage! @@ -3381,7 +3569,7 @@ type Query { """Search users.""" users(organizationId: UUID, query: String!, workspaceSlug: String): [User!]! - webapp(workspaceSlug: String!, slug: String!): Webapp + webapp(slug: String!, workspaceSlug: String!): Webapp webapps(favorite: Boolean, page: Int, perPage: Int, workspaceSlug: String): WebappsPage! workspace(slug: String!): Workspace workspaces(organizationId: UUID, page: Int, perPage: Int, query: String): WorkspacePage! @@ -3452,6 +3640,22 @@ type RemoveFromFavoritesResult { success: Boolean! } +"""Represents the error message for removing a webapp from shortcuts.""" +enum RemoveWebappFromShortcutsError { + ITEM_NOT_FOUND +} + +"""Represents the input for removing a webapp from shortcuts.""" +input RemoveWebappFromShortcutsInput { + webappId: UUID! +} + +"""Represents the result of removing a webapp from shortcuts.""" +type RemoveWebappFromShortcutsResult { + errors: [RemoveWebappFromShortcutsError!]! + success: Boolean! +} + enum RequestAccessmodAccessError { ALREADY_EXISTS INVALID @@ -3527,6 +3731,30 @@ type ResetPasswordResult { success: Boolean! } +"""Resource counts""" +type ResourceCounts { + """Number of pipeline runs.""" + pipelineRuns: Int! + + """Number of users.""" + users: Int! + + """Number of workspaces.""" + workspaces: Int! +} + +"""Input type for resource counts (used for subscription limits).""" +input ResourceCountsInput { + """Number of pipeline runs.""" + pipelineRuns: Int! + + """Number of users.""" + users: Int! + + """Number of workspaces.""" + workspaces: Int! +} + enum RunDAGError { DAG_NOT_FOUND INVALID_CONFIG @@ -3690,6 +3918,14 @@ type SetPasswordResult { success: Boolean! } +"""Represents a shortcut item in the sidebar.""" +type ShortcutItem { + id: UUID! + name: String! + order: Int! + url: String! +} + scalar SimplifiedExtentType scalar StackPriorities @@ -3705,6 +3941,24 @@ type StopPipelineResult { success: Boolean! } +"""Subscription details from the Bluesquare Console.""" +type Subscription { + """The date when the subscription ends.""" + endDate: Date! + + """Resource limits for this subscription.""" + limits: ResourceCounts! + + """The subscription plan code (e.g., "openhexa_starter").""" + planCode: String! + + """The date when the subscription starts.""" + startDate: Date! + + """The external subscription ID from the Bluesquare Console.""" + subscriptionId: UUID! +} + type TableColumn { name: String! type: String! @@ -4022,6 +4276,52 @@ type UpdateMembershipResult { success: Boolean! } +""" +The UpdateOrganizationError enum represents the possible errors that can occur during the updateOrganization mutation. +""" +enum UpdateOrganizationError { + """Indicates that the provided logo is in an invalid format.""" + INVALID_LOGO + + """ + Indicates that the provided short name is invalid (must be max 5 uppercase letters). + """ + INVALID_SHORT_NAME + + """Indicates that an organization with the same name already exists.""" + NAME_DUPLICATE + + """Indicates that the organization was not found.""" + NOT_FOUND + + """ + Indicates that the user does not have permission to update the organization. + """ + PERMISSION_DENIED + + """ + Indicates that an organization with the same short name already exists. + """ + SHORT_NAME_DUPLICATE +} + +""" +The UpdateOrganizationInput type represents the input for the updateOrganization mutation. +""" +input UpdateOrganizationInput { + """The unique identifier of the organization.""" + id: UUID! + + """The updated logo of the organization (base64 encoded).""" + logo: String + + """The updated name of the organization.""" + name: String + + """The updated short name of the organization.""" + shortName: String +} + """ The UpdateOrganizationMemberError enum represents the possible errors that can occur during the updateOrganizationMember mutation. """ @@ -4070,6 +4370,83 @@ type UpdateOrganizationMemberResult { success: Boolean! } +""" +The UpdateOrganizationResult type represents the result of the updateOrganization mutation. +""" +type UpdateOrganizationResult { + """ + The list of errors that occurred during the updateOrganization mutation. + """ + errors: [UpdateOrganizationError!]! + + """The updated organization object.""" + organization: Organization + + """Indicates whether the updateOrganization mutation was successful.""" + success: Boolean! +} + +""" +The UpdateOrganizationSubscriptionError enum represents the possible errors that can occur. +""" +enum UpdateOrganizationSubscriptionError { + """Indicates that the organization was not found.""" + NOT_FOUND + + """ + Indicates that the user does not have permission to update the organization subscription. + """ + PERMISSION_DENIED +} + +""" +The UpdateOrganizationSubscriptionInput type represents the input for updating organization subscription. +Used by the Bluesquare Console to update subscription when it changes. + +Behavior: If a subscription with the given subscriptionId exists, it will be updated. +Otherwise, a new subscription record will be created (used for downgrades/renewals). +""" +input UpdateOrganizationSubscriptionInput { + """The resource limits for the organization.""" + limits: ResourceCountsInput! + + """The unique identifier of the organization.""" + organizationId: UUID! + + """The updated subscription plan code.""" + planCode: String! + + """The subscription end date.""" + subscriptionEndDate: Date! + + """ + The external subscription ID from the Bluesquare Console. + Used as lookup key - if exists, update; if not, create new record. + """ + subscriptionId: UUID! + + """The subscription start date.""" + subscriptionStartDate: Date! +} + +""" +The UpdateOrganizationSubscriptionResult type represents the result of the updateOrganizationSubscription mutation. +""" +type UpdateOrganizationSubscriptionResult { + """ + The list of errors that occurred during the updateOrganizationSubscription mutation. + """ + errors: [UpdateOrganizationSubscriptionError!]! + + """The updated organization object.""" + organization: Organization + + """ + Indicates whether the updateOrganizationSubscription mutation was successful. + """ + success: Boolean! +} + """ Enum representing the possible errors that can occur when updating a pipeline. """ @@ -4292,6 +4669,7 @@ input UpdateWebappInput { icon: String id: UUID! name: String + type: WebappType url: String } @@ -4477,8 +4855,11 @@ type Webapp { icon: String id: UUID! isFavorite: Boolean! + isShortcut: Boolean! name: String! permissions: WebappPermissions! + slug: String! + type: WebappType! url: String! workspace: Workspace! } @@ -4489,6 +4870,14 @@ type WebappPermissions { update: Boolean! } +"""Represents the type of a web app.""" +enum WebappType { + BUNDLE + HTML + IFRAME + SUPERSET +} + """Represents a page of webapps.""" type WebappsPage { items: [Webapp!]! @@ -4519,8 +4908,10 @@ type Workspace { name: String! organization: Organization permissions: WorkspacePermissions! + pipelineLastRunStatuses: [PipelineRunStatus!]! pipelineTags: [String!]! pipelineTemplateTags: [String!]! + shortcuts: [ShortcutItem!]! slug: String! updatedAt: DateTime } @@ -4635,4 +5026,10 @@ type WorkspacePermissions { launchNotebookServer: Boolean! manageMembers: Boolean! update: Boolean! +} + +"""Represents a minimal workspace reference in the token payload.""" +type WorkspaceRef { + id: UUID! + slug: String! } \ No newline at end of file From 8b5c0cf3dc15b6fba3b474ab65e42fbfd4b3898a Mon Sep 17 00:00:00 2001 From: YolanFery Date: Mon, 19 Jan 2026 16:16:29 +0100 Subject: [PATCH 3/4] Better message --- openhexa/cli/api.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/openhexa/cli/api.py b/openhexa/cli/api.py index 56e51a5b..896553c5 100644 --- a/openhexa/cli/api.py +++ b/openhexa/cli/api.py @@ -150,12 +150,19 @@ def _detect_graphql_breaking_changes(token): ) for change in breaking_changes: click.secho(f"- {change.description}", fg="yellow") - click.secho( - "This could lead to unexpected results.\n" - f"Please update the SDK to the latest version {latest_version} " - f"(using `pip install openhexa-sdk=={latest_version}`) or use a version of the SDK compatible with the server.", - fg="red", - ) + click.secho("This could lead to unexpected results.", fg="red") + if current_version == latest_version: + click.secho( + "The server has schema changes not yet supported by the latest SDK. " + "To ensure compatibility with the server, it might be required to use a older version of the SDK.", + fg="red", + ) + else: + click.secho( + f"Please update the SDK to the latest version {latest_version} " + f"(using `pip install openhexa-sdk=={latest_version}`) to ensure compatibility.", + fg="red", + ) def graphql(query: str, variables=None, token=None): From d8740a6fa943b6f4f11f1e3ec9bc49da6e5db595 Mon Sep 17 00:00:00 2001 From: YolanFery Date: Mon, 19 Jan 2026 16:25:15 +0100 Subject: [PATCH 4/4] Better message --- openhexa/cli/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openhexa/cli/api.py b/openhexa/cli/api.py index 896553c5..c247be38 100644 --- a/openhexa/cli/api.py +++ b/openhexa/cli/api.py @@ -153,7 +153,7 @@ def _detect_graphql_breaking_changes(token): click.secho("This could lead to unexpected results.", fg="red") if current_version == latest_version: click.secho( - "The server has schema changes not yet supported by the latest SDK. " + "The SDK has schema changes not yet supported by the server. " "To ensure compatibility with the server, it might be required to use a older version of the SDK.", fg="red", )