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 Jun 11, 2015
1 parent bd3e873 commit ee8b3c3
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 9 deletions.
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ Negotiator.prototype.mediaType = function mediaType(available) {
return set && set[0];
};

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

// Backwards compatibility
Expand Down
34 changes: 27 additions & 7 deletions lib/mediaType.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,25 +120,45 @@ function specify(type, spec, index) {

}

function preferredMediaTypes(accept, provided) {
/**
* Returns a list of the acceptable media ranges in preferred order.
*
* @param {string} accept The raw contents of the `Accept` header
* @param {string[]} provided The media ranges the server can produce
* for the resource in question.
* @param {Object} options Configuration options
* @param {boolean} options.detailed If true, this function returns a
* {type, parameters, q} object for each acceptable type, rather than a string.
*/
function preferredMediaTypes(accept, provided, options) {
// RFC 2616 sec 14.2: no header = */*
var accepts = parseAccept(accept === undefined ? '*/*' : accept || '');
var detailed = options && options.detailed;

var specDetails = function(spec) {
return {
type: "" + spec.type + '/' + spec.subtype,
parameters: spec.params,
q: spec.q
};
};

// return sorted list of all types if none are provided.
if (!provided) {
// sorted list of all types
return accepts.filter(isQuality).sort(compareSpecs).map(function getType(spec) {
return spec.full;
});
var mapper = detailed ? specDetails : function(spec) { return spec.full; };
return accepts.filter(isQuality).sort(compareSpecs).map(mapper);
}

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) {
// sorted list of acceptable types
var acceptables = priorities.filter(isQuality).sort(compareSpecs).map(function getType(priority) {
return provided[priorities.indexOf(priority)];
});

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

function compareSpecs(a, b) {
Expand Down
26 changes: 26 additions & 0 deletions test/mediaType.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,32 @@ describe('negotiator.mediaTypes()', function () {
})
})

describe('negotiator.mediaTypes(_, {detailed: true})', function() {
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},
]
);
});

it('should accept a provided list, but still match parameters exactly', 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}]
);
});
});
});

describe('negotiator.mediaTypes(array)', function () {
whenAccept(undefined, function () {
it('should return return original list', mediaTypesNegotiated(
Expand Down

0 comments on commit ee8b3c3

Please sign in to comment.