diff --git a/package-lock.json b/package-lock.json index ffc63022c..df90adb4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@adobe/helix-universal-logger": "3.0.27", "@adobe/spacecat-shared-athena-client": "1.2.4", "@adobe/spacecat-shared-brand-client": "1.1.19", - "@adobe/spacecat-shared-data-access": "2.43.2", + "@adobe/spacecat-shared-data-access": "https://gitpkg.now.sh/adobe/spacecat-shared/packages/spacecat-shared-data-access?f9cdcb7", "@adobe/spacecat-shared-gpt-client": "1.5.19", "@adobe/spacecat-shared-http-utils": "1.15.2", "@adobe/spacecat-shared-ims-client": "1.8.7", @@ -7864,9 +7864,9 @@ "license": "BSD-2-Clause" }, "node_modules/@adobe/spacecat-shared-data-access": { - "version": "2.43.2", - "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-data-access/-/spacecat-shared-data-access-2.43.2.tgz", - "integrity": "sha512-EG5J4s09638kY6o4xiDrURjtQmkqMiV2z56EhE+NM4xbm2gkAlhS1rewoWxcvd2uYXPHXGOKshS0s5WSAhIOrA==", + "version": "2.43.0", + "resolved": "https://gitpkg.now.sh/adobe/spacecat-shared/packages/spacecat-shared-data-access?f9cdcb7", + "integrity": "sha512-FugCOuK9MDCR02PnW9jEQqHIHNld11faqiC6wCt7rwJBcosqQi02Oh2FAxe/dbwRDwP1O1xJxlztX/aA7yCikg==", "license": "Apache-2.0", "dependencies": { "@adobe/spacecat-shared-utils": "1.39.1", @@ -32082,15 +32082,16 @@ } }, "node_modules/@types/request/node_modules/form-data": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", - "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" }, diff --git a/package.json b/package.json index d50fff0ba..be01ad0aa 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@adobe/helix-universal-logger": "3.0.27", "@adobe/spacecat-shared-athena-client": "1.2.4", "@adobe/spacecat-shared-brand-client": "1.1.19", - "@adobe/spacecat-shared-data-access": "2.43.2", + "@adobe/spacecat-shared-data-access": "https://gitpkg.now.sh/adobe/spacecat-shared/packages/spacecat-shared-data-access?f9cdcb7", "@adobe/spacecat-shared-gpt-client": "1.5.19", "@adobe/spacecat-shared-http-utils": "1.15.2", "@adobe/spacecat-shared-ims-client": "1.8.7", diff --git a/src/controllers/organizations.js b/src/controllers/organizations.js index 59a622c7d..d5c144c22 100644 --- a/src/controllers/organizations.js +++ b/src/controllers/organizations.js @@ -64,7 +64,19 @@ function OrganizationsController(ctx, env) { } try { - const organization = await Organization.create(context.data); + const { imsClient, data, log } = context; + const { imsOrgId } = data; + log.info(`Getting IMS org details for ${imsOrgId}`); + const imsOrg = await imsClient.getImsOrganizationDetails(imsOrgId); + log.info(`IMS org details: ${JSON.stringify(imsOrg)}`); + const { tenantId, orgName } = imsOrg; + const newOrgData = { + name: orgName, + imsOrgId, + tenantId, + }; + const organization = await Organization.create(newOrgData); + log.info(`Organization created: ${JSON.stringify(organization)}`); return createResponse(OrganizationDto.toJSON(organization), 201); } catch (e) { return badRequest(e.message); diff --git a/src/index.js b/src/index.js index 907979ecb..5017bfbcd 100644 --- a/src/index.js +++ b/src/index.js @@ -163,6 +163,7 @@ async function run(request, context) { const routeMatch = matchPath(method, suffix, routeHandlers); if (routeMatch) { + log.info(`Route match: ${JSON.stringify(routeMatch)}`); const { handler, params } = routeMatch; if (params.siteId && !isValidUUIDV4(params.siteId)) { @@ -172,8 +173,9 @@ async function run(request, context) { && (!isValidUUIDV4(params.organizationId) && params.organizationId !== 'default')) { return badRequest('Organization Id is invalid. Please provide a valid UUID.'); } + log.info(`Params: ${JSON.stringify(params)}`); context.params = params; - + log.info(`Calling handler: ${handler.name}`); return await handler(context); } else { const notFoundMessage = `no such route /${route}`; diff --git a/src/support/slack/commands/set-ims-org.js b/src/support/slack/commands/set-ims-org.js index 4e9d61b28..c9a1082be 100644 --- a/src/support/slack/commands/set-ims-org.js +++ b/src/support/slack/commands/set-ims-org.js @@ -98,6 +98,7 @@ function SetSiteOrganizationCommand(context) { spaceCatOrg = await Organization.create({ name: imsOrgDetails.orgName, imsOrgId: userImsOrgId, + tenantId: imsOrgDetails.tenantId, }); await spaceCatOrg.save(); diff --git a/test/controllers/organizations.test.js b/test/controllers/organizations.test.js index 4a75228c2..55c9ed8a9 100755 --- a/test/controllers/organizations.test.js +++ b/test/controllers/organizations.test.js @@ -204,46 +204,111 @@ describe('Organizations Controller', () => { expect(() => OrganizationsController({ dataAccess: mockDataAccess })).to.throw('Environment object required'); }); - it('creates an organization', async () => { + it('creates an organization for non admin users', async () => { + context.attributes.authInfo.withProfile({ is_admin: false }); + mockDataAccess.Organization.create.resolves(organizations[0]); + const response = await organizationsController.createOrganization({ + data: { name: 'Org 1', tenantId: 'test-tenant' }, + ...context, + }); + expect(response.status).to.equal(403); + + const error = await response.json(); + expect(error).to.have.property('message', 'Only admins can create new Organizations'); + }); + + it('creates an organization with IMS integration', async () => { + const mockImsOrg = { + tenantId: 'test-tenant-id', + orgName: 'Test Organization', + }; + + const mockImsClient = { + getImsOrganizationDetails: sinon.stub().resolves(mockImsOrg), + }; + + const mockLog = { + info: sinon.stub(), + }; + mockDataAccess.Organization.create.resolves(organizations[0]); + const response = await organizationsController.createOrganization({ - data: { name: 'Org 1' }, + data: { imsOrgId: 'test-ims-org-id' }, + imsClient: mockImsClient, + log: mockLog, ...context, }); - expect(mockDataAccess.Organization.create).to.have.been.calledOnce; + expect(mockImsClient.getImsOrganizationDetails).to.have.been.calledOnceWith('test-ims-org-id'); + expect(mockDataAccess.Organization.create).to.have.been.calledOnceWith({ + name: 'Test Organization', + imsOrgId: 'test-ims-org-id', + tenantId: 'test-tenant-id', + }); expect(response.status).to.equal(201); const organization = await response.json(); expect(organization).to.have.property('id', '9033554c-de8a-44ac-a356-09b51af8cc28'); - expect(organization).to.have.property('name', 'Org 1'); }); - it('creates an organization for non admin users', async () => { - context.attributes.authInfo.withProfile({ is_admin: false }); - mockDataAccess.Organization.create.resolves(organizations[0]); + it('handles IMS client error during organization creation', async () => { + const mockImsClient = { + getImsOrganizationDetails: sinon.stub().rejects(new Error('IMS API Error')), + }; + + const mockLog = { + info: sinon.stub(), + }; + const response = await organizationsController.createOrganization({ - data: { name: 'Org 1' }, + data: { imsOrgId: 'test-ims-org-id' }, + imsClient: mockImsClient, + log: mockLog, ...context, }); - expect(response.status).to.equal(403); + + expect(mockImsClient.getImsOrganizationDetails).to.have.been.calledOnceWith('test-ims-org-id'); + expect(mockDataAccess.Organization.create).to.not.have.been.called; + expect(response.status).to.equal(400); const error = await response.json(); - expect(error).to.have.property('message', 'Only admins can create new Organizations'); + expect(error).to.have.property('message', 'IMS API Error'); }); - it('returns bad request when creating an organization fails', async () => { - mockDataAccess.Organization.create.rejects(new Error('Failed to create organization')); + it('handles organization creation error after IMS lookup', async () => { + const mockImsOrg = { + tenantId: 'test-tenant-id', + orgName: 'Test Organization', + }; + + const mockImsClient = { + getImsOrganizationDetails: sinon.stub().resolves(mockImsOrg), + }; + + const mockLog = { + info: sinon.stub(), + }; + + mockDataAccess.Organization.create.rejects(new Error('Database Error')); + const response = await organizationsController.createOrganization({ - data: { name: 'Org 1' }, + data: { imsOrgId: 'test-ims-org-id' }, + imsClient: mockImsClient, + log: mockLog, ...context, }); - expect(mockDataAccess.Organization.create).to.have.been.calledOnce; + expect(mockImsClient.getImsOrganizationDetails).to.have.been.calledOnceWith('test-ims-org-id'); + expect(mockDataAccess.Organization.create).to.have.been.calledOnceWith({ + name: 'Test Organization', + imsOrgId: 'test-ims-org-id', + tenantId: 'test-tenant-id', + }); expect(response.status).to.equal(400); const error = await response.json(); - expect(error).to.have.property('message', 'Failed to create organization'); + expect(error).to.have.property('message', 'Database Error'); }); it('updates an organization', async () => { diff --git a/test/support/slack/commands/set-ims-org.test.js b/test/support/slack/commands/set-ims-org.test.js index 163f52df0..1943a5733 100644 --- a/test/support/slack/commands/set-ims-org.test.js +++ b/test/support/slack/commands/set-ims-org.test.js @@ -124,7 +124,10 @@ describe('SetSiteOrganizationCommand', () => { }; siteStub.findByBaseURL.resolves(mockSite); organizationStub.findByImsOrgId.resolves(null); - imsClientStub.getImsOrganizationDetails.resolves({ orgName: 'Mock IMS Org' }); + imsClientStub.getImsOrganizationDetails.resolves({ + orgName: 'Mock IMS Org', + tenantId: 'test-tenant-id', + }); const mockNewOrg = { getId: () => 'newOrgId', @@ -141,6 +144,7 @@ describe('SetSiteOrganizationCommand', () => { expect(organizationStub.create.calledWith({ name: 'Mock IMS Org', imsOrgId: 'newImsOrgId', + tenantId: 'test-tenant-id', })).to.be.true; expect(mockNewOrg.save.calledOnce).to.be.true; expect(mockSite.setOrganizationId.calledWith('newOrgId')).to.be.true;