diff --git a/server/.env b/server/.env new file mode 100644 index 0000000..3dad52c --- /dev/null +++ b/server/.env @@ -0,0 +1 @@ +DEBUG_FLAG=True diff --git a/server/api/api.py b/server/api/api.py index d1bb910..ab2ea29 100644 --- a/server/api/api.py +++ b/server/api/api.py @@ -7,7 +7,7 @@ from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.permissions import IsAuthenticated, AllowAny from .models import LibraryRegistration -from webcligui_api import ParameterList, ParameterOptionsToList, ParameterPreference, ParameterStringValue +# from webcligui_api import ParameterList, ParameterOptionsToList, ParameterPreference, ParameterStringValue libraryApis = [] library2Idx = {} @@ -15,6 +15,7 @@ def instantiateLibraryModule(obj): module = importlib.import_module(obj.module_path) cls = getattr(module, obj.class_name) + print(f"api.py-- cls={cls} obj={obj} class_name={obj.class_name} module={module} ") libraryAPIImpl = cls() return libraryAPIImpl @@ -24,9 +25,11 @@ def load_library_apis(): return for idx, obj in enumerate(LibraryRegistration.objects.all()): + print(f"api.py-- idx={idx} obj={obj} name={obj.library_name} module={obj.module_path} ") libraryAPIImpl = instantiateLibraryModule(obj) libraryApis.append(libraryAPIImpl) library2Idx[obj.library_name] = idx + # library2Idx[obj.module_path] = idx def to_json_safe(obj): if isinstance(obj, Enum): @@ -69,7 +72,7 @@ def get_description(request): libraryApiImpl = getLibraryApi(libraryName) if not libraryApiImpl: errMsg = f'Libraryname "{libraryName}" not known!' - print('get_description():', errMsg) + print('api.py--get_description():', errMsg) return HttpResponseBadRequest(errMsg) operationBranch = body["operationBranch"][1:] @@ -84,9 +87,10 @@ def get_parameters(request): body = json.loads(request.body) libraryName = body["operationBranch"][0] libraryApiImpl = getLibraryApi(libraryName) + print(f"api.py--get_parameters(): libraryName={libraryName}") if not libraryApiImpl: errMsg = f'Libraryname "{libraryName}" not known!' - print('get_parameters():', errMsg) + print('api.py--get_parameters():', errMsg) return HttpResponseBadRequest(errMsg) operationBranch = body["operationBranch"][1:] @@ -100,16 +104,20 @@ def get_parameters(request): @permission_classes([AllowAny]) def submit_operation(request): body = json.loads(request.body) + libraryName = body["operationBranch"][0] + libraryApiImpl = getLibraryApi(libraryName) + if not libraryApiImpl: + errMsg = f'Libraryname "{libraryName}" not known!' + print('api.py--submit_operation():', errMsg) + return HttpResponseBadRequest(errMsg) + + operationBranch = body["operationBranch"][1:] command = body['command'] servers = body["servers"] + + print(f"api.py--submit_operation(): libraryName={libraryName}, operationBranch={operationBranch}") + print(f"command={command}, servers={servers}") + + result = libraryApiImpl.submitOperation(body['operationBranch'], command, servers) - print('command:', command, 'servers:', servers) - fullCommand = command + ['localhost'] - print('fullCommand:', fullCommand) - try: - subprocess.run(fullCommand, check=True) - except Exception as exc: - print('Exception:', exc) - return HttpResponseServerError(str(exc)); - - return HttpResponse('OK') + return JsonResponse(result, safe=False) diff --git a/server/djangoProc.spcs b/server/djangoProc.spcs index ce6b463..9741419 100755 --- a/server/djangoProc.spcs +++ b/server/djangoProc.spcs @@ -52,6 +52,12 @@ def examples_spcs() -> None: cs.examples.menuSection(f'*Register csLib*') literal(f"manage.py register_library csLib csLib LibraryAPIImpl --description 'Command Services Library'") + literal(f"manage.py register_library static bisos.csPlayer.drf_csPlayer_static LibraryAPIImpl --description 'A Static CSXU Library For Testing'") + literal(f"manage.py register_library pip:bisos3 bisos.csPlayer.drf_csPlayer_pipBisos3 LibraryAPIImpl --description 'pip bisos3 CSXUs'") + literal(f"manage.py register_library pip:dev-bisos3 bisos.csPlayer.drf_csPlayer_pipDevBisos3 LibraryAPIImpl --description 'pip dev-bisos3 CSXUs'") + literal(f"manage.py register_library pipx bisos.csPlayer.drf_csPlayer_pipx LibraryAPIImpl --description 'pipx Packages and CSXUs'") + literal(f"manage.py register_library modules:facter bisos.csPlayer.drf_modPlayer_facter LibraryAPIImpl --description 'CSXU Modules for facterModule.cs'") + literal(f"manage.py register_library modules:soncli bisos.csPlayer.drf_modPlayer_soncli LibraryAPIImpl --description 'Uploadable Modules for soncli.cs'") cs.examples.menuSection(f'*Virtual Domain Deployment Preparations*') literal(f"sudo chown {userName}:www-data .") # Make the current directory writable by www-data @@ -66,6 +72,8 @@ def examples_spcs() -> None: cmnd('dotEnv', wrapper="rm .env ; ", comment="# Create .env with DEBUG_FLAG=True for development") cmnd('bxDjango_report') cmnd('bxDjango_fullUpdate') + literal(f"(pushd {gitClonesBaseDir}/webCliGui/webcligui_api && pip install -e .)") + literal(f"(pushd {gitClonesBaseDir}/csLib && pip install -e .)") class bxDjango_fullUpdate(cs.Cmnd): cmndParamsMandatory = [ ] diff --git a/server/webCliGui/settings.py b/server/webCliGui/settings.py index 451131a..eb068fa 100644 --- a/server/webCliGui/settings.py +++ b/server/webCliGui/settings.py @@ -43,6 +43,8 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'drf_spectacular', + 'drf_spectacular_sidecar', # Optional, for serving static files 'api', ] @@ -136,4 +138,12 @@ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', +} + +SPECTACULAR_SETTINGS = { + 'TITLE': 'Your Project API', + 'DESCRIPTION': 'Documentation for Your Project API', + 'VERSION': '1.0.0', + # Other settings like authentication can be added here } diff --git a/server/webCliGui/urls.py b/server/webCliGui/urls.py index 15878d6..5ec9c09 100644 --- a/server/webCliGui/urls.py +++ b/server/webCliGui/urls.py @@ -3,8 +3,19 @@ """ from django.contrib import admin from django.urls import path, include +from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView urlpatterns = [ -path('admin/', admin.site.urls), -path('api/', include("api.urls")), + path('admin/', admin.site.urls), + path('api/', include("api.urls")), + + # OpenAPI 3 Schema + path('api/schema/', SpectacularAPIView.as_view(), name='schema'), + + # Optional: Swagger UI web interface + path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), + + # Optional: ReDoc web interface + path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), + ] diff --git a/src/components/App.tsx b/src/components/App.tsx index 2194cfc..6a851f1 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -7,8 +7,7 @@ const App = () => { return (
- -

Web UI Command Services Executor

+ {/*

Web UI Command Services Executor

*/} { +interface CreationStepsProps { + operationPos: string | null; +} + +const CreationSteps = ({ operationPos }: CreationStepsProps) => { const { taskCreationStep, isNextStepValid, @@ -23,7 +27,9 @@ const CreationSteps = () => { if (taskCreationStep !== TaskCreationSteps.Preview) { setNextTaskCreationStep(taskCreationStep + 1); } else { - submitOperation(); + if (operationPos) { + submitOperation(operationPos); + } } }; @@ -66,9 +72,10 @@ const CreationSteps = () => { interface OperationSelectionProps { isVisible: boolean; + onSelectOperation: (pos: string) => void; } -const OperationSelection = ({ isVisible }: OperationSelectionProps) => { +const OperationSelection = ({ isVisible, onSelectOperation }: OperationSelectionProps) => { const { taskTrees, selectedOperationBranch, @@ -78,7 +85,9 @@ const OperationSelection = ({ isVisible }: OperationSelectionProps) => { } = useDataStore(state => state.createTask); const onSelect = (selectedKeys: React.Key[], selectData: any) => { - setSelectedOperation(selectData.node.pos); + const pos = selectData.node.pos; + onSelectOperation(pos); + setSelectedOperation(pos); }; let selectedOperation: Operation | null = null; @@ -362,12 +371,17 @@ const Preview = ({ ); }; -const CreationViews = () => { +interface CreationViewsProps { + operationPos: string | null; + setOperationPos: (pos: string) => void; +} + +const CreationViews = ({ operationPos, setOperationPos }: CreationViewsProps) => { const { taskCreationStep } = useDataStore(state => state.createTask); return ( <> - + @@ -376,7 +390,8 @@ const CreationViews = () => { }; const CreateTask = () => { -const { getLibraryOperators } = useDataStore(state => state.createTask); + const { getLibraryOperators } = useDataStore(state => state.createTask); + const [operationPos, setOperationPos] = React.useState(null); useEffect(() => { getLibraryOperators(); @@ -384,8 +399,8 @@ const { getLibraryOperators } = useDataStore(state => state.createTask); return (
- - + +
); }; diff --git a/src/store/createTaskIf.ts b/src/store/createTaskIf.ts index dce5471..7131834 100644 --- a/src/store/createTaskIf.ts +++ b/src/store/createTaskIf.ts @@ -35,5 +35,5 @@ export interface CreateTaskIf { loadParameters: () => Promise; setParameterValue: (parameterBranch: number[], value: ParameterValue) => void; getExecuteCommand: () => string[] | null; - submitOperation: () => void; + submitOperation: (operationPos: string) => Promise; } diff --git a/src/store/dataStore.ts b/src/store/dataStore.ts index 807115c..b664f75 100644 --- a/src/store/dataStore.ts +++ b/src/store/dataStore.ts @@ -425,9 +425,14 @@ const doGetExecuteCommand = (get: GetFunction) => { return [operation.name, ...params]; } -const doSubmitOperation = async (get: GetFunction, set: SetFunction) => { +const doSubmitOperation = async (operationPos: string, get: GetFunction, set: SetFunction) => { const cmd = doGetExecuteCommand(get) as string[]; + const operationBranch = getOperationBranch(operationPos, get().createTask.taskTrees); + if (!operationBranch) { + return FetchState.Idle; + } + const setFetchStatus = (stat: FetchStatus) => set(state => { state.createTask.submitOperationFetchAndError.fetchStatus = stat; }, false, 'submitOperationFetchStatus'); @@ -439,6 +444,7 @@ const doSubmitOperation = async (get: GetFunction, set: SetFunction) => { }, false, 'submitOperationError'); interface SubmitOperation { + operationBranch: string[]; command: string[]; servers: string[]; } @@ -446,7 +452,7 @@ const doSubmitOperation = async (get: GetFunction, set: SetFunction) => { return await fetchData( '/api/submit-operation', { - postData: { command: cmd, servers: [WEB_CLI_GUI_SERVER] }, + postData: { operationBranch: operationBranch, command: cmd, servers: [WEB_CLI_GUI_SERVER] }, setFetchStatus, setError, } @@ -489,7 +495,7 @@ export const useDataStore = create()( loadParameters: async () => await doLoadParameters(get, set), setParameterValue: (parameterBranch: number[], value: ParameterValue) => doSetParameterValue(parameterBranch, value, get, set), getExecuteCommand: () => doGetExecuteCommand(get), - submitOperation: () => doSubmitOperation(get, set), + submitOperation: (operationPos: string) => doSubmitOperation(operationPos, get, set), } })), { name: 'DataStore', diff --git a/webcligui_api/src/webcligui_api/library_api.py b/webcligui_api/src/webcligui_api/library_api.py index 7ff209c..c3010c2 100644 --- a/webcligui_api/src/webcligui_api/library_api.py +++ b/webcligui_api/src/webcligui_api/library_api.py @@ -16,3 +16,7 @@ def getDescription(self, operationBranch: list[str]) -> str: @abstractmethod def getParameters(self, operationBranch: list[str]) -> ParameterData: pass + + @abstractmethod + def submitOperation(self, operationBranch: list[str], command: list[str], servers: list[str]): + pass diff --git a/webpack.config.js b/webpack.config.js index 8cc698b..3e47654 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -70,11 +70,11 @@ export default { historyApiFallback: true, hot: true, host: "0.0.0.0", - port: 9002, + port: 25002, proxy: [ { context: ['/api', '/admin', '/static'], - target: 'http://127.0.0.1:5000', + target: 'http://127.0.0.1:23501', secure: false, changeOrigin: true, } @@ -99,4 +99,4 @@ export default { ] }) ], -}; \ No newline at end of file +};