Saltar al contenido principal

labs_documentation

Ver en Git


AuraLabs API Documentation

Overview

This document describes the API changes introduced to support AuraLabs (flavour 3) across the platform. AuraLabs extends the existing multi-tenant architecture to include a third flavour alongside AuraView (fields) and AuraSales.

Flavours Overview

IDNameDescription
1auraviewFields/Agri management
2aurasalesSales management
3auralabsLabs/Experimental

New Permission

Permission IDNameDescription
3000subadmin_auralabsGrants AuraLabs subadmin rights

New Database Column

ALTER TABLE usuarios ADD COLUMN id_tipo_usuario_labs integer;

Endpoints Modified

1. User Data API - /users/data

Class: UserDataAPI
File: aurapi_src/apis/users.py

New Response Fields

FieldTypeDescription
id_tipo_usuario_labsinteger (nullable)AuraLabs user type ID
auralabsobjectAuraLabs user details
flavoursarrayNow includes entry with id:3
subadmin.labsbooleanAuraLabs subadmin flag

New Query Fields Added

usuarios.id_tipo_usuario_labs,
los_permisos_labs.permisos_finales_labs,
tipo_usuario_labs.descripcion_corta AS "user_type_description_labs",
tipo_usuario_labs.tipo_de_usuario AS "user_type_labs"

Old Response Example

{
"nombre": "John",
"apellido": "Doe",
"email": "john@example.com",
"tipo_usuario": "Productor",
"id_tipo_usuario": 5,
"id_tipo_usuario_sales": null,
"descripcion_corta": "Productor",
"auraview": {
"id_tipo_usuario": 5,
"tipo_usuario": "Productor"
},
"aurasales": null,
"flavours": [
{"id": 1, "description": "fields", "has_access": true},
{"id": 2, "description": "sales", "has_access": false}
],
"subadmin": {
"fields": false,
"sales": false
}
}

New Response Example

{
"nombre": "John",
"apellido": "Doe",
"email": "john@example.com",
"tipo_usuario": "Productor",
"id_tipo_usuario": 5,
"id_tipo_usuario_sales": null,
"id_tipo_usuario_labs": 10,
"descripcion_corta": "Productor",
"auraview": {
"id_tipo_usuario": 5,
"tipo_usuario": "Productor"
},
"aurasales": null,
"auralabs": {
"descripcion_corta": "Lab User",
"tipo_usuario": "Lab Type"
},
"flavours": [
{"id": 1, "description": "fields", "has_access": true},
{"id": 2, "description": "sales", "has_access": false},
{"id": 3, "description": "labs", "has_access": true}
],
"subadmin": {
"fields": false,
"sales": false,
"labs": true
}
}

2. Desktop Users API - /desktop/users

Class: DesktopUsersAPI
File: aurapi_src/apis/desktops.py

New Response Fields

FieldTypeDescription
subadmin_auralabsbooleanAuraLabs subadmin flag
auralabsobjectAuraLabs user details

New Query Fields

3000 = ANY(usuarios.permisos_agregar) AS subadmin_auralabs,
jsonb_build_object(
'id_tipo_usuario_labs', usuarios.id_tipo_usuario_labs,
'tipo_usuario', tu_labs.tipo_de_usuario,
'grupos', COALESCE(ARRAY_AGG(miembros_grupos.id_grupo)
FILTER (WHERE miembros_grupos.id_grupo IS NOT NULL AND tec.id_flavour = 3), ARRAY[]::int[]),
'supervisor', COALESCE(ARRAY_AGG(miembros_grupos.id_grupo)
FILTER (WHERE miembros_grupos.id_grupo IS NOT NULL AND miembros_grupos.id_tipo in (1,3) AND tec.id_flavour = 3), ARRAY[]::int[])
) as auralabs

Old Response Example

{
"usuarios": [
{
"id": 123,
"apellido": "Doe",
"nombre": "John",
"email": "john@example.com",
"admin": false,
"subadmin_auraview": false,
"subadmin_aurasales": false,
"auraview": {
"id_tipo_usuario": 5,
"tipo_usuario": "Productor",
"grupos": [1, 2],
"supervisor": []
},
"aurasales": null
}
]
}

New Response Example

