@velocitycareerlabs/server-webwallet 1.26.0-dev-build.1ad63e207 → 1.26.0-dev-build.15052cf8c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velocitycareerlabs/server-webwallet",
3
- "version": "1.26.0-dev-build.1ad63e207",
3
+ "version": "1.26.0-dev-build.15052cf8c",
4
4
  "description": "Web Wallet application",
5
5
  "repository": "https://github.com/velocitycareerlabs/packages",
6
6
  "engines": {
@@ -33,7 +33,7 @@
33
33
  "@fastify/swagger": "^9.0.0",
34
34
  "@fastify/swagger-ui": "^5.0.0",
35
35
  "@spencejs/spence-mongo-repos": "^0.10.2",
36
- "@velocitycareerlabs/migrations": "1.26.0-dev-build.1ad63e207",
36
+ "@velocitycareerlabs/migrations": "1.26.0-dev-build.15052cf8c",
37
37
  "@verii/auth": "1.0.0-pre.1754473517",
38
38
  "@verii/common-functions": "1.0.0-pre.1754473517",
39
39
  "@verii/common-schemas": "1.0.0-pre.1754473517",
@@ -73,5 +73,5 @@
73
73
  "nodemon": "3.1.10",
74
74
  "prettier": "2.8.8"
75
75
  },
76
- "gitHead": "244188d5b206fb4a39eb198807b26cff6bf21b93"
76
+ "gitHead": "0784b85e60ee15b52086d6e1ff0196b24e355881"
77
77
  }
@@ -7,6 +7,7 @@ const {
7
7
  VCLDidJwk,
8
8
  VCLAuthTokenDescriptor,
9
9
  } = require('@verii/vnf-nodejs-wallet-sdk');
10
+ const { feedsService } = require('../../entities/feeds');
10
11
 
