diff --git a/package.json b/package.json index bf50e5686..96fd2bb93 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "screwdriver-artifact-bookend": "^1.0.1", "screwdriver-build-bookend": "^2.0.1", "screwdriver-config-parser": "^3.3.0", - "screwdriver-data-schema": "^16.12.1", + "screwdriver-data-schema": "^16.14.1", "screwdriver-datastore-sequelize": "^2.0.0", "screwdriver-executor-docker": "^2.2.2", "screwdriver-executor-k8s": "^10.3.3", diff --git a/plugins/templates/README.md b/plugins/templates/README.md index f24fc9f42..5424b4ed7 100644 --- a/plugins/templates/README.md +++ b/plugins/templates/README.md @@ -34,7 +34,17 @@ server.register({ #### Get single template -`GET /templates/{id}` +You can get a single template by providing the template name and the specific version or the tag. + +`GET /templates/{name}/{tag}` or `GET /templates/{name}/{version}` + +**Arguments** + +'name', 'tag' or 'version' + +* `name` - Name of the template +* `tag` - Tag of the template (e.g. `stable`, `latest`, etc) +* `version` - Version of the template #### Create a template Create a template will store the template data (`config`, `name`, `version`, `description`, `maintainer`, `labels`) into the datastore. diff --git a/plugins/templates/get.js b/plugins/templates/get.js index 97dda2b38..7b09f7e3e 100644 --- a/plugins/templates/get.js +++ b/plugins/templates/get.js @@ -5,27 +5,59 @@ const joi = require('joi'); const schema = require('screwdriver-data-schema'); const getSchema = schema.models.template.get; const baseSchema = schema.models.template.base; +const versionRegex = schema.config.regex.VERSION; +const versionSchema = joi.reach(baseSchema, 'version'); module.exports = () => ({ method: 'GET', - path: '/templates/{name}/{version}', + path: '/templates/{name}/{versionOrTag}', config: { - description: 'Get a single template', + description: 'Get a single template given template name and version or tag', notes: 'Returns a template record', tags: ['api', 'templates'], handler: (request, reply) => { - const factory = request.server.app.templateFactory; - - return factory.getTemplate({ - name: request.params.name, - version: request.params.version - }).then((template) => { - if (!template) { - throw boom.notFound('Template does not exist'); + const versionOrTag = request.params.versionOrTag; + const templateFactory = request.server.app.templateFactory; + + // check if version or tag + const isVersion = versionOrTag.match(versionRegex); + + return new Promise((resolve, reject) => { + // if tag, get template tag version + if (!isVersion) { + const templateTagFactory = request.server.app.templateTagFactory; + + return templateTagFactory.get({ + name: request.params.name, + tag: request.params.versionOrTag + }) + .then((templateTag) => { + if (!templateTag) { + return reject(boom.notFound(`Template ${request.params.name} ` + + `does not exist with tag ${request.params.versionOrTag}`)); + } + + return resolve(templateTag.version); + }); } - return reply(template); + // otherwise just return the version + return resolve(versionOrTag); }) + .then(version => + // get the template + templateFactory.getTemplate({ + name: request.params.name, + version + }).then((template) => { + if (!template) { + throw boom.notFound(`Template ${request.params.name} ` + + `does not exist with version ${version}`); + } + + return reply(template); + }) + ) .catch(err => reply(boom.wrap(err))); }, response: { @@ -34,7 +66,10 @@ module.exports = () => ({ validate: { params: { name: joi.reach(baseSchema, 'name'), - version: joi.reach(baseSchema, 'version') + versionOrTag: joi.alternatives().try( + versionSchema, + joi.reach(schema.models.templateTag.base, 'tag') + ) } } } diff --git a/test/plugins/templates.test.js b/test/plugins/templates.test.js index a15c262be..463814650 100644 --- a/test/plugins/templates.test.js +++ b/test/plugins/templates.test.js @@ -54,6 +54,7 @@ const getPipelineMocks = (pipelines) => { describe('template plugin test', () => { let templateFactoryMock; + let templateTagFactoryMock; let pipelineFactoryMock; let plugin; let server; @@ -71,6 +72,9 @@ describe('template plugin test', () => { list: sinon.stub(), getTemplate: sinon.stub() }; + templateTagFactoryMock = { + get: sinon.stub() + }; pipelineFactoryMock = { get: sinon.stub() }; @@ -81,6 +85,7 @@ describe('template plugin test', () => { server = new hapi.Server(); server.app = { templateFactory: templateFactoryMock, + templateTagFactory: templateTagFactoryMock, pipelineFactory: pipelineFactoryMock }; server.connection({ @@ -147,7 +152,7 @@ describe('template plugin test', () => { }); }); - describe('GET /templates/name/version', () => { + describe('GET /templates/name/versionOrTag', () => { let options; beforeEach(() => { @@ -157,7 +162,32 @@ describe('template plugin test', () => { }; }); - it('returns 200 and all templates', () => { + it('returns 200 and a template when given the template name and version', () => { + templateFactoryMock.getTemplate.resolves(testtemplate); + + return server.inject(options).then((reply) => { + assert.deepEqual(reply.result, testtemplate); + assert.calledWith(templateFactoryMock.getTemplate, { + name: 'screwdriver/build', + version: '1.7.3' + }); + assert.equal(reply.statusCode, 200); + }); + }); + + it('returns 200 and a template when given the template name and tag', () => { + const testTagTemplate = { + id: 1, + name: 'template_namespace/nodejs_main', + tag: 'stable', + version: '1.7.3' + }; + + options = { + method: 'GET', + url: '/templates/screwdriver%2Fbuild/stable' + }; + templateTagFactoryMock.get.resolves(testTagTemplate); templateFactoryMock.getTemplate.resolves(testtemplate); return server.inject(options).then((reply) => { @@ -178,6 +208,18 @@ describe('template plugin test', () => { }); }); + it('returns 404 when template tag does not exist', () => { + options = { + method: 'GET', + url: '/templates/screwdriver%2Fbuild/stable' + }; + templateTagFactoryMock.get.resolves(null); + + return server.inject(options).then((reply) => { + assert.equal(reply.statusCode, 404); + }); + }); + it('returns 500 when datastore fails', () => { templateFactoryMock.getTemplate.rejects(new Error('some error'));