Spike: MODKBEKBJ-390 - describe design for "Single tenant multiple EBSCO KBs" scenario

MODKBEKBJ-390 - Getting issue details... STATUS

Participants:

RoleNameApproval
Solution Architect(tick)
Java Lead(tick)
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 to mod-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.

current implementation assumed that there is no permission distinction between users and particular credentials. This means the following:

  • in case system has 3 credentials (Hampshire, Amherst, Smith) 
  • if John has permission to edit KB credentials - he can edit credentials for all (Hampshire, Amherst, Smith) knowledge bases. This applies to all CRUD operations.


MethodPathPermission(s)ResponseNote(s)
GET/eholdings/kb-credentialskb-ebsco.credentials.collection.get200 OK(with collection)
POST/eholdings/kb-credentialskb-ebsco.credentials.collection.post201 Created(with record)
GET/eholdings/kb-credentials/{credId}kb-ebsco.credentials.item.get200 OK(with record){credId} - UUID of the credential
PUT/eholdings/kb-credentials/{credId}kb-ebsco.credentials.item.put204 No Content{credId} - UUID of the credential
DELETE/eholdings/kb-credentials/{credId}kb-ebsco.credentials.item.delete204 No Content

{credId} - UUID of the credential

in case of credentials deletion users, already assigned to it, will be deleted. 

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"
  }
}
  • property "name" proposed to have unique value
  • property "customerId" assumed to have a unique value (in case of shared credentials will be duplicated). Back-end will not perform any check on that.
Json schema
 JSON schema 
KB Credentials JSON schema 
{
  "$schema": "http://json-schema.org/draft-04/schema",
  "type": "object",
  "title": "The Root Schema",
  "description": "The root schema comprises the entire JSON document.",
  "required": [
    "id",
    "type",
    "attributes"
  ],
  "properties": {
    "id": {
      "type": "string",
      "title": "The UUID of entry",
      "$ref": "../../raml-util/schemas/uuid.schema",
      "examples": "2ffa1940-2cf6-48b1-8cc9-5e539c61d93f"
    },
    "type": {
      "type": "string",
      "title": "The Type Schema",
      "examples": ["kbCredentials"]
    },
    "attributes": {
      "type": "object",
      "title": "The Attributes Schema",
      "examples": [
        {
          "url": "YYYY",
          "customerId": "ZZZZ",
          "name": "University of Massachusetts - Amherst",
          "apiKey": "XXXX"
        }
      ],
      "required": [
        "name",
        "apiKey",
        "url",
        "customerId"
      ],
      "properties": {
        "name": {
          "type": "string",
          "title": "The Unique Name of the credentials",
          "examples": ["University of Massachusetts - Amherst"]
        },
        "apiKey": {
          "type": "string",
          "title": "The Apikey Schema",
          "examples": ["XXXX"]
        },
        "url": {
          "type": "string",
          "title": "The Url Schema",
          "examples": ["YYYY"]
        },
        "customerId": {
          "type": "string",
          "title": "The Unique Customer id",
          "examples": ["ZZZZ"]
        }
      }
    },
    "metadata": {
      "type": "object",
      "$ref" : "../metadata.schema",
      "readonly": true,
      "examples": [
        {
          "updatedByUserId": "6893f51f-b40c-479d-bd78-1704ab5b802b",
          "createdByUserId": "1f8f660e-7dc9-4f6f-828f-96284c68a250",
          "updatedDate": "2020-03-17T15:23:04.098+0000",
          "createdDate": "2020-03-17T15:22:04.098",
          "createdByUsername": "john_doe",
          "updatedByUsername": "jane_doe"
        }
      ]
    }
  }
}

Configuration cache

kb-ebsco-java sequence diagram

codex-ekb sequence diagram

UI mockups


Storing association between user and settings(EBSCO KB Credentials)


Proposed Solution:

MethodPathPermission(s)ResponseNote(s)
GET/eholdings/kb-credentials/{credId}/userskb-ebsco.credentials.users.collection.get200 OK(with collection){credId} - UUID of the credential
POST/eholdings/kb-credentials/{credId}/userskb-ebsco.credentials.users.collection.post201 Created(with record){credId} - UUID of the credential
PUT/eholdings/kb-credentials/{credId}/users/{userId}kb-ebsco.credentials.users.item.put204 No Content

{credId} - UUID of the credential

