mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-22 14:54:22 +00:00
✨ Implement a new path parser
This commit is contained in:
parent
4b6a0fd5db
commit
a8e78c29d7
3 changed files with 140 additions and 58 deletions
|
@ -5,17 +5,17 @@ import { InstanceEncoder } from "./InstanceEncoder"
|
||||||
|
|
||||||
test("encodes pathless URL", () => {
|
test("encodes pathless URL", () => {
|
||||||
expect(
|
expect(
|
||||||
InstanceEncoder.encode(new URL("https://api.sophon.steffo.eu")),
|
InstanceEncoder.encode(new URL("https://api.sophon.steffo.eu/")),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
"https:api.sophon.steffo.eu",
|
"https:api.sophon.steffo.eu:",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("encodes URL with port number", () => {
|
test("encodes URL with port number", () => {
|
||||||
expect(
|
expect(
|
||||||
InstanceEncoder.encode(new URL("http://localhost:30033")),
|
InstanceEncoder.encode(new URL("http://localhost:30033/")),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
"http:localhost%3A30033",
|
"http:localhost%3A30033:",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -43,23 +43,23 @@ test("does not encode URL with %3A in path", () => {
|
||||||
|
|
||||||
test("decodes pathless URL", () => {
|
test("decodes pathless URL", () => {
|
||||||
expect(
|
expect(
|
||||||
InstanceEncoder.decode("https:api.sophon.steffo.eu"),
|
InstanceEncoder.decode("https:api.sophon.steffo.eu:").toString(),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
"https://api.sophon.steffo.eu",
|
"https://api.sophon.steffo.eu/",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("decodes URL with port number", () => {
|
test("decodes URL with port number", () => {
|
||||||
expect(
|
expect(
|
||||||
InstanceEncoder.decode("http:localhost%3A30033"),
|
InstanceEncoder.decode("http:localhost%3A30033:").toString(),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
"http://localhost:30033",
|
"http://localhost:30033/",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("decodes URL with simple path", () => {
|
test("decodes URL with simple path", () => {
|
||||||
expect(
|
expect(
|
||||||
InstanceEncoder.decode("https:steffo.eu:sophon:api:"),
|
InstanceEncoder.decode("https:steffo.eu:sophon:api:").toString(),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
"https://steffo.eu/sophon/api/",
|
"https://steffo.eu/sophon/api/",
|
||||||
)
|
)
|
||||||
|
@ -67,7 +67,7 @@ test("decodes URL with simple path", () => {
|
||||||
|
|
||||||
test("decodes URL with colon in path", () => {
|
test("decodes URL with colon in path", () => {
|
||||||
expect(
|
expect(
|
||||||
InstanceEncoder.decode("https:steffo.eu:sophon%3Aapi:"),
|
InstanceEncoder.decode("https:steffo.eu:sophon%3Aapi:").toString(),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
"https://steffo.eu/sophon:api/",
|
"https://steffo.eu/sophon:api/",
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,74 +5,79 @@ test("parses empty path", () => {
|
||||||
expect(
|
expect(
|
||||||
parsePath("/"),
|
parsePath("/"),
|
||||||
).toMatchObject(
|
).toMatchObject(
|
||||||
{},
|
{
|
||||||
|
count: 0,
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("parses instance path", () => {
|
test("parses instance path", () => {
|
||||||
expect(
|
expect(
|
||||||
parsePath("/i/https:api:sophon:steffo:eu:"),
|
parsePath("/i/https:api:sophon:steffo:eu:/"),
|
||||||
).toMatchObject(
|
).toMatchObject(
|
||||||
{
|
{
|
||||||
instance: "https:api:sophon:steffo:eu:",
|
instance: "https:api:sophon:steffo:eu:",
|
||||||
|
count: 1,
|
||||||
|
valid: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("parses username path", () => {
|
test("parses logged in path", () => {
|
||||||
expect(
|
expect(
|
||||||
parsePath("/i/https:api:sophon:steffo:eu:/u/steffo"),
|
parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/"),
|
||||||
).toMatchObject(
|
).toMatchObject(
|
||||||
{
|
{
|
||||||
instance: "https:api:sophon:steffo:eu:",
|
instance: "https:api:sophon:steffo:eu:",
|
||||||
userName: "steffo",
|
loggedIn: "logged-in",
|
||||||
},
|
count: 2,
|
||||||
)
|
valid: true,
|
||||||
})
|
|
||||||
|
|
||||||
test("parses userid path", () => {
|
|
||||||
expect(
|
|
||||||
parsePath("/i/https:api:sophon:steffo:eu:/u/1"),
|
|
||||||
).toMatchObject(
|
|
||||||
{
|
|
||||||
instance: "https:api:sophon:steffo:eu:",
|
|
||||||
userId: "1",
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("parses research group path", () => {
|
test("parses research group path", () => {
|
||||||
expect(
|
expect(
|
||||||
parsePath("/i/https:api:sophon:steffo:eu:/g/testers"),
|
parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/"),
|
||||||
).toMatchObject(
|
).toMatchObject(
|
||||||
{
|
{
|
||||||
instance: "https:api:sophon:steffo:eu:",
|
instance: "https:api:sophon:steffo:eu:",
|
||||||
|
loggedIn: "logged-in",
|
||||||
researchGroup: "testers",
|
researchGroup: "testers",
|
||||||
|
count: 3,
|
||||||
|
valid: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("parses research project path", () => {
|
test("parses research project path", () => {
|
||||||
expect(
|
expect(
|
||||||
parsePath("/i/https:api:sophon:steffo:eu:/g/testers/p/test"),
|
parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/p/test/"),
|
||||||
).toMatchObject(
|
).toMatchObject(
|
||||||
{
|
{
|
||||||
instance: "https:api:sophon:steffo:eu:",
|
instance: "https:api:sophon:steffo:eu:",
|
||||||
|
loggedIn: "logged-in",
|
||||||
researchGroup: "testers",
|
researchGroup: "testers",
|
||||||
researchProject: "test",
|
researchProject: "test",
|
||||||
|
count: 4,
|
||||||
|
valid: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("parses research project path", () => {
|
test("parses notebook path", () => {
|
||||||
expect(
|
expect(
|
||||||
parsePath("/i/https:api:sophon:steffo:eu:/g/testers/p/test/n/testerino"),
|
parsePath("/i/https:api:sophon:steffo:eu:/l/logged-in/g/testers/p/test/n/testerino/"),
|
||||||
).toMatchObject(
|
).toMatchObject(
|
||||||
{
|
{
|
||||||
instance: "https:api:sophon:steffo:eu:",
|
instance: "https:api:sophon:steffo:eu:",
|
||||||
|
loggedIn: "logged-in",
|
||||||
researchGroup: "testers",
|
researchGroup: "testers",
|
||||||
researchProject: "test",
|
researchProject: "test",
|
||||||
notebook: "testerino",
|
notebook: "testerino",
|
||||||
|
count: 5,
|
||||||
|
valid: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,44 +25,121 @@ export interface ParsedPath {
|
||||||
/**
|
/**
|
||||||
* Passed the login page (either by browsing as guest or by logging in).
|
* Passed the login page (either by browsing as guest or by logging in).
|
||||||
*/
|
*/
|
||||||
loggedIn?: boolean,
|
loggedIn?: string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of pages that separate this to the website root.
|
* The number of pages that separate this to the website root.
|
||||||
*/
|
*/
|
||||||
count: number,
|
count: number,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the path is valid or not.
|
||||||
|
*/
|
||||||
|
valid: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const INSTANCE_REGEX = /^[/]i[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
|
||||||
|
const AUTHORIZATION_REGEX = /^[/]l[/](?<value>logged-in)(?<rest>[/].*?)?$/
|
||||||
|
const GROUP_REGEX = /^[/]g[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
|
||||||
|
const PROJECT_REGEX = /^[/]p[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
|
||||||
|
const NOTEBOOK_REGEX = /^[/]n[/](?<value>[^\\\n]+?)(?<rest>[/].*?)?$/
|
||||||
|
|
||||||
|
|
||||||
|
interface ParsePathSegmentConfig {
|
||||||
|
path: string,
|
||||||
|
parsed: ParsedPath,
|
||||||
|
|
||||||
|
regex: RegExp,
|
||||||
|
key: keyof ParsedPath,
|
||||||
|
|
||||||
|
next: ((path: string, parsed: ParsedPath) => ParsedPath)[],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function parsePathSegment({path, parsed, regex, key, next}: ParsePathSegmentConfig): ParsedPath {
|
||||||
|
// If the path is empty, return
|
||||||
|
if(!path) {
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try matching the regex
|
||||||
|
const match = path.match(regex)
|
||||||
|
|
||||||
|
// If the match fails, it means the path is invalid
|
||||||
|
if(!match || !match.groups) {
|
||||||
|
parsed.valid = Boolean(path)
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack the groups
|
||||||
|
const {value, rest} = match.groups
|
||||||
|
parsed[key] = value as never // WHAT?
|
||||||
|
parsed.count += 1
|
||||||
|
|
||||||
|
const results = next.map((func) => {
|
||||||
|
return func(rest, parsed)
|
||||||
|
}).reduce((a, b) => {
|
||||||
|
return {...a, ...b}
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...parsed,
|
||||||
|
...results,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split the URL path into various components.
|
* Split the URL path into various components.
|
||||||
* @param path - The path to split.
|
* @param path - The path to split.
|
||||||
*/
|
*/
|
||||||
export function parsePath(path: string): ParsedPath {
|
export function parsePath(path: string): ParsedPath {
|
||||||
let result: ParsedPath = {
|
return parsePathSegment({
|
||||||
count: 0,
|
path,
|
||||||
}
|
parsed: {count: 0, valid: true},
|
||||||
|
regex: INSTANCE_REGEX,
|
||||||
result.instance = path.match(/[/]i[/]([^/]+)/)?.[1]
|
key: "instance",
|
||||||
result.researchGroup = path.match(/[/]g[/]([A-Za-z0-9_-]+)/)?.[1]
|
next: [
|
||||||
result.researchProject = path.match(/[/]p[/]([A-Za-z0-9_-]+)/)?.[1]
|
(path, parsed) => {
|
||||||
result.notebook = path.match(/[/]n[/]([A-Za-z0-9_-]+)/)?.[1]
|
return parsePathSegment({
|
||||||
result.loggedIn = Boolean(path.match(/[/]l[/]logged-in/))
|
path,
|
||||||
|
parsed,
|
||||||
if(result.instance) {
|
regex: AUTHORIZATION_REGEX,
|
||||||
result.count += 1
|
key: "loggedIn",
|
||||||
}
|
next: [
|
||||||
if(result.researchGroup) {
|
(path, parsed) => {
|
||||||
result.count += 1
|
return parsePathSegment({
|
||||||
}
|
path,
|
||||||
if(result.researchProject) {
|
parsed,
|
||||||
result.count += 1
|
regex: GROUP_REGEX,
|
||||||
}
|
key: "researchGroup",
|
||||||
if(result.notebook) {
|
next: [
|
||||||
result.count += 1
|
(path, parsed) => {
|
||||||
}
|
return parsePathSegment({
|
||||||
if(result.loggedIn) {
|
path,
|
||||||
result.count += 1
|
parsed,
|
||||||
}
|
regex: PROJECT_REGEX,
|
||||||
|
key: "researchProject",
|
||||||
return result
|
next: [
|
||||||
|
(path, parsed) => {
|
||||||
|
return parsePathSegment({
|
||||||
|
path,
|
||||||
|
parsed,
|
||||||
|
regex: NOTEBOOK_REGEX,
|
||||||
|
key: "notebook",
|
||||||
|
next: [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue