Skip to content

Commit

Permalink
Media Type: Expose more-complete, unstringified parse results
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanresnick committed Oct 21, 2015
1 parent d904ca6 commit e2fe4ab
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 7 deletions.
8 changes: 4 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ Negotiator.prototype.languages = function languages(available) {
return preferredLanguages(this.request.headers['accept-language'], available);
};

Negotiator.prototype.mediaType = function mediaType(available) {
var set = this.mediaTypes(available);
Negotiator.prototype.mediaType = function mediaType(available, options) {
var set = this.mediaTypes(available, options);
return set && set[0];
};

Negotiator.prototype.mediaTypes = function mediaTypes(available) {
Negotiator.prototype.mediaTypes = function mediaTypes(available, options) {
var preferredMediaTypes = loadModule('mediaType').preferredMediaTypes;
return preferredMediaTypes(this.request.headers.accept, available);
return preferredMediaTypes(this.request.headers.accept, available, options);
};

// Backwards compatibility
Expand Down
22 changes: 19 additions & 3 deletions lib/mediaType.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,26 +159,29 @@ function specify(type, spec, index) {
* @public
*/

function preferredMediaTypes(accept, provided) {
function preferredMediaTypes(accept, provided, options) {
// RFC 2616 sec 14.2: no header = */*
var accepts = parseAccept(accept === undefined ? '*/*' : accept || '');
var detailed = options && options.detailed;

if (!provided) {
// sorted list of all types
return accepts
.filter(isQuality)
.sort(compareSpecs)
.map(getFullType);
.map(detailed ? getDetailedSpec : getFullType);
}

var priorities = provided.map(function getPriority(type, index) {
return getMediaTypePriority(type, accepts, index);
});

// sorted list of accepted types
return priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {
var acceptables = priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {
return provided[priorities.indexOf(priority)];
});

return detailed ? acceptables.map(parseMediaType).map(getDetailedSpec) : acceptables;
}

/**
Expand All @@ -199,6 +202,19 @@ function getFullType(spec) {
return spec.type + '/' + spec.subtype;
}

/**
* Get detailed description object for this spec.
* @private
*/

function getDetailedSpec(spec) {
return {
type: spec.type + '/' + spec.subtype,
parameters: spec.params,
q: spec.q
};
};

/**
* Check if a spec has any quality.
* @private
Expand Down
93 changes: 93 additions & 0 deletions test/mediaType.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ describe('negotiator.mediaType()', function () {
})
})

describe('negotiator.mediaType(undefined, {detailed: true})', function () {
whenAccept('text/*', function () {
it('should return a detailed spec object with text/*', function () {
assert.deepEqual(
this.negotiator.mediaType(undefined, {detailed: true}),
{"type": "text/*", parameters: {}, "q": 1}
)
})
})

whenAccept('text/plain;charset=utf-8;q=0.8, application/json;q=0.5, text/html;q=0.7, */*;q=0.1', function () {
it('should return a detailed object for text/plain', function () {
assert.deepEqual(
this.negotiator.mediaType(undefined, {detailed: true}),
{"type": "text/plain", parameters: {"charset": "utf-8"}, "q": .8}
)
})
})
})

describe('negotiator.mediaType(array)', function () {
whenAccept(undefined, function () {
it('should return first item in list', function () {
Expand Down Expand Up @@ -126,6 +146,31 @@ describe('negotiator.mediaType(array)', function () {
})
})

describe('negotiator.mediaType(array, {detailed: true})', function() {
whenAccept('text/*', function () {
it('should return a detailed spec object with text/html', function () {
assert.deepEqual(
this.negotiator.mediaType(["text/html"], {detailed: true}),
{"type": "text/html", parameters: {}, "q": 1}
)
})
})

whenAccept('text/html;LEVEL=1, application/json;q=0.5', function () {
it('should return parameters, but require an exact match', function () {
assert.deepEqual(
this.negotiator.mediaType(["text/html"], {detailed: true}),
undefined
)

assert.deepEqual(
this.negotiator.mediaType(["text/html; level=1"], {detailed: true}),
{"type": "text/html", "parameters": {"level": "1"}, "q": 1}
)
})
})
})

describe('negotiator.mediaTypes()', function () {
whenAccept(undefined, function () {
it('should return */*', function () {
Expand Down Expand Up @@ -194,6 +239,29 @@ describe('negotiator.mediaTypes()', function () {
})
})

describe('negotiator.mediaTypes(undefined, {detailed: true})', function() {
whenAccept('text/*', function () {
it('should return a detailed spec object with text/*', function () {
assert.deepEqual(
this.negotiator.mediaTypes(undefined, {detailed: true}),
[{"type": "text/*", parameters: {}, "q": 1}]
)
})
})

whenAccept('text/html;LEVEL=1, application/json;q=0.5', function () {
it('should return more-detailed spec objects', function () {
assert.deepEqual(
this.negotiator.mediaTypes(undefined, {detailed: true}),
[
{"type": "text/html", "parameters": {"level": "1"}, "q": 1},
{"type": "application/json", "parameters": {}, "q": 0.5}
]
)
})
})
})

describe('negotiator.mediaTypes(array)', function () {
whenAccept(undefined, function () {
it('should return return original list', mediaTypesNegotiated(
Expand Down Expand Up @@ -458,6 +526,31 @@ describe('negotiator.mediaTypes(array)', function () {
})
})

describe('negotiator.mediaTypes(array, {detailed: true})', function() {
whenAccept('text/*', function () {
it('should return a detailed spect object with text/html', function () {
assert.deepEqual(
this.negotiator.mediaTypes(["text/html"], {detailed: true}),
[{"type": "text/html", parameters: {}, "q": 1}]
)
})
})

whenAccept('text/html;LEVEL=1, application/json;q=0.5', function () {
it('should return parameters, but require an exact match', function () {
assert.deepEqual(
this.negotiator.mediaTypes(["text/html"], {detailed: true}),
[]
)

assert.deepEqual(
this.negotiator.mediaTypes(["text/html; level=1"], {detailed: true}),
[{"type": "text/html", "parameters": {"level": "1"}, "q": 1}]
)
})
})
})

function createRequest(headers) {
var request = {
headers: {}
Expand Down

0 comments on commit e2fe4ab

Please sign in to comment.