FlourishHealth / ferns-api

Apache License 2.0
1 stars 3 forks source link

Update how we populate models #444

Closed joshgachnang closed 3 weeks ago

joshgachnang commented 1 month ago

User description

Ditch the option to pass a function for populatePaths. This was causing a lot of type errors and was from the days before responseHandler was used all over. Now versioning can be handled in responseHandler. Add a utility function unpopulate to help with versioning (for clients that shouldn't have a population).

Update the signature for PopulatePaths to allow limiting the fields that are populated and an openApiComponent to allow a nice, reusable component, which makes frontend SDKs nicer.

This is a backwards incompatible change, so apps using this will need to change their populatePaths to match the new style. I tried to make it work but typing and OpenAPI wasn't possible.


PR Type

enhancement, tests


Description


Changes walkthrough 📝

Relevant files
Tests
4 files
api.test.ts
Update and extend tests for populate functionality             

src/api.test.ts
  • Updated populatePaths to use objects with path and fields.
  • Added new test cases for listing with populated fields.
  • Removed tests related to the old populate function.
  • +33/-70 
    openApi.test.ts
    Add OpenAPI tests for populated fields                                     

    src/openApi.test.ts
  • Added tests for OpenAPI with populated fields.
  • Introduced addRoutesPopulate for testing OpenAPI population.
  • +136/-1 
    populate.test.ts
    Add tests for unpopulate function                                               

    src/populate.test.ts
  • Added tests for unpopulate function.
  • Verified unpopulation of nested fields.
  • +65/-0   
    tests.ts
    Extend test schemas for population testing                             

    src/tests.ts
  • Updated User and Food interfaces with new fields.
  • Added likesSchema for nested population testing.
  • +16/-4   
    Enhancement
    6 files
    api.ts
    Refactor population handling in API                                           

    src/api.ts
  • Removed the option to pass a function for populatePaths.
  • Introduced addPopulateToQuery for handling population.
  • Updated FernsRouterOptions to use PopulatePath type.
  • +17/-41 
    expressServer.ts
    Enhance error logging in server setup                                       

    src/expressServer.ts - Improved error logging by including error stack.
    +2/-2     
    index.ts
    Export populate module                                                                     

    src/index.ts - Exported new `populate` module.
    +1/-0     
    openApi.ts
    Refactor OpenAPI model conversion                                               

    src/openApi.ts
  • Removed old convertModel function.
  • Integrated convertModel from populate module.
  • +2/-77   
    permissions.ts
    Update permissions middleware for new population method   

    src/permissions.ts
  • Replaced populate with addPopulateToQuery for query population.
  • +5/-5     
    populate.ts
    Implement population utilities and OpenAPI integration     

    src/populate.ts
  • Introduced addPopulateToQuery and unpopulate functions.
  • Implemented convertModel for OpenAPI integration.
  • +239/-0 

    💡 PR-Agent usage: Comment /help "your question" on any pull request to receive relevant information

    codiumai-pr-agent-pro[bot] commented 1 month ago

    CI Failure Feedback 🧐

    (Checks updated until commit https://github.com/FlourishHealth/ferns-api/commit/ca3546a15f912703d84c60f1602ab696e5e59010)

    **Action:** Run all tests (18.x, 6.0)
    **Failed stage:** [yarn lint](https://github.com/FlourishHealth/ferns-api/actions/runs/11133729646/job/30940439917) [❌]
    **Failed test name:** ""
    **Failure summary:** The action failed due to a linting error detected by ESLint:
  • In the file src/populate.ts, at line 49, column 24, there is a Prettier formatting error.
  • The error suggests replacing single quotes with double quotes in the expression '__proto__' ||
    firstKey === 'constructor' || firstKey === 'prototype'.
  • This issue can potentially be fixed automatically using the --fix option with Prettier.
  • Relevant error logs: ```yaml 1: ##[group]Operating System 2: Ubuntu ... 286: electionTimeout: { called: Long('0'), successful: Long('0') }, 287: freezeTimeout: { called: Long('0'), successful: Long('0') }, 288: numStepDownsCausedByHigherTerm: Long('0'), 289: numCatchUps: Long('0'), 290: numCatchUpsSucceeded: Long('0'), 291: numCatchUpsAlreadyCaughtUp: Long('0'), 292: numCatchUpsSkipped: Long('0'), 293: numCatchUpsTimedOut: Long('0'), 294: numCatchUpsFailedWithError: Long('0'), 295: numCatchUpsFailedWithNewTerm: Long('0'), 296: numCatchUpsFailedWithReplSetAbortPrimaryCatchUpCmd: Long('0'), ... 547: 'Bytes released to the OS take up virtual address space but no physical memory.\n' 548: } 549: }, 550: tenantMigrations: { 551: currentMigrationsDonating: Long('0'), 552: currentMigrationsReceiving: Long('0'), 553: totalSuccessfulMigrationsDonated: Long('0'), 554: totalSuccessfulMigrationsReceived: Long('0'), 555: totalFailedMigrationsDonated: Long('0'), 556: totalFailedMigrationsReceived: Long('0') ... 663: 'eviction gave up due to needing to remove a record from the history store but checkpoint is running': 0, 664: 'eviction passes of a file': 0, 665: 'eviction server candidate queue empty when topping up': 0, 666: 'eviction server candidate queue not empty when topping up': 0, 667: 'eviction server evicting pages': 0, 668: 'eviction server skips dirty pages during a running checkpoint': 0, 669: 'eviction server skips metadata pages with history': 0, 670: 'eviction server skips pages that are written with transactions greater than the last running': 0, 671: 'eviction server skips pages that previously failed eviction and likely will again': 0, ... 689: 'eviction walk target pages reduced due to history store cache pressure': 0, 690: 'eviction walk target strategy both clean and dirty pages': 0, 691: 'eviction walk target strategy only clean pages': 0, 692: 'eviction walk target strategy only dirty pages': 0, 693: 'eviction walks abandoned': 0, 694: 'eviction walks gave up because they restarted their walk twice': 0, 695: 'eviction walks gave up because they saw too many pages and found no candidates': 0, 696: 'eviction walks gave up because they saw too many pages and found too few candidates': 0, 697: 'eviction walks random search fails to locate a page, results in a null position': 0, ... 703: 'eviction worker thread created': 0, 704: 'eviction worker thread evicting pages': 0, 705: 'eviction worker thread removed': 0, 706: 'eviction worker thread stable number': 0, 707: 'files with active eviction walks': 0, 708: 'files with new eviction walks started': 0, 709: 'force re-tuning of eviction workers once in a while': 0, 710: 'forced eviction - do not retry count to evict pages selected to evict during reconciliation': 0, 711: 'forced eviction - history store pages failed to evict while session has history store cursor open': 0, ... 769: 'pages read into cache after truncate': 7, 770: 'pages read into cache after truncate in prepare state': 0, 771: 'pages removed from the ordinary queue to be queued for urgent eviction': 0, 772: 'pages requested from the cache': 188, 773: 'pages seen by eviction walk': 0, 774: 'pages seen by eviction walk that are already queued': 0, 775: 'pages selected for eviction unable to be evicted': 0, 776: 'pages selected for eviction unable to be evicted because of active children on an internal page': 0, 777: 'pages selected for eviction unable to be evicted because of failure in reconciliation': 0, ... 1029: session: { 1030: 'attempts to remove a local object and the object is in use': 0, 1031: 'flush_tier operation calls': 0, 1032: 'flush_tier tables skipped due to no checkpoint': 0, 1033: 'flush_tier tables switched': 0, 1034: 'local objects removed': 0, 1035: 'open session count': 15, 1036: 'session query timestamp calls': 0, 1037: 'table alter failed calls': 0, 1038: 'table alter successful calls': 0, 1039: 'table alter triggering checkpoint calls': 0, 1040: 'table alter unchanged and skipped': 0, 1041: 'table compact failed calls': 0, 1042: 'table compact failed calls due to cache pressure': 0, 1043: 'table compact running': 0, 1044: 'table compact skipped as process would not reduce file size': 0, 1045: 'table compact successful calls': 0, 1046: 'table compact timeout': 0, 1047: 'table create failed calls': 0, 1048: 'table create successful calls': 9, 1049: 'table drop failed calls': 0, 1050: 'table drop successful calls': 0, 1051: 'table rename failed calls': 0, 1052: 'table rename successful calls': 0, 1053: 'table salvage failed calls': 0, 1054: 'table salvage successful calls': 0, 1055: 'table truncate failed calls': 0, 1056: 'table truncate successful calls': 0, 1057: 'table verify failed calls': 0, ... 1159: 'transactions rolled back': 9, 1160: 'update conflicts': 0 1161: }, 1162: concurrentTransactions: { 1163: write: { out: 0, available: 128, totalTickets: 128 }, 1164: read: { out: 0, available: 128, totalTickets: 128 } 1165: }, 1166: 'snapshot-window-settings': { 1167: 'total number of SnapshotTooOld errors': Long('0'), ... 1251: '$skip': Long('0'), 1252: '$sort': Long('0'), 1253: '$sortByCount': Long('0'), 1254: '$unionWith': Long('0'), 1255: '$unset': Long('0'), 1256: '$unwind': Long('0'), 1257: '$vectorSearch': Long('0') 1258: }, 1259: changeStreams: { largeEventsFailed: Long('0'), largeEventsSplit: Long('0') }, 1260: commands: { 1261: '': Long('1'), 1262: _addShard: { failed: Long('0'), total: Long('0') }, 1263: _configsvrAbortReshardCollection: { failed: Long('0'), total: Long('0') }, 1264: _configsvrAddShard: { failed: Long('0'), total: Long('0') }, 1265: _configsvrAddShardToZone: { failed: Long('0'), total: Long('0') }, 1266: _configsvrBalancerCollectionStatus: { failed: Long('0'), total: Long('0') }, 1267: _configsvrBalancerStart: { failed: Long('0'), total: Long('0') }, 1268: _configsvrBalancerStatus: { failed: Long('0'), total: Long('0') }, 1269: _configsvrBalancerStop: { failed: Long('0'), total: Long('0') }, 1270: _configsvrCleanupReshardCollection: { failed: Long('0'), total: Long('0') }, 1271: _configsvrClearJumboFlag: { failed: Long('0'), total: Long('0') }, 1272: _configsvrCollMod: { failed: Long('0'), total: Long('0') }, 1273: _configsvrCommitChunkMigration: { failed: Long('0'), total: Long('0') }, 1274: _configsvrCommitChunkSplit: { failed: Long('0'), total: Long('0') }, 1275: _configsvrCommitChunksMerge: { failed: Long('0'), total: Long('0') }, 1276: _configsvrCommitMovePrimary: { failed: Long('0'), total: Long('0') }, 1277: _configsvrCommitReshardCollection: { failed: Long('0'), total: Long('0') }, 1278: _configsvrConfigureCollectionBalancing: { failed: Long('0'), total: Long('0') }, 1279: _configsvrCreateDatabase: { failed: Long('0'), total: Long('0') }, 1280: _configsvrEnsureChunkVersionIsGreaterThan: { failed: Long('0'), total: Long('0') }, 1281: _configsvrMoveChunk: { failed: Long('0'), total: Long('0') }, 1282: _configsvrMoveRange: { failed: Long('0'), total: Long('0') }, 1283: _configsvrRefineCollectionShardKey: { failed: Long('0'), total: Long('0') }, 1284: _configsvrRemoveChunks: { failed: Long('0'), total: Long('0') }, 1285: _configsvrRemoveShard: { failed: Long('0'), total: Long('0') }, 1286: _configsvrRemoveShardFromZone: { failed: Long('0'), total: Long('0') }, 1287: _configsvrRemoveTags: { failed: Long('0'), total: Long('0') }, 1288: _configsvrRenameCollectionMetadata: { failed: Long('0'), total: Long('0') }, 1289: _configsvrRepairShardedCollectionChunksHistory: { failed: Long('0'), total: Long('0') }, 1290: _configsvrReshardCollection: { failed: Long('0'), total: Long('0') }, 1291: _configsvrRunRestore: { failed: Long('0'), total: Long('0') }, 1292: _configsvrSetAllowMigrations: { failed: Long('0'), total: Long('0') }, 1293: _configsvrSetClusterParameter: { failed: Long('0'), total: Long('0') }, 1294: _configsvrSetUserWriteBlockMode: { failed: Long('0'), total: Long('0') }, 1295: _configsvrUpdateZoneKeyRange: { failed: Long('0'), total: Long('0') }, 1296: _flushDatabaseCacheUpdates: { failed: Long('0'), total: Long('0') }, 1297: _flushDatabaseCacheUpdatesWithWriteConcern: { failed: Long('0'), total: Long('0') }, 1298: _flushReshardingStateChange: { failed: Long('0'), total: Long('0') }, 1299: _flushRoutingTableCacheUpdates: { failed: Long('0'), total: Long('0') }, 1300: _flushRoutingTableCacheUpdatesWithWriteConcern: { failed: Long('0'), total: Long('0') }, 1301: _getNextSessionMods: { failed: Long('0'), total: Long('0') }, 1302: _getUserCacheGeneration: { failed: Long('0'), total: Long('0') }, 1303: _isSelf: { failed: Long('0'), total: Long('0') }, 1304: _killOperations: { failed: Long('0'), total: Long('0') }, 1305: _mergeAuthzCollections: { failed: Long('0'), total: Long('0') }, 1306: _migrateClone: { failed: Long('0'), total: Long('0') }, 1307: _recvChunkAbort: { failed: Long('0'), total: Long('0') }, 1308: _recvChunkCommit: { failed: Long('0'), total: Long('0') }, 1309: _recvChunkReleaseCritSec: { failed: Long('0'), total: Long('0') }, 1310: _recvChunkStart: { failed: Long('0'), total: Long('0') }, 1311: _recvChunkStatus: { failed: Long('0'), total: Long('0') }, 1312: _shardsvrAbortReshardCollection: { failed: Long('0'), total: Long('0') }, 1313: _shardsvrCleanupReshardCollection: { failed: Long('0'), total: Long('0') }, 1314: _shardsvrCloneCatalogData: { failed: Long('0'), total: Long('0') }, 1315: _shardsvrCollMod: { failed: Long('0'), total: Long('0') }, 1316: _shardsvrCollModParticipant: { failed: Long('0'), total: Long('0') }, 1317: _shardsvrCommitReshardCollection: { failed: Long('0'), total: Long('0') }, 1318: _shardsvrCompactStructuredEncryptionData: { failed: Long('0'), total: Long('0') }, 1319: _shardsvrCreateCollection: { failed: Long('0'), total: Long('0') }, 1320: _shardsvrCreateCollectionParticipant: { failed: Long('0'), total: Long('0') }, 1321: _shardsvrDropCollection: { failed: Long('0'), total: Long('0') }, 1322: _shardsvrDropCollectionIfUUIDNotMatching: { failed: Long('0'), total: Long('0') }, 1323: _shardsvrDropCollectionParticipant: { failed: Long('0'), total: Long('0') }, 1324: _shardsvrDropDatabase: { failed: Long('0'), total: Long('0') }, 1325: _shardsvrDropDatabaseParticipant: { failed: Long('0'), total: Long('0') }, 1326: _shardsvrDropIndexes: { failed: Long('0'), total: Long('0') }, 1327: _shardsvrGetStatsForBalancing: { failed: Long('0'), total: Long('0') }, 1328: _shardsvrJoinMigrations: { failed: Long('0'), total: Long('0') }, 1329: _shardsvrMovePrimary: { failed: Long('0'), total: Long('0') }, 1330: _shardsvrMoveRange: { failed: Long('0'), total: Long('0') }, 1331: _shardsvrParticipantBlock: { failed: Long('0'), total: Long('0') }, 1332: _shardsvrRefineCollectionShardKey: { failed: Long('0'), total: Long('0') }, 1333: _shardsvrRenameCollection: { failed: Long('0'), total: Long('0') }, 1334: _shardsvrRenameCollectionParticipant: { failed: Long('0'), total: Long('0') }, 1335: _shardsvrRenameCollectionParticipantUnblock: { failed: Long('0'), total: Long('0') }, 1336: _shardsvrReshardCollection: { failed: Long('0'), total: Long('0') }, 1337: _shardsvrReshardingOperationTime: { failed: Long('0'), total: Long('0') }, 1338: _shardsvrSetAllowMigrations: { failed: Long('0'), total: Long('0') }, 1339: _shardsvrSetClusterParameter: { failed: Long('0'), total: Long('0') }, 1340: _shardsvrSetUserWriteBlockMode: { failed: Long('0'), total: Long('0') }, 1341: _transferMods: { failed: Long('0'), total: Long('0') }, 1342: abortShardSplit: { failed: Long('0'), total: Long('0') }, 1343: abortTransaction: { failed: Long('0'), total: Long('0') }, 1344: aggregate: { failed: Long('0'), total: Long('1') }, 1345: appendOplogNote: { failed: Long('0'), total: Long('0') }, 1346: applyOps: { failed: Long('0'), total: Long('0') }, 1347: authenticate: { failed: Long('0'), total: Long('0') }, 1348: autoSplitVector: { failed: Long('0'), total: Long('0') }, 1349: availableQueryOptions: { failed: Long('0'), total: Long('0') }, 1350: buildInfo: { failed: Long('0'), total: Long('1') }, 1351: checkShardingIndex: { failed: Long('0'), total: Long('0') }, 1352: cleanupOrphaned: { failed: Long('0'), total: Long('0') }, 1353: cloneCollectionAsCapped: { failed: Long('0'), total: Long('0') }, 1354: clusterAbortTransaction: { failed: Long('0'), total: Long('0') }, 1355: clusterAggregate: { failed: Long('0'), total: Long('0') }, 1356: clusterCommitTransaction: { failed: Long('0'), total: Long('0') }, 1357: clusterDelete: { failed: Long('0'), total: Long('0') }, 1358: clusterFind: { failed: Long('0'), total: Long('0') }, 1359: clusterGetMore: { failed: Long('0'), total: Long('0') }, 1360: clusterInsert: { failed: Long('0'), total: Long('0') }, 1361: clusterUpdate: { 1362: arrayFilters: Long('0'), 1363: failed: Long('0'), 1364: pipeline: Long('0'), 1365: total: Long('0') 1366: }, 1367: collMod: { 1368: failed: Long('0'), 1369: total: Long('0'), 1370: validator: { failed: Long('0'), jsonSchema: Long('0'), total: Long('0') } 1371: }, 1372: collStats: { failed: Long('0'), total: Long('0') }, 1373: commitShardSplit: { failed: Long('0'), total: Long('0') }, 1374: commitTransaction: { failed: Long('0'), total: Long('0') }, 1375: compact: { failed: Long('0'), total: Long('0') }, 1376: compactStructuredEncryptionData: { failed: Long('0'), total: Long('0') }, 1377: connPoolStats: { failed: Long('0'), total: Long('0') }, 1378: connPoolSync: { failed: Long('0'), total: Long('0') }, 1379: connectionStatus: { failed: Long('0'), total: Long('0') }, 1380: convertToCapped: { failed: Long('0'), total: Long('0') }, 1381: coordinateCommitTransaction: { failed: Long('0'), total: Long('0') }, 1382: count: { failed: Long('0'), total: Long('0') }, 1383: create: { 1384: failed: Long('0'), 1385: total: Long('0'), 1386: validator: { failed: Long('0'), jsonSchema: Long('0'), total: Long('0') } 1387: }, 1388: createIndexes: { failed: Long('0'), total: Long('1') }, 1389: createRole: { failed: Long('0'), total: Long('0') }, 1390: createUser: { failed: Long('0'), total: Long('0') }, 1391: currentOp: { failed: Long('0'), total: Long('0') }, 1392: dataSize: { failed: Long('0'), total: Long('0') }, 1393: dbCheck: { failed: Long('0'), total: Long('0') }, 1394: dbHash: { failed: Long('0'), total: Long('0') }, 1395: dbStats: { failed: Long('0'), total: Long('0') }, 1396: delete: { failed: Long('0'), total: Long('0') }, 1397: distinct: { failed: Long('0'), total: Long('0') }, 1398: donorAbortMigration: { failed: Long('0'), total: Long('0') }, 1399: donorForgetMigration: { failed: Long('0'), total: Long('0') }, 1400: donorStartMigration: { failed: Long('0'), total: Long('0') }, 1401: driverOIDTest: { failed: Long('0'), total: Long('0') }, 1402: drop: { failed: Long('0'), total: Long('0') }, 1403: dropAllRolesFromDatabase: { failed: Long('0'), total: Long('0') }, 1404: dropAllUsersFromDatabase: { failed: Long('0'), total: Long('0') }, 1405: dropConnections: { failed: Long('0'), total: Long('0') }, 1406: dropDatabase: { failed: Long('0'), total: Long('0') }, 1407: dropIndexes: { failed: Long('0'), total: Long('0') }, 1408: dropRole: { failed: Long('0'), total: Long('0') }, 1409: dropUser: { failed: Long('0'), total: Long('0') }, 1410: endSessions: { failed: Long('0'), total: Long('0') }, 1411: explain: { failed: Long('0'), total: Long('0') }, 1412: features: { failed: Long('0'), total: Long('0') }, 1413: filemd5: { failed: Long('0'), total: Long('0') }, 1414: find: { failed: Long('0'), total: Long('0') }, 1415: findAndModify: { 1416: arrayFilters: Long('0'), 1417: failed: Long('0'), 1418: pipeline: Long('0'), 1419: total: Long('0') 1420: }, 1421: flushRouterConfig: { failed: Long('0'), total: Long('0') }, 1422: forgetShardSplit: { failed: Long('0'), total: Long('0') }, 1423: fsync: { failed: Long('0'), total: Long('0') }, 1424: fsyncUnlock: { failed: Long('0'), total: Long('0') }, 1425: getClusterParameter: { failed: Long('0'), total: Long('0') }, 1426: getCmdLineOpts: { failed: Long('0'), total: Long('0') }, 1427: getDatabaseVersion: { failed: Long('0'), total: Long('0') }, 1428: getDefaultRWConcern: { failed: Long('0'), total: Long('0') }, 1429: getDiagnosticData: { failed: Long('0'), total: Long('0') }, 1430: getLastError: { failed: Long('0'), total: Long('0') }, 1431: getLog: { failed: Long('0'), total: Long('0') }, 1432: getMore: { failed: Long('0'), total: Long('0') }, 1433: getParameter: { failed: Long('0'), total: Long('1') }, 1434: getShardMap: { failed: Long('0'), total: Long('0') }, 1435: getShardVersion: { failed: Long('0'), total: Long('0') }, 1436: getnonce: { failed: Long('0'), total: Long('0') }, 1437: grantPrivilegesToRole: { failed: Long('0'), total: Long('0') }, 1438: grantRolesToRole: { failed: Long('0'), total: Long('0') }, 1439: grantRolesToUser: { failed: Long('0'), total: Long('0') }, 1440: hello: { failed: Long('0'), total: Long('0') }, 1441: hostInfo: { failed: Long('0'), total: Long('0') }, 1442: insert: { failed: Long('0'), total: Long('0') }, 1443: internalRenameIfOptionsAndIndexesMatch: { failed: Long('0'), total: Long('0') }, 1444: invalidateUserCache: { failed: Long('0'), total: Long('0') }, 1445: isMaster: { failed: Long('0'), total: Long('5') }, 1446: killAllSessions: { failed: Long('0'), total: Long('0') }, 1447: killAllSessionsByPattern: { failed: Long('0'), total: Long('0') }, 1448: killCursors: { failed: Long('0'), total: Long('0') }, 1449: killOp: { failed: Long('0'), total: Long('0') }, 1450: killSessions: { failed: Long('0'), total: Long('0') }, 1451: listCollections: { failed: Long('0'), total: Long('0') }, 1452: listCommands: { failed: Long('0'), total: Long('0') }, 1453: listDatabases: { failed: Long('0'), total: Long('0') }, 1454: listIndexes: { failed: Long('2'), total: Long('2') }, 1455: lockInfo: { failed: Long('0'), total: Long('0') }, 1456: logRotate: { failed: Long('0'), total: Long('0') }, 1457: logout: { failed: Long('0'), total: Long('0') }, 1458: mapReduce: { failed: Long('0'), total: Long('0') }, 1459: mergeChunks: { failed: Long('0'), total: Long('0') }, 1460: ping: { failed: Long('0'), total: Long('0') }, 1461: planCacheClear: { failed: Long('0'), total: Long('0') }, 1462: planCacheClearFilters: { failed: Long('0'), total: Long('0') }, 1463: planCacheListFilters: { failed: Long('0'), total: Long('0') }, 1464: planCacheSetFilter: { failed: Long('0'), total: Long('0') }, 1465: prepareTransaction: { failed: Long('0'), total: Long('0') }, 1466: profile: { failed: Long('0'), total: Long('0') }, 1467: reIndex: { failed: Long('0'), total: Long('0') }, 1468: recipientForgetMigration: { failed: Long('0'), total: Long('0') }, 1469: recipientSyncData: { failed: Long('0'), total: Long('0') }, 1470: recipientVoteImportedFiles: { failed: Long('0'), total: Long('0') }, 1471: refreshSessions: { failed: Long('0'), total: Long('0') }, 1472: renameCollection: { failed: Long('0'), total: Long('0') }, 1473: replSetAbortPrimaryCatchUp: { failed: Long('0'), total: Long('0') }, 1474: replSetFreeze: { failed: Long('0'), total: Long('0') }, 1475: replSetGetConfig: { failed: Long('0'), total: Long('0') }, 1476: replSetGetRBID: { failed: Long('0'), total: Long('0') }, 1477: replSetGetStatus: { failed: Long('0'), total: Long('0') }, 1478: replSetHeartbeat: { failed: Long('0'), total: Long('0') }, 1479: replSetInitiate: { failed: Long('0'), total: Long('0') }, 1480: replSetMaintenance: { failed: Long('0'), total: Long('0') }, 1481: replSetReconfig: { failed: Long('0'), total: Long('0') }, 1482: replSetRequestVotes: { failed: Long('0'), total: Long('0') }, 1483: replSetResizeOplog: { failed: Long('0'), total: Long('0') }, 1484: replSetStepDown: { failed: Long('0'), total: Long('0') }, 1485: replSetStepDownWithForce: { failed: Long('0'), total: Long('0') }, 1486: replSetStepUp: { failed: Long('0'), total: Long('0') }, 1487: replSetSyncFrom: { failed: Long('0'), total: Long('0') }, 1488: replSetUpdatePosition: { failed: Long('0'), total: Long('0') }, 1489: revokePrivilegesFromRole: { failed: Long('0'), total: Long('0') }, 1490: revokeRolesFromRole: { failed: Long('0'), total: Long('0') }, 1491: revokeRolesFromUser: { failed: Long('0'), total: Long('0') }, 1492: rolesInfo: { failed: Long('0'), total: Long('0') }, 1493: rotateCertificates: { failed: Long('0'), total: Long('0') }, 1494: saslContinue: { failed: Long('0'), total: Long('0') }, 1495: saslStart: { failed: Long('0'), total: Long('0') }, 1496: serverStatus: { failed: Long('0'), total: Long('1') }, 1497: setClusterParameter: { failed: Long('0'), total: Long('0') }, 1498: setDefaultRWConcern: { failed: Long('0'), total: Long('0') }, 1499: setFeatureCompatibilityVersion: { failed: Long('0'), total: Long('0') }, 1500: setIndexCommitQuorum: { failed: Long('0'), total: Long('0') }, 1501: setParameter: { failed: Long('0'), total: Long('0') }, 1502: setProfilingFilterGlobally: { failed: Long('0'), total: Long('0') }, 1503: setShardVersion: { failed: Long('0'), total: Long('0') }, 1504: setUserWriteBlockMode: { failed: Long('0'), total: Long('0') }, 1505: shardingState: { failed: Long('0'), total: Long('0') }, 1506: shutdown: { failed: Long('0'), total: Long('0') }, 1507: splitChunk: { failed: Long('0'), total: Long('0') }, 1508: splitVector: { failed: Long('0'), total: Long('0') }, 1509: startRecordingTraffic: { failed: Long('0'), total: Long('0') }, 1510: startSession: { failed: Long('0'), total: Long('0') }, 1511: stopRecordingTraffic: { failed: Long('0'), total: Long('0') }, 1512: top: { failed: Long('0'), total: Long('0') }, 1513: update: { 1514: arrayFilters: Long('0'), 1515: failed: Long('0'), 1516: pipeline: Long('0'), 1517: total: Long('0') 1518: }, 1519: updateRole: { failed: Long('0'), total: Long('0') }, 1520: updateUser: { failed: Long('0'), total: Long('0') }, 1521: usersInfo: { failed: Long('0'), total: Long('0') }, 1522: validate: { failed: Long('0'), total: Long('0') }, 1523: validateDBMetadata: { failed: Long('0'), total: Long('0') }, 1524: voteCommitIndexBuild: { failed: Long('0'), total: Long('0') }, 1525: waitForFailPoint: { failed: Long('0'), total: Long('0') }, 1526: whatsmyuri: { failed: Long('0'), total: Long('0') } ... 1542: }, 1543: document: { 1544: deleted: Long('0'), 1545: inserted: Long('0'), 1546: returned: Long('0'), 1547: updated: Long('0') 1548: }, 1549: dotsAndDollarsFields: { inserts: Long('0'), updates: Long('0') }, 1550: getLastError: { 1551: wtime: { num: 0, totalMillis: 0 }, 1552: wtimeouts: Long('0'), 1553: default: { unsatisfiable: Long('0'), wtimeouts: Long('0') } 1554: }, 1555: mongos: { cursor: { moreThanOneBatch: Long('0'), totalOpened: Long('0') } }, 1556: operation: { 1557: scanAndOrder: Long('0'), 1558: temporarilyUnavailableErrors: Long('0'), 1559: temporarilyUnavailableErrorsConvertedToWriteConflict: Long('0'), 1560: temporarilyUnavailableErrorsEscaped: Long('0'), 1561: transactionTooLargeForCacheErrors: Long('0'), 1562: transactionTooLargeForCacheErrorsConvertedToWriteConflict: Long('0'), ... 1913: }, 1914: queryExecutor: { 1915: scanned: Long('0'), 1916: scannedObjects: Long('0'), 1917: collectionScans: { nonTailable: Long('0'), total: Long('0') } 1918: }, 1919: queryStats: { 1920: numEvicted: Long('0'), 1921: numHmacApplicationErrors: Long('0'), 1922: numQueryStatsStoreWriteErrors: Long('0'), ... 1939: }, 1940: buffer: { 1941: count: Long('0'), 1942: maxSizeBytes: Long('0'), 1943: sizeBytes: Long('0') 1944: }, 1945: initialSync: { 1946: completed: Long('0'), 1947: failedAttempts: Long('0'), ... 1992: ##[group]Run yarn lint 1993: yarn lint 1994: shell: /usr/bin/bash -e {0} 1995: ##[endgroup] 1996: yarn run v1.22.22 1997: $ eslint "src/**/*.ts*" 1998: Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. Assuming latest React version for linting. 1999: /home/runner/work/ferns-api/ferns-api/src/populate.ts 2000: ##[error] 49:24 error Replace `'__proto__'·||·firstKey·===·'constructor'·||·firstKey·===·'prototype'` with `"__proto__"·||·firstKey·===·"constructor"·||·firstKey·===·"prototype"` prettier/prettier 2001: ✖ 1 problem (1 error, 0 warnings) 2002: 1 error and 0 warnings potentially fixable with the `--fix` option. 2003: error Command failed with exit code 1. 2004: info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. 2005: ##[error]Process completed with exit code 1. ```

    ✨ CI feedback usage guide:
    The CI feedback tool (`/checks)` automatically triggers when a PR has a failed check. The tool analyzes the failed checks and provides several feedbacks: - Failed stage - Failed test name - Failure summary - Relevant error logs In addition to being automatically triggered, the tool can also be invoked manually by commenting on a PR: ``` /checks "https://github.com/{repo_name}/actions/runs/{run_number}/job/{job_number}" ``` where `{repo_name}` is the name of the repository, `{run_number}` is the run number of the failed check, and `{job_number}` is the job number of the failed check. #### Configuration options - `enable_auto_checks_feedback` - if set to true, the tool will automatically provide feedback when a check is failed. Default is true. - `excluded_checks_list` - a list of checks to exclude from the feedback, for example: ["check1", "check2"]. Default is an empty list. - `enable_help_text` - if set to true, the tool will provide a help message with the feedback. Default is true. - `persistent_comment` - if set to true, the tool will overwrite a previous checks comment with the new feedback. Default is true. - `final_update_message` - if `persistent_comment` is true and updating a previous checks message, the tool will also create a new message: "Persistent checks updated to latest commit". Default is true. See more information about the `checks` tool in the [docs](https://pr-agent-docs.codium.ai/tools/ci_feedback/).
    codiumai-pr-agent-pro[bot] commented 1 month ago

    PR Reviewer Guide 🔍

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Key issues to review

    Breaking Change
    Removal of function-based populatePaths option may break existing implementations Performance Concern
    Recursive unpopulate function may be inefficient for deeply nested structures Code Duplication
    Potential duplication of model conversion logic between openApi.ts and populate.ts
    codiumai-pr-agent-pro[bot] commented 1 month ago

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Error handling
    Add error handling for cases where the populate model is not found ___ **Consider adding error handling for the case when populateModel is undefined after
    attempting to retrieve it. This could prevent potential runtime errors if the model
    is not found.** [src/populate.ts [147-153]](https://github.com/FlourishHealth/ferns-api/pull/444/files#diff-fe6875cb88f2efad3be804290e45138f69636fd6fc90eeae56768702cfce1484R147-R153) ```diff let populateModel = model.schema.path(populatePath.path)?.options?.ref; const populatePathIsArray = Array.isArray(model.schema.path(populatePath.path).options.type); if (populatePathIsArray) { populateModel = model.schema.path(populatePath.path).options.type[0].ref; } if (!populateModel) { - return; + throw new Error(`Unable to find populate model for path: ${populatePath.path}`); } ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 9 Why: Adding error handling for undefined `populateModel` is crucial for preventing runtime errors and ensuring the application can handle unexpected situations gracefully. This suggestion addresses a potential bug and improves code reliability.
    9
    Code simplification
    Simplify nested property access using optional chaining and nullish coalescing ___ **Consider using optional chaining and nullish coalescing operators to simplify the
    nested property access and provide a default value for populateModel.** [src/populate.ts [147-151]](https://github.com/FlourishHealth/ferns-api/pull/444/files#diff-fe6875cb88f2efad3be804290e45138f69636fd6fc90eeae56768702cfce1484R147-R151) ```diff -let populateModel = model.schema.path(populatePath.path)?.options?.ref; -const populatePathIsArray = Array.isArray(model.schema.path(populatePath.path).options.type); -if (populatePathIsArray) { - populateModel = model.schema.path(populatePath.path).options.type[0].ref; -} +const populatePathOptions = model.schema.path(populatePath.path)?.options; +const populateModel = populatePathOptions?.ref ?? + (Array.isArray(populatePathOptions?.type) ? populatePathOptions.type[0].ref : undefined); ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 8 Why: The suggestion simplifies the code by using modern JavaScript features, making it more concise and readable. This change also ensures that `populateModel` is assigned a default value if not found, enhancing robustness.
    8
    Type safety
    Improve type safety by using more specific types for variables ___ **Consider using a more specific type for populateModel instead of any. This could be
    a generic type parameter or a union of possible model types.** [src/populate.ts [147-151]](https://github.com/FlourishHealth/ferns-api/pull/444/files#diff-fe6875cb88f2efad3be804290e45138f69636fd6fc90eeae56768702cfce1484R147-R151) ```diff -let populateModel = model.schema.path(populatePath.path)?.options?.ref; +let populateModel: string | undefined = model.schema.path(populatePath.path)?.options?.ref; const populatePathIsArray = Array.isArray(model.schema.path(populatePath.path).options.type); if (populatePathIsArray) { populateModel = model.schema.path(populatePath.path).options.type[0].ref; } ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 7 Why: The suggestion improves type safety by specifying a more precise type for `populateModel`. This change enhances code maintainability and reduces potential runtime errors related to type mismatches.
    7
    Naming convention
    Use a more descriptive variable name for imported modules to improve readability ___ **Consider using a more descriptive variable name for m2s to improve code readability.
    The full name "mongoose-to-swagger" or an abbreviation like "mongooseToSwagger"
    would be clearer.** [src/populate.ts [4]](https://github.com/FlourishHealth/ferns-api/pull/444/files#diff-fe6875cb88f2efad3be804290e45138f69636fd6fc90eeae56768702cfce1484R4-R4) ```diff -import m2s from "mongoose-to-swagger"; +import mongooseToSwagger from "mongoose-to-swagger"; ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 5 Why: While the suggestion improves code readability by using a more descriptive variable name, it is a minor enhancement and does not address any functional issues. It contributes to better maintainability and understanding of the code.
    5

    💡 Need additional feedback ? start a PR chat