{userId) - UUID of a user

DELETE/eholdings/kb-credentials/{credId}/users/{userId}kb-ebsco.credentials.users.item.delete204 No Content

{credId} - UUID of the credential

{userId) - UUID of a user

The appropriate files attached below:

DB schema

It means that we have one-to-many relationship between users and credentials. Below we can see a simplified example of storing such relationship

kb_credentials
idname
2ffa1940-2cf6-48b1-8cc9-5e539c61d93fUniversity of Massachusetts - Amherst
09db24ba-a562-42c3-acf6-325efaf34958Hampshire
assigned_user 
2ffa1940-2cf6-48b1-8cc9-5e539c61d93fjohn_doe
2ffa1940-2cf6-48b1-8cc9-5e539c61d93fjane_doe
09db24ba-a562-42c3-acf6-325efaf34958aleen_braun

It means that:

  • john_doe and jane_doe belong to a University of Massachusetts - Amherst
  • aleen_braun to Hampshire

Restrictions

  • user can be assigned to one KB credential only

Json Example
 GET collection
/eholdings/kb-credentials/{credId}/users
{
  "data": [
    {
      "id": "1f8f660e-7dc9-4f6f-828f-96284c68a25",
      "type": "assignedUsers",
      "attributes": {
        "credentialsId": "2ffa1940-2cf6-48b1-8cc9-5e539c61d93f",
        "firstName": "John",
        "middleName": "William",
        "lastName": "Doe",
        "patronGroup": "Staff",
        "userName": "john_doe"
      }
    },
    {
   	  "id": "6893f51f-b40c-479d-bd78-1704ab5b802b",
      "type": "assignedUsers",
      "attributes": {
        "credentialsId": "2ffa1940-2cf6-48b1-8cc9-5e539c61d93f",
        "firstName": "Jane",
        "middleName": "Rosemary",
        "lastName": "Doe",
        "patronGroup": "Staff",
        "userName": "jane_doe"
      }
    }
  ],
  "meta": {
    "totalResults": 2
  },
  "jsonapi": {
    "version": "1.0"
  }
}
  • array of users should not have duplicates
 POST collection item
/eholdings/kb-credentials/{credId}/users
{
 "user":
	{
	  "id": "1f8f660e-7dc9-4f6f-828f-96284c68a25",
	  "credentialsId": "2ffa1940-2cf6-48b1-8cc9-5e539c61d93f",	
	  "firstName": "John",
	  "middleName": "William",
	  "lastName": "Doe",
	  "patronGroup": "Staff",
	  "userName": "john_doe"
	}
}
Json schema
 GET collection schema
{
    "$schema": "http://json-schema.org/draft-04/schema",
    "type": "object",
    "title": "The Root Schema",
    "properties": {
        "credentialsId": {
            "type": "string",
            "title": "The Credentials Id Schema",
			"$ref": "../../raml-util/schemas/uuid.schema",
            "examples": ["2ffa1940-2cf6-48b1-8cc9-5e539c61d93f"]
        },
        "users": {
            "type": "array",
			"uniqueItems": true,
            "title": "The Assignment users Schema",
            "items": {
                "type": "object",
                "title": "The Items Schema",
                "examples": [
                    {
						"id": "1f8f660e-7dc9-4f6f-828f-96284c68a25",
                        "userName": "john_doe",
						"firstName": "John",
						"middleName": "William",
                        "lastName": "Doe",
                        "patronGroup": "Staff"
                    },
   					{
      					"id": "6893f51f-b40c-479d-bd78-1704ab5b802b",
						"userName": "jane_doe",
	  					"firstName": "Jane",
	  					"middleName": "Rosemary",
	  					"lastName": "Doe",
	  					"patronGroup": "Staff"
  					}
                ],
                "properties": {
                    "id": {
                        "type": "string",
                        "title": "The Id Schema",
						"$ref": "../../raml-util/schemas/uuid.schema",
                        "examples": ["1f8f660e-7dc9-4f6f-828f-96284c68a25"]
                    },
                    "firstName": {
                        "type": "string",
                        "title": "The Firstname Schema",
                        "examples": ["John"]
                    },
                    "middleName": {
                        "type": "string",
                        "title": "The Middlename Schema",
                        "examples": ["William"]
                    },
                    "lastName": {
                        "type": "string",
                        "title": "The Lastname Schema",
                        "examples": ["Doe"]
                    },
                    "patronGroup": {
                        "type": "string",
                        "title": "The Patrongroup Schema",
                        "examples": ["Staff"]
                    },
                    "userName": {
                        "type": "string",
                        "title": "The Username Schema",
                        "examples": ["john_doe"]
                    }
                },
                "required": [
                    "id",
                    "firstName",
                    "middleName",
                    "lastName",
                    "patronGroup",
                    "userName"
                ]
            }
        }
    },
    "required": [
        "credentialsId",
        "users"
    ]
}

