Spike: MODKBEKBJ-390 - describe design for "Single tenant multiple EBSCO KBs" scenario
Participants:
Role | Name | Approval |
---|---|---|
Solution Architect | ||
Java Lead | ||
Product Owner |
Spike points:
Storing EBSCO KB Credentials
Existing Solution:
Currently, mod-configuration
stores EBSCO KB Credentials as 3 separate config files, such as follows
KB Credentials API Key { "id": "b5dbb466-f941-4ef0-9a8f-ff6003dbb5eb", "code": "kb.ebsco.apiKey", "value": "XXXX", "module": "EKB", "enabled": true, "metadata": { "createdDate": "2020-03-17T15:22:04.098", "updatedDate": "2020-03-17T15:22:04.098+0000", "createdByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b", "updatedByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b" }, "configName": "api_access", "description": "EBSCO RM-API API Key" } | KB Credentials Url { "id": "9ef1c0c9-b774-4a1d-9f3f-1399e9a0292e", "code": "kb.ebsco.url", "value": "https://test.url", "module": "EKB", "enabled": true, "metadata": { "createdDate": "2020-03-17T15:22:04.052", "updatedDate": "2020-03-17T15:22:04.052+0000", "createdByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b", "updatedByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b" }, "configName": "api_access", "description": "EBSCO RM-API URL" } | KB Credentials Customer Id { "id": "ba80d8c1-a6c5-4df0-9b32-b35960c3a68b", "code": "kb.ebsco.customerId", "value": "test", "module": "EKB", "enabled": true, "metadata": { "createdDate": "2020-03-17T15:22:04.154", "updatedDate": "2020-03-17T15:22:04.154+0000", "createdByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b", "updatedByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b" }, "configName": "api_access", "description": "EBSCO RM-API Customer ID" } |
Proposed Solution:
- move configuration from
mod-configuration
tomod-kb-ebsco-java
- store configuration information(
apiKey, url, customerId
) as one entity in db. DB schema, json schema and sample payload file can be found below.
Method | Path | Permission(s) | Response | Note(s) |
---|---|---|---|---|
GET | /eholdings/kb-credentials | kb-ebsco.credentials.collection.get | 200 OK(with collection) | |
POST | /eholdings/kb-credentials | kb-ebsco.credentials.collection.post | 201 Created(with record) | |
GET | /eholdings/kb-credentials/{credId} | kb-ebsco.credentials.item.get | 200 OK(with record) | {credId} - UUID of the credential |
PUT | /eholdings/kb-credentials/{credId} | kb-ebsco.credentials.item.put | 204 No Content | {credId} - UUID of the credential |
DELETE | /eholdings/kb-credentials/{credId} | kb-ebsco.credentials.item.delete | 204 No Content | {credId} - UUID of the credential |
The appropriate files attached below:
DB schema | |
Json Example | KB Credentials JSON example { "id": "2ffa1940-2cf6-48b1-8cc9-5e539c61d93f", "type": "kbCredentials", "attributes": { "name": "University of Massachusetts - Amherst", "apiKey": "XXXX", "url": "YYYY", "customerId": "ZZZZ" }, "metadata": { "createdDate": "2020-03-17T15:22:04.098", "updatedDate": "2020-03-17T15:23:04.098+0000", "createdByUserId": "1f8f660e-7dc9-4f6f-828f-96284c68a250", "updatedByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b", "createdByUsername": "john_doe", "updatedByUsername": "jane_doe" } }
|
Json schema |
Configuration cache
kb-ebsco-java sequence diagram
codex-ekb sequence diagram
UI mockups
Storing association between user and settings(EBSCO KB Credentials)
Proposed Solution:
Method | Path | Permission(s) | Response | Note(s) |
---|---|---|---|---|
GET | /eholdings/kb-credentials/{credId}/users | kb-ebsco.credentials.users.collection.get | 200 OK(with collection) | {credId} - UUID of the credential |
POST | /eholdings/kb-credentials/{credId}/users | kb-ebsco.credentials.users.collection.post | 201 Created(with record) | {credId} - UUID of the credential |
PUT | /eholdings/kb-credentials/{credId}/users/{userId} | kb-ebsco.credentials.users.item.put | 204 No Content | {credId} - UUID of the credential {userId) - UUID of a user |
DELETE | /eholdings/kb-credentials/{credId}/users/{userId} | kb-ebsco.credentials.users.item.delete | 204 No Content | {credId} - UUID of the credential {userId) - UUID of a user |
The appropriate files attached below:
DB schema | It means that we have
It means that:
| |||||||||||||||||
Json Example | ||||||||||||||||||
Json schema |
UI mockups
Update mapping between credentials and user
Option #1 - add PUT collection endpoint
- UI sends full collection of users assigned to a credential(see json example above) to PUT /eholdings/kb-credentials/{credId}/users
- back-end selects records tied to credential and removes them
- back-end inserts new records based on data received
Option #2 - use separate endpoints
- on each update (assignment user to a credential) UI will send request to PUT /eholdings/kb-credentials/{credId}/users/{userId}
- back-end adds record based on credential and user id
- on each removal (unassignment user from a credential) UI will send request to DELETE /eholdings/kb-credentials/{credId}/users/{userId}
- back-end removes record based on credential and user id
Summary
Association between KB Credentials and access status types
To implement the association between KB Credentials and access status types several changes should be made:
DB schema
It means that we have
This approach will provide the ability to separate assignment-data between each KB credentials |
Endpoints
Method | Old endpoint | New endpoint | Request body | Response status | Response body |
---|---|---|---|---|---|
GET | /eholdings/access-types | /eholdings/kb-credentials/{credId}/access-types | 200 OK | ||
POST | /eholdings/access-types | /eholdings/kb-credentials/{credId}/access-types | 201 Created(with record) | ||
GET | /eholdings/access-types/{atId} | /eholdings/kb-credentials/{credId}/access-types/{atId} | 200 OK | ||
PUT | /eholdings/access-types/{atId} | /eholdings/kb-credentials/{credId}/access-types/{atId} | 204 No Content | ||
DELETE | /eholdings/access-types/{atId} | /eholdings/kb-credentials/{credId}/access-types/{atId} | 204 No Content |
JSON schemas that will be used (changes will be affected only for Access Types Collection Item Schema):
Access status type to record (package, resource) mapping and filtering implementation
Endpoints that will be affected:
Method | Endpoint | Action | Notes |
---|---|---|---|
GET | /eholdings/packages | filtering | Most interactions with DB must be updated. In most cases, we need to update only ' SELECT ... FROM access_types WHERE credentialsId = ? AND ...; In other cases SQL-queries need to be updated by joining SELECT ... FROM access_types_mapping atp JOIN access_types at ON at.id = atp.accessTypeId WHERE ... AND at.credentialsId = ?; |
POST | /eholdings/packages | mapping | |
PUT | /eholdings/packages/{packageId} | mapping | |
GET | /eholdings/packages/{packageId}/resources | filtering | |
PUT | /eholdings/resources/{resourceId} | mapping | |
GET | /eholdings/providers/{providerId}/packages | filtering | |
GET | /eholdings/titles | filtering |
Association between KB Credentials and custom labels
Endpoints are needed to be changed
Method | Old endpoint | New endpoint | Request body | Response status | Response body |
---|---|---|---|---|---|
GET | /eholdings/custom-labels | /eholdings/kb-credentials/{credId}/custom-labels | 200 OK | ||
PUT | /eholdings/custom-labels | /eholdings/kb-credentials/{credId}/custom-labels | 200 OK |
JSON schemas that will be used (changes will be affected only for Custom Labels Collection Item Schema):
Association between KB Credentials and Proxy
Endpoints are needed to be changed
Method | Old endpoint | New endpoint | Request body | Response status | Response body |
---|---|---|---|---|---|
GET | /eholdings/proxy-types | /eholdings/kb-credentials/{credId}/proxy-types | 200 OK | ||
GET | /eholdings/root-proxy | /eholdings/kb-credentials/{credId}/root-proxy | 200 OK | ||
PUT | /eholdings/root-proxy | /eholdings/kb-credentials/{credId}/root-proxy | 200 OK |
JSON schemas that will be used:
Updating existing endpoints for providers/packages/resources/titles
Problem
Eholdings module provides several endpoints to operate with HoldingsIQ resources (providers/packages/resources/titles). Endpoint interfaces are listed below:
- EholdingsProviders
EholdingsPackages
- EholdingsResources
- EholdingsTitles
Detailed definition with methods of each enpoint can be found in Eholdings Record Interfaces appendix
Endpoints call HoldingsIQ services (EBSCO KB) to get or update records. Each call has to be preconfigured with valid EBSCO knowledgebase credentials. There is only one credential set available at the moment. But it has to be changed to support multiple sets and a set can be associated with a user. This requires changes in the configuration routine of endpoints so that the appropriate KB credentials could be selected from several options depending on the current user.
Another point to consider is the data that stored internally in Eholdings module and has to be also associated with different KB credentials. Some samples of the data are:
- package details (name, content type)
- provider name
- access types assigned to resource.
Each time an endpoint needs to work with such data it has to understand what the correct set of credentials is in the current context.
The code snippets provided below represent both cases:
- working with locally stored data
- calling HoldingsIQ services
Steps to address the problem
- modify configuration routine to support user related KB credentials
- apply the routine to each enpoint method which talks to HoldingsIQ service(s)
- make the code that works with locally stored data aware of applicable KB credentials
Let's describe the steps one by one in more details
Modify configuration routine to support user related KB credentials
Following points to consider:
Configuration service modification
Retrieving KB credentials for the user
Changes in RM API template context
Configuration service modification
The core service that works with KB credentials at the moment is ConfigurationService
. It provides 3 methods as shown below:
Modifications required:
retrieveConfiguration()
method will return KB credentials associated with the current user.- It differes from how it works right now: single available credentials obtained from
mod-configuration
regardless of the current user - new method implementation will benefit from a new method in
KBCredentialsService
(see later on) which is going to be responsible for finding KB credentials applicable to the current user. SoretrieveConfiguration()
will delegate almost all of its work to this new method inKBCredentialsService
- It differes from how it works right now: single available credentials obtained from
updateConfiguration()
method should be removed- all KB credentals management operations (adding, changing, removing) will be done by
KBCredentialsService
- all KB credentals management operations (adding, changing, removing) will be done by
verifyCredentials()
– NO changes needed
Retrieving KB credentials for the user
All credential management will be accumulated inside a new service: KBCredentialsService
. It will be responsible for:
- CRUD operations with KB credentials
- resolving the approproate KB credentials for user
Below is a class diagram of the service.
First four methods should provide standart CRUD operations.
The last one is responsible for getting the right KB credentials for the user. It will do the work by
- quering users assigned to KB credentials with the current user
- if no assignment present then
- testing if there is single KB credential present
Changes in RM API template context
The context serves as a holder for preconfigured HoldingsIQ services and a couple of parameters important during the calls to those services and module methods. It already contains Configuration
object to provide apiKey/customerId/url to access KB knowledge base. What's missing is KB credentials details such as id
(credentialsId) and name
. Credentials id will be important to filter local data, name might come in handy in troubleshooting/logging. So both these attributes will be added to the context and it'll look like the following:
Respectively RMAPITemplateContextBuilder
that constructs the context will be adjusted to support new fields.
Alternative
Another option would be to include credentials id/name into Configuration
class. Then the context doesn't need to be updated
Pros:
- all configuration attributes incupsulated in a single class – all the details will be available whereever
Configuration
class is currently available at - no changes in RM API template context
Cons:
- Configuration contains only the data that is really necessary to communicate with HoldingsIQ. By adding credentials id/name it gets poluted with irrelevant info.
KBCredentials
becomes equal toConfiguration
: duplication of code and resposibility. Something has to be gone then
Apply new configuration routine to each enpoint method which talks to HoldingsIQ service(s)
This should be fairly straightforward since there is a single place where credentail details (configuration) are obtained and applied to a service. This takes place inside RMAPITemplate
class:
- configuration retrieved by
ConfigurationService
- context builder populated with configuration found
- context with configuration is created by builder and passed to request action which calls HoldingIQ service
What has to be changed:
- replace
ConfigurationService
withKBCredentialsService
- instead of calling
retrieveConfiguration()
method callfindByUser()
method ofKBCredentialsService
to findKBCredentials
applicable in the current context - populate context with
Configuration, credentialsId, credentialsName
obtained fromKBCredentials
found
Make the code that works with locally stored data aware of applicable KB credentials
The code that works with local data which has become dependent on KB credentials should be refactored to accept credentailsId
and apply it to DB statements. There are 2 cases:
- the code is called from inside RMAPITemplate request action →
In this case context
already contains valid credentialsId
thus the method could get it from there. So for instance findById() method could be changed to:
accessTypesService.findById(accessTypeId, context)
- the code is executed on its own (there is no RMAPITemplate which wraps it around) →
There is no place to take credentialsId
from so it has to be retreived first by executing KBCredentialsService.findByUser()
method. The result should be transformed into RMAPIContext
similar to how it would be done in RMAPITemplate.executeInternal()
method (see previous section). Then context can be passed to updateTags()
.
Having the above RMAPIContext becomes more general than just a place to store everything relevant to RM API calls. It's worth thinking about renaming it to something more universal
Updating the process of holdings loading
To support the background loading of holdings by an invocation of OKAPI timer interface we have to add an additional endpoint to add the possibility of loading holdings for particular credentials
The path or pathPattern can be any fixed string (no pattern).
which means the URL /loadHoldings will remain the same.
No user is involved in this.
which means we can not use 'X-Okapi-Header' to define a user and then find tied credentials to load holdings.
Method | Old Endpoint | New Endpoint | Notes |
---|---|---|---|
POST | /loadHoldings | /eholdings/loading | an endpoint to load holdings for all credentials available in the system |
POST | /loadHoldings | /eholdings/loading/kb-credentials/{credentialsId} | load holdings for particular KB Credentials Interactions with the database should be updated by adding additional For instance, the query to get holdings will look like SELECT ... FROM holdings WHERE credentialsId = ? AND id IN (...); all other SQL queries should be updated accordingly. |
GET | /loadHoldings/status | /eholdings/loading/kb-credentials/{credentialsId}/status | Back-end will support storing a single record for with one difference - adding the additional column between KB credential and status Select queries should be updated by adding SELECT ... FROM holdings_status WHERE credentialsId = ?;
|