-
Notifications
You must be signed in to change notification settings - Fork 275
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Next.js][Multi-site] Dynamic site resolver (#1271)
* [Next.js][Multi-site] Dynamic site resolver * add changes * Add unit tests, parse pattern * etc * simplify reg exp
- Loading branch information
1 parent
946a6c5
commit 9d42e01
Showing
3 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { expect } from 'chai'; | ||
import { HostInfo, SiteResolver } from './site-resolver'; | ||
|
||
describe.only('SiteResolver', () => { | ||
const hostInfo: HostInfo = { | ||
hostName: 'foo.com', | ||
}; | ||
|
||
const hostInfoWithLanguage: HostInfo = { | ||
...hostInfo, | ||
language: 'en', | ||
}; | ||
|
||
describe('resolve', () => { | ||
describe('fallback site name', () => { | ||
it('should return default when sites info is empty', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, []); | ||
|
||
expect(siteName).to.equal('website'); | ||
}); | ||
|
||
it('should return default when there is no appropriate site info', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, [ | ||
{ hostName: 'bar.com', language: '', name: 'bar' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
]); | ||
|
||
expect(siteName).to.equal('website'); | ||
}); | ||
|
||
it('should return custom when sites info is empty', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, [], 'sample'); | ||
|
||
expect(siteName).to.equal('sample'); | ||
}); | ||
|
||
it('should return custom when there is no appropriate site info', () => { | ||
const siteName = SiteResolver.resolve( | ||
hostInfo, | ||
[ | ||
{ hostName: 'bar.com', language: '', name: 'bar' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
], | ||
'sample' | ||
); | ||
|
||
expect(siteName).to.equal('sample'); | ||
}); | ||
}); | ||
|
||
it('should return site name when only hostname is provided', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, [ | ||
{ hostName: 'bar.net', language: '', name: 'bar' }, | ||
{ hostName: 'foo.com', language: '', name: 'foo' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
|
||
it('should return site name when hostname includes wildcard', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, [ | ||
{ hostName: 'var.com', language: 'da-DK', name: 'var' }, | ||
{ hostName: 'bar.net', language: 'en', name: 'bar' }, | ||
{ hostName: '*.com', language: '', name: 'foo' }, | ||
{ hostName: 'foo.com', language: 'en', name: 'foo-en' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
|
||
it('should return site name when wildcard is provided', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, [ | ||
{ hostName: 'bar.net', language: '', name: 'bar' }, | ||
{ hostName: '*', language: '', name: 'wildcard' }, | ||
{ hostName: 'foo.com', language: '', name: 'foo' }, | ||
]); | ||
|
||
expect(siteName).to.equal('wildcard'); | ||
}); | ||
|
||
it('should return site name when language is provided', () => { | ||
const siteName = SiteResolver.resolve(hostInfoWithLanguage, [ | ||
{ hostName: 'foo.com', language: 'ca', name: 'foo-ca' }, | ||
{ hostName: 'var.com', language: 'da-DK', name: 'var' }, | ||
{ hostName: 'bar.net', language: 'en', name: 'bar' }, | ||
{ hostName: 'foo.com', language: 'en', name: 'foo-en' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo-en'); | ||
}); | ||
|
||
it('should return site name when language is omit', () => { | ||
const siteName = SiteResolver.resolve(hostInfoWithLanguage, [ | ||
{ hostName: 'var.com', language: 'da-DK', name: 'var' }, | ||
{ hostName: 'bar.net', language: 'en', name: 'bar' }, | ||
{ hostName: 'foo.com', language: '', name: 'foo' }, | ||
{ hostName: 'foo.com', language: 'en', name: 'foo-en' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
|
||
describe('should return site name when multi-value hostnames are provided', () => { | ||
it('hostnames include wildcard characters', () => { | ||
expect( | ||
SiteResolver.resolve({ hostName: 'test.foo.bar.com' }, [ | ||
{ hostName: '*.bat.com|foo.bar.com', language: '', name: 'bar' }, | ||
{ hostName: 'test.com|*.foo.*.com|foo.com', language: '', name: 'foo' }, | ||
]) | ||
).to.equal('foo'); | ||
|
||
expect( | ||
SiteResolver.resolve({ hostName: 'xfoo.bar.com.en' }, [ | ||
{ hostName: 'foo.bar.com', language: '', name: 'bar' }, | ||
{ hostName: 'test.com|*foo.*.com*|foo.com', language: '', name: 'foo' }, | ||
]) | ||
).to.equal('foo'); | ||
}); | ||
|
||
it('hostname contains whitespaces', () => { | ||
const siteName = SiteResolver.resolve(hostInfo, [ | ||
{ hostName: 'bar.net', language: '', name: 'bar' }, | ||
{ hostName: 'test.com; foo.net | foo.com', language: '', name: 'foo' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
|
||
it('comma delimiter is used', () => { | ||
const siteName = SiteResolver.resolve(hostInfoWithLanguage, [ | ||
{ hostName: 'bar.net', language: '', name: 'bar' }, | ||
{ hostName: 'test.com,foo.net,foo.com', language: 'en', name: 'foo' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
|
||
it('semicolon delimiter is used', () => { | ||
const siteName = SiteResolver.resolve(hostInfoWithLanguage, [ | ||
{ hostName: 'bar.net', language: '', name: 'bar' }, | ||
{ hostName: 'test.com;foo.net;foo.com', language: 'en', name: 'foo' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
|
||
it('pipe delimiter is used', () => { | ||
const siteName = SiteResolver.resolve(hostInfoWithLanguage, [ | ||
{ hostName: 'bar.net', language: '', name: 'bar' }, | ||
{ hostName: 'test.com|foo.net|foo.com', language: 'en', name: 'foo' }, | ||
{ hostName: 'var.com', language: '', name: 'var' }, | ||
]); | ||
|
||
expect(siteName).to.equal('foo'); | ||
}); | ||
}); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { SiteInfo } from './graphql-siteinfo-service'; | ||
|
||
/** | ||
* Information about the current host | ||
*/ | ||
export type HostInfo = { | ||
hostName: string; | ||
language?: string; | ||
}; | ||
|
||
// Delimiters for multi-value hostnames | ||
const DELIMITERS = /\||,|;/g; | ||
|
||
/** | ||
* Determines site name based on the provided host information | ||
*/ | ||
export class SiteResolver { | ||
/** | ||
* Resolve siteName by host information | ||
* @param {HostInfo} hostInfo information about current host | ||
* @param {SiteInfo[]} sitesInfo list of available sites | ||
* @param {string} [fallbackSiteName] siteName to be returned in case siteName is not found | ||
* @returns {string} siteName resolved site name | ||
*/ | ||
static resolve = ( | ||
hostInfo: HostInfo, | ||
sitesInfo: SiteInfo[], | ||
fallbackSiteName = 'website' | ||
): string => { | ||
const siteInfo = sitesInfo.find((info) => { | ||
const hostnames = info.hostName.replace(/\s/g, '').split(DELIMITERS); | ||
|
||
const languageMatches = | ||
info.language === '' || !hostInfo.language || hostInfo.language === info.language; | ||
|
||
return hostnames.some( | ||
(hostname) => | ||
languageMatches && | ||
(hostInfo.hostName === hostname || | ||
SiteResolver.matchesPattern(hostInfo.hostName, hostname)) | ||
); | ||
}); | ||
|
||
return siteInfo?.name || fallbackSiteName; | ||
}; | ||
|
||
private static matchesPattern(hostname: string, pattern: string): boolean { | ||
// dots should be treated as chars | ||
// stars should be treated as wildcards | ||
const regExpPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'); | ||
|
||
const regExp = new RegExp(`^${regExpPattern}$`, 'g'); | ||
|
||
return !!hostname.match(regExp); | ||
} | ||
} |