UI mockups

Update mapping between credentials and user

(error) 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

(tick) 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

Option #2 has been chosen since it's easy to implement and it follows more traditional pattern

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 

  • one-to-many relationship between kb_credentials and access_types
  • one-to-many relationship between access_types and access_types_mapping

This approach will provide the ability to separate assignment-data between each KB credentials

  • Endpoints

MethodOld endpointNew endpointRequest bodyResponse status Response body
GET/eholdings/access-types/eholdings/kb-credentials/{credId}/access-types
200 OK
 Response
Example
{
  "data": [
    {
      "id": "77e8b4b1-81d6-4030-892e-29e4bceaf862",
	  "credentialsId": "{credId}",
      "type": "accessTypes",
      "attributes": {
        "name": "Trial",
        "description": "some description"
      },
      "creator": {
        "lastName": "John",
        "firstName": "Dough"
      },
      "usageNumber": 1,
      "metadata": {
        "createdDate": "2020-03-13T14:39:04.663+0000",
        "createdByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065",
        "createdByUsername": "diku_admin",
        "updatedDate": "2020-03-13T14:39:04.663+0000",
        "updatedByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065"
      }
    },
    {
      "id": "63f35ca5-317f-49e5-9404-9ec8e25b28a4",
      "type": "accessTypes",
      "attributes": {
        "name": "Subscribed",
        "description": "some description"
      },
      "creator": {
        "lastName": "John",
        "firstName": "Dough"
      },
      "usageNumber": 0,
      "metadata": {
        "createdDate": "2020-03-13T14:44:36.072+0000",
        "createdByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065",
        "createdByUsername": "dough",
        "updatedDate": "2020-03-13T14:44:36.072+0000",
        "updatedByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065"
      }
    }
  ],
  "meta": {
    "totalResults": 2
  },
  "jsonapi": {
    "version": "1.0"
  }
}
POST/eholdings/access-types/eholdings/kb-credentials/{credId}/access-types
 Request
Example
{
  "type": "accessTypes",
  "attributes": {
    "name": "Subscribed",
    "description": "Indicates subscription of the entity"
  }
}
201 Created(with record)
 Response
Example
{
  "id": "{randomUUID}",
  "credentialsId": "{credId}",
  "type": "accessTypes",
  "attributes": {
    "name": "Subscribed",
    "description": "Indicates subscription of the entity"
  },
  "creator": {
    "lastName": "John",
    "firstName": "Dough"
  },
  "usageNumber": 0,
  "metadata": {
    "createdDate": "2020-03-13T14:39:04.663+0000",
    "createdByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065",
    "createdByUsername": "dough",
    "updatedDate": "2020-03-13T14:39:04.663+0000",
    "updatedByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065"
  }
}
GET/eholdings/access-types/{atId}/eholdings/kb-credentials/{credId}/access-types/{atId}
200 OK
 Response
Example
{
  "id": "{atId}",
  "credentialsId": "{credId}",
  "type": "accessTypes",
  "attributes": {
    "name": "Trial",
    "description": "some description"
  },
  "creator": {
    "lastName": "John",
    "firstName": "Dough"
  },
  "usageNumber": 0,
  "metadata": {
    "createdDate": "2020-03-13T14:39:04.663+0000",
    "createdByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065",
    "createdByUsername": "dough",
    "updatedDate": "2020-03-13T14:39:04.663+0000",
    "updatedByUserId": "97da18d8-dda7-5cac-84d6-b393bea2b065"
  }
}
PUT/eholdings/access-types/{atId}/eholdings/kb-credentials/{credId}/access-types/{atId}
 Request
