FlourishHealth / ferns-api

Apache License 2.0
1 stars 3 forks source link

Add DateOnly schema type #413

Closed joshgachnang closed 2 months ago

joshgachnang commented 2 months ago

User description

Add a type that forces dates to end in 00:00:00.000Z UTC to match ferns-ui handling of date only fields.

Also remove the verbose but not very useful startup logging. It didn't include any of the FernsRouter routes and gets chatty with microservices and tests.


PR Type

enhancement, tests, dependencies


Description


Changes walkthrough ๐Ÿ“

Relevant files
Enhancement
expressServer.ts
Remove verbose startup logging in express server                 

src/expressServer.ts
  • Removed verbose startup logging.
  • Simplified route initialization.
  • +0/-7     
    plugins.ts
    Introduce `DateOnly` schema type for date normalization   

    src/plugins.ts
  • Introduced DateOnly schema type.
  • Implemented date casting to 00:00:00.000Z using Luxon.
  • Registered DateOnly with Mongoose.
  • +51/-1   
    Tests
    plugins.test.ts
    Add tests for `DateOnly` schema type                                         

    src/plugins.test.ts
  • Added DateOnly schema type tests.
  • Verified date adjustment to 00:00:00.000Z.
  • Tested invalid date handling.
  • +30/-1   
    Dependencies
    package.json
    Add `luxon` dependency for date handling                                 

    package.json - Added `luxon` dependency.
    +1/-0     

    ๐Ÿ’ก PR-Agent usage: Comment /help on the PR to get a list of all available PR-Agent tools and their descriptions

    codiumai-pr-agent-pro[bot] commented 2 months ago

    PR Reviewer Guide ๐Ÿ”

    โฑ๏ธ Estimated effort to review: 3 ๐Ÿ”ต๐Ÿ”ต๐Ÿ”ตโšชโšช
    ๐Ÿงช PR contains tests
    ๐Ÿ”’ No security concerns identified
    โšก Key issues to review

    Error Handling
    The error message in the `DateOnly` class cast method might not provide enough context about why the date is invalid. Consider enhancing the error details to help developers understand the cause of the error more clearly. Type Safety
    The casting logic in `DateOnly` uses `any` type for the input value, which could lead to potential type safety issues. Consider using more specific type annotations to improve code robustness and maintainability.
    codiumai-pr-agent-pro[bot] commented 2 months ago

    CI Failure Feedback ๐Ÿง

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

    PR Code Suggestions โœจ

    CategorySuggestion                                                                                                                                    Score
    Possible bug
    Handle null or undefined values gracefully in the DateOnly class to prevent runtime errors ___ **To improve the robustness and maintainability of the DateOnly class, consider
    handling the scenario where the input value is null or undefined. This will prevent
    runtime errors when null or undefined values are passed to the cast method.** [src/plugins.ts [165-195]](https://github.com/FlourishHealth/ferns-api/pull/413/files#diff-5d37292cf21755714262793e7553ee317a12b157d0601e27a0ca26e096a60c00R165-R195) ```diff +if (val == null) { + return undefined; +} if (val instanceof Date) { const date = DateTime.fromJSDate(val).toUTC().startOf("day"); if (!date.isValid) { throw new MongooseError.CastError( "DateOnly", val, this.path, new Error("Value is not a valid date") ); } return date.toJSDate(); } else if (typeof val === "string" || typeof val === "number") { const date = DateTime.fromJSDate(new Date(val)).toUTC().startOf("day"); if (!date.isValid) { throw new MongooseError.CastError( "DateOnly", val, this.path, new Error("Value is not a valid date") ); } return date.toJSDate(); } throw new MongooseError.CastError( "DateOnly", val, this.path, new Error("Value is not a valid date") ); ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 9 Why: This suggestion improves the robustness of the `DateOnly` class by handling `null` or `undefined` values, which can prevent potential runtime errors. This is a significant improvement for maintainability and reliability.
    9
    Performance
    Cache computationally expensive operations to enhance performance ___ **To enhance the performance and reduce redundancy, consider caching the result of
    DateTime.fromJSDate(new Date(val)).toUTC().startOf("day") when the input val is a
    string or number, as this operation is computationally expensive and is repeated in
    the else if block.** [src/plugins.ts [178-188]](https://github.com/FlourishHealth/ferns-api/pull/413/files#diff-5d37292cf21755714262793e7553ee317a12b157d0601e27a0ca26e096a60c00R178-R188) ```diff -const date = DateTime.fromJSDate(new Date(val)).toUTC().startOf("day"); -if (!date.isValid) { +const parsedDate = DateTime.fromJSDate(new Date(val)).toUTC().startOf("day"); +if (!parsedDate.isValid) { throw new MongooseError.CastError( "DateOnly", val, this.path, new Error("Value is not a valid date") ); } -return date.toJSDate(); +return parsedDate.toJSDate(); ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 8 Why: Caching the result of a computationally expensive operation can significantly enhance performance and reduce redundancy. This is a valuable optimization for the `DateOnly` class.
    8
    Enhancement
    Add back route logging for better traceability and debugging ___ **Consider adding back the logging of routes during the initialization for better
    traceability and debugging. This can be particularly useful in development
    environments or when diagnosing issues in production.** [src/expressServer.ts [281]](https://github.com/FlourishHealth/ferns-api/pull/413/files#diff-d8c51792951866599be44999fb26695bc96b6b7435d64cab68e024b90efdeb1cR281-R281) ```diff +logger.debug("Listening on routes:"); +app._router.stack.forEach((r: any) => { + if (r.route && r.route.path) { + logger.debug(`[Route] ${r.route.path}`); + } +}); return app; ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 7 Why: Adding back the route logging can be useful for debugging and traceability, especially in development environments. However, it may not be crucial for all use cases, so it is a moderate enhancement.
    7
    Maintainability
    Use more descriptive variable names in test cases to enhance readability ___ **Refactor the test case for DateOnly to use a more descriptive variable name than res
    for the result of the stuffModel.create method, enhancing code readability.** [src/plugins.test.ts [208-213]](https://github.com/FlourishHealth/ferns-api/pull/413/files#diff-80dedbdb2ef4dd17540ab73a859430da8c9ce7d17a11ccffa1ea73834c320a6eR208-R213) ```diff -const res = await stuffModel.create({ +const createdItem = await stuffModel.create({ name: "Things", ownerId: "123", date: "2005-10-10T17:17:17.017Z" as any, }); -assert.strictEqual(res.date.toISOString(), "2005-10-10T00:00:00.000Z"); +assert.strictEqual(createdItem.date.toISOString(), "2005-10-10T00:00:00.000Z"); ``` - [ ] **Apply this suggestion**
    Suggestion importance[1-10]: 6 Why: Using more descriptive variable names improves code readability and maintainability. This is a minor but valuable improvement for understanding the test cases.
    6