Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
add addTag/deleteTag api, apend tags filed to job detail/list (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
suiguoxin committed Sep 21, 2020
1 parent 404625e commit 3789582
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 2 deletions.
12 changes: 12 additions & 0 deletions docs/rest-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,18 @@ const openPAIClient = new PAIV2.OpenPAIClient(cluster);
await openPAIClient.job.updateJobExecutionType(username, jobname, 'STOP');
```

- [x] Add a tag to a job (PUT /api/v2/jobs/{username}~{jobname}/tag)

```ts
await openPAIClient.job.addTag(username, jobname, tag);
```

- [x] Delete a tag from a job (DELETE /api/v2/jobs/{username}~{jobname}/tag)

```ts
await openPAIClient.job.deleteTag(username, jobname, tag);
```

## job history

- [x] Check if job attempts is healthy (GET /api/v2/jobs/{username}~{jobname}/job-attempts/healthz)
Expand Down
28 changes: 28 additions & 0 deletions src/api/v2/clients/jobClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,32 @@ export class JobClient extends OpenPAIBaseClient {
);
return await this.httpClient.put(url, { value: type });
}

/**
* Add a tag.
* @param userName The user name.
* @param jobName The job name.
* @param tag The tag.
*/
public async addTag(userName: string, jobName: string, tag: string): Promise<any> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/tag`,
this.cluster.https
);
return await this.httpClient.put(url, { value: tag });
}