Example
{
  "type": "accessTypes",
  "attributes": {
    "name": "Subscribed updated",
    "description": "Indicates subscription of the entity"
  }
}
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 Types Collection Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Access Types Collection Schema",
  "description": "Access Types Collection Schema",
  "javaType": "org.folio.rest.jaxrs.model.AccessTypeCollection",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "data": {
      "type": "array",
      "description": "List of access types",
      "items": {
        "type": "object",
        "$ref": "accessTypeCollectionItem.json"
      }
    },
    "meta": {
      "type": "object",
      "description": "metadata containing total results in collection",
      "$ref": "../metaTotalResults.json",
      "readonly": true
    },
    "jsonapi": {
      "type": "object",
      "description": "version of json api",
      "$ref": "../jsonapi.json"
    }
  },
  "required": ["data", "meta", "jsonapi"]
}
 Access Types Collection Item Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Access Types Collection Item Schema",
  "description": "Access Types Collection Item Schema",
  "javaType": "org.folio.rest.jaxrs.model.AccessTypeCollectionItem",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "string",
      "description": "UUID of access type",
      "$ref": "../../raml-util/schemas/uuid.schema",
      "example": "f973c3b6-85fc-4d35-bda8-f31b568957bf"
    },
    "type": {
      "type": "string",
      "description": "Type of resource",
      "enum": ["accessTypes"],
      "example": "accessTypes"
    },
    "attributes": {
      "type": "object",
      "description": "Custom label object data attributes",
      "$ref": "accessTypeDataAttributes.json"
    },
    "credentialsId": {
      "type": "string",
      "description": "UUID of KB credentials",
      "$ref": "../../raml-util/schemas/uuid.schema",
      "example": "a173c3b6-134d-4d35-bda8-f31bfwd957bf"
    },
    "usageNumber": {
      "type": "integer",
      "description": "Number of records that use the access type",
      "readonly": true
    },
    "creator": {
      "type": "object",
      "description": "User display info for creator of the note",
      "$ref": "userDisplayInfo.json",
      "readonly": true
    },
    "updater": {
      "type": "object",
      "description": "User display info for updater of the note",
      "$ref": "userDisplayInfo.json",
      "readonly": true
    },
    "metadata": {
      "type": "object",
      "description": "Metadata for the entity",
      "$ref": "../../raml-util/schemas/metadata.schema",
      "readonly": true
    }
  },
  "required": [
    "type",
    "attributes"
  ]
}
 Access Types Collection Item Data Attributes Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Access Types Collection Item Data Attributes Schema",
  "description": "Access Types Collection Item Data Attributes Schema",
  "javaType": "org.folio.rest.jaxrs.model.AccessTypeDataAttributes",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "name": {
      "type": "string",
      "description": "Access Type Name",
      "example": "Subscribed"
    },
    "description": {
      "type": "string",
      "description": "Access Type Description",
      "example": "Indicates subscription of the entity"
    }
  },
  "required": ["name"]
}

 User Display Information
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "description": "User Display Information",
  "javaType": "org.folio.rest.jaxrs.model.UserDisplayInfo",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "lastName": {
      "type": "string",
      "description": "Last name of the user",
      "example": "Doe",
      "readonly": true
    },
    "firstName": {
      "type": "string",
      "description": "First name of the user",
      "example": "John",
      "readonly": true
    },
    "middleName": {
      "type": "string",
      "description": "Middle name or initial of the user",
      "example": "X.",
      "readonly": true
    }
  }
}
  • Access status type to record (package, resource) mapping and filtering implementation

Endpoints that will be affected:

MethodEndpointActionNotes
GET/eholdings/packagesfiltering

Most interactions with DB must be updated.

In most cases, we need to update only 'SELECT ... FROM access_types' SQL-queries to get ids of access status types that will be used in queries to access_types_mapping 



SELECT ...
  FROM access_types
 WHERE credentialsId = ? AND ...;



In other cases SQL-queries need to be updated by joining access_types_mapping and access_types tables: 

SELECT ...
  FROM access_types_mapping atp
  JOIN access_types at
    ON at.id = atp.accessTypeId
 WHERE ... AND at.credentialsId = ?;
POST/eholdings/packagesmapping
PUT/eholdings/packages/{packageId}mapping
GET/eholdings/packages/{packageId}/resourcesfiltering
PUT/eholdings/resources/{resourceId}mapping
GET/eholdings/providers/{providerId}/packagesfiltering
GET/eholdings/titlesfiltering

Association between KB Credentials and custom labels


Custom labels are stored on the RM API side, so in this case, we don't have to change DB schema for custom labels.

