Skip to content
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

Sdmext 178/creation of sdm object #4

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ In case you are the maintainer of a new cap-js open source project, these are th
- Add information about your project to this README (name, description, requirements etc). Especially take care for the <your-project> placeholders - those ones need to be replaced with your project name. See the sections below the horizontal line and [our guidelines on our wiki page](https://wiki.wdf.sap.corp/wiki/display/ospodocs/Guidelines+for+README.md+file) what is required and recommended.
- Remove all content in this README above and including the horizontal line ;)

***
---

## About this project

CAP plugin for SAP Document Management service

## Requirements and Setup

*Insert a short description what is required to get your project running...*
_Insert a short description what is required to get your project running..._

## Support, Feedback, Contributing

Expand Down
70 changes: 68 additions & 2 deletions cds-plugin.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,69 @@
const cds = require('@sap/cds');
const { getConfigurations } = require('./lib/config');
const {
onCreate,
beforeAll
} = require('./lib/handlers');
const customServices = [];

/**
* Entry point for the plugin
*/
* Initialize the plugin and define its handlers.
*/
cds.once('served', async services => {
if (cds.env.requires?.['@cap-js/document-management']) await initializePlugin(services);
});

/**
* Initializes the plugin by configuring services and registering event handlers.
*/
async function initializePlugin(services) {
getConfigurations();

cds.env.requires['cmis-client'] = { impl: `${__dirname}/srv/cmis/client` };

// Extract services with the "@Sdm.Entity" annotation
const annotatedServices = extractAnnotatedServices(services);

// Register handlers for each service
await Promise.all(annotatedServices.map(registerServiceHandlers));
}

/**
* Extracts services with the "@Sdm.Entity" annotation.
* @param {Object} services - All services.
* @returns {Array} An array of services with the "@Sdm.Entity" annotation.
*/
function extractAnnotatedServices(services) {
return Object.values(services)
.filter(service => service instanceof cds.ApplicationService)
.map(service => ({
name: service.name,
srv: service,
entities: Object.values(service.entities).filter(
entity => entity?.['@Sdm.Entity'],
),
}))
.filter(service => service.entities.length > 0);
}

const eventHandlersMap = {
CREATE: onCreate
};