{
"usuarios": [
{
"id": 123,
"apellido": "Doe",
"nombre": "John",
"email": "john@example.com",
"admin": false,
"subadmin_auraview": false,
"subadmin_aurasales": false,
"subadmin_auralabs": true,
"auraview": {
"id_tipo_usuario": 5,
"tipo_usuario": "Productor",
"grupos": [1, 2],
"supervisor": []
},
"aurasales": null,
"auralabs": {
"id_tipo_usuario_labs": 10,
"tipo_usuario": "Lab User",
"grupos": [5, 6],
"supervisor": [7]
}
}
]
}

3. Desktop Admin V2 Users API - /desktop/admin/v2/users

Class: UsersDesktopAdminV2API
File: aurapi_src/apis/desktops.py

New Request Parameters (formData)

ParameterTypeDescription
id_tipo_usuario_labsstring (optional)Set AuraLabs user type ID
subadmin_auralabsstring (optional)Grant/remove AuraLabs subadmin permission

New Validation Logic

is_auralabs_subadmin = validar_permisos(admin_id, 3000) if not is_admin else False

if not (is_admin or is_auraview_subadmin or is_aurasales_subadmin or is_auralabs_subadmin):
raise ApiErr(12, "Request available only for administrators")

New Permission Handler in PATCH For Loop

Added missing elif key == "subadmin_auralabs": handler in the PATCH parameter processing loop:

elif key == "subadmin_auralabs":
if not is_admin:
raise ApiErr(20, "Only general administrator can modify subadmin permissions")
value = flask.request.form.get(key)
if value is None or value == "":
params["subadmin_auralabs"] = None
else:
params["subadmin_auralabs"] = str(value).lower() == "true"

New Permission Validation

When setting subadmin_auralabs to true, the system validates that the user has a defined labs user type:

if params.get("subadmin_auralabs"):
has_labs_type = user.get('id_tipo_usuario_labs') or params.get('id_tipo_usuario_labs')
if not has_labs_type:
raise ApiErr(15, "To be AuraLabs subadmin must have a defined labs user type")

New Permission Assignment

if subadmin_auralabs is True:
permisos_parts.append(" CASE WHEN 3000::smallint = ANY(permisos_agregar) THEN permisos_agregar ELSE permisos_agregar || 3000::smallint END ")
elif subadmin_auralabs is False:
permisos_parts.append(" array_remove(permisos_agregar, 3000::smallint) ")

Swagger Documentation Added

- name: id_tipo_usuario_labs
in: formData
required: false
type: string
description: AuraLabs user type ID

- name: subadmin_auralabs
in: formData
required: false
type: string
description: Grant AuraLabs subadmin permission (3000)

4. Internal Workspace User Type Quota API

Class: InternalWorkspaceUserTypeQuotaManagerAPI
File: aurapi_src/apis/internal/workspace_user_types.py

Change Description

The endpoint now includes AuraLabs quotas in the combined response.

Old Code

all_quotas = quotas_result.get('auraview', []) + quotas_result.get('aurasales', [])

New Code

all_quotas = quotas_result.get('auraview', []) + quotas_result.get('aurasales', []) + quotas_result.get('auralabs', [])

Old Response Example

{
"data": [
{"id": 1, "user_type": "Productor", "quota": 100, "flavour": 1},
{"id": 2, "user_type": "Manager", "quota": 50, "flavour": 2}
]
}

New Response Example

{
"data": [
{"id": 1, "user_type": "Productor", "quota": 100, "flavour": 1},
{"id": 2, "user_type": "Manager", "quota": 50, "flavour": 2},
{"id": 3, "user_type": "Lab User", "quota": 25, "flavour": 3}
]
}

5. Auth Login API - /auth/v2/login

Function: get_flavours
File: aurapi_src/apis/auth/auth.py

Change Description

The flavours query now includes flavour 3 (AuraLabs) for users with id_tipo_usuario_labs set.

Old Query

SELECT DISTINCT wf.flavour 
FROM usuarios u
JOIN workspaces_flavours wf ON wf.id_workspace = %(workspace_id)s
AND wf.fecha_fin IS NULL
AND ((u.id_tipo_usuario IS NOT NULL AND wf.flavour = 1)
OR (u.id_tipo_usuario_sales IS NOT NULL AND wf.flavour = 2))
WHERE u.id = %(user_id)s

New Query