Endpoints are needed to be changed

MethodOld endpointNew endpointRequest bodyResponse status Response body
GET/eholdings/custom-labels/eholdings/kb-credentials/{credId}/custom-labels
200 OK
 Response
Example
{
  "data": [
    {
      "type": "customLabel",
      "credentialsId": "{credId}",
      "attributes": {
        "id": 1,
        "displayLabel": "Department",
        "displayOnFullTextFinder": false,
        "displayOnPublicationFinder": false
      }
    },
    {
      "type": "customLabel",
      "credentialsId": "{credId}",
      "attributes": {
        "id": 2,
        "displayLabel": "Faculty",
        "displayOnFullTextFinder": false,
        "displayOnPublicationFinder": false
      }
    }
  ],
  "meta": {
    "totalResults": 2
  },
  "jsonapi": {
    "version": "1.0"
  }
}
PUT/eholdings/custom-labels/eholdings/kb-credentials/{credId}/custom-labels
 Request
Example
{
  "data": [
    {
      "type": "customLabel",
      "attributes": {
        "id": 1,
        "displayLabel": "Department",
        "displayOnFullTextFinder": false,
        "displayOnPublicationFinder": false
      }
    },
    {
      "type": "customLabel",
      "attributes": {
        "id": 2,
        "displayLabel": "Faculty",
        "displayOnFullTextFinder": false,
        "displayOnPublicationFinder": false
      }
    }
  ]
}
200 OK
 Response
Example
{
  "data": [
    {
      "type": "customLabel",
      "credentialsId": "{credId}",
      "attributes": {
        "id": 1,
        "displayLabel": "Department",
        "displayOnFullTextFinder": false,
        "displayOnPublicationFinder": false
      }
    },
    {
      "type": "customLabel",
      "credentialsId": "{credId}",
      "attributes": {
        "id": 2,
        "displayLabel": "Faculty",
        "displayOnFullTextFinder": false,
        "displayOnPublicationFinder": false
      }
    }
  ],
  "meta": {
    "totalResults": 2
  },
  "jsonapi": {
    "version": "1.0"
  }
}

JSON schemas that will be used (changes will be affected only for Custom Labels Collection Item Schema):

 Custom Labels Collection Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Custom Labels Collection Schema",
  "description": "Custom Labels Collection Schema",
  "javaType": "org.folio.rest.jaxrs.model.CustomLabelsCollection",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "data": {
      "type": "array",
      "description": "List of custom labels",
      "items": {
        "type": "object",
        "$ref": "customLabelCollectionItem.json"
      }
    },
    "meta": {
      "type": "object",
      "description": "metadata containing total results in packages collection",
      "$ref": "../metaTotalResults.json"
    },
    "jsonapi": {
      "type": "object",
      "description": "version of json api",
      "$ref": "../jsonapi.json"
    }
  },
  "required": ["data", "meta", "jsonapi"]
}
 Custom Labels Collection Item Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Custom Labels Collection Item Schema",
  "description": "Custom Labels Collection Item Schema",
  "javaType": "org.folio.rest.jaxrs.model.CustomLabelCollectionItem",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "type": {
      "type": "string",
      "description": "Type of resource",
      "example": "customLabel"
    },
    "credentialsId": {
      "type": "string",
      "description": "UUID of KB credentials",
      "$ref": "../../raml-util/schemas/uuid.schema",
      "example": "a173c3b6-134d-4d35-bda8-f31bfwd957bf"
    },
    "attributes": {
      "type": "object",
      "description": "Custom label object data attributes",
      "$ref": "customLabelDataAttributes.json"
    }
  },
  "required": ["type", "attributes"]
}
 Custom Labels Collection Item Data Attributes Schema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Custom Labels Collection Item Data Attributes Schema",
  "description": "Custom Labels Collection Item Data Attributes Schema",
  "javaType": "org.folio.rest.jaxrs.model.CustomLabelDataAttributes",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "integer",
      "description": "Label id",
      "example": 1
    },
    "displayLabel": {
      "type": "string",
      "description": "Display Label Name",
      "example": "User defined field 1"
    },
    "displayOnFullTextFinder": {
      "type": "boolean",
      "description": "Indicates if displayed on Full Text Finder",
      "example": false
    },
    "displayOnPublicationFinder": {
      "type": "boolean",
      "description&