-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multitenancy support #141
Merged
Merged
Multitenancy support #141
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
c98707e
Add the notion of tenant provider to the client
dmitrykuzmin f43fab9
Use the tenant provider in the `ActorRequestFactory`
dmitrykuzmin b8e74d4
Add tests for the multitenancy-related classes
dmitrykuzmin f5eba2d
Make integration tests use multitenant context and client
dmitrykuzmin 2c89a9f
Add basic single-tenant tests to be executed along with multitenant ones
dmitrykuzmin 6c132af
Fix the tests
dmitrykuzmin 3cd023b
Increase the timeout on single-tenant client tests
dmitrykuzmin 92250b7
Make the `TenantProvider` checks stricter
dmitrykuzmin 4a02628
Further increase the timeout on single-tenant client tests
dmitrykuzmin eb2717c
Simplify the single-tenant query test
dmitrykuzmin 33f0d0e
Fix a typo
dmitrykuzmin 562b335
Make the timeout in a query test smaller
dmitrykuzmin eca3dd4
Get rid of the semicolons in doc
dmitrykuzmin d6d7824
Fix grammar
dmitrykuzmin 058e85a
Make test model entities package-private and remove unnecessary c-tors
dmitrykuzmin 24921eb
Fix a typo
dmitrykuzmin c1491e5
Set the Spine version to `1.5.4`
dmitrykuzmin f702289
Remove a redundant empty line
dmitrykuzmin 96390cf
Make all `test-app` repos `package-private` and `final`
dmitrykuzmin 6159a41
Keep the version in sync in `package.json` files and the license report
dmitrykuzmin 8975799
Add more shortcuts for the `TenantId` creation
dmitrykuzmin c12a2f2
Use the default repositories in tests
dmitrykuzmin 143f1dd
Update the `config`
dmitrykuzmin c074f47
Update the license report
dmitrykuzmin e253663
Fix the single-tenant query test
dmitrykuzmin d4eb0d1
Make the timeout smaller
dmitrykuzmin 19a8aa2
Fix a typo
dmitrykuzmin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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,144 @@ | ||
/* | ||
* Copyright 2020, TeamDev. All rights reserved. | ||
* | ||
* Redistribution and use in source and/or binary forms, with or without | ||
* modification, must retain the above copyright notice and the following | ||
* disclaimer. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
|
||
import {TenantId} from "../proto/spine/core/tenant_id_pb"; | ||
import {EmailAddress} from "../proto/spine/net/email_address_pb"; | ||
import {InternetDomain} from "../proto/spine/net/internet_domain_pb"; | ||
import {isProtobufMessage, Type} from "./typed-message"; | ||
|
||
/** | ||
* A factory of `TenantId` instances. | ||
* | ||
* Exposes methods that are "shortcuts" for the convenient creation of `TenantId` message | ||
* instances. | ||
*/ | ||
export class TenantIds { | ||
|
||
/** | ||
* Constructs a `TenantId` which represents an internet domain. | ||
* | ||
* @param {!string} domainName the domain name as a plain string | ||
* @return {TenantId} a new `TenantId` instance | ||
*/ | ||
static internetDomain(domainName) { | ||
if (!domainName) { | ||
throw new Error('Expected a non-empty internet domain name.'); | ||
} | ||
const domain = new InternetDomain(); | ||
domain.setValue(domainName); | ||
const result = new TenantId(); | ||
result.setDomain(domain); | ||
return result; | ||
} | ||
|
||
/** | ||
* Constructs a `TenantId` which represents an email address. | ||
* | ||
* @param {!string} address the email address as a plain string | ||
* @return {TenantId} a new `TenantId` instance | ||
*/ | ||
static emailAddress(address) { | ||
if (!address) { | ||
throw new Error('Expected a non-empty email address value.'); | ||
} | ||
const emailAddress = new EmailAddress(); | ||
emailAddress.setValue(address); | ||
const result = new TenantId(); | ||
result.setEmail(emailAddress); | ||
return result; | ||
} | ||
|
||
/** | ||
* Constructs a `TenantId` which is a plain string value. | ||
* | ||
* @param {!string} tenantIdValue the tenant ID | ||
* @return {TenantId} a new `TenantId` instance | ||
*/ | ||
static plainString(tenantIdValue) { | ||
if (!tenantIdValue) { | ||
throw new Error('Expected a valid tenant ID value.'); | ||
} | ||
const result = new TenantId(); | ||
result.setValue(tenantIdValue); | ||
return result; | ||
} | ||
} | ||
|
||
/** | ||
* The current tenant provider. | ||
* | ||
* This object is passed to the `ActorRequestFactory` and is used during creation of all | ||
* client-side requests. | ||
* | ||
* The current tenant ID can be switched dynamically with the help of the `update` method. | ||
* | ||
* If it is necessary to update the current ID to a "no tenant" value (to work in a single-tenant | ||
* environment), pass the default tenant ID to the `update` method as follows: | ||
* `tenantProvider.update(new TenantId())`. | ||
*/ | ||
export class TenantProvider { | ||
|
||
/** | ||
* Creates a new `TenantProvider` configured with the passed tenant ID. | ||
* | ||
* The argument may be omitted but until the `_tenantId` is assigned some non-default value, the | ||
* application is considered single-tenant. | ||
* | ||
* @param {?TenantId} tenantId the ID of the currently active tenant | ||
*/ | ||
constructor(tenantId) { | ||
if (tenantId) { | ||
TenantProvider._checkIsValidTenantId(tenantId); | ||
this._tenantId = tenantId; | ||
} | ||
} | ||
|
||
/** | ||
* @param {!TenantId} tenantId the ID of the currently active tenant | ||
*/ | ||
update(tenantId) { | ||
TenantProvider._checkIsValidTenantId(tenantId); | ||
this._tenantId = tenantId; | ||
} | ||
|
||
/** | ||
* Returns the currently active tenant ID. | ||
* | ||
* @return {TenantId} | ||
*/ | ||
tenantId() { | ||
return this._tenantId; | ||
} | ||
|
||
/** | ||
* Checks that the passed object represents a `TenantId` message. | ||
* | ||
* @param {!object} tenantId the object to check | ||
* @private | ||
*/ | ||
static _checkIsValidTenantId(tenantId) { | ||
if (!tenantId | ||
|| !isProtobufMessage(tenantId) | ||
|| Type.forMessage(tenantId).url().value() !== 'type.spine.io/spine.core.TenantId') { | ||
throw new Error(`Expected a valid instance of the 'TenantId' message.` | ||
+ `The '${tenantId}' was passed instead.`); | ||
} | ||
} | ||
} |
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
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,82 @@ | ||
/* | ||
* Copyright 2020, TeamDev. All rights reserved. | ||
* | ||
* Redistribution and use in source and/or binary forms, with or without | ||
* modification, must retain the above copyright notice and the following | ||
* disclaimer. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
|
||
import assert from 'assert'; | ||
import {TenantIds} from "../../main/client/tenant"; | ||
|
||
describe('TenantIds', function () { | ||
|
||
it('create a tenant ID which represents an internet domain', done => { | ||
const internetDomain = "en.wikipedia.org"; | ||
const tenantId = TenantIds.internetDomain(internetDomain); | ||
assert.equal(tenantId.getDomain(), internetDomain); | ||
done(); | ||
}); | ||
|
||
it('throws an `Error` when the passed internet domain name is not defined', () => { | ||
assert.throws( | ||
() => TenantIds.internetDomain(undefined) | ||
); | ||
}); | ||
|
||
it('throws an `Error` when the passed internet domain name is empty', () => { | ||
assert.throws( | ||
() => TenantIds.internetDomain('') | ||
); | ||
}); | ||
|
||
it('create a tenant ID which represents an email address', done => { | ||
const emailAddress = "user@test.com"; | ||
const tenantId = TenantIds.emailAddress(emailAddress); | ||
assert.equal(tenantId.getEmail(), emailAddress); | ||
done(); | ||
}); | ||
|
||
it('throws an `Error` when the passed email address value is not defined', () => { | ||
assert.throws( | ||
() => TenantIds.emailAddress(undefined) | ||
); | ||
}); | ||
|
||
it('throws an `Error` when the passed email address value is empty', () => { | ||
assert.throws( | ||
() => TenantIds.emailAddress('') | ||
); | ||
}); | ||
|
||
it('create a plain string tenant ID', done => { | ||
const tenantIdValue = "some-tenant-ID"; | ||
const tenantId = TenantIds.plainString(tenantIdValue); | ||
assert.equal(tenantId.getValue(), tenantIdValue); | ||
done(); | ||
}); | ||
|
||
it('throws an `Error` when the passed string is not defined', () => { | ||
assert.throws( | ||
() => TenantIds.plainString(undefined) | ||
); | ||
}); | ||
|
||
it('throws an `Error` when the passed string is empty', () => { | ||
assert.throws( | ||
() => TenantIds.plainString('') | ||
); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have convenience methods for other types of
TennantId
s as well.