/**
* Delelte a tag.
* @param userName The user name.
* @param jobName The job name.
* @param tag The tag.
*/
public async deleteTag(userName: string, jobName: string, tag: string): Promise<any> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/tag`,
this.cluster.https
);
return await this.httpClient.delete(url, undefined, { data: { value: tag } });
}
}
120 changes: 118 additions & 2 deletions src/api/v2/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ info:
Open Platform for AI RESTful API docs.
Version 2.0.1: add more examples and fix typos
Version 2.0.2: update job detail and job attempt schema
Version 2.0.3: update parameters description of get storage list
Version 2.0.3: update parameters description of get storage list, update storage example and add get job config example
Version 2.0.4: add default field in get storage list
Version 2.0.5: add more parameters to job list; add submissionTime
Version 2.1.0: add add/delete tag api; add tags field in get job detail and get job list; add tags filter in get job list
license:
name: MIT License
url: "https://github.com/microsoft/pai/blob/master/LICENSE"
version: 2.0.5
version: 2.1.0
externalDocs:
description: Find out more about OpenPAI
url: "https://github.com/microsoft/pai"
Expand Down Expand Up @@ -1120,6 +1121,16 @@ paths:
description: filter jobs with keyword, we search keyword in user name, job name, and virtual cluster name
schema:
type: string
- name: tagsContain
in: query
description: filter jobs with tags. When multiple tags are specified, every job selected should have at least one of these tags
schema:
type: string
- name: tagsNotContain
in: query
description: filter jobs with tags. When multiple tags are specified, every job selected should have none of these tags
schema:
type: string
- name: offset
in: query
description: list job offset
Expand Down Expand Up @@ -1156,6 +1167,7 @@ paths:
state: SUCCEEDED
subState: Completed
executionType: STOP
tags: ['abnormal', 'low_gpu_utilization']
retries: 0
submissionTime: 0
createdTime: 0
Expand Down Expand Up @@ -1185,6 +1197,7 @@ paths:
$ref: "#/components/schemas/JobDetail"
example:
name: job name
tags: ['abnormal', 'low_gpu_utilization']
jobStatus:
username: user name
state: SUCCEEDED
Expand Down Expand Up @@ -1302,6 +1315,88 @@ paths:
$ref: "#/components/responses/NoJobError"
"500":
$ref: "#/components/responses/UnknownError"
"/api/v2/jobs/{user}~{job}/tag":
put:
tags:
- job
summary: Add a tag to a job.
description: Add a tag to a job.
operationId: addTag
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/user"
- $ref: "#/components/parameters/job"
requestBody:
content:
application/json:
schema:
type: object
properties:
value:
type: string
description: tag
required:
- value
required: true
responses:
"200":
description: Succeeded
content:
application/json:
schema:
$ref: "#/components/schemas/Response"
example:
message: "Add tag {tag} for job {job} successfully."
"404":
$ref: "#/components/responses/NoJobError"
"500":
$ref: "#/components/responses/UnknownError"
delete:
tags:
- job
summary: Delete a tag from a job.
description: Delete a tag from a job.
operationId: deleteTag
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/user"
- $ref: "#/components/parameters/job"
requestBody:
content:
application/json:
schema:
type: object
properties:
value:
type: string
description: tag
required:
- value
required: true
responses:
"200":
description: Succeeded
content:
application/json:
schema:
$ref: "#/components/schemas/Response"
example:
message: "Delete tag {tag} from job {job} successfully."
"404":
description: NoJobError or NoTagError
content:
application/json:
schema:
$ref: "#/components/schemas/Response"
examples:
NoJobError:
$ref: "#/components/responses/NoJobError/content/application~1json/examples/NoJobError"
NoTagError:
$ref: "#/components/responses/NoTagError/content/application~1json/examples/NoTagError"
"500":
$ref: "#/components/responses/UnknownError"
"/api/v2/jobs/{user}~{job}/job-attempts/healthz":
get:
tags:
Expand Down Expand Up @@ -1674,6 +1769,11 @@ components:
enum:
- START
- STOP
tags:
type: array
description: tags
items:
type: string
retries:
type: integer
description: job retried times
Expand Down Expand Up @@ -1738,6 +1838,11 @@ components:
name:
type: string
description: job name
tags:
type: array
description: tags
items:
type: string
jobStatus:
type: object
description: job status
Expand Down Expand Up @@ -2565,6 +2670,17 @@ components:
value:
code: NoJobError
message: "Job {job} is not found."
NoTagError:
description: NoTagError
content:
application/json:
schema:
$ref: "#/components/schemas/Response"
examples:
NoTagError:
value:
code: NoTagError
message: "Tag {tag} is not found for job {job} ."
NoJobConfigError:
description: NoJobConfigError
content:
Expand Down
52 changes: 52 additions & 0 deletions tests/common/apiTestCases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,44 @@ const deleteTestGroup: IApiOperation = {
}]
};

const addTestTag: IApiOperation = {
tag: 'job',
operationId: 'addTag',
parameters: [
{
type: 'raw',
value: clustersJson[0].username
},
{
type: 'raw',
value: 'sdk_test_job' + randomString.get()
},
{
type: 'raw',
value: 'testTag'
}
]
};

const deleteTestTag: IApiOperation = {
tag: 'job',
operationId: 'deleteTag',
parameters: [
{
type: 'raw',
value: clustersJson[0].username
},
{
type: 'raw',
value: 'sdk_test_job' + randomString.get()
},
{
type: 'raw',
value: 'testTag'
}
]
};

function createTestJob(): IApiOperation {
return {
tag: 'job',
Expand Down Expand Up @@ -898,6 +936,20 @@ export const ApiDefaultTestCases: {[key: string]: IApiTestCase} = {
],
after: [ updateTestJobExecutionType('STOP') ]
},
'put /api/v2/jobs/{user}~{job}/tag': {
before: [ createTestJob() ],
tests: [{
operation: addTestTag
}],
after: [ updateTestJobExecutionType('STOP'), deleteTestTag ]
},
'delete /api/v2/jobs/{user}~{job}/tag': {
before: [ createTestJob(), addTestTag ],
tests: [{
operation: deleteTestTag
}],
after: [ updateTestJobExecutionType('STOP') ]
},
'get /api/v2/jobs/{user}~{job}/job-attempts': {
before: [ createTestJob() ],
tests: [{
Expand Down
30 changes: 30 additions & 0 deletions tests/unit_tests/jobClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,33 @@ describe('Stop a job', () => {
expect(result).to.be.eql(response);
});
});

describe('Add a tag', () => {
const response: any = {
message: 'Add tag testTag for job tensorflow_serving_mnist_2019_6585ba19 successfully.'
};
const userName: string = 'core';
const jobName: string = 'tensorflow_serving_mnist_2019_6585ba19';
before(() => nock(`http://${testUri}`).put(`/api/v2/jobs/${userName}~${jobName}/tag`).reply(200, response));

it('should add a tag', async () => {
const jobClient: JobClient = new JobClient(cluster);
const result: any = await jobClient.addTag(userName, jobName, 'testTag');
expect(result).to.be.eql(response);
});
});

describe('Delete a tag', () => {
const response: any = {
message: 'Delete tag testTag from job tensorflow_serving_mnist_2019_6585ba19 successfully.'
};
const userName: string = 'core';
const jobName: string = 'tensorflow_serving_mnist_2019_6585ba19';
before(() => nock(`http://${testUri}`).delete(`/api/v2/jobs/${userName}~${jobName}/tag`).reply(200, response));

it('should delete a tag', async () => {
const jobClient: JobClient = new JobClient(cluster);
const result: any = await jobClient.deleteTag(userName, jobName, 'testTag');
expect(result).to.be.eql(response);
});
});

0 comments on commit 3789582

Please sign in to comment.