SELECT DISTINCT wf.flavour
FROM usuarios u
JOIN workspaces_flavours wf ON wf.id_workspace = %(workspace_id)s
AND wf.fecha_fin IS NULL
AND ((u.id_tipo_usuario IS NOT NULL AND wf.flavour = 1)
OR (u.id_tipo_usuario_sales IS NOT NULL AND wf.flavour = 2)
OR (u.id_tipo_usuario_labs IS NOT NULL AND wf.flavour = 3))
WHERE u.id = %(user_id)s

Old Response Example

{
"flavours": [1, 2]
}

New Response Example

{
"flavours": [1, 2, 3]
}

6. Permissions Calculation - commons/permisos.py

Class: Permissions
File: commons/permisos.py

Change Description

The permissions calculation now includes AuraLabs user type permissions.

Old Code

| CASE 
WHEN wf_sales.flavour IS NOT NULL THEN COALESCE(tipo_usuario_sales.permisos_default, ARRAY[]::int[])
ELSE ARRAY[]::int[]
END
| CASE
WHEN lue."admin" THEN COALESCE(esc.permisos_supremos_admin, ARRAY[]::int[])
ELSE ARRAY[]::int[]
END

New Code

| CASE
WHEN wf_sales.flavour IS NOT NULL THEN COALESCE(tipo_usuario_sales.permisos_default, ARRAY[]::int[])
ELSE ARRAY[]::int[]
END
| CASE
WHEN wf_labs.flavour IS NOT NULL THEN COALESCE(tipo_usuario_labs.permisos_default, ARRAY[]::int[])
ELSE ARRAY[]::int[]
END
| CASE
WHEN lue."admin" THEN COALESCE(esc.permisos_supremos_admin, ARRAY[]::int[])
ELSE ARRAY[]::int[]
END

New JOIN Added

LEFT JOIN tipo_usuario AS tipo_usuario_labs ON usuarios.id_tipo_usuario_labs = tipo_usuario_labs.id
LEFT JOIN workspaces_flavours AS wf_labs
ON wf_labs.id_workspace = lue.id_escritorio
AND wf_labs.flavour = 3
AND wf_labs.fecha_fin IS NULL

Repository Changes

validate_workspace_user_type_availability Function

File: aurapi_src/repository/workspaces/workspace_user_types.py

Problem

When assigning a Labs user type (id_tipo_usuario_labs), the validation would fail with "No es posible asignar ese tipo de usuario labs" because the function only searched for quotas in auraview and aurasales.

Old Code

quota_result = get_workspace_user_type_quota(sponsor_workspace_id, user_type_id)
# Buscar en auraview y aurasales
quota = None
if quota_result.get('auraview'):
quota = quota_result['auraview'][0]
elif quota_result.get('aurasales'):
quota = quota_result['aurasales'][0]
if not quota or quota.available_quota <= 0:
raise wsa_exceptions.NotEnoughQuotaError()

New Code

quota_result = get_workspace_user_type_quota(sponsor_workspace_id, user_type_id)
# Buscar en auraview, aurasales y auralabs
quota = None
if quota_result.get('auraview'):
quota = quota_result['auraview'][0]
elif quota_result.get('aurasales'):
quota = quota_result['aurasales'][0]
elif quota_result.get('auralabs'):
quota = quota_result['auralabs'][0]
if not quota or quota.available_quota <= 0:
raise wsa_exceptions.NotEnoughQuotaError()

Repository Changes

Workspace User Types Repository

File: aurapi_src/repository/workspaces/workspace_user_types.py

New Function Call

results_auralabs = _get_quotas_by_flavour(workspace_id, user_type_id, 3, 'id_tipo_usuario_labs')

return {
'auraview': [WorkspaceUserTypeQuota(**r) for r in results_auraview],
'aurasales': [WorkspaceUserTypeQuota(**r) for r in results_aurasales],
'auralabs': [WorkspaceUserTypeQuota(**r) for r in results_auralabs]
}

Database Migration

A new migration file was created:

File: migrations/2026-05-12T113499.up.sql

This migration adds the necessary column to support AuraLabs user types.


7. Workspace Admin API - GET /internal/workspace_admin/[id]

Class: WorkSpaceAdminAPI
File: aurapi_src/apis/internal/workspace_admin.py
Repository: aurapi_src/repository/workspaces/workspace.py

