Created
June 15, 2018 16:11
-
-
Save dherges/d5f29bee6757221d44319eb6402aab78 to your computer and use it in GitHub Desktop.
reframe stuff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable, Inject } from "@angular/core"; | |
import { ParsedUrl } from './url/url.interfaces'; | |
import { AppResolverOptions, APP_RESOLVER_OPTIONS } from './app-launcher.interfaces'; | |
@Injectable() | |
export class AppResolver { | |
constructor( | |
@Inject(APP_RESOLVER_OPTIONS) private options: AppResolverOptions | |
) {} | |
public resolveIframeUrl(url: ParsedUrl) { | |
let value = this.options.prefix | |
.concat('u/', url.appName, '/'); | |
if (url.entryPoint) { | |
value = value.concat('#/external/', url.entryPoint); | |
} | |
if (url.params) { | |
// TODO: append query params also to url?!? | |
} | |
return value; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ParsedUrl } from './url.interfaces'; | |
const PREFIX = 'u://'; | |
type STATE = 'key' | 'value'; | |
const QUERY_PARAM_RE = /^[^=?&#]+/; | |
// Return the name of the query param at the start of the string or an empty string | |
function matchQueryParamName(str: string): string { | |
const match = str.match(QUERY_PARAM_RE); | |
return match ? match[0] : ''; | |
} | |
const QUERY_PARAM_VALUE_RE = /^[^?&#]+/; | |
// Return the value of the query param at the start of the string or an empty string | |
function matchQueryParamValue(str: string): string { | |
const match = str.match(QUERY_PARAM_VALUE_RE); | |
return match ? match[0] : ''; | |
} | |
// Query keys/values should have the "+" replaced first, as "+" in a query string is " ". | |
// decodeURIComponent function will not decode "+" as a space. | |
function decodeQuery(s: string): string { | |
return decodeURIComponent(s.replace(/\+/g, '%20')); | |
} | |
export class UrlParser { | |
private remaining: string; | |
constructor(url: string) { | |
this.remaining = url; | |
} | |
parse(): ParsedUrl { | |
const url = '' + this.remaining; | |
if (!this.consumeOptional(PREFIX)) { | |
throw new Error(`Url must start with ${PREFIX}, given ${this.remaining}`); | |
} | |
const appName = this.captureOptional('/'); | |
const entryPoint = this.captureOptional('?'); | |
const params = {}; | |
do { | |
const key = matchQueryParamName(this.remaining); | |
if (!key) { | |
break; | |
} | |
this.consumeOptional(key); | |
let value: any = ''; | |
if (this.consumeOptional('=')) { | |
value = matchQueryParamValue(this.remaining); | |
if (value) { | |
this.consumeOptional(value); | |
} else { | |
value = ''; | |
} | |
} | |
const decodedKey = decodeQuery(key); | |
const decodedVal = decodeQuery(value); | |
if (params.hasOwnProperty(decodedKey)) { | |
// Append to existing values | |
let currentVal = params[decodedKey]; | |
if (!Array.isArray(currentVal)) { | |
currentVal = [currentVal]; | |
params[decodedKey] = currentVal; | |
} | |
currentVal.push(decodedVal); | |
} else { | |
// Create a new value | |
params[decodedKey] = decodedVal; | |
} | |
} while (this.consumeOptional('&')); | |
return { | |
url, | |
appName, | |
entryPoint, | |
params | |
}; | |
} | |
private peekStartsWith(str: string): boolean { | |
return this.remaining.startsWith(str); | |
} | |
// Consumes the prefix when it is present and returns whether it has been consumed | |
private consumeOptional(str: string): boolean { | |
if (this.peekStartsWith(str)) { | |
this.remaining = this.remaining.substring(str.length); | |
return true; | |
} | |
return false; | |
} | |
private captureOptional(str: string): string { | |
const idx = this.remaining.indexOf(str); | |
let value: string; | |
if (idx >= 0) { | |
value = this.remaining.substr(0, idx); | |
this.remaining = this.remaining.substr(idx + 1); | |
} else { | |
value = this.remaining.substr(0); | |
this.remaining = ''; | |
} | |
return value; | |
} | |
} | |
export function deserializeUrl(url: string): ParsedUrl { | |
return new UrlParser(url).parse(); | |
} | |
export function serializeUrl(url: ParsedUrl) { | |
let value = `${PREFIX}/${url.appName}/${url.entryPoint}`; | |
if (url.params) { | |
const queryString = Object.keys(url.params).map(key => { | |
const value = url.params[key]; | |
return encodeURIComponent(key) + '=' + encodeURIComponent(value); | |
}).join('&'); | |
value = value + '?' + queryString | |
} | |
return value; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { UrlParser } from './url-parser'; | |
describe('UrlParser', () => { | |
it('parses u://appName', () => { | |
const url = 'u://appName'; | |
const result = new UrlParser(url).parse(); | |
expect(result.url).toBe(url); | |
expect(result.appName).toBe('appName'); | |
}); | |
it('parses u://appName/entryPoint', () => { | |
const url = 'u://appName/entryPoint'; | |
const result = new UrlParser(url).parse(); | |
expect(result.url).toBe(url); | |
expect(result.appName).toBe('appName'); | |
expect(result.entryPoint).toBe('entryPoint'); | |
}); | |
it('parses u://app/entry?a=b', () => { | |
const url = 'u://app/entry?a=b'; | |
const result = new UrlParser(url).parse(); | |
expect(result.params).toBeTruthy(); | |
expect(result.params.a).toBe('b'); | |
}); | |
it('parses u://app/entry?a=b&x=yz', () => { | |
const url = 'u://app/entry?a=b&x=yz'; | |
const result = new UrlParser(url).parse(); | |
expect(result.params).toBeTruthy(); | |
expect(result.params.a).toBe('b'); | |
expect(result.params.x).toBe('yz'); | |
}); | |
it('parses u://app/entry?a=b&a=x&a=y', () => { | |
const url = 'u://app/entry?a=b&a=x&a=y'; | |
const result = new UrlParser(url).parse(); | |
expect(result.params).toBeTruthy(); | |
expect(result.params.a.length).toEqual(3); | |
expect(result.params.a[0]).toEqual('b'); | |
expect(result.params.a[1]).toEqual('x'); | |
expect(result.params.a[2]).toEqual('y'); | |
}); | |
it('parses u://app/entry?a', () => { | |
const url = 'u://app/entry?a'; | |
const result = new UrlParser(url).parse(); | |
expect(result.params).toBeTruthy(); | |
expect(result.params.a).toEqual(''); | |
}); | |
it('parses u://app/entry?a&a', () => { | |
const url = 'u://app/entry?a&a'; | |
const result = new UrlParser(url).parse(); | |
expect(result.params.a.length).toEqual(2); | |
expect(result.params.a[0]).toEqual(''); | |
expect(result.params.a[1]).toEqual(''); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from "@angular/core"; | |
import { ParsedUrl } from './url.interfaces'; | |
import { deserializeUrl, serializeUrl } from './url-parser'; | |
@Injectable() | |
export class UrlSerializer { | |
public serialize(url: ParsedUrl): string { | |
return serializeUrl(url); | |
} | |
public deserialize(url: string): ParsedUrl { | |
return deserializeUrl(url); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface ParsedUrl { | |
url: string; | |
appName: string; | |
entryPoint: string; | |
params?: { | |
[key: string]: any | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment