diff --git a/src/api-service/__app__/job_templates_manage/__init__.py b/src/api-service/__app__/job_templates_manage/__init__.py index 355b041d5..1999cff78 100644 --- a/src/api-service/__app__/job_templates_manage/__init__.py +++ b/src/api-service/__app__/job_templates_manage/__init__.py @@ -6,9 +6,9 @@ import azure.functions as func from onefuzztypes.enums import ErrorCode from onefuzztypes.job_templates import ( - JobTemplateCreate, JobTemplateDelete, - JobTemplateUpdate, + JobTemplateGet, + JobTemplateUpload, ) from onefuzztypes.models import Error from onefuzztypes.responses import BoolResult @@ -18,40 +18,36 @@ from ..onefuzzlib.request import not_ok, ok, parse_request def get(req: func.HttpRequest) -> func.HttpResponse: + request = parse_request(JobTemplateGet, req) + if isinstance(request, Error): + return not_ok(request, context="JobTemplateGet") + + if request.name: + entry = JobTemplateIndex.get_base_entry(request.name) + if entry is None: + return not_ok( + Error(code=ErrorCode.INVALID_REQUEST, errors=["no such job template"]), + context="JobTemplateGet", + ) + return ok(entry.template) + templates = JobTemplateIndex.get_index() return ok(templates) def post(req: func.HttpRequest) -> func.HttpResponse: - request = parse_request(JobTemplateCreate, req) + request = parse_request(JobTemplateUpload, req) if isinstance(request, Error): - return not_ok(request, context="JobTemplateCreate") + return not_ok(request, context="JobTemplateUpload") entry = JobTemplateIndex(name=request.name, template=request.template) - result = entry.save(new=True) + result = entry.save() if isinstance(result, Error): - return not_ok(result, context="JobTemplateCreate") + return not_ok(result, context="JobTemplateUpload") return ok(BoolResult(result=True)) -def patch(req: func.HttpRequest) -> func.HttpResponse: - request = parse_request(JobTemplateUpdate, req) - if isinstance(request, Error): - return not_ok(request, context="JobTemplateUpdate") - - entry = JobTemplateIndex.get(request.name) - if entry is None: - return not_ok( - Error(code=ErrorCode.UNABLE_TO_UPDATE, errors=["no such job template"]), - context="JobTemplateUpdate", - ) - - entry.template = request.template - entry.save() - return ok(BoolResult(result=True)) - - def delete(req: func.HttpRequest) -> func.HttpResponse: request = parse_request(JobTemplateDelete, req) if isinstance(request, Error): @@ -68,7 +64,5 @@ def main(req: func.HttpRequest) -> func.HttpResponse: return post(req) elif req.method == "DELETE": return delete(req) - elif req.method == "PATCH": - return patch(req) else: raise Exception("invalid method") diff --git a/src/api-service/__app__/job_templates_manage/function.json b/src/api-service/__app__/job_templates_manage/function.json index 99c78cc49..57d1baecc 100644 --- a/src/api-service/__app__/job_templates_manage/function.json +++ b/src/api-service/__app__/job_templates_manage/function.json @@ -9,8 +9,7 @@ "methods": [ "get", "post", - "delete", - "patch" + "delete" ], "route": "job_templates/manage" }, diff --git a/src/api-service/__app__/onefuzzlib/job_templates/templates.py b/src/api-service/__app__/onefuzzlib/job_templates/templates.py index 9f7726ddb..0a5cb6b16 100644 --- a/src/api-service/__app__/onefuzzlib/job_templates/templates.py +++ b/src/api-service/__app__/onefuzzlib/job_templates/templates.py @@ -25,6 +25,18 @@ class JobTemplateIndex(BASE_INDEX, ORMMixin): def key_fields(cls) -> Tuple[str, Optional[str]]: return ("name", None) + @classmethod + def get_base_entry(cls, name: str) -> Optional[BASE_INDEX]: + result = cls.get(name) + if result is not None: + return BASE_INDEX(name=name, template=result.template) + + template = TEMPLATES.get(name) + if template is None: + return None + + return BASE_INDEX(name=name, template=template) + @classmethod def get_index(cls) -> List[BASE_INDEX]: entries = [BASE_INDEX(name=x.name, template=x.template) for x in cls.search()] diff --git a/src/cli/onefuzz/job_templates/manage.py b/src/cli/onefuzz/job_templates/manage.py index f0bb26705..4dedc5dbf 100644 --- a/src/cli/onefuzz/job_templates/manage.py +++ b/src/cli/onefuzz/job_templates/manage.py @@ -7,10 +7,10 @@ from typing import List from onefuzztypes.job_templates import ( JobTemplate, - JobTemplateCreate, JobTemplateDelete, + JobTemplateGet, JobTemplateIndex, - JobTemplateUpdate, + JobTemplateUpload, ) from onefuzztypes.responses import BoolResult @@ -27,31 +27,29 @@ class Manage(Endpoint): self.onefuzz._warn_preview(PreviewFeature.job_templates) self.onefuzz.logger.debug("listing job templates") - return self._req_model_list("GET", JobTemplateIndex) + return self._req_model_list( + "GET", JobTemplateIndex, data=JobTemplateGet(name=None) + ) - def create(self, domain: str, name: str, template: JobTemplate) -> BoolResult: - """ Create a Job Template """ + def get(self, name: str) -> JobTemplate: + """ Get an existing Job Template """ self.onefuzz._warn_preview(PreviewFeature.job_templates) - self.onefuzz.logger.debug("creating job templates") + self.onefuzz.logger.debug("get job template") + return self._req_model("GET", JobTemplate, data=JobTemplateGet(name=name)) + + def upload(self, name: str, template: JobTemplate) -> BoolResult: + """ Upload a Job Template """ + self.onefuzz._warn_preview(PreviewFeature.job_templates) + + self.onefuzz.logger.debug("upload job template") return self._req_model( "POST", BoolResult, - data=JobTemplateCreate(domain=domain, name=name, template=template), + data=JobTemplateUpload(name=name, template=template), ) - def update(self, domain: str, name: str, template: JobTemplate) -> BoolResult: - """ Update an existing Job Template """ - self.onefuzz._warn_preview(PreviewFeature.job_templates) - - self.onefuzz.logger.debug("update job templates") - return self._req_model( - "POST", - BoolResult, - data=JobTemplateUpdate(domain=domain, name=name, template=template), - ) - - def delete(self, domain: str, name: str) -> BoolResult: + def delete(self, name: str) -> BoolResult: """ Delete a Job Template """ self.onefuzz._warn_preview(PreviewFeature.job_templates) @@ -59,5 +57,5 @@ class Manage(Endpoint): return self._req_model( "DELETE", BoolResult, - data=JobTemplateDelete(domain=domain, name=name), + data=JobTemplateDelete(name=name), ) diff --git a/src/pytypes/onefuzztypes/job_templates.py b/src/pytypes/onefuzztypes/job_templates.py index b3e5d911a..f1dc41b24 100644 --- a/src/pytypes/onefuzztypes/job_templates.py +++ b/src/pytypes/onefuzztypes/job_templates.py @@ -12,7 +12,7 @@ from .models import JobConfig, NotificationConfig, TaskConfig, TaskContainers from .primitives import File from .requests import BaseRequest from .responses import BaseResponse -from .validators import check_template_name +from .validators import check_template_name, check_template_name_optional class UserFieldLocation(BaseModel): @@ -44,7 +44,7 @@ class JobTemplateNotification(BaseModel): notification: NotificationConfig -class JobTemplate(BaseModel): +class JobTemplate(BaseResponse): os: OS job: JobConfig tasks: List[TaskConfig] @@ -151,7 +151,7 @@ TEMPLATE_BASE_FIELDS = [ ] -class JobTemplateCreate(BaseRequest): +class JobTemplateUpload(BaseRequest): name: str template: JobTemplate @@ -164,13 +164,6 @@ class JobTemplateDelete(BaseRequest): _verify_name: classmethod = validator("name", allow_reuse=True)(check_template_name) -class JobTemplateUpdate(BaseRequest): - name: str - template: JobTemplate - - _verify_name: classmethod = validator("name", allow_reuse=True)(check_template_name) - - class JobTemplateRequest(BaseRequest): name: str user_fields: TemplateUserFields @@ -181,5 +174,13 @@ class JobTemplateRequest(BaseRequest): ) +class JobTemplateGet(BaseRequest): + name: Optional[str] + + _validate_name: classmethod = validator("name", allow_reuse=True)( + check_template_name_optional + ) + + class JobTemplateRequestParameters(BaseRequest): user_fields: TemplateUserFields diff --git a/src/pytypes/onefuzztypes/validators.py b/src/pytypes/onefuzztypes/validators.py index 59215710e..07bf34beb 100644 --- a/src/pytypes/onefuzztypes/validators.py +++ b/src/pytypes/onefuzztypes/validators.py @@ -4,6 +4,7 @@ # Licensed under the MIT License. from string import ascii_letters, digits +from typing import Optional ALPHA_NUM = ascii_letters + digits ALPHA_NUM_DASH = ALPHA_NUM + "-" @@ -36,3 +37,10 @@ def check_template_name(value: str) -> str: raise ValueError("invalid value: %s" % value) return check_alnum_underscore(value) + + +def check_template_name_optional(value: Optional[str]) -> Optional[str]: + if value is None: + return value + + return check_template_name(value)