11
12
  /**
12
13
  * @param {import('fastify').FastifyInstance} fastify
@@ -31,9 +32,26 @@ const feedsController = async (fastify) => {
31
32
  filter: { auth0UserId: user.sub },
32
33
  });
33
34
 
34
- return feeds;
35
+ if (feeds.length === 0) {
36
+ return { feeds: [] };
37
+ }
38
+
39
+ const disclosureIds = feeds.map((feed) => feed.disclosureId);
40
+
41
+ const disclosures = await repos.disclosures.find({
42
+ filter: feedsService.buildDisclosureFilter(user.sub, disclosureIds),
43
+ });
44
+
45
+ const disclosureMap = feedsService.createDisclosureMap(disclosures);
46
+ const enrichedFeeds = feedsService.enrichFeedsWithDisclosureData(
47
+ feeds,
48
+ disclosureMap
49
+ );
50
+
51
+ return { feeds: enrichedFeeds };
35
52
  }
36
53
  );
54
+
37
55
  fastify.delete(
38
56
  '/:feedId',
39
57
  {
@@ -2,36 +2,49 @@ const webWalletGetFeedsResponseSchema = {
2
2
  $id: 'https://velocitycareerlabs.io/webwallet-get-feeds-response.schema.json',
3
3
  title: 'webwallet-get-feeds-response',
4
4
  description: 'an array of feed objects returned by webwallet get feeds',
5
- type: 'array',
6
- items: {
7
- type: 'object',
8
- properties: {
9
- id: { type: 'string' },
10
- auth0UserId: { type: 'string' },
11
- authToken: { type: 'object', additionalProperties: true },
12
- deeplink: { type: 'string' },
13
- disclosureId: { type: 'string' },
14
- inspectorDid: { type: 'string' },
15
- credentialTypes: {
16
- type: 'array',
17
- items: { type: 'string' },
5
+ type: 'object',
6
+ properties: {
7
+ feeds: {
8
+ type: 'array',
9
+ items: {
10
+ type: 'object',
11
+ properties: {
12
+ id: { type: 'string' },
13
+ inspectorDid: { type: 'string' },
14
+ credentialTypes: {
15
+ type: 'array',
16
+ items: { type: 'string' },
17
+ },
18
+ presentationMetadata: {
19
+ type: 'object',
20
+ additionalProperties: true,
21
+ },
22
+ sharedCredentials: {
23
+ type: 'array',
24
+ items: {
25
+ type: 'string',
26
+ },
27
+ },
28
+ disclosureId: { type: 'string' },
29
+ createdAt: { type: 'string' },
30
+ updatedAt: { type: 'string' },
31
+ },
32
+ required: [
33
+ 'id',
34
+ 'disclosureId',
35
+ 'inspectorDid',
36
+ 'credentialTypes',
37
+ 'presentationMetadata',
38
+ 'sharedCredentials',
39
+ 'createdAt',
40
+ 'updatedAt',
41
+ ],
42
+ additionalProperties: false,
18
43
  },
19
- createdAt: { type: 'string' },
20
- updatedAt: { type: 'string' },
21
- revokedAt: { type: 'string' },
22
44
  },
23
- required: [
24
- 'auth0UserId',
25
- 'authToken',
26
- 'deeplink',
27
- 'disclosureId',
28
- 'inspectorDid',
29
- 'credentialTypes',
30
- 'createdAt',
31
- 'updatedAt',
32
- ],
33
- additionalProperties: false,
34
45
  },
46
+ required: ['feeds'],
47
+ additionalProperties: false,
35
48
  };
36
49
 
37
50
  module.exports = {
@@ -1,4 +1,5 @@
1
1
  module.exports = {
2
2
  feedsRepoPlugin: require('./repos/feeds.repo'),
3
+ feedsService: require('./orchestrators'),
3
4
  ...require('./schemas'),
4
5
  };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Extract disclosureId from presentationDefinitionId
3
+ * @param {string} presentationDefinitionId - Format: "exchangeId.disclosureId"
4
+ * @returns {string} disclosureId
5
+ */
6
+ const extractDisclosureId = (presentationDefinitionId) => {
7
+ if (
8
+ !presentationDefinitionId ||
9
+ typeof presentationDefinitionId !== 'string'
10
+ ) {
11
+ throw new Error('Invalid presentationDefinitionId');
12
+ }
13
+
14
+ const parts = presentationDefinitionId.split('.');
15
+ if (parts.length < 2) {
16
+ throw new Error(
17
+ 'presentationDefinitionId must contain exchangeId and disclosureId separated by "."'
18
+ );
19
+ }
20
+
21
+ return parts[1];
22
+ };
23
+
24
+ /**
25
+ * Create a map of disclosures grouped by disclosureId
26
+ * @param {Array} disclosures - Array of disclosure objects
27
+ * @returns {Object} Map with disclosureId as key and {presentationMetadata, sharedCredentials} as value
28
+ */
29
+ const createDisclosureMap = (disclosures) => {
30
+ if (!Array.isArray(disclosures)) {
31
+ return {};
32
+ }
33
+
34
+ return disclosures.reduce((disclosureMatched, disclosure) => {
35
+ const disclosureId = extractDisclosureId(
36
+ disclosure.presentationDefinitionId
37
+ );
38
+
39
+ if (!disclosureMatched[disclosureId]) {
40
+ return {
41
+ ...disclosureMatched,
42
+ [disclosureId]: {
43
+ presentationMetadata: disclosure.presentation?.metadata || {},
44
+ sharedCredentials: disclosure.sharedCredentials || [],
45
+ },
46
+ };
47
+ }
48
+
49
+ return {
50
+ ...disclosureMatched,
51
+ [disclosureId]: {
52
+ ...disclosureMatched[disclosureId],
53
+ sharedCredentials: [
54
+ ...disclosureMatched[disclosureId].sharedCredentials,
55
+ ...(disclosure.sharedCredentials || []),
56
+ ],
57
+ },
58
+ };
59
+ }, {});
60
+ };
61
+
62
+ /**
63
+ * Enrich feeds with presentation metadata and shared credentials
64
+ * @param {Array} feeds - Array of feed objects
65
+ * @param {Object} disclosureMap - Map of disclosures by disclosureId
66
+ * @returns {Array} Enriched feeds array
67
+ */
68
+ const enrichFeedsWithDisclosureData = (feeds, disclosureMap) => {
69
+ if (!Array.isArray(feeds)) {
70
+ return [];
71
+ }
72
+
73
+ return feeds.map((feed) => ({
74
+ ...feed,
75
+ presentationMetadata:
76
+ disclosureMap[feed.disclosureId]?.presentationMetadata || {},
77
+ sharedCredentials:
78
+ disclosureMap[feed.disclosureId]?.sharedCredentials || [],
79
+ }));
80
+ };
81
+
82
+ /**
83
+ * Build MongoDB filter for finding disclosures by disclosureIds
84
+ * @param {string} auth0UserId - User ID
85
+ * @param {Array} disclosureIds - Array of disclosure IDs
86
+ * @returns {Object} MongoDB filter object
87
+ */
88
+ const buildDisclosureFilter = (auth0UserId, disclosureIds) => {
89
+ return {
90
+ auth0UserId,
91
+ feed: true,
92
+ $expr: {
93
+ $in: [
94
+ { $arrayElemAt: [{ $split: ['$presentationDefinitionId', '.'] }, 1] },
95
+ disclosureIds,
96
+ ],
97
+ },
98
+ };
99
+ };
100
+
101
+ module.exports = {
102
+ extractDisclosureId,
103
+ createDisclosureMap,
104
+ enrichFeedsWithDisclosureData,
105
+ buildDisclosureFilter,
106
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ ...require('./feedsService'),
3
+ };
@@ -2,6 +2,7 @@ const { mongoDb } = require('@spencejs/spence-mongo-repos');
2
2
  const nock = require('nock');
