feat: better media types detection for JSON, XML and HTML lenses (#1438)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com> Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
This commit is contained in:
@@ -20,11 +20,26 @@ describe("getSuitableLenses", () => {
|
|||||||
expect(undefinedResult).toContainEqual(rawLens)
|
expect(undefinedResult).toContainEqual(rawLens)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const contentTypes = {
|
||||||
|
JSON: ["application/json", "application/ld+json", "application/hal+json; charset=utf8"],
|
||||||
|
Image: [
|
||||||
|
"image/gif",
|
||||||
|
"image/jpeg; foo=bar",
|
||||||
|
"image/png",
|
||||||
|
"image/bmp",
|
||||||
|
"image/svg+xml",
|
||||||
|
"image/x-icon",
|
||||||
|
"image/vnd.microsoft.icon",
|
||||||
|
],
|
||||||
|
HTML: ["text/html", "application/xhtml+xml", "text/html; charset=utf-8"],
|
||||||
|
XML: ["text/xml", "application/xml", "application/xhtml+xml; charset=utf-8"],
|
||||||
|
}
|
||||||
|
|
||||||
lenses
|
lenses
|
||||||
.filter(({ lensName }) => lensName != rawLens.lensName)
|
.filter(({ lensName }) => lensName != rawLens.lensName)
|
||||||
.forEach((el) => {
|
.forEach((el) => {
|
||||||
test(`returns ${el.lensName} lens for its content-types`, () => {
|
test(`returns ${el.lensName} lens for its content-types`, () => {
|
||||||
el.supportedContentTypes.forEach((contentType) => {
|
contentTypes[el.lensName].forEach((contentType) => {
|
||||||
expect(
|
expect(
|
||||||
getSuitableLenses({
|
getSuitableLenses({
|
||||||
headers: {
|
headers: {
|
||||||
@@ -36,7 +51,7 @@ describe("getSuitableLenses", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test(`returns Raw Lens along with ${el.lensName} for the content types`, () => {
|
test(`returns Raw Lens along with ${el.lensName} for the content types`, () => {
|
||||||
el.supportedContentTypes.forEach((contentType) => {
|
contentTypes[el.lensName].forEach((contentType) => {
|
||||||
expect(
|
expect(
|
||||||
getSuitableLenses({
|
getSuitableLenses({
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const htmlLens = {
|
const htmlLens = {
|
||||||
lensName: "HTML",
|
lensName: "HTML",
|
||||||
supportedContentTypes: ["text/html"],
|
isSupportedContentType: (contentType) =>
|
||||||
|
/\btext\/html|application\/xhtml\+xml\b/i.test(contentType),
|
||||||
renderer: "htmlres",
|
renderer: "htmlres",
|
||||||
rendererImport: () => import("~/components/lenses/renderers/HTMLLensRenderer"),
|
rendererImport: () => import("~/components/lenses/renderers/HTMLLensRenderer"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
const imageLens = {
|
const imageLens = {
|
||||||
lensName: "Image",
|
lensName: "Image",
|
||||||
supportedContentTypes: [
|
isSupportedContentType: (contentType) =>
|
||||||
"image/gif",
|
/\bimage\/(?:gif|jpeg|png|bmp|svg\+xml|x-icon|vnd\.microsoft\.icon)\b/i.test(contentType),
|
||||||
"image/jpeg",
|
|
||||||
"image/png",
|
|
||||||
"image/bmp",
|
|
||||||
"image/svg+xml",
|
|
||||||
"image/x-icon",
|
|
||||||
"image/vnd.microsoft.icon",
|
|
||||||
],
|
|
||||||
renderer: "imageres",
|
renderer: "imageres",
|
||||||
rendererImport: () => import("~/components/lenses/renderers/ImageLensRenderer"),
|
rendererImport: () => import("~/components/lenses/renderers/ImageLensRenderer"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { isJSONContentType } from "../utils/contenttypes";
|
||||||
|
|
||||||
const jsonLens = {
|
const jsonLens = {
|
||||||
lensName: "JSON",
|
lensName: "JSON",
|
||||||
supportedContentTypes: ["application/json", "application/hal+json", "application/vnd.api+json"],
|
isSupportedContentType: isJSONContentType,
|
||||||
renderer: "json",
|
renderer: "json",
|
||||||
rendererImport: () => import("~/components/lenses/renderers/JSONLensRenderer"),
|
rendererImport: () => import("~/components/lenses/renderers/JSONLensRenderer"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,22 +7,13 @@ import xmlLens from "./xmlLens"
|
|||||||
export const lenses = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
export const lenses = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
||||||
|
|
||||||
export function getSuitableLenses(response) {
|
export function getSuitableLenses(response) {
|
||||||
|
if (!response || !response.headers || !response.headers["content-type"])
|
||||||
|
return [rawLens]
|
||||||
|
|
||||||
const result = []
|
const result = []
|
||||||
|
for (const lens of lenses) {
|
||||||
if (response && response.headers && response.headers["content-type"]) {
|
if (lens.isSupportedContentType(response.headers["content-type"]))
|
||||||
const properContentType = response.headers["content-type"].split(";")[0]
|
result.push(lens)
|
||||||
|
|
||||||
for (const lens of lenses) {
|
|
||||||
if (
|
|
||||||
lens.supportedContentTypes === null ||
|
|
||||||
lens.supportedContentTypes.includes(properContentType)
|
|
||||||
) {
|
|
||||||
result.push(lens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We don't know the content type, so lets just add rawLens
|
|
||||||
result.push(rawLens)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const rawLens = {
|
const rawLens = {
|
||||||
lensName: "Raw",
|
lensName: "Raw",
|
||||||
supportedContentTypes: null,
|
isSupportedContentType: () => true,
|
||||||
renderer: "raw",
|
renderer: "raw",
|
||||||
rendererImport: () => import("~/components/lenses/renderers/RawLensRenderer"),
|
rendererImport: () => import("~/components/lenses/renderers/RawLensRenderer"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const xmlLens = {
|
const xmlLens = {
|
||||||
lensName: "XML",
|
lensName: "XML",
|
||||||
supportedContentTypes: ["application/xml", "image/svg+xml", "text/xml", "application/rss+xml"],
|
isSupportedContentType: (contentType) => /\bxml\b/i.test(contentType),
|
||||||
renderer: "xmlres",
|
renderer: "xmlres",
|
||||||
rendererImport: () => import("~/components/lenses/renderers/XMLLensRenderer"),
|
rendererImport: () => import("~/components/lenses/renderers/XMLLensRenderer"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,24 +5,28 @@ describe("isJSONContentType", () => {
|
|||||||
expect(isJSONContentType("application/json")).toBe(true)
|
expect(isJSONContentType("application/json")).toBe(true)
|
||||||
expect(isJSONContentType("application/vnd.api+json")).toBe(true)
|
expect(isJSONContentType("application/vnd.api+json")).toBe(true)
|
||||||
expect(isJSONContentType("application/hal+json")).toBe(true)
|
expect(isJSONContentType("application/hal+json")).toBe(true)
|
||||||
|
expect(isJSONContentType("application/ld+json")).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns true for JSON types with charset specified", () => {
|
test("returns true for JSON types with charset specified", () => {
|
||||||
expect(isJSONContentType("application/json; charset=utf-8")).toBe(true)
|
expect(isJSONContentType("application/json; charset=utf-8")).toBe(true)
|
||||||
expect(isJSONContentType("application/vnd.api+json; charset=utf-8")).toBe(true)
|
expect(isJSONContentType("application/vnd.api+json; charset=utf-8")).toBe(true)
|
||||||
expect(isJSONContentType("application/hal+json; charset=utf-8")).toBe(true)
|
expect(isJSONContentType("application/hal+json; charset=utf-8")).toBe(true)
|
||||||
|
expect(isJSONContentType("application/ld+json; charset=utf-8")).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns false for non-JSON content types", () => {
|
test("returns false for non-JSON content types", () => {
|
||||||
expect(isJSONContentType("application/xml")).toBe(false)
|
expect(isJSONContentType("application/xml")).toBe(false)
|
||||||
expect(isJSONContentType("text/html")).toBe(false)
|
expect(isJSONContentType("text/html")).toBe(false)
|
||||||
expect(isJSONContentType("application/x-www-form-urlencoded")).toBe(false)
|
expect(isJSONContentType("application/x-www-form-urlencoded")).toBe(false)
|
||||||
|
expect(isJSONContentType("foo/jsoninword")).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns false for non-JSON content types with charset", () => {
|
test("returns false for non-JSON content types with charset", () => {
|
||||||
expect(isJSONContentType("application/xml; charset=utf-8")).toBe(false)
|
expect(isJSONContentType("application/xml; charset=utf-8")).toBe(false)
|
||||||
expect(isJSONContentType("text/html; charset=utf-8")).toBe(false)
|
expect(isJSONContentType("text/html; charset=utf-8")).toBe(false)
|
||||||
expect(isJSONContentType("application/x-www-form-urlencoded; charset=utf-8")).toBe(false)
|
expect(isJSONContentType("application/x-www-form-urlencoded; charset=utf-8")).toBe(false)
|
||||||
|
expect(isJSONContentType("foo/jsoninword; charset=utf-8")).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns false for null/undefined", () => {
|
test("returns false for null/undefined", () => {
|
||||||
|
|||||||
@@ -10,19 +10,5 @@ export const knownContentTypes = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export function isJSONContentType(contentType) {
|
export function isJSONContentType(contentType) {
|
||||||
if (contentType && contentType.includes(";")) {
|
return /\bjson\b/i.test(contentType);
|
||||||
const [justContentType] = contentType.split(";")
|
|
||||||
|
|
||||||
return (
|
|
||||||
justContentType === "application/json" ||
|
|
||||||
justContentType === "application/vnd.api+json" ||
|
|
||||||
justContentType === "application/hal+json"
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
contentType === "application/json" ||
|
|
||||||
contentType === "application/vnd.api+json" ||
|
|
||||||
contentType === "application/hal+json"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user