Change Description

The flavours query now includes flavour 3 (AuraLabs).

Old Query

FROM flavours f
WHERE f.id IN (1, 2)
ORDER BY f.id

New Query

FROM flavours f
WHERE f.id IN (1, 2, 3)
ORDER BY f.id

Old Response Example

{
"id": 1,
"nombre": "My Workspace",
"flavours": [
{"id": 1, "description": "AuraView", "enable": true},
{"id": 2, "description": "AuraSales", "enable": true}
]
}

New Response Example

{
"id": 1,
"nombre": "My Workspace",
"flavours": [
{"id": 1, "description": "AuraView", "enable": true},
{"id": 2, "description": "AuraSales", "enable": true},
{"id": 3, "description": "AuraLabs", "enable": true}
]
}

8. Workspace Admin Flavours API - GET /internal/workspace_admin/flavours

Class: WorkSpaceAdminFlavoursAPI
File: aurapi_src/apis/internal/workspace_admin.py
Repository: aurapi_src/repository/workspaces/workspace_admin.py

Change Description

This endpoint already returns all flavours from the database (no filter applied). With the migration adding flavour 3, it now returns AuraLabs automatically.

Response Example

[
{"id": 1, "description": "AuraView"},
{"id": 2, "description": "AuraSales"},
{"id": 3, "description": "AuraLabs"}
]

9. Workspace Admin API - PATCH /internal/workspace_admin/[id]

Class: WorkSpaceAdminAPI
File: aurapi_src/apis/internal/workspace_admin.py
Repository: aurapi_src/repository/workspaces/workspace_admin.py

Change Description

The endpoint already supports enabling/disabling any flavour (including flavour 3). No code changes were required since it dynamically handles flavour IDs.

Request Payload Example

[
{"flavour_id": 1, "enable": true},
{"flavour_id": 2, "enable": true},
{"flavour_id": 3, "enable": true}
]

10. Workspaces User Types API - GET /api/workspaces/user_types

Class: WorkspaceUserTypesAddonsAPI
File: aurapi_src/apis/workspaces/workspace_user_types.py

Change Description

Added auralabs to the response and filter. The repository already returns auralabs data; this endpoint now exposes it.

Old Response Example

{
"data": {
"auraview": [...],
"aurasales": []
}
}

New Response Example

{
"data": {
"auraview": [...],
"aurasales": [],
"auralabs": [
{
"user_type_id": 212,
"user_type": "Recepción",
"original_quota": 10,
"available_quota": 8,
"consumed_quota": 2,
"users": [162173, 141554],
"valid_from": "2025-10-10T00:00:00Z",
"valid_to": "2030-02-20T00:00:00Z"
}
]
}
}

Error Codes

CodeMessage
15To be AuraLabs subadmin must have a defined labs user type
17No es posible asignar ese tipo de usuario labs

Testing Checklist

  • User data endpoint returns id_tipo_usuario_labs and auralabs object
  • User data endpoint returns subadmin.labs flag
  • Desktop users endpoint returns subadmin_auralabs and auralabs object
  • Desktop admin endpoint accepts id_tipo_usuario_labs parameter
  • Desktop admin endpoint accepts subadmin_auralabs parameter
  • Workspace user type quota includes auralabs quotas
  • Auth login returns flavour 3 for users with labs user type
  • Permissions calculation includes labs user type permissions
  • Permission 3000 (subadmin_auralabs) can be assigned to users
  • GET /internal/workspace_admin/[id] returns flavour 3 in flavours array
  • GET /internal/workspace_admin/flavours returns AuraLabs
  • PATCH /internal/workspace_admin/[id] can enable/disable flavour 3
  • GET /api/workspaces/user_types returns auralabs in response

  • aurapi_src/apis/users.py
  • aurapi_src/apis/desktops.py
  • aurapi_src/apis/auth/auth.py
  • aurapi_src/apis/internal/workspace_user_types.py
  • aurapi_src/apis/internal/workspace_admin.py
  • aurapi_src/apis/workspaces/workspace_user_types.py
  • aurapi_src/repository/workspaces/workspace_user_types.py
  • aurapi_src/repository/workspaces/workspace_admin.py
  • aurapi_src/repository/workspaces/workspace.py
  • commons/permisos.py
  • migrations/2026-05-12T113499.up.sql