3
3
  const buildFastify = require('./helpers/webwallet-build-fastify');
4
4
  const initFeedsFactory = require('./factories/feeds');
5
+ const initDisclosuresFactory = require('./factories/disclosures');
5
6
 
6
7
  const auth0Token =
7
8
  // eslint-disable-next-line max-len
@@ -23,18 +24,22 @@ jest.mock('@verii/vnf-nodejs-wallet-sdk', () => {
23
24
  describe('Feeds Controller', () => {
24
25
  let fastify;
25
26
  let persistFeeds;
27
+ let persistDisclosures;
26
28
 
27
29
  beforeAll(async () => {
28
30
  fastify = buildFastify();
29
31
  nock('https://localhost/').get('/.well-known/jwks.json').reply(200, jwks);
30
32
  await fastify.ready();
31
33
  await mongoDb().collection('feeds').deleteMany({});
34
+ await mongoDb().collection('disclosures').deleteMany({});
32
35
  ({ persistFeeds } = initFeedsFactory(fastify));
36
+ ({ persistDisclosures } = initDisclosuresFactory(fastify));
33
37
  });
34
38
 
35
39
  afterEach(async () => {
36
40
  nock.cleanAll();
37
41
  await mongoDb().collection('feeds').deleteMany({});
42
+ await mongoDb().collection('disclosures').deleteMany({});
38
43
  });
39
44
 
40
45
  afterAll(async () => {
@@ -43,9 +48,27 @@ describe('Feeds Controller', () => {
43
48
  });
44
49
 
45
50
  describe('getFeeds', () => {
46
- it('should return list of feeds on success', async () => {
51
+ it('should return list of feeds with presentation metadata and shared credentials', async () => {
52
+ const disclosureId = '68736ac609c41e44796d040e';
53
+ const exchangeId = '657b15a1b4de9ac4f06b63ee';
54
+
47
55
  await persistFeeds({
48
56
  auth0UserId,
57
+ disclosureId,
58
+ });
59
+
60
+ await persistDisclosures({
61
+ auth0UserId,
62
+ presentationDefinitionId: `${exchangeId}.${disclosureId}`,
63
+ feed: true,
64
+ sharedCredentials: ['credential1', 'credential2'],
65
+ presentation: {
66
+ metadata: {
67
+ client_name: 'University of Massachusetts Amherst',
68
+ logo_uri: 'https://example.com/logo.png',
69
+ tos_uri: 'https://example.com/terms',
70
+ },
71
+ },
49
72
  });
50
73
 
51
74
  const response = await fastify.inject({
@@ -57,7 +80,19 @@ describe('Feeds Controller', () => {
57
80
  });
58
81
 
59
82
  expect(response.statusCode).toBe(200);
60
- expect(response.json()).toHaveLength(1);
83
+ const responseData = response.json();
84
+ expect(responseData).toHaveProperty('feeds');
85
+ expect(responseData.feeds).toHaveLength(1);
86
+
87
+ const feed = responseData.feeds[0];
88
+ expect(feed).toHaveProperty('presentationMetadata');
89
+ expect(feed.presentationMetadata).toEqual({
90
+ client_name: 'University of Massachusetts Amherst',
91
+ logo_uri: 'https://example.com/logo.png',
92
+ tos_uri: 'https://example.com/terms',
93
+ });
94
+ expect(feed).toHaveProperty('sharedCredentials');
95
+ expect(feed.sharedCredentials).toEqual(['credential1', 'credential2']);
61
96
  });
62
97
  });
63
98