/**
* Register event handlers for the given service.
* @param {Object} service - The service to register handlers for.
*/
async function registerServiceHandlers(service) {
const { srv, entities } = service;

srv.prepend(() => {
for (let entity of entities) {
for (let [event, handler] of Object.entries(eventHandlersMap)) {
srv.on(event, entity.name, handler);
}

srv.before('*', entity.name, beforeAll);
}
});
}
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const config = {
verbose: true,
testTimeout: 100000,
testMatch: ['**/test/*.test.js'],
testMatch: ['**/test/**/**/*.test.js'],
collectCoverageFrom: [
'**/lib/*',
'**/srv/*',
Expand Down
7 changes: 7 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const cds = require('@sap/cds');

const getConfigurations = () => {
return cds.env.requires?.['@cap-js/document-management']?.settings || {};
};

module.exports = { getConfigurations };
46 changes: 46 additions & 0 deletions lib/converters/cmis-document-to-odata-entity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const { getConfigurations } = require('../config');
const {
extractPluginPropertiesFromElement,
generateUrlFromField,
} = require('../util');

/**
* Converts a CMIS document structure to OData format based on the provided elements mapping.
*
* @param {Array<Object>} data - An array of CMIS document structures to be converted.
* @param {Object} elements - The OData properties
* @returns {Promise<Array<Object>>} An array of data transformed into OData format.
*/
const convertCMISDocumentToOData = (data, elements) => {
const cmisDocuments = Array.isArray(data) ? data : [data];

console.log('data -------------');
console.log(JSON.stringify(data));
console.log('elements -------------');
console.log(JSON.stringify(elements));
return cmisDocuments.map(query => {
const result = {};

for (let [key, value] of Object.entries(elements)) {
const sdmField = extractPluginPropertiesFromElement(value);
if (!sdmField) continue;

switch (sdmField?.type) {
case 'property': {
const newValue = query.succinctProperties[sdmField?.path];
if (newValue) result[key] = newValue;
break;
}
case 'url': {
const { repositoryId } = getConfigurations();
result[key] = generateUrlFromField(repositoryId, sdmField, query);
break;
}
}
}

return result;
});
};

module.exports = convertCMISDocumentToOData;
7 changes: 7 additions & 0 deletions lib/converters/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const convertCMISDocumentToOData = require('./cmis-document-to-odata-entity');
const convertODataToCMISDocument = require('./odata-entity-to-cmis-document');

module.exports = {
convertCMISDocumentToOData,
convertODataToCMISDocument,
};
19 changes: 19 additions & 0 deletions lib/converters/odata-entity-to-cmis-document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { getColumnsMapping } = require('../util');

/**
* Converts a CMIS document structure to OData format based on the provided elements mapping.
*
* @param {Object} data - OData entity data
* @param {Object} elements - The OData properties
* @returns {Promise<Array<Object>>} An array of data transformed into OData format.
*/
module.exports = function convertODataToCMISDocument(data, elements) {
const columnsMapping = getColumnsMapping(elements);
console.log(JSON.stringify(columnsMapping));
return Object.entries(data).reduce((acc, [key, value]) => {
if (columnsMapping[key]) {
acc[columnsMapping[key].path] = value;
}
return acc;
}, {});
};
60 changes: 60 additions & 0 deletions lib/handlers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const { loadDestination } = require('../util');
const {
convertCMISDocumentToOData,
convertODataToCMISDocument
} = require('../converters');
const { getConfigurations } = require('../config');

/**
* Transforms the request data to match the CMIS document structure.
*
* @param {Object} req - The request object containing data and target elements.
* @returns {void} Modifies the `data` property of the request object in place.
*/
const beforeAll = async req => {
const { data } = req;
const reqParams = convertODataToCMISDocument(data, req.target.elements);
req.reqParams = reqParams;
};

/**
* Handles create operations.
* @param {Object} req - The request object.
* @returns {Promise<void>}
*/
const onCreate = async req => {
const { repositoryId } = getConfigurations();
const destination = await loadDestination(req);
const { reqParams } = req;
const objectName = reqParams['cmis:name'];
const objectTypeId = reqParams['cmis:objectTypeId'];
const objectId = (reqParams['cmis:objectId']) ? reqParams['cmis:objectId'] : null;
const description = (reqParams['cmis:description']) ? reqParams['cmis:description'] : null;

const srv = await cds.connect.to('cmis-client');
if (objectTypeId !== 'cmis:document') {
const result = await srv.createFolder(repositoryId, objectName, objectId, description).execute(destination);
return convertCMISDocumentToOData(result, req.target.elements);
} else {
const content = req.data.content;
const result = await srv.createDocument(repositoryId, objectName, objectId, description, content).execute(destination);
return convertCMISDocumentToOData(result, req.target.elements);
}
};

/**
* Tries to create a specified folder. If the folder already exists, no action is taken.
* @param {Object} req - The request object.
* @param {string} folderName - The name of the folder to create.
*/
// const onCreateFolder = async (req, folderName) => {
// const { repositoryId } = getConfigurations();
// const destination = await loadDestination(req);
// const srv = await cds.connect.to('cmis-client');
// return await srv.createFolder(repositoryId, folderName).execute(destination);
// };

module.exports = {
beforeAll,
onCreate
};
58 changes: 58 additions & 0 deletions lib/util/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const { getDestination } = require('@sap-cloud-sdk/core');
const { getConfigurations } = require('../config');

const extractPluginPropertiesFromElement = element => {
const sdmField = {};
for (const [key, value] of Object.entries(element)) {
if (key.startsWith('@Sdm.Field.')) {
const propName = key.split('.').pop().replace(':', '');
sdmField[propName] = value;
}
}

return Object.keys(sdmField).length ? sdmField : null;
};

const getColumnsMapping = elements => {
return Object.values(elements).reduce((acc, e) => {
const sdmField = extractPluginPropertiesFromElement(e);
if (sdmField?.type === 'property') {
acc[e.name] = sdmField;
}
return acc;
}, {});
};

/**
* Generates a URL based on the given SDM field and query.
* @param {Object} sdmField - The SDM field data.
* @param {Object} query - The CMIS query data.
* @returns {string} The generated URL.
*/
const generateUrlFromField = (repositoryId, sdmField, query) => {
const { path: contentStreamId } = sdmField;
const { 'cmis:objectId': objectId } = query.succinctProperties;
let url = `/browser/${repositoryId}/root?cmisselector=content&objectId=${objectId}`;
if (contentStreamId) {
url += `&streamId=${contentStreamId}`;
}
return url;
};

const loadDestination = async (req, options = { useCache: false }) => {
const { destination: destinationName } = getConfigurations();
const userJwt =
typeof req?._req?.tokenInfo?.getTokenValue === 'function'
? req?._req?.tokenInfo?.getTokenValue()
: undefined;

const opt = userJwt ? { ...options, userJwt } : options;
return await getDestination(destinationName, opt);
};

module.exports = {
extractPluginPropertiesFromElement,
getColumnsMapping,
generateUrlFromField,
loadDestination
};
30 changes: 28 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,42 @@
"@sap/cds-lsp": "^7.6.1",
"@sap/xssec": "^3.6.1",
"dotenv": "^16.3.1",
"express": "^4"
"express": "^4",
"@sap-cloud-sdk/core": "^1.54.2",
"@sap-cloud-sdk/openapi": "^3.7.0",
"form-data": "^4.0.0",
"mime": "^3.0.0",
"odata-v4-parser": "^0.1.29"
},
"devDependencies": {
"@cap-js/sqlite": "^1",
"@sap/cds-dk": "^7",
"eslint": "^8.57.0",
"jest": "^29.7.0"
"jest": "^29.7.0",
"@sap-cloud-sdk/openapi-generator": "^3.7.0",
"semantic-release": "^22.0.5",
"sqlite3": "^5.1.6"
},
"scripts": {
"start": "cds-serve",
"test": "jest --coverage --config jest.config.js"
},
"cds": {
"requires": {
"kinds": {
"=dms": {
"impl": "@cap-js/attachments/lib/dms"
}
},
"[development]": {
"attachments": { "kind": "dms"}
},
"[production]": {
"attachments": { "kind": "dms"}
},
"[hybrid]": {
"attachments": { "kind": "dms"}
}
}
}
}
Loading
Loading