)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"cc40a11b484ac9182cffadfbfe5bc2326a428071","unresolved":false,"context_lines":[{"line_number":6,"context_line":""},{"line_number":7,"context_line":"[WIP] Add password expiration queries for PCI-DSS"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"New queries added for determining when passwords will expires."},{"line_number":10,"context_line":"The following are the new queries:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/users?password_expires_at\u003d{timestamp}"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"5a74a57a_0bbc446b","line":9,"range":{"start_line":9,"start_character":60,"end_line":9,"end_character":61},"updated":"2016-11-30 23:45:27.000000000","message":"can get rid of the s","commit_id":"18392dbf8bb760c02a39c155415fe1ad69753d10"},{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"64f31c4b5bf75e7fae08198931fe2130afcde57b","unresolved":false,"context_lines":[{"line_number":18,"context_line":""},{"line_number":19,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."},{"line_number":20,"context_line":""},{"line_number":21,"context_line":"Partially-Implements: pci-dss-query-password-expired-users"},{"line_number":22,"context_line":"Change-Id: If0b9cc3c8af92b2ea5d41a0e8afeb78e12b7689c"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":5,"id":"3a71b18c_60608936","line":21,"range":{"start_line":21,"start_character":21,"end_line":21,"end_character":22},"updated":"2016-12-02 05:33:32.000000000","message":"Need to add \"bp\" here so it\u0027ll link to the blueprint: \"bp pci-dss-query-password-expired-users\"","commit_id":"16413169c1427cf19638522d4c6782aabf25147d"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"36f2472b5ae6b21e9ee2ba0a6ed62cc93d95a6f7","unresolved":false,"context_lines":[{"line_number":9,"context_line":"New query added for determining when passwords will expire."},{"line_number":10,"context_line":"The following are the new query:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/users?password_expires_at\u003d{timestamp}"},{"line_number":13,"context_line":"- Lists users whose password will expire at the given timestamp"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"3a71b18c_00037c0e","line":12,"updated":"2016-12-07 18:42:48.000000000","message":"/v3/users?password_expires_at\u003d{comparator}:{timestamp}","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8afa4e0411291aaf494159ada1935ca6625285f4","unresolved":false,"context_lines":[{"line_number":9,"context_line":"New query added for determining when passwords will expire."},{"line_number":10,"context_line":"The following are the new query:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/users?password_expires_at\u003d{timestamp}"},{"line_number":13,"context_line":"- Lists users whose password will expire at the given timestamp"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"3a71b18c_7b8cba35","line":12,"in_reply_to":"3a71b18c_00037c0e","updated":"2016-12-08 14:25:51.000000000","message":"Done","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"36f2472b5ae6b21e9ee2ba0a6ed62cc93d95a6f7","unresolved":false,"context_lines":[{"line_number":10,"context_line":"The following are the new query:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/users?password_expires_at\u003d{timestamp}"},{"line_number":13,"context_line":"- Lists users whose password will expire at the given timestamp"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."},{"line_number":16,"context_line":"Also allows for comparators lt, le, gt ge, and eq to filter further."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"3a71b18c_800f8c2d","line":13,"range":{"start_line":13,"start_character":0,"end_line":13,"end_character":63},"updated":"2016-12-07 18:42:48.000000000","message":"this is not always true, depends on the comparator","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8afa4e0411291aaf494159ada1935ca6625285f4","unresolved":false,"context_lines":[{"line_number":10,"context_line":"The following are the new query:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/users?password_expires_at\u003d{timestamp}"},{"line_number":13,"context_line":"- Lists users whose password will expire at the given timestamp"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."},{"line_number":16,"context_line":"Also allows for comparators lt, le, gt ge, and eq to filter further."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"3a71b18c_9b8196fe","line":13,"range":{"start_line":13,"start_character":0,"end_line":13,"end_character":63},"in_reply_to":"3a71b18c_800f8c2d","updated":"2016-12-08 14:25:51.000000000","message":"Done","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"36f2472b5ae6b21e9ee2ba0a6ed62cc93d95a6f7","unresolved":false,"context_lines":[{"line_number":13,"context_line":"- Lists users whose password will expire at the given timestamp"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."},{"line_number":16,"context_line":"Also allows for comparators lt, le, gt ge, and eq to filter further."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"Partially-Implements: bp pci-dss-query-password-expired-users"},{"line_number":19,"context_line":"Change-Id: If0b9cc3c8af92b2ea5d41a0e8afeb78e12b7689c"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"3a71b18c_201f985c","line":16,"range":{"start_line":16,"start_character":0,"end_line":16,"end_character":68},"updated":"2016-12-07 18:42:48.000000000","message":"{comparator} is one of lt, le, gt ge, and eq to filter in.","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8afa4e0411291aaf494159ada1935ca6625285f4","unresolved":false,"context_lines":[{"line_number":13,"context_line":"- Lists users whose password will expire at the given timestamp"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"{timestamp} is a datetime in the format \"YYYY-MM-DDTHH:mm:ssZ\"."},{"line_number":16,"context_line":"Also allows for comparators lt, le, gt ge, and eq to filter further."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"Partially-Implements: bp pci-dss-query-password-expired-users"},{"line_number":19,"context_line":"Change-Id: If0b9cc3c8af92b2ea5d41a0e8afeb78e12b7689c"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"3a71b18c_db7b0e28","line":16,"range":{"start_line":16,"start_character":0,"end_line":16,"end_character":68},"in_reply_to":"3a71b18c_201f985c","updated":"2016-12-08 14:25:51.000000000","message":"Done","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"75c5b4227e3732b6558b92876253b3bb087b8e07","unresolved":false,"context_lines":[{"line_number":20,"context_line":"If no operator is given, it is treated as eq."},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"Examples:"},{"line_number":23,"context_line":"- GET /v3/users?password_expires_at\u003dlt:2016-11-06T15:32:17Z"},{"line_number":24,"context_line":"- GET /v3/groups/079c578fd99b428ab61fcd4c9bd88ecd/users?password_expires_at\u003dgt:2016-12-08T22:02:00Z"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"Partially-Implements: bp pci-dss-query-password-expired-users"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":33,"id":"ba5201f7_eae50809","line":23,"range":{"start_line":23,"start_character":38,"end_line":23,"end_character":39},"updated":"2017-01-11 16:59:37.000000000","message":"why did you choose \":\" as a separator?\n\nI am asking because we use __ for filtering. For example, in order to filter users that start with \"hello\", we make a query to /users/?name\u003dstartswith__hello.\n\nMaybe we should keep using __ for separator everywhere in keystone?","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"018f57bf62417c0d99ee734d3dca26b22da3ff49","unresolved":false,"context_lines":[{"line_number":20,"context_line":"If no operator is given, it is treated as eq."},{"line_number":21,"context_line":""},{"line_number":22,"context_line":"Examples:"},{"line_number":23,"context_line":"- GET /v3/users?password_expires_at\u003dlt:2016-11-06T15:32:17Z"},{"line_number":24,"context_line":"- GET /v3/groups/079c578fd99b428ab61fcd4c9bd88ecd/users?password_expires_at\u003dgt:2016-12-08T22:02:00Z"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"Partially-Implements: bp pci-dss-query-password-expired-users"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":33,"id":"ba5201f7_5b46dd4f","line":23,"range":{"start_line":23,"start_character":38,"end_line":23,"end_character":39},"in_reply_to":"ba5201f7_eae50809","updated":"2017-01-11 19:44:54.000000000","message":"I used \u0027:\u0027 as per http://specs.openstack.org/openstack/api-wg/guidelines/pagination_filter_sort.html#time-based-filtering-queries","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"9877311534d44f67b0e1a75c250c7909c8260fba","unresolved":false,"context_lines":[{"line_number":7,"context_line":"Add password expiration queries for PCI-DSS"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"New query added for determining when passwords will expire."},{"line_number":10,"context_line":"The following is the new queries:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/groups/{group_id}/users?password_expires_at\u003d{operator}:{timestamp}"},{"line_number":13,"context_line":"- Lists users belonging to a group whose password will expire based"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":41,"id":"5a3905b3_d1256409","line":10,"range":{"start_line":10,"start_character":14,"end_line":10,"end_character":16},"updated":"2017-01-21 11:34:51.000000000","message":"nit: are","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"1ceaa2a907ff144917913101211c11ef15f64375","unresolved":false,"context_lines":[{"line_number":7,"context_line":"Add password expiration queries for PCI-DSS"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"New query added for determining when passwords will expire."},{"line_number":10,"context_line":"The following is the new queries:"},{"line_number":11,"context_line":""},{"line_number":12,"context_line":"/v3/groups/{group_id}/users?password_expires_at\u003d{operator}:{timestamp}"},{"line_number":13,"context_line":"- Lists users belonging to a group whose password will expire based"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":41,"id":"5a3905b3_67a597a0","line":10,"range":{"start_line":10,"start_character":14,"end_line":10,"end_character":16},"in_reply_to":"5a3905b3_d1256409","updated":"2017-01-23 15:48:42.000000000","message":"Done","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"}],"keystone/exception.py":[{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"34f922bc0781a2e1fd29bfaca6c0fe218d68310e","unresolved":false,"context_lines":[{"line_number":136,"context_line":"class ValidationOperatorError(Error):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027ne\u0027, \u0027lt\u0027, \u0027le\u0027, \u0027gt\u0027, or \u0027ge\u0027.\")"},{"line_number":140,"context_line":"    code \u003d 400"},{"line_number":141,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":142,"context_line":""}],"source_content_type":"text/x-python","patch_set":27,"id":"ba5201f7_6070f30b","line":139,"range":{"start_line":139,"start_character":59,"end_line":139,"end_character":61},"updated":"2017-01-08 05:51:53.000000000","message":"s/ge/gte/","commit_id":"784d91ab959af14c63cdec26571da30faf341bc8"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"34f922bc0781a2e1fd29bfaca6c0fe218d68310e","unresolved":false,"context_lines":[{"line_number":136,"context_line":"class ValidationOperatorError(Error):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027ne\u0027, \u0027lt\u0027, \u0027le\u0027, \u0027gt\u0027, or \u0027ge\u0027.\")"},{"line_number":140,"context_line":"    code \u003d 400"},{"line_number":141,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":142,"context_line":""}],"source_content_type":"text/x-python","patch_set":27,"id":"ba5201f7_20826bc8","line":139,"range":{"start_line":139,"start_character":44,"end_line":139,"end_character":46},"updated":"2017-01-08 05:51:53.000000000","message":"s/le/lte/: see http://specs.openstack.org/openstack/api-wg/guidelines/pagination_filter_sort.html#filtering","commit_id":"784d91ab959af14c63cdec26571da30faf341bc8"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"34f922bc0781a2e1fd29bfaca6c0fe218d68310e","unresolved":false,"context_lines":[{"line_number":136,"context_line":"class ValidationOperatorError(Error):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027ne\u0027, \u0027lt\u0027, \u0027le\u0027, \u0027gt\u0027, or \u0027ge\u0027.\")"},{"line_number":140,"context_line":"    code \u003d 400"},{"line_number":141,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":142,"context_line":""}],"source_content_type":"text/x-python","patch_set":27,"id":"ba5201f7_4085afcc","line":139,"range":{"start_line":139,"start_character":32,"end_line":139,"end_character":34},"updated":"2017-01-08 05:51:53.000000000","message":"s/ne/neq/","commit_id":"784d91ab959af14c63cdec26571da30faf341bc8"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027neq\u0027, \u0027lt\u0027, \u0027lte\u0027, \u0027gt\u0027, or \u0027gte\u0027.\")"},{"line_number":140,"context_line":"    code \u003d 400"},{"line_number":141,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"class ValidationExpirationError(Error):"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_e1f9a281","line":141,"updated":"2017-01-11 20:45:13.000000000","message":"Since this is a ValidationError, we could remove the need for defining `code` and `title` by inheriting from ValidationError directly:\n\n  class ValidationOperationError(ValidationError):\n      message_format \u003d ...","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8b84033c312c1e11146bc70d8a3c89401dfc017d","unresolved":false,"context_lines":[{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027neq\u0027, \u0027lt\u0027, \u0027lte\u0027, \u0027gt\u0027, or \u0027gte\u0027.\")"},{"line_number":140,"context_line":"    code \u003d 400"},{"line_number":141,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"class ValidationExpirationError(Error):"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_6c2da945","line":141,"in_reply_to":"ba5201f7_e1f9a281","updated":"2017-01-11 22:53:08.000000000","message":"Done","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"82f55c2a6815f52cb2c77cd9abf6c294f54309ed","unresolved":false,"context_lines":[{"line_number":133,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":134,"context_line":""},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"class ValidationOperatorError(ValidationError):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027neq\u0027, \u0027lt\u0027, \u0027lte\u0027, \u0027gt\u0027, or \u0027gte\u0027.\")"}],"source_content_type":"text/x-python","patch_set":37,"id":"7a3c09a3_ffae2080","line":136,"updated":"2017-01-18 22:13:32.000000000","message":"nit: This is totally a readability thing - but for some reason when I read this I want to naturally say InvalidOperatorError. ValidationOperatorError sounds like \"Operator\" got misplaced inbetween \"Validation\" and \"Error\". Another possible option would be OperatorValidationError. It\u0027s something with how the noun (Operator) and verb (Validation) are being used together.","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"ae3e0dbd1de1538b469b519bd7a6f83c668be1fb","unresolved":false,"context_lines":[{"line_number":133,"context_line":"    title \u003d \u0027Bad Request\u0027"},{"line_number":134,"context_line":""},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"class ValidationOperatorError(ValidationError):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027neq\u0027, \u0027lt\u0027, \u0027lte\u0027, \u0027gt\u0027, or \u0027gte\u0027.\")"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a3905b3_49092095","line":136,"in_reply_to":"7a3c09a3_ffae2080","updated":"2017-01-19 17:26:15.000000000","message":"Done","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"9dbdf6837992d41c4ca77344a192944c273c9e02","unresolved":false,"context_lines":[{"line_number":134,"context_line":""},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"class InvalidOperatorError(ValidationError):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027neq\u0027, \u0027lt\u0027, \u0027lte\u0027, \u0027gt\u0027, or \u0027gte\u0027.\")"},{"line_number":140,"context_line":""}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_140ac826","line":137,"range":{"start_line":137,"start_character":28,"end_line":137,"end_character":46},"updated":"2017-01-19 22:59:25.000000000","message":"this should really be:\n\n  \"... given operator \u0027%(_op)s\u0027 is not valid... \"\n\nand then pass in the invalid one","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"9a540d4656f60684e34876c7230e742f520e93c6","unresolved":false,"context_lines":[{"line_number":134,"context_line":""},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"class InvalidOperatorError(ValidationError):"},{"line_number":137,"context_line":"    message_format \u003d _(\"The given operator is not valid.\""},{"line_number":138,"context_line":"                       \" It must be one of the following:\""},{"line_number":139,"context_line":"                       \" \u0027eq\u0027, \u0027neq\u0027, \u0027lt\u0027, \u0027lte\u0027, \u0027gt\u0027, or \u0027gte\u0027.\")"},{"line_number":140,"context_line":""}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_4cee5bed","line":137,"range":{"start_line":137,"start_character":28,"end_line":137,"end_character":46},"in_reply_to":"5a3905b3_140ac826","updated":"2017-01-20 14:02:56.000000000","message":"Done","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"}],"keystone/identity/backends/sql.py":[{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"cc40a11b484ac9182cffadfbfe5bc2326a428071","unresolved":false,"context_lines":[{"line_number":141,"context_line":"                model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":142,"context_line":"                model.Password.expires_at \u003c\u003d filter_[\u0027value\u0027]))"},{"line_number":143,"context_line":"        # using \u0027password_expires_after\u0027"},{"line_number":144,"context_line":"        else:"},{"line_number":145,"context_line":"            query \u003d query.filter(sqlalchemy.and_("},{"line_number":146,"context_line":"                model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":147,"context_line":"                model.Password.expires_at \u003e\u003d filter_[\u0027value\u0027]))"}],"source_content_type":"text/x-python","patch_set":3,"id":"5a74a57a_4baabbd3","line":144,"range":{"start_line":144,"start_character":8,"end_line":144,"end_character":12},"updated":"2016-11-30 23:45:27.000000000","message":"I would specifically check for \u0027password_expires_after\u0027 here and let the else handle something unexpected since that\u0027s what we will expect if it even reaches it.","commit_id":"18392dbf8bb760c02a39c155415fe1ad69753d10"},{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"cbb507a071d70b9330b490dea1ba3a9d62170260","unresolved":false,"context_lines":[{"line_number":34,"context_line":"    # config parameter to enable sql to be used as a domain-specific driver."},{"line_number":35,"context_line":"    def __init__(self, conf\u003dNone):"},{"line_number":36,"context_line":"        self.conf \u003d conf"},{"line_number":37,"context_line":"        self.password_expire \u003d [\u0027password_expires_at\u0027,"},{"line_number":38,"context_line":"                                \u0027password_expires_before\u0027,"},{"line_number":39,"context_line":"                                \u0027password_expires_after\u0027]"},{"line_number":40,"context_line":"        super(Identity, self).__init__()"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_a6b8282c","line":39,"range":{"start_line":37,"start_character":0,"end_line":39,"end_character":57},"updated":"2016-12-07 16:46:26.000000000","message":"Does this need to be changed as well?","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"36f2472b5ae6b21e9ee2ba0a6ed62cc93d95a6f7","unresolved":false,"context_lines":[{"line_number":34,"context_line":"    # config parameter to enable sql to be used as a domain-specific driver."},{"line_number":35,"context_line":"    def __init__(self, conf\u003dNone):"},{"line_number":36,"context_line":"        self.conf \u003d conf"},{"line_number":37,"context_line":"        self.password_expire \u003d [\u0027password_expires_at\u0027,"},{"line_number":38,"context_line":"                                \u0027password_expires_before\u0027,"},{"line_number":39,"context_line":"                                \u0027password_expires_after\u0027]"},{"line_number":40,"context_line":"        super(Identity, self).__init__()"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_402494ae","line":39,"range":{"start_line":37,"start_character":0,"end_line":39,"end_character":57},"in_reply_to":"3a71b18c_a6b8282c","updated":"2016-12-07 18:42:48.000000000","message":"++","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8afa4e0411291aaf494159ada1935ca6625285f4","unresolved":false,"context_lines":[{"line_number":34,"context_line":"    # config parameter to enable sql to be used as a domain-specific driver."},{"line_number":35,"context_line":"    def __init__(self, conf\u003dNone):"},{"line_number":36,"context_line":"        self.conf \u003d conf"},{"line_number":37,"context_line":"        self.password_expire \u003d [\u0027password_expires_at\u0027,"},{"line_number":38,"context_line":"                                \u0027password_expires_before\u0027,"},{"line_number":39,"context_line":"                                \u0027password_expires_after\u0027]"},{"line_number":40,"context_line":"        super(Identity, self).__init__()"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_fb91aa53","line":39,"range":{"start_line":37,"start_character":0,"end_line":39,"end_character":57},"in_reply_to":"3a71b18c_a6b8282c","updated":"2016-12-08 14:25:51.000000000","message":"Done","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"9bd97847044589da76679ddfcf31f7e276ee8cd3","unresolved":false,"context_lines":[{"line_number":139,"context_line":"        if \u0027lt\u0027 in value:"},{"line_number":140,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":141,"context_line":"                                                   value[3:-1], operator.lt)"},{"line_number":142,"context_line":"        elif \u0027le\u0027 in value:"},{"line_number":143,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":144,"context_line":"                                                   value[3:-1], operator.le)"},{"line_number":145,"context_line":"        elif \u0027gt\u0027 in value:"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_863b2c38","line":142,"range":{"start_line":142,"start_character":14,"end_line":142,"end_character":16},"updated":"2016-12-07 16:56:26.000000000","message":"Per [1], this should be \u0027lte\u0027.\n\n[1] http://specs.openstack.org/openstack/api-wg/guidelines/pagination_filter_sort.html#filtering","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"36f2472b5ae6b21e9ee2ba0a6ed62cc93d95a6f7","unresolved":false,"context_lines":[{"line_number":139,"context_line":"        if \u0027lt\u0027 in value:"},{"line_number":140,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":141,"context_line":"                                                   value[3:-1], operator.lt)"},{"line_number":142,"context_line":"        elif \u0027le\u0027 in value:"},{"line_number":143,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":144,"context_line":"                                                   value[3:-1], operator.le)"},{"line_number":145,"context_line":"        elif \u0027gt\u0027 in value:"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_a061e8d5","line":142,"range":{"start_line":142,"start_character":14,"end_line":142,"end_character":16},"in_reply_to":"3a71b18c_863b2c38","updated":"2016-12-07 18:42:48.000000000","message":"++","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8afa4e0411291aaf494159ada1935ca6625285f4","unresolved":false,"context_lines":[{"line_number":139,"context_line":"        if \u0027lt\u0027 in value:"},{"line_number":140,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":141,"context_line":"                                                   value[3:-1], operator.lt)"},{"line_number":142,"context_line":"        elif \u0027le\u0027 in value:"},{"line_number":143,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":144,"context_line":"                                                   value[3:-1], operator.le)"},{"line_number":145,"context_line":"        elif \u0027gt\u0027 in value:"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_1bbe66ff","line":142,"range":{"start_line":142,"start_character":14,"end_line":142,"end_character":16},"in_reply_to":"3a71b18c_863b2c38","updated":"2016-12-08 14:25:51.000000000","message":"Done","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"9bd97847044589da76679ddfcf31f7e276ee8cd3","unresolved":false,"context_lines":[{"line_number":145,"context_line":"        elif \u0027gt\u0027 in value:"},{"line_number":146,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":147,"context_line":"                                                   value[3:-1], operator.gt)"},{"line_number":148,"context_line":"        elif \u0027ge\u0027 in value:"},{"line_number":149,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":150,"context_line":"                                                   value[3:-1], operator.ge)"},{"line_number":151,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_c61c44c4","line":148,"range":{"start_line":148,"start_character":13,"end_line":148,"end_character":17},"updated":"2016-12-07 16:56:26.000000000","message":"Per [1], this should be \u0027gte\u0027\n[1] http://specs.openstack.org/openstack/api-wg/guidelines/pagination_filter_sort.html#filtering","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8afa4e0411291aaf494159ada1935ca6625285f4","unresolved":false,"context_lines":[{"line_number":145,"context_line":"        elif \u0027gt\u0027 in value:"},{"line_number":146,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":147,"context_line":"                                                   value[3:-1], operator.gt)"},{"line_number":148,"context_line":"        elif \u0027ge\u0027 in value:"},{"line_number":149,"context_line":"            return self._get_user_password_expires(session, query,"},{"line_number":150,"context_line":"                                                   value[3:-1], operator.ge)"},{"line_number":151,"context_line":"        else:"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_3bbb2210","line":148,"range":{"start_line":148,"start_character":13,"end_line":148,"end_character":17},"in_reply_to":"3a71b18c_c61c44c4","updated":"2016-12-08 14:25:51.000000000","message":"Done","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"3e9182cd862c015dde9be783b4aca215f7c93a3d","unresolved":false,"context_lines":[{"line_number":152,"context_line":"        with sql.session_for_read() as session:"},{"line_number":153,"context_line":"            query \u003d session.query(model.User).outerjoin(model.LocalUser)"},{"line_number":154,"context_line":"            for filter_ in hints.filters:"},{"line_number":155,"context_line":"                if \u0027password_expires_at\u0027 in filter_[\u0027name\u0027]:"},{"line_number":156,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":157,"context_line":"                                                        filter_[\u0027value\u0027])"},{"line_number":158,"context_line":"                    hints.filters.remove(filter_)"}],"source_content_type":"text/x-python","patch_set":13,"id":"3a71b18c_b25d627b","line":155,"range":{"start_line":155,"start_character":41,"end_line":155,"end_character":43},"updated":"2016-12-09 13:53:34.000000000","message":"\u003d\u003d\n\nwe don\u0027t want the filtering to work if a filter called password_expires_at_blabla is passed","commit_id":"70bb631644846475e4470a9d1a7583e83ba2687e"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"3e9182cd862c015dde9be783b4aca215f7c93a3d","unresolved":false,"context_lines":[{"line_number":156,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":157,"context_line":"                                                        filter_[\u0027value\u0027])"},{"line_number":158,"context_line":"                    hints.filters.remove(filter_)"},{"line_number":159,"context_line":"                    continue"},{"line_number":160,"context_line":"            user_refs \u003d sql.filter_limit_query(model.User, query, hints)"},{"line_number":161,"context_line":"            return [base.filter_user(x.to_dict()) for x in user_refs]"},{"line_number":162,"context_line":""}],"source_content_type":"text/x-python","patch_set":13,"id":"3a71b18c_d2b01e8e","line":159,"range":{"start_line":159,"start_character":20,"end_line":159,"end_character":28},"updated":"2016-12-09 13:53:34.000000000","message":"I guess this should be a \"break\" clause. continue will cause the for clause to continue with the next filter.","commit_id":"70bb631644846475e4470a9d1a7583e83ba2687e"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"3e9182cd862c015dde9be783b4aca215f7c93a3d","unresolved":false,"context_lines":[{"line_number":298,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":299,"context_line":"                                                        filter_[\u0027value\u0027])"},{"line_number":300,"context_line":"                    hints.filters.remove(filter_)"},{"line_number":301,"context_line":"                    continue"},{"line_number":302,"context_line":"            query \u003d sql.filter_limit_query(model.User, query, hints)"},{"line_number":303,"context_line":"            return [base.filter_user(u.to_dict()) for u in query]"},{"line_number":304,"context_line":""}],"source_content_type":"text/x-python","patch_set":13,"id":"3a71b18c_32a6b253","line":301,"range":{"start_line":301,"start_character":20,"end_line":301,"end_character":28},"updated":"2016-12-09 13:53:34.000000000","message":"same ehre, should be a break","commit_id":"70bb631644846475e4470a9d1a7583e83ba2687e"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"b78ed5a3ef41754054d78dc9b32f882aacdb3bcd","unresolved":false,"context_lines":[{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def _user_password_expires(self, session, query, value):"},{"line_number":134,"context_line":"        query \u003d query.join(model.Password)"},{"line_number":135,"context_line":"        col \u003d 0"},{"line_number":136,"context_line":"        test \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":137,"context_line":"                \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge, \u0027neq\u0027: operator.ne}"},{"line_number":138,"context_line":"        for i in test.keys():"},{"line_number":139,"context_line":"            if i in value:"}],"source_content_type":"text/x-python","patch_set":14,"id":"3a71b18c_52bde3d5","line":136,"range":{"start_line":135,"start_character":8,"end_line":136,"end_character":12},"updated":"2016-12-09 17:30:11.000000000","message":"these var names aren\u0027t really suggestive and do not help understanding the logic","commit_id":"86895936feade978cdaa0fbad5275ab73bf2a12f"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"b78ed5a3ef41754054d78dc9b32f882aacdb3bcd","unresolved":false,"context_lines":[{"line_number":156,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":157,"context_line":"                                                        filter_[\u0027value\u0027])"},{"line_number":158,"context_line":"                    hints.filters.remove(filter_)"},{"line_number":159,"context_line":"                    break"},{"line_number":160,"context_line":"            user_refs \u003d sql.filter_limit_query(model.User, query, hints)"},{"line_number":161,"context_line":"            return [base.filter_user(x.to_dict()) for x in user_refs]"},{"line_number":162,"context_line":""}],"source_content_type":"text/x-python","patch_set":14,"id":"3a71b18c_92121b08","line":159,"range":{"start_line":159,"start_character":20,"end_line":159,"end_character":25},"updated":"2016-12-09 17:30:11.000000000","message":"oh wait. I suggested you adding a break here, but ...\n\nHow are we expecting those filters to be combined ?\n\nIf it is like\n\n    /users?password_expires_at\u003dgt:{timestamp1}\u0026password_expires_at\u003dlt:{timestamp2}\n\nIf so, we might want to continue iterating over the filters because there can be one more.\n\nI suggest adding a test for this. In that case you could simply remove this break and leave nothing here, so that the iteration continues.","commit_id":"86895936feade978cdaa0fbad5275ab73bf2a12f"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"8a728c85eba3eb95e8fab4b446e81adb38139f97","unresolved":false,"context_lines":[{"line_number":141,"context_line":"                colon_location \u003d value.find(\u0027:\u0027)"},{"line_number":142,"context_line":"                break"},{"line_number":143,"context_line":"        op \u003d operators[\u0027eq\u0027]"},{"line_number":144,"context_line":"        if colon_location \u003d\u003d 2 or colon_location \u003d\u003d 3:"},{"line_number":145,"context_line":"            op \u003d operators[value[0:colon_location]]"},{"line_number":146,"context_line":"            value \u003d value[colon_location + 1:]"},{"line_number":147,"context_line":""},{"line_number":148,"context_line":"        return self._get_user_password_expires(session, query,"}],"source_content_type":"text/x-python","patch_set":18,"id":"1a6eadb0_5c23ac05","line":145,"range":{"start_line":144,"start_character":0,"end_line":145,"end_character":51},"updated":"2016-12-12 17:24:18.000000000","message":"add a note about the logic behind this indexes and the values that are expected. Also, would be nice to have variables representing the indexes values.","commit_id":"a33a3535b5cd9f6783d5c9f0b78399c9ff728e25"},{"author":{"_account_id":8482,"name":"Colleen Murphy","email":"colleen@gazlene.net","username":"krinkle"},"change_message_id":"3336a0d4bc9644f9409f673f1cf5157655fc1a8a","unresolved":false,"context_lines":[{"line_number":138,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":139,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":140,"context_line":"                     \u0027neq\u0027: operator.ne}"},{"line_number":141,"context_line":"        for i in operators.keys():"},{"line_number":142,"context_line":"            if i in value:"},{"line_number":143,"context_line":"                colon_location \u003d value.find(\u0027:\u0027)"},{"line_number":144,"context_line":"                break"},{"line_number":145,"context_line":"        op \u003d operators[\u0027eq\u0027]"},{"line_number":146,"context_line":"        # there is an operator present if the colon is the 3rd or 4th"},{"line_number":147,"context_line":"        # character of the timestamp."},{"line_number":148,"context_line":"        if colon_location in op_present_indices:"},{"line_number":149,"context_line":"            op \u003d operators[value[0:colon_location]]"},{"line_number":150,"context_line":"            value \u003d value[colon_location + 1:]"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"        return self._get_user_password_expires(session, query,"},{"line_number":153,"context_line":"                                               value, op)"}],"source_content_type":"text/x-python","patch_set":19,"id":"1a6eadb0_68b09d8c","line":150,"range":{"start_line":141,"start_character":8,"end_line":150,"end_character":46},"updated":"2016-12-12 21:51:46.000000000","message":"I think it would be more pythonic and concise if this used more of python\u0027s builtin string parsing instead of treating it as a character array, something like this:\n\n op, timestamp \u003d value.split(\u0027:\u0027, 1)\n if op in operators:\n   op \u003d operators[op]\n   value \u003d timestamp\n else:\n   op \u003d operators[\u0027eq\u0027]","commit_id":"b8c97e0f326ce0b490cb23f44f783c65cd68d216"},{"author":{"_account_id":18464,"name":"Richard Avelar","email":"csravelar@gmail.com","username":"razone"},"change_message_id":"511bfc981bb3d0388a2c87588ce55436cdf05ae2","unresolved":false,"context_lines":[{"line_number":138,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":139,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":140,"context_line":"                     \u0027neq\u0027: operator.ne}"},{"line_number":141,"context_line":"        for i in operators.keys():"},{"line_number":142,"context_line":"            if i in value:"},{"line_number":143,"context_line":"                colon_location \u003d value.find(\u0027:\u0027)"},{"line_number":144,"context_line":"                break"},{"line_number":145,"context_line":"        op \u003d operators[\u0027eq\u0027]"},{"line_number":146,"context_line":"        # there is an operator present if the colon is the 3rd or 4th"},{"line_number":147,"context_line":"        # character of the timestamp."},{"line_number":148,"context_line":"        if colon_location in op_present_indices:"},{"line_number":149,"context_line":"            op \u003d operators[value[0:colon_location]]"},{"line_number":150,"context_line":"            value \u003d value[colon_location + 1:]"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"        return self._get_user_password_expires(session, query,"},{"line_number":153,"context_line":"                                               value, op)"}],"source_content_type":"text/x-python","patch_set":19,"id":"1a6eadb0_04327f10","line":150,"range":{"start_line":141,"start_character":8,"end_line":150,"end_character":46},"in_reply_to":"1a6eadb0_68b09d8c","updated":"2016-12-13 20:40:30.000000000","message":"++","commit_id":"b8c97e0f326ce0b490cb23f44f783c65cd68d216"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"de9ac6a4287a73a55b2e31ca8a77a67d7759b28c","unresolved":false,"context_lines":[{"line_number":138,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":139,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":140,"context_line":"                     \u0027neq\u0027: operator.ne}"},{"line_number":141,"context_line":"        for i in operators.keys():"},{"line_number":142,"context_line":"            if i in value:"},{"line_number":143,"context_line":"                colon_location \u003d value.find(\u0027:\u0027)"},{"line_number":144,"context_line":"                break"},{"line_number":145,"context_line":"        op \u003d operators[\u0027eq\u0027]"},{"line_number":146,"context_line":"        # there is an operator present if the colon is the 3rd or 4th"},{"line_number":147,"context_line":"        # character of the timestamp."},{"line_number":148,"context_line":"        if colon_location in op_present_indices:"},{"line_number":149,"context_line":"            op \u003d operators[value[0:colon_location]]"},{"line_number":150,"context_line":"            value \u003d value[colon_location + 1:]"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"        return self._get_user_password_expires(session, query,"},{"line_number":153,"context_line":"                                               value, op)"}],"source_content_type":"text/x-python","patch_set":19,"id":"1a6eadb0_6b541754","line":150,"range":{"start_line":141,"start_character":8,"end_line":150,"end_character":46},"in_reply_to":"1a6eadb0_68b09d8c","updated":"2016-12-12 22:17:09.000000000","message":"++\n\nthis is much clearer, and would not need a comment.","commit_id":"b8c97e0f326ce0b490cb23f44f783c65cd68d216"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"c54006a16581e5a507683db117645bc0ca07bbc5","unresolved":false,"context_lines":[{"line_number":138,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":139,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":140,"context_line":"                     \u0027neq\u0027: operator.ne}"},{"line_number":141,"context_line":"        for i in operators.keys():"},{"line_number":142,"context_line":"            if i in value:"},{"line_number":143,"context_line":"                colon_location \u003d value.find(\u0027:\u0027)"},{"line_number":144,"context_line":"                break"},{"line_number":145,"context_line":"        op \u003d operators[\u0027eq\u0027]"},{"line_number":146,"context_line":"        # there is an operator present if the colon is the 3rd or 4th"},{"line_number":147,"context_line":"        # character of the timestamp."},{"line_number":148,"context_line":"        if colon_location in op_present_indices:"},{"line_number":149,"context_line":"            op \u003d operators[value[0:colon_location]]"},{"line_number":150,"context_line":"            value \u003d value[colon_location + 1:]"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"        return self._get_user_password_expires(session, query,"},{"line_number":153,"context_line":"                                               value, op)"}],"source_content_type":"text/x-python","patch_set":19,"id":"1a6eadb0_ee91ac9a","line":150,"range":{"start_line":141,"start_character":8,"end_line":150,"end_character":46},"in_reply_to":"1a6eadb0_68b09d8c","updated":"2016-12-14 17:27:13.000000000","message":"Done","commit_id":"b8c97e0f326ce0b490cb23f44f783c65cd68d216"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"454a00f30057f6ba27e89e78c9f03ab587429ce3","unresolved":false,"context_lines":[{"line_number":125,"context_line":"            session.add(user_ref)"},{"line_number":126,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"    def _get_user_password_expires(self, session, query, value, operator):"},{"line_number":129,"context_line":"        return query.filter(sqlalchemy.and_("},{"line_number":130,"context_line":"            model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":131,"context_line":"            operator(model.Password.expires_at, value)))"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def _user_password_expires(self, session, query, value):"},{"line_number":134,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_f7426f3d","line":131,"range":{"start_line":128,"start_character":0,"end_line":131,"end_character":56},"updated":"2016-12-14 13:21:42.000000000","message":"this method is not needed, it is a single command used only by _user_password_expires()","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"eabf266788e59bd94df9967ab3548a9d1b2a69c6","unresolved":false,"context_lines":[{"line_number":125,"context_line":"            session.add(user_ref)"},{"line_number":126,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"    def _get_user_password_expires(self, session, query, value, operator):"},{"line_number":129,"context_line":"        return query.filter(sqlalchemy.and_("},{"line_number":130,"context_line":"            model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":131,"context_line":"            operator(model.Password.expires_at, value)))"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def _user_password_expires(self, session, query, value):"},{"line_number":134,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_6ecbdca5","line":131,"range":{"start_line":128,"start_character":0,"end_line":131,"end_character":56},"in_reply_to":"1a6eadb0_f7426f3d","updated":"2016-12-14 17:27:38.000000000","message":"Done","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"454a00f30057f6ba27e89e78c9f03ab587429ce3","unresolved":false,"context_lines":[{"line_number":130,"context_line":"            model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":131,"context_line":"            operator(model.Password.expires_at, value)))"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def _user_password_expires(self, session, query, value):"},{"line_number":134,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":135,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":136,"context_line":"                     \u0027neq\u0027: operator.ne}"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_f7e8afe4","line":133,"range":{"start_line":133,"start_character":8,"end_line":133,"end_character":30},"updated":"2016-12-14 13:21:42.000000000","message":"lines 152-158 and 295-301 are exactly the same, why not extract everything to here?","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"eabf266788e59bd94df9967ab3548a9d1b2a69c6","unresolved":false,"context_lines":[{"line_number":130,"context_line":"            model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":131,"context_line":"            operator(model.Password.expires_at, value)))"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"    def _user_password_expires(self, session, query, value):"},{"line_number":134,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":135,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":136,"context_line":"                     \u0027neq\u0027: operator.ne}"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_8ece78b2","line":133,"range":{"start_line":133,"start_character":8,"end_line":133,"end_character":30},"in_reply_to":"1a6eadb0_f7e8afe4","updated":"2016-12-14 17:27:38.000000000","message":"Done","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"454a00f30057f6ba27e89e78c9f03ab587429ce3","unresolved":false,"context_lines":[{"line_number":149,"context_line":"    def list_users(self, hints):"},{"line_number":150,"context_line":"        with sql.session_for_read() as session:"},{"line_number":151,"context_line":"            query \u003d session.query(model.User).outerjoin(model.LocalUser)"},{"line_number":152,"context_line":"            for filter_ in hints.filters:"},{"line_number":153,"context_line":"                if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":154,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":155,"context_line":"                                                        filter_[\u0027value\u0027])"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_77065fcf","line":152,"range":{"start_line":152,"start_character":16,"end_line":152,"end_character":23},"updated":"2016-12-14 13:21:42.000000000","message":"nit: why \"filter_\" (with a _) instead of just \"filter\"?","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"765bed860a83f007ae8573110331ce35a58b91ac","unresolved":false,"context_lines":[{"line_number":149,"context_line":"    def list_users(self, hints):"},{"line_number":150,"context_line":"        with sql.session_for_read() as session:"},{"line_number":151,"context_line":"            query \u003d session.query(model.User).outerjoin(model.LocalUser)"},{"line_number":152,"context_line":"            for filter_ in hints.filters:"},{"line_number":153,"context_line":"                if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":154,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":155,"context_line":"                                                        filter_[\u0027value\u0027])"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_6b2af37f","line":152,"range":{"start_line":152,"start_character":16,"end_line":152,"end_character":23},"in_reply_to":"1a6eadb0_60c052e1","updated":"2016-12-14 15:57:23.000000000","message":"hmm it\u0027s ok, just wanted to know if there was reasoning behind it.","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"5951435b0952d3690156845bbeddd75a68fd3f70","unresolved":false,"context_lines":[{"line_number":149,"context_line":"    def list_users(self, hints):"},{"line_number":150,"context_line":"        with sql.session_for_read() as session:"},{"line_number":151,"context_line":"            query \u003d session.query(model.User).outerjoin(model.LocalUser)"},{"line_number":152,"context_line":"            for filter_ in hints.filters:"},{"line_number":153,"context_line":"                if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":154,"context_line":"                    query \u003d self._user_password_expires(session, query,"},{"line_number":155,"context_line":"                                                        filter_[\u0027value\u0027])"}],"source_content_type":"text/x-python","patch_set":20,"id":"1a6eadb0_60c052e1","line":152,"range":{"start_line":152,"start_character":16,"end_line":152,"end_character":23},"in_reply_to":"1a6eadb0_77065fcf","updated":"2016-12-14 15:54:45.000000000","message":"I was using this as a reference:\nhttps://github.com/openstack/keystone/blob/master/keystone/common/sql/core.py#L342\n\nI have no problem changing it.","commit_id":"50dc7f39697ddece6deca2d634c4251fad75edaf"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"b89e07bd40237415c0432c72de4c68773de2cbb2","unresolved":false,"context_lines":[{"line_number":144,"context_line":"                    model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":145,"context_line":"                    op(model.Password.expires_at, value)))"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"        hints.filters \u003d [x for x in hints.filters if x[\u0027name\u0027] !\u003d"},{"line_number":148,"context_line":"                         \u0027password_expires_at\u0027]"},{"line_number":149,"context_line":"        return query"},{"line_number":150,"context_line":""},{"line_number":151,"context_line":"    @driver_hints.truncated"}],"source_content_type":"text/x-python","patch_set":22,"id":"1a6eadb0_f31b5519","line":148,"range":{"start_line":147,"start_character":0,"end_line":148,"end_character":47},"updated":"2016-12-14 17:17:15.000000000","message":"maybe do this outside of the method, since it is a side effect or also return the hints.","commit_id":"fbee8e9a8955be2f8a32e95d66109d0d2519fb01"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6d0e50d57effceafd733fe0a00b23990b9454986","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator"},{"line_number":17,"context_line":"import sqlalchemy"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"from keystone.common import driver_hints"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_a15707c9","line":16,"updated":"2016-12-19 21:59:26.000000000","message":"This is a standard library so we should group it with datetime, but keep it isolated from sqlalchemy - since it\u0027s a third-party library:\n\n  import datetime\n  import operator\n\n  import sqlalchemy\n\n  ..","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"73b6aae99258329cb855438c9bfc87416a69f499","unresolved":false,"context_lines":[{"line_number":126,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"    def _user_password_expires(self, session, query, hints):"},{"line_number":129,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt, \u0027eq\u0027: operator.eq,"},{"line_number":130,"context_line":"                     \u0027lte\u0027: operator.le, \u0027gte\u0027: operator.ge,"},{"line_number":131,"context_line":"                     \u0027neq\u0027: operator.ne}"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"        for filter_ in hints.filters:"},{"line_number":134,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":135,"context_line":"                value \u003d filter_[\u0027value\u0027]"},{"line_number":136,"context_line":"                op, timestamp \u003d value.split(\u0027:\u0027, 1)"},{"line_number":137,"context_line":"                if op in operators:"},{"line_number":138,"context_line":"                    op \u003d operators[op]"},{"line_number":139,"context_line":"                    value \u003d timestamp"},{"line_number":140,"context_line":"                else:"},{"line_number":141,"context_line":"                    op \u003d operators[\u0027eq\u0027]"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"                query \u003d query.filter(sqlalchemy.and_("},{"line_number":144,"context_line":"                    model.LocalUser.id \u003d\u003d model.Password.local_user_id,"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_10a76c0b","line":141,"range":{"start_line":129,"start_character":0,"end_line":141,"end_character":40},"updated":"2016-12-16 23:21:34.000000000","message":"this logic should be in the manager, not the backend. around here: https://github.com/openstack/keystone/blob/master/keystone/identity/core.py#L974\n\ncreate a private function that handles the hints before going to the backend","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6d0e50d57effceafd733fe0a00b23990b9454986","unresolved":false,"context_lines":[{"line_number":138,"context_line":"                    op \u003d operators[op]"},{"line_number":139,"context_line":"                    value \u003d timestamp"},{"line_number":140,"context_line":"                else:"},{"line_number":141,"context_line":"                    op \u003d operators[\u0027eq\u0027]"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"                query \u003d query.filter(sqlalchemy.and_("},{"line_number":144,"context_line":"                    model.LocalUser.id \u003d\u003d model.Password.local_user_id,"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_016fdb92","line":141,"in_reply_to":"1a6eadb0_10a76c0b","updated":"2016-12-19 21:59:26.000000000","message":"++","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"ec593590f8806e957683560b78e6f3b362775a66","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_de290939","line":16,"range":{"start_line":16,"start_character":0,"end_line":16,"end_character":23},"updated":"2017-01-11 05:33:53.000000000","message":"why import if its not used?","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"1f4ce0c0024f1171b476dc1d7286530e9783295c","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_a7009967","line":16,"range":{"start_line":16,"start_character":0,"end_line":16,"end_character":23},"in_reply_to":"ba5201f7_86f55eee","updated":"2017-01-11 16:45:38.000000000","message":"so... how does it happen that it doesn\u0027t work without import and works with import? \"# noqa\" is not enough here, could you please add description why this is being imported, what is the root cause and when it can be deleted?\n\nOtherwise we are going to get a lot of reviews removing this.","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"e016aaa5fb7a8f5929597c59f6da09f0d1d7b480","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_dbcc8dbd","line":16,"range":{"start_line":16,"start_character":0,"end_line":16,"end_character":23},"in_reply_to":"ba5201f7_a7009967","updated":"2017-01-11 19:48:30.000000000","message":"Testing showed me I was wrong and this can get removed. Not sure what I thought was causing this to be needed.","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"51fa3492db16a7ab8b4d572538e3ce35c7e8cb2a","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_86f55eee","line":16,"range":{"start_line":16,"start_character":0,"end_line":16,"end_character":23},"in_reply_to":"ba5201f7_de290939","updated":"2017-01-11 14:55:30.000000000","message":"There is an implicit call to the operators (I marked it below) that without importing the library causes equalities, specifically anything involving equals, to notwork as intended. I\u0027m still looking into potentially better ways around this.","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"51fa3492db16a7ab8b4d572538e3ce35c7e8cb2a","unresolved":false,"context_lines":[{"line_number":132,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":133,"context_line":"                query \u003d query.filter(sqlalchemy.and_("},{"line_number":134,"context_line":"                    model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":135,"context_line":"                    filter_[\u0027comparator\u0027](model.Password.expires_at,"},{"line_number":136,"context_line":"                                          filter_[\u0027value\u0027])))"},{"line_number":137,"context_line":""},{"line_number":138,"context_line":"        hints.filters \u003d [x for x in hints.filters if x[\u0027name\u0027] !\u003d"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_46d61650","line":135,"range":{"start_line":135,"start_character":20,"end_line":135,"end_character":41},"updated":"2017-01-11 14:55:30.000000000","message":"This is where the operator library is being called.","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_8131b65c","line":16,"updated":"2017-01-11 20:45:13.000000000","message":"It doesn\u0027t look like this is used anywhere. Can we remove it?","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8b84033c312c1e11146bc70d8a3c89401dfc017d","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_8c32cda5","line":16,"in_reply_to":"ba5201f7_8131b65c","updated":"2017-01-11 22:53:08.000000000","message":"Done","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"0dab4753ef0f6282877bd9f754b84ec61a88ba3b","unresolved":false,"context_lines":[{"line_number":13,"context_line":"# under the License."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"import datetime"},{"line_number":16,"context_line":"import operator  # noqa"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from oslo_db import api as oslo_db_api"},{"line_number":19,"context_line":"import sqlalchemy"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_f90b1622","line":16,"in_reply_to":"ba5201f7_8131b65c","updated":"2017-01-11 21:46:20.000000000","message":"That is being used by ``filter_[\u0027comparator\u0027]`` at L137.","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"0dab4753ef0f6282877bd9f754b84ec61a88ba3b","unresolved":false,"context_lines":[{"line_number":127,"context_line":"            session.add(user_ref)"},{"line_number":128,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":129,"context_line":""},{"line_number":130,"context_line":"    def _user_password_expires(self, session, query, hints):"},{"line_number":131,"context_line":"        for filter_ in hints.filters:"},{"line_number":132,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":133,"context_line":"                # Filter on users who\u0027s password expires based on the operator"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_b93d0e91","line":130,"range":{"start_line":130,"start_character":8,"end_line":130,"end_character":30},"updated":"2017-01-11 21:46:20.000000000","message":"Since the topic of \":\" vs. \"__\" came up, would it be better if we do this filtering in https://github.com/openstack/keystone/blob/55755e3bf7c461ceee2bc033657eefb4060453f2/keystone/common/sql/core.py#L276 or https://github.com/openstack/keystone/blob/55755e3bf7c461ceee2bc033657eefb4060453f2/keystone/common/sql/core.py#L317 to generically handle inequality filter vs. doing it just for ``password_expires_at``.","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8ac3239ffe5dddad08555724a141988163232aeb","unresolved":false,"context_lines":[{"line_number":135,"context_line":"                    model.LocalUser.id \u003d\u003d model.Password.local_user_id,"},{"line_number":136,"context_line":"                    filter_[\u0027comparator\u0027](model.Password.expires_at,"},{"line_number":137,"context_line":"                                          filter_[\u0027value\u0027])))"},{"line_number":138,"context_line":"        # Removes the `password_expired_at` fitlers so there are no errors"},{"line_number":139,"context_line":"        # if the call is filtered further. This is because the"},{"line_number":140,"context_line":"        # `password_expires_at` value is not store in the `User` table but"},{"line_number":141,"context_line":"        # derived from the `Password` table\u0027s value `expires_at`."}],"source_content_type":"text/x-python","patch_set":34,"id":"ba5201f7_126a6fb3","line":138,"updated":"2017-01-11 23:00:58.000000000","message":"filters*","commit_id":"36dbd34f501305fbf745ed731c9e8e43c4204d3c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8ac3239ffe5dddad08555724a141988163232aeb","unresolved":false,"context_lines":[{"line_number":137,"context_line":"                                          filter_[\u0027value\u0027])))"},{"line_number":138,"context_line":"        # Removes the `password_expired_at` fitlers so there are no errors"},{"line_number":139,"context_line":"        # if the call is filtered further. This is because the"},{"line_number":140,"context_line":"        # `password_expires_at` value is not store in the `User` table but"},{"line_number":141,"context_line":"        # derived from the `Password` table\u0027s value `expires_at`."},{"line_number":142,"context_line":"        hints.filters \u003d [x for x in hints.filters if x[\u0027name\u0027] !\u003d"},{"line_number":143,"context_line":"                         \u0027password_expires_at\u0027]"}],"source_content_type":"text/x-python","patch_set":34,"id":"ba5201f7_b2a1a3fa","line":140,"updated":"2017-01-11 23:00:58.000000000","message":"stored*","commit_id":"36dbd34f501305fbf745ed731c9e8e43c4204d3c"},{"author":{"_account_id":18338,"name":"Ron De Rose","email":"ronald.de.rose@intel.com","username":"derosenet"},"change_message_id":"69dd2204e7ade16c13b8bb43ef1248eddc17bda4","unresolved":false,"context_lines":[{"line_number":126,"context_line":"            session.add(user_ref)"},{"line_number":127,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    def _user_password_expires(self, session, query, hints):"},{"line_number":130,"context_line":"        for filter_ in hints.filters:"},{"line_number":131,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":132,"context_line":"                # Filter on users who\u0027s password expires based on the operator"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_55a317b2","line":129,"range":{"start_line":129,"start_character":8,"end_line":129,"end_character":30},"updated":"2017-01-22 01:48:15.000000000","message":"nit: Needs a better name","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"1ceaa2a907ff144917913101211c11ef15f64375","unresolved":false,"context_lines":[{"line_number":126,"context_line":"            session.add(user_ref)"},{"line_number":127,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    def _user_password_expires(self, session, query, hints):"},{"line_number":130,"context_line":"        for filter_ in hints.filters:"},{"line_number":131,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":132,"context_line":"                # Filter on users who\u0027s password expires based on the operator"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_0796933e","line":129,"range":{"start_line":129,"start_character":8,"end_line":129,"end_character":30},"in_reply_to":"5a3905b3_4b2e1444","updated":"2017-01-23 15:48:42.000000000","message":"Done","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"2b5cd1b4a21b653bde85a1bb5edad61c2aebd4e7","unresolved":false,"context_lines":[{"line_number":126,"context_line":"            session.add(user_ref)"},{"line_number":127,"context_line":"            return base.filter_user(user_ref.to_dict())"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    def _user_password_expires(self, session, query, hints):"},{"line_number":130,"context_line":"        for filter_ in hints.filters:"},{"line_number":131,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":132,"context_line":"                # Filter on users who\u0027s password expires based on the operator"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_4b2e1444","line":129,"range":{"start_line":129,"start_character":8,"end_line":129,"end_character":30},"in_reply_to":"5a3905b3_55a317b2","updated":"2017-01-22 04:23:35.000000000","message":"++ maybe something like \"_create_password_expires_query\"","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":20380,"name":"Jeffrey Augustine","email":"jaugustineOS@outlook.com","username":"jaugustine"},"change_message_id":"9b09ca9e62ea47aced451571c3ab8c14b88f9029","unresolved":false,"context_lines":[{"line_number":281,"context_line":"            query \u003d query.join(model.UserGroupMembership)"},{"line_number":282,"context_line":"            query \u003d query.filter("},{"line_number":283,"context_line":"                model.UserGroupMembership.group_id \u003d\u003d group_id)"},{"line_number":284,"context_line":"            query, hints \u003d self._create_password_expires_query(session, query, hints)"},{"line_number":285,"context_line":"            query \u003d sql.filter_limit_query(model.User, query, hints)"},{"line_number":286,"context_line":"            return [base.filter_user(u.to_dict()) for u in query]"},{"line_number":287,"context_line":""}],"source_content_type":"text/x-python","patch_set":42,"id":"5a3905b3_8f3291a5","line":284,"range":{"start_line":284,"start_character":79,"end_line":284,"end_character":84},"updated":"2017-01-23 18:32:35.000000000","message":"¯\\_(ツ)_/¯","commit_id":"3a5ae32d847ec0b31883346823aa5bb1e202eb9e"}],"keystone/identity/controllers.py":[{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"36f2472b5ae6b21e9ee2ba0a6ed62cc93d95a6f7","unresolved":false,"context_lines":[{"line_number":232,"context_line":"    @controller.filterprotected(\u0027domain_id\u0027, \u0027enabled\u0027, \u0027name\u0027,"},{"line_number":233,"context_line":"                                \u0027password_expires_at\u0027,"},{"line_number":234,"context_line":"                                callback\u003d_check_group_protection)"},{"line_number":235,"context_line":"    def list_users_in_group(self, request, filters, group_id):"},{"line_number":236,"context_line":"        hints \u003d UserV3.build_driver_hints(request, filters)"},{"line_number":237,"context_line":"        refs \u003d self.identity_api.list_users_in_group(group_id, hints\u003dhints)"},{"line_number":238,"context_line":"        return UserV3.wrap_collection(request.context_dict, refs, hints\u003dhints)"}],"source_content_type":"text/x-python","patch_set":8,"id":"3a71b18c_a08f2874","line":235,"updated":"2016-12-07 18:42:48.000000000","message":"Do we want to also filter users that are in a given group AND match the password_expires_at criteria? If so, it also needs tests","commit_id":"017db9eebc2a8512b48991163ef65ce6217b8953"}],"keystone/identity/core.py":[{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"34f922bc0781a2e1fd29bfaca6c0fe218d68310e","unresolved":false,"context_lines":[{"line_number":961,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":962,"context_line":""},{"line_number":963,"context_line":"    def _clean_up_expired_password_hints(self, hints):"},{"line_number":964,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt,"},{"line_number":965,"context_line":"                     \u0027eq\u0027: operator.eq, \u0027le\u0027: operator.le,"},{"line_number":966,"context_line":"                     \u0027ge\u0027: operator.ge, \u0027ne\u0027: operator.ne}"},{"line_number":967,"context_line":""},{"line_number":968,"context_line":"        for filter_ in hints.filters:"},{"line_number":969,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"}],"source_content_type":"text/x-python","patch_set":27,"id":"ba5201f7_a0765b03","line":966,"range":{"start_line":964,"start_character":22,"end_line":966,"end_character":57},"updated":"2017-01-08 05:51:53.000000000","message":"le -\u003e lte, ge -\u003e gte, and ne -\u003e neq.  See: http://specs.openstack.org/openstack/api-wg/guidelines/pagination_filter_sort.html#filtering","commit_id":"784d91ab959af14c63cdec26571da30faf341bc8"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"ec593590f8806e957683560b78e6f3b362775a66","unresolved":false,"context_lines":[{"line_number":960,"context_line":"        return self._set_domain_id_and_mapping("},{"line_number":961,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":962,"context_line":""},{"line_number":963,"context_line":"    def _check_valid_timestamp(self, timestamp):"},{"line_number":964,"context_line":"        pattern \u003d re.compile(\u0027[1-9]\\d{3}\\-\\d{2}\\-\\d{2}[ T]\u0027"},{"line_number":965,"context_line":"                             \u0027\\d{2}\\:\\d{2}\\:\\d{2}(Z|\\.\\d*)\u0027)"},{"line_number":966,"context_line":"        return pattern.match(timestamp)"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_7e881d23","line":963,"range":{"start_line":963,"start_character":8,"end_line":963,"end_character":48},"updated":"2017-01-11 05:33:53.000000000","message":"add a comment / docstring about what we expect a valid timestamp to look like and the behaviour on bad ones (do we let any slide, etc).\n\ni don\u0027t want to look up regex rules every time i see code","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"ec593590f8806e957683560b78e6f3b362775a66","unresolved":false,"context_lines":[{"line_number":961,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":962,"context_line":""},{"line_number":963,"context_line":"    def _check_valid_timestamp(self, timestamp):"},{"line_number":964,"context_line":"        pattern \u003d re.compile(\u0027[1-9]\\d{3}\\-\\d{2}\\-\\d{2}[ T]\u0027"},{"line_number":965,"context_line":"                             \u0027\\d{2}\\:\\d{2}\\:\\d{2}(Z|\\.\\d*)\u0027)"},{"line_number":966,"context_line":"        return pattern.match(timestamp)"},{"line_number":967,"context_line":""}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_3ea0559f","line":964,"range":{"start_line":964,"start_character":55,"end_line":964,"end_character":57},"updated":"2017-01-11 05:33:53.000000000","message":"so does this mean we\u0027ll need to put a space before the \"T\"?","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":13478,"name":"Boris Bobrov","email":"b.bobrov@sap.com","username":"bbobrov"},"change_message_id":"75c5b4227e3732b6558b92876253b3bb087b8e07","unresolved":false,"context_lines":[{"line_number":966,"context_line":"        The regex accepts the following timestamp formats:"},{"line_number":967,"context_line":"        - YYYY-MM-DD HH:MM:SSZ"},{"line_number":968,"context_line":"        - YYYY-MM-DDTHH:MM:SSZ"},{"line_number":969,"context_line":"        - YYYY-MM-DD HH:MM:SS.XXXXXX"},{"line_number":970,"context_line":"        - YYYY-MM-DDTHH:MM:SS.XXXXXX"},{"line_number":971,"context_line":"        There can be 1-6 digitss for the microseconds (X)."},{"line_number":972,"context_line":""}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_aa594037","line":969,"updated":"2017-01-11 16:59:37.000000000","message":"why there is no Z in the end? Z stands for Zulu time afaik","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"17ca8cf17f3878193155f7c5384b72bfa4a1fc4c","unresolved":false,"context_lines":[{"line_number":977,"context_line":"        otherwise returns False."},{"line_number":978,"context_line":""},{"line_number":979,"context_line":"        \"\"\""},{"line_number":980,"context_line":"        pattern \u003d re.compile(\u0027[1-9]\\d{3}\\-\\d{2}\\-\\d{2}( |T)\u0027"},{"line_number":981,"context_line":"                             \u0027\\d{2}\\:\\d{2}\\:\\d{2}(Z|\\.\\d{1,6})\u0027)"},{"line_number":982,"context_line":"        return pattern.match(timestamp)"},{"line_number":983,"context_line":""},{"line_number":984,"context_line":"    def _clean_up_expired_password_hints(self, hints):"},{"line_number":985,"context_line":"        \"\"\"Clean Up Expired Password Hints."}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_76881d72","line":982,"range":{"start_line":980,"start_character":0,"end_line":982,"end_character":39},"updated":"2017-01-11 17:49:15.000000000","message":"Instead of using regex, perhaps we can use oslo_utils\u0027s timeutils.parse_isotime() and check to see if it raises a ValueError.  This feels odd that we are saying 2017-99-99T99:99:99Z is actually valid.","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":979,"context_line":"        \"\"\""},{"line_number":980,"context_line":"        pattern \u003d re.compile(\u0027[1-9]\\d{3}\\-\\d{2}\\-\\d{2}( |T)\u0027"},{"line_number":981,"context_line":"                             \u0027\\d{2}\\:\\d{2}\\:\\d{2}(Z|\\.\\d{1,6})\u0027)"},{"line_number":982,"context_line":"        return pattern.match(timestamp)"},{"line_number":983,"context_line":""},{"line_number":984,"context_line":"    def _clean_up_expired_password_hints(self, hints):"},{"line_number":985,"context_line":"        \"\"\"Clean Up Expired Password Hints."}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_fbb15190","line":982,"in_reply_to":"ba5201f7_76881d72","updated":"2017-01-11 20:45:13.000000000","message":"It would be nice if we could use jsonschema\u0027s built-in support for date-time validation[0]. The thing is that it has a dependency on strict-rfc3339 [1], which is GPL licensed :( \n\nI am on board with Tin\u0027s approach towards finding something to do this validation for us instead of having to maintain an test our own regular expression. Exploring timeutils sounds like a good idea.\n\n[0] https://python-jsonschema.readthedocs.io/en/latest/validate/#validating-formats\n\n[1] https://pypi.python.org/pypi/strict-rfc3339/","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8b84033c312c1e11146bc70d8a3c89401dfc017d","unresolved":false,"context_lines":[{"line_number":979,"context_line":"        \"\"\""},{"line_number":980,"context_line":"        pattern \u003d re.compile(\u0027[1-9]\\d{3}\\-\\d{2}\\-\\d{2}( |T)\u0027"},{"line_number":981,"context_line":"                             \u0027\\d{2}\\:\\d{2}\\:\\d{2}(Z|\\.\\d{1,6})\u0027)"},{"line_number":982,"context_line":"        return pattern.match(timestamp)"},{"line_number":983,"context_line":""},{"line_number":984,"context_line":"    def _clean_up_expired_password_hints(self, hints):"},{"line_number":985,"context_line":"        \"\"\"Clean Up Expired Password Hints."}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_cc285536","line":982,"in_reply_to":"ba5201f7_fbb15190","updated":"2017-01-11 22:53:08.000000000","message":"Done","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8ac3239ffe5dddad08555724a141988163232aeb","unresolved":false,"context_lines":[{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"},{"line_number":997,"context_line":"                try:"},{"line_number":998,"context_line":"                    filter_[\u0027value\u0027] \u003d timeutils.parse_isotime("},{"line_number":999,"context_line":"                        filter_[\u0027value\u0027])"}],"source_content_type":"text/x-python","patch_set":34,"id":"ba5201f7_324a1327","line":996,"updated":"2017-01-11 23:00:58.000000000","message":"The try/except cases here are interest and something tells me they could be simplified but I haven\u0027t been able to put my finger on it yet (unless I\u0027m not reading them properly)... still pondering though.","commit_id":"36dbd34f501305fbf745ed731c9e8e43c4204d3c"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"71b99e16bac68b3ab1d18f65b0f46659972ba8b7","unresolved":false,"context_lines":[{"line_number":990,"context_line":"                        continue"},{"line_number":991,"context_line":"                    else:"},{"line_number":992,"context_line":"                        raise exception.ValidationOperatorError"},{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"}],"source_content_type":"text/x-python","patch_set":36,"id":"7a3c09a3_0a713376","line":993,"updated":"2017-01-17 21:14:33.000000000","message":"You don\u0027t need this at all. Just let the exception propagate up naturally.","commit_id":"eba919918df8d290ae98c172f8e69e0a72cc18ee"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"54761cf04f5e9d62542f101553daf1bcfa981dcc","unresolved":false,"context_lines":[{"line_number":990,"context_line":"                        continue"},{"line_number":991,"context_line":"                    else:"},{"line_number":992,"context_line":"                        raise exception.ValidationOperatorError"},{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"}],"source_content_type":"text/x-python","patch_set":36,"id":"7a3c09a3_48cdd964","line":993,"in_reply_to":"7a3c09a3_0a713376","updated":"2017-01-18 14:50:04.000000000","message":"Done","commit_id":"eba919918df8d290ae98c172f8e69e0a72cc18ee"},{"author":{"_account_id":7725,"name":"David Stanek","email":"dstanek@dstanek.com","username":"dstanek"},"change_message_id":"71b99e16bac68b3ab1d18f65b0f46659972ba8b7","unresolved":false,"context_lines":[{"line_number":992,"context_line":"                        raise exception.ValidationOperatorError"},{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"},{"line_number":997,"context_line":"                try:"},{"line_number":998,"context_line":"                    filter_[\u0027value\u0027] \u003d timeutils.parse_isotime("}],"source_content_type":"text/x-python","patch_set":36,"id":"7a3c09a3_8a0b63d5","line":995,"updated":"2017-01-17 21:14:33.000000000","message":"Do we have a test case where the ValueError is actually raised?","commit_id":"eba919918df8d290ae98c172f8e69e0a72cc18ee"},{"author":{"_account_id":16465,"name":"Kristi Nikolla","email":"knikolla@bu.edu","username":"knikolla"},"change_message_id":"0b834aec2b3ad9bcb26a8221f23bf5643b7ef523","unresolved":false,"context_lines":[{"line_number":992,"context_line":"                        raise exception.ValidationOperatorError"},{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"},{"line_number":997,"context_line":"                try:"},{"line_number":998,"context_line":"                    filter_[\u0027value\u0027] \u003d timeutils.parse_isotime("}],"source_content_type":"text/x-python","patch_set":36,"id":"7a3c09a3_aa3adfe4","line":995,"in_reply_to":"7a3c09a3_8a0b63d5","updated":"2017-01-17 21:28:34.000000000","message":"ValueError is what is raised here in the case that no operator is provided. Because the split happens in the middle of the date. The try/catch block in the end is what fills in the default operator and instead uses the entire string for parsing the time. This isn\u0027t very obvious at first though. \n\nI spent a brief time thinking about ways this can be rewritten and came up with this http://paste.openstack.org/show/GJh0NsV0fqNM188hy65c/","commit_id":"eba919918df8d290ae98c172f8e69e0a72cc18ee"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"54761cf04f5e9d62542f101553daf1bcfa981dcc","unresolved":false,"context_lines":[{"line_number":992,"context_line":"                        raise exception.ValidationOperatorError"},{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"},{"line_number":997,"context_line":"                try:"},{"line_number":998,"context_line":"                    filter_[\u0027value\u0027] \u003d timeutils.parse_isotime("}],"source_content_type":"text/x-python","patch_set":36,"id":"7a3c09a3_e8bbe5c1","line":995,"in_reply_to":"7a3c09a3_aa3adfe4","updated":"2017-01-18 14:50:04.000000000","message":"Done","commit_id":"eba919918df8d290ae98c172f8e69e0a72cc18ee"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"90ab984403deda7fe88e7dac9b8422add0758ec2","unresolved":false,"context_lines":[{"line_number":992,"context_line":"                        raise exception.ValidationOperatorError"},{"line_number":993,"context_line":"                except exception.ValidationOperatorError:"},{"line_number":994,"context_line":"                    raise"},{"line_number":995,"context_line":"                except ValueError:"},{"line_number":996,"context_line":"                    pass"},{"line_number":997,"context_line":"                try:"},{"line_number":998,"context_line":"                    filter_[\u0027value\u0027] \u003d timeutils.parse_isotime("}],"source_content_type":"text/x-python","patch_set":36,"id":"7a3c09a3_c55abcf1","line":995,"in_reply_to":"7a3c09a3_aa3adfe4","updated":"2017-01-17 21:38:40.000000000","message":"ValueError is from\n\n the timestamp \u003d timeutils.parse_isotime(timestamp)\n\nparse_isotime(timestamp) raises a ValueError if timestamp is not valid.\nAlso I like Kristi\u0027s changes and am testing them out now.","commit_id":"eba919918df8d290ae98c172f8e69e0a72cc18ee"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"82f55c2a6815f52cb2c77cd9abf6c294f54309ed","unresolved":false,"context_lines":[{"line_number":960,"context_line":"        return self._set_domain_id_and_mapping("},{"line_number":961,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":962,"context_line":""},{"line_number":963,"context_line":"    def _clean_up_expired_password_hints(self, hints):"},{"line_number":964,"context_line":"        \"\"\"Clean Up Expired Password Hints."},{"line_number":965,"context_line":""},{"line_number":966,"context_line":"        Any `password_expires_at` filters on the `list_users` or"}],"source_content_type":"text/x-python","patch_set":37,"id":"7a3c09a3_da0892dc","line":963,"updated":"2017-01-18 22:13:32.000000000","message":"Putting the word \u0027translate\u0027 into the method name here might be a little more descriptive of what the method is actually doing. Clean up seems pretty general for this sense.","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"ae3e0dbd1de1538b469b519bd7a6f83c668be1fb","unresolved":false,"context_lines":[{"line_number":960,"context_line":"        return self._set_domain_id_and_mapping("},{"line_number":961,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":962,"context_line":""},{"line_number":963,"context_line":"    def _clean_up_expired_password_hints(self, hints):"},{"line_number":964,"context_line":"        \"\"\"Clean Up Expired Password Hints."},{"line_number":965,"context_line":""},{"line_number":966,"context_line":"        Any `password_expires_at` filters on the `list_users` or"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a3905b3_a9f8c47d","line":963,"in_reply_to":"7a3c09a3_da0892dc","updated":"2017-01-19 17:26:15.000000000","message":"Done","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"89a882f804ea201bf7990452dea253a86c1dc0fe","unresolved":false,"context_lines":[{"line_number":960,"context_line":"        return self._set_domain_id_and_mapping("},{"line_number":961,"context_line":"            ref, domain_id, driver, mapping.EntityType.USER)"},{"line_number":962,"context_line":""},{"line_number":963,"context_line":"    def _translate_expired_password_hints(self, hints):"},{"line_number":964,"context_line":"        \"\"\"Clean Up Expired Password Hints."},{"line_number":965,"context_line":""},{"line_number":966,"context_line":"        Any `password_expires_at` filters on the `list_users` or"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_606bfcf1","line":963,"range":{"start_line":963,"start_character":8,"end_line":963,"end_character":41},"updated":"2017-01-21 06:01:24.000000000","message":"nice job overall here!","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"89a882f804ea201bf7990452dea253a86c1dc0fe","unresolved":false,"context_lines":[{"line_number":967,"context_line":"        `list_users_in_group` queries are modified so the call will"},{"line_number":968,"context_line":"        return valid data."},{"line_number":969,"context_line":""},{"line_number":970,"context_line":"        The filters `comparator` is changed to the operator specified in"},{"line_number":971,"context_line":"        the call, otherwise it is assumed to be `equals`. The filters"},{"line_number":972,"context_line":"        `value` becomes the timestamp specified. Both the operator and"},{"line_number":973,"context_line":"        timestamp are validated, and will raise a InvalidOperatorError"},{"line_number":974,"context_line":"        or ValidationTimeStampError exception respectively if invalid."},{"line_number":975,"context_line":""},{"line_number":976,"context_line":"        \"\"\""},{"line_number":977,"context_line":"        operators \u003d {\u0027lt\u0027: operator.lt, \u0027gt\u0027: operator.gt,"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_008f98cc","line":974,"range":{"start_line":970,"start_character":8,"end_line":974,"end_character":70},"updated":"2017-01-21 06:01:24.000000000","message":"great docstring","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"89a882f804ea201bf7990452dea253a86c1dc0fe","unresolved":false,"context_lines":[{"line_number":979,"context_line":"                     \u0027gte\u0027: operator.ge, \u0027neq\u0027: operator.ne}"},{"line_number":980,"context_line":"        for filter_ in hints.filters:"},{"line_number":981,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":982,"context_line":"                # Dates are usually in the format \u0027lt:2016-11-06T15:32:17Z\u0027"},{"line_number":983,"context_line":"                # So we can assume the position of the \u0027:\u0027 otherwise assign"},{"line_number":984,"context_line":"                # the operator to equals."},{"line_number":985,"context_line":"                if \u0027:\u0027 in filter_[\u0027value\u0027][2:4]:"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_a0b24492","line":982,"range":{"start_line":982,"start_character":18,"end_line":982,"end_character":35},"updated":"2017-01-21 06:01:24.000000000","message":"s/Dates are usually/password_expires_at must be","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"1ceaa2a907ff144917913101211c11ef15f64375","unresolved":false,"context_lines":[{"line_number":979,"context_line":"                     \u0027gte\u0027: operator.ge, \u0027neq\u0027: operator.ne}"},{"line_number":980,"context_line":"        for filter_ in hints.filters:"},{"line_number":981,"context_line":"            if \u0027password_expires_at\u0027 \u003d\u003d filter_[\u0027name\u0027]:"},{"line_number":982,"context_line":"                # Dates are usually in the format \u0027lt:2016-11-06T15:32:17Z\u0027"},{"line_number":983,"context_line":"                # So we can assume the position of the \u0027:\u0027 otherwise assign"},{"line_number":984,"context_line":"                # the operator to equals."},{"line_number":985,"context_line":"                if \u0027:\u0027 in filter_[\u0027value\u0027][2:4]:"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_e790a745","line":982,"range":{"start_line":982,"start_character":18,"end_line":982,"end_character":35},"in_reply_to":"5a3905b3_a0b24492","updated":"2017-01-23 15:48:42.000000000","message":"Done","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"}],"keystone/tests/unit/test_v3_filters.py":[{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"b78ed5a3ef41754054d78dc9b32f882aacdb3bcd","unresolved":false,"context_lines":[{"line_number":236,"context_line":"            self.assertEqual(1, len(r.result.get(\u0027users\u0027)))"},{"line_number":237,"context_line":"            self.assertEqual(user[\u0027id\u0027], r.result.get(\u0027users\u0027)[0][\u0027id\u0027])"},{"line_number":238,"context_line":""},{"line_number":239,"context_line":"    def _get_user_ref(self, user_id):"},{"line_number":240,"context_line":"        with sql.session_for_read() as session:"},{"line_number":241,"context_line":"            return session.query(model.User).get(user_id)"},{"line_number":242,"context_line":""}],"source_content_type":"text/x-python","patch_set":14,"id":"3a71b18c_f2f18fce","line":239,"updated":"2016-12-09 17:30:11.000000000","message":"as suggested in the other comment, please add a test combining filters","commit_id":"86895936feade978cdaa0fbad5275ab73bf2a12f"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"7a53a0138e5539589da4ccfee6f028f225031e5e","unresolved":false,"context_lines":[{"line_number":251,"context_line":"        return self.get(url_by_name, auth\u003dself.auth)"},{"line_number":252,"context_line":""},{"line_number":253,"context_line":"    def test_list_users_by_password_expires_at(self):"},{"line_number":254,"context_line":"        \"\"\"GET /users?password_expires_at\u003d{operator}:{timestamp}."},{"line_number":255,"context_line":""},{"line_number":256,"context_line":"        Test Plan:"},{"line_number":257,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"1a6eadb0_7dee798c","line":254,"range":{"start_line":254,"start_character":0,"end_line":254,"end_character":65},"updated":"2016-12-13 13:59:24.000000000","message":"How do we combine 2 filters ? Please create a test like that, example: /users?password_expires_at\u003dlt:{timestamp1}\u0026password_expires_at\u003dgt:{timestamp2}\n\nI am concerned on how we do that because I am not sure we can use the same query param key twice (password_expires_at appearing twice in the url)","commit_id":"b8c97e0f326ce0b490cb23f44f783c65cd68d216"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"c54006a16581e5a507683db117645bc0ca07bbc5","unresolved":false,"context_lines":[{"line_number":251,"context_line":"        return self.get(url_by_name, auth\u003dself.auth)"},{"line_number":252,"context_line":""},{"line_number":253,"context_line":"    def test_list_users_by_password_expires_at(self):"},{"line_number":254,"context_line":"        \"\"\"GET /users?password_expires_at\u003d{operator}:{timestamp}."},{"line_number":255,"context_line":""},{"line_number":256,"context_line":"        Test Plan:"},{"line_number":257,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"1a6eadb0_8ef758f4","line":254,"range":{"start_line":254,"start_character":0,"end_line":254,"end_character":65},"in_reply_to":"1a6eadb0_7dee798c","updated":"2016-12-14 17:27:13.000000000","message":"Done","commit_id":"b8c97e0f326ce0b490cb23f44f783c65cd68d216"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"5660ca082e8c20f9890bc892354d9de9c7a4428b","unresolved":false,"context_lines":[{"line_number":279,"context_line":"        with sql.session_for_write() as session:"},{"line_number":280,"context_line":"            user_ref.password_ref.created_at \u003d created_time"},{"line_number":281,"context_line":"            user_ref.password_ref.expires_at \u003d expired_time"},{"line_number":282,"context_line":"            session.add(user_ref)"},{"line_number":283,"context_line":""},{"line_number":284,"context_line":"            for op, times in op_assoc.items():"},{"line_number":285,"context_line":"                self.assertTrue("}],"source_content_type":"text/x-python","patch_set":22,"id":"1a6eadb0_e959a65b","line":282,"updated":"2016-12-14 18:04:25.000000000","message":"if we only have a single user, how do we know combined filters are working? there should be a user who timestamps are outside the query timestamps","commit_id":"fbee8e9a8955be2f8a32e95d66109d0d2519fb01"},{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"ed40116e76f67a0fbd46decefc9008a4552e5b60","unresolved":false,"context_lines":[{"line_number":279,"context_line":"        with sql.session_for_write() as session:"},{"line_number":280,"context_line":"            user_ref.password_ref.created_at \u003d created_time"},{"line_number":281,"context_line":"            user_ref.password_ref.expires_at \u003d expired_time"},{"line_number":282,"context_line":"            session.add(user_ref)"},{"line_number":283,"context_line":""},{"line_number":284,"context_line":"            for op, times in op_assoc.items():"},{"line_number":285,"context_line":"                self.assertTrue("}],"source_content_type":"text/x-python","patch_set":22,"id":"1a6eadb0_bfc0bc7d","line":282,"in_reply_to":"1a6eadb0_e959a65b","updated":"2016-12-14 18:28:52.000000000","message":"++","commit_id":"fbee8e9a8955be2f8a32e95d66109d0d2519fb01"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"5660ca082e8c20f9890bc892354d9de9c7a4428b","unresolved":false,"context_lines":[{"line_number":298,"context_line":"                            op, times[1], op2, times2[0]))"},{"line_number":299,"context_line":"                    self.assertFalse("},{"line_number":300,"context_line":"                        self._list_users_by_multiple_password_expires_at("},{"line_number":301,"context_line":"                            op, times[1], op2, times2[1]))"},{"line_number":302,"context_line":""},{"line_number":303,"context_line":"    def _get_domain_fixture(self):"},{"line_number":304,"context_line":"        domain \u003d unit.new_domain_ref()"}],"source_content_type":"text/x-python","patch_set":22,"id":"1a6eadb0_442a817f","line":301,"updated":"2016-12-14 18:04:25.000000000","message":"this test is trying to be exhaustive, but it\u0027s a bit hard to read too.\n\nI don\u0027t think we need to be exhaustive, just use a few combinations and write a couple of tests that make sense testing the behavior.","commit_id":"fbee8e9a8955be2f8a32e95d66109d0d2519fb01"},{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"ed40116e76f67a0fbd46decefc9008a4552e5b60","unresolved":false,"context_lines":[{"line_number":298,"context_line":"                            op, times[1], op2, times2[0]))"},{"line_number":299,"context_line":"                    self.assertFalse("},{"line_number":300,"context_line":"                        self._list_users_by_multiple_password_expires_at("},{"line_number":301,"context_line":"                            op, times[1], op2, times2[1]))"},{"line_number":302,"context_line":""},{"line_number":303,"context_line":"    def _get_domain_fixture(self):"},{"line_number":304,"context_line":"        domain \u003d unit.new_domain_ref()"}],"source_content_type":"text/x-python","patch_set":22,"id":"1a6eadb0_ff5154d9","line":301,"in_reply_to":"1a6eadb0_442a817f","updated":"2016-12-14 18:28:52.000000000","message":"agreed, maybe split it up into multiple tests for each intended behavior, but that may be too much.","commit_id":"fbee8e9a8955be2f8a32e95d66109d0d2519fb01"},{"author":{"_account_id":21420,"name":"Gage Hugo","email":"gagehugo@gmail.com","username":"ghugo"},"change_message_id":"ed40116e76f67a0fbd46decefc9008a4552e5b60","unresolved":false,"context_lines":[{"line_number":362,"context_line":"            user_ref.password_ref.expires_at \u003d expired_time"},{"line_number":363,"context_line":"            session.add(user_ref)"},{"line_number":364,"context_line":""},{"line_number":365,"context_line":"            for op, times in op_assoc.items():"},{"line_number":366,"context_line":"                self.assertTrue("},{"line_number":367,"context_line":"                    self._list_users_in_group_by_password_expires_at("},{"line_number":368,"context_line":"                        new_group[\u0027id\u0027], op, times[0]))"},{"line_number":369,"context_line":"                self.assertFalse("},{"line_number":370,"context_line":"                    self._list_users_in_group_by_password_expires_at("},{"line_number":371,"context_line":"                        new_group[\u0027id\u0027], op, times[1]))"},{"line_number":372,"context_line":"                for op2, times2 in op_assoc.items():"},{"line_number":373,"context_line":"                    self.assertTrue("},{"line_number":374,"context_line":"                        self._list_users_in_group_multiple_password_expires_at("},{"line_number":375,"context_line":"                            new_group[\u0027id\u0027], op, times[0], op2, times2[0]))"},{"line_number":376,"context_line":"                    self.assertFalse("},{"line_number":377,"context_line":"                        self._list_users_in_group_multiple_password_expires_at("},{"line_number":378,"context_line":"                            new_group[\u0027id\u0027], op, times[0], op2, times2[1]))"},{"line_number":379,"context_line":"                    self.assertFalse("},{"line_number":380,"context_line":"                        self._list_users_in_group_multiple_password_expires_at("},{"line_number":381,"context_line":"                            new_group[\u0027id\u0027], op, times[1], op2, times2[0]))"},{"line_number":382,"context_line":"                    self.assertFalse("},{"line_number":383,"context_line":"                        self._list_users_in_group_multiple_password_expires_at("},{"line_number":384,"context_line":"                            new_group[\u0027id\u0027], op, times[1], op2, times2[1]))"},{"line_number":385,"context_line":""},{"line_number":386,"context_line":"    def test_inexact_filters(self):"},{"line_number":387,"context_line":"        # Create 20 users"}],"source_content_type":"text/x-python","patch_set":22,"id":"1a6eadb0_ff687489","line":384,"range":{"start_line":365,"start_character":0,"end_line":384,"end_character":75},"updated":"2016-12-14 18:28:52.000000000","message":"Same as above","commit_id":"fbee8e9a8955be2f8a32e95d66109d0d2519fb01"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":26,"id":"1a6eadb0_6aa98c23","updated":"2016-12-17 03:29:03.000000000","message":"additional tests are necessary. specifically negative ones, like a bad operator or bad timestamp.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"d2219cfeef2ef694d878882800abf8c8c07b2130","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":26,"id":"da4df55a_a7142c44","in_reply_to":"1a6eadb0_6aa98c23","updated":"2016-12-25 17:56:18.000000000","message":"I pulled down this patch set to play around today, and added something like this:\n\n        self.assertEqual(\n            0, len(self._list_users_by_password_expires_at(\n                \u0027xx\u0027, self.test_starttime - datetime.timedelta(seconds\u003d1)))\n        )\n\nI noticed bad operator name is ignored, i.e., that test above will pass.  Should it not raise a 400 Bad Request error?","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"59ebaa96dfdb77cbc31568114fea90d100f17daf","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":26,"id":"da4df55a_4dd47f46","in_reply_to":"da4df55a_a7142c44","updated":"2016-12-26 01:57:31.000000000","message":"probably should","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":14,"context_line":"# under the License."},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"import datetime"},{"line_number":17,"context_line":"import json"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"import freezegun"},{"line_number":20,"context_line":"from oslo_serialization import jsonutils"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_2afb147a","line":17,"range":{"start_line":17,"start_character":0,"end_line":17,"end_character":11},"updated":"2016-12-17 03:29:03.000000000","message":"jsonutils is imported 2 lines below, it\u0027s basically the same as json but with optimizations, just use that. it\u0027s a straight rip and replace\n\n  json.loads()\n  jsonutils.loads()\n\n  json.dumps()\n  jsonutils.dumps()","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":20,"context_line":"from oslo_serialization import jsonutils"},{"line_number":21,"context_line":"from six.moves import range"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"from keystone.common import sql"},{"line_number":24,"context_line":"from keystone.identity.backends import sql_model as model"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"import keystone.conf"},{"line_number":27,"context_line":"from keystone.tests import unit"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_0a001865","line":24,"range":{"start_line":23,"start_character":0,"end_line":24,"end_character":57},"updated":"2016-12-17 03:29:03.000000000","message":"group these with the other imports below","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":21,"context_line":"from six.moves import range"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"from keystone.common import sql"},{"line_number":24,"context_line":"from keystone.identity.backends import sql_model as model"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"import keystone.conf"},{"line_number":27,"context_line":"from keystone.tests import unit"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_eafcbc73","line":24,"range":{"start_line":24,"start_character":49,"end_line":24,"end_character":57},"updated":"2016-12-17 03:29:03.000000000","message":"this rename seems unnecessary","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":237,"context_line":"            self.assertEqual(user[\u0027id\u0027], r.result.get(\u0027users\u0027)[0][\u0027id\u0027])"},{"line_number":238,"context_line":""},{"line_number":239,"context_line":"    def _get_user_ref(self, user_id):"},{"line_number":240,"context_line":"        with sql.session_for_read() as session:"},{"line_number":241,"context_line":"            return session.query(model.User).get(user_id)"},{"line_number":242,"context_line":""},{"line_number":243,"context_line":"    def _list_users_by_password_expires_at(self, operator, time):"},{"line_number":244,"context_line":"        url_by_name \u003d \u0027/users?password_expires_at\u003d%s:%s\u0027 % (operator, time)"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_ea0afc77","line":241,"range":{"start_line":240,"start_character":8,"end_line":241,"end_character":57},"updated":"2016-12-17 03:29:03.000000000","message":"no need to go into the sql portion of the code to get the user. we have various controllers available to us in the tests, try:\n\n  user \u003d self.identity_api.get_user(user_id)\n\nwith that, you shouldn\u0027t have to create a private function","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"79031b43d04fc295e567932f241d99d2442a7396","unresolved":false,"context_lines":[{"line_number":241,"context_line":"            return session.query(model.User).get(user_id)"},{"line_number":242,"context_line":""},{"line_number":243,"context_line":"    def _list_users_by_password_expires_at(self, operator, time):"},{"line_number":244,"context_line":"        url_by_name \u003d \u0027/users?password_expires_at\u003d%s:%s\u0027 % (operator, time)"},{"line_number":245,"context_line":"        response \u003d self.get(url_by_name, auth\u003dself.auth)"},{"line_number":246,"context_line":"        return json.loads(response.body.decode(\u0027utf-8\u0027))[\u0027users\u0027]"},{"line_number":247,"context_line":""}],"source_content_type":"text/x-python","patch_set":26,"id":"da4df55a_bebf8ea8","line":244,"range":{"start_line":244,"start_character":7,"end_line":244,"end_character":75},"updated":"2016-12-28 16:44:06.000000000","message":"After trying to refactor and debug this a bit, I believe the way this test is written will not handle the parameter `time` without some formatting.  See the following:\n\n   \u003e\u003e\u003e import datetime\n   \u003e\u003e\u003e created_time \u003d datetime.datetime.utcnow()\n   \u003e\u003e\u003e expired_time \u003d created_time + datetime.timedelta(days\u003d1)   \n   \u003e\u003e\u003e url \u003d \u0027/users?password_expires_at\u003d%s\u0027 % expired_time\n   \u003e\u003e\u003e print(url)\n   /users?password_expires_at\u003d2016-12-29 16:37:14.458924\n\nFor one, it puts in the microseconds instead of just a Z.  Also, it adds a space between the date and time instead of a T.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":243,"context_line":"    def _list_users_by_password_expires_at(self, operator, time):"},{"line_number":244,"context_line":"        url_by_name \u003d \u0027/users?password_expires_at\u003d%s:%s\u0027 % (operator, time)"},{"line_number":245,"context_line":"        response \u003d self.get(url_by_name, auth\u003dself.auth)"},{"line_number":246,"context_line":"        return json.loads(response.body.decode(\u0027utf-8\u0027))[\u0027users\u0027]"},{"line_number":247,"context_line":""},{"line_number":248,"context_line":"    def _list_users_by_multiple_password_expires_at("},{"line_number":249,"context_line":"            self, operator1, time1, operator2, time2):"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_6a48accc","line":246,"range":{"start_line":246,"start_character":15,"end_line":246,"end_character":65},"updated":"2016-12-17 03:29:03.000000000","message":"you shouldn\u0027t need to go into the body to get stuff, try:\n\n  json.loads(response.result[\u0027users\u0027])","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":247,"context_line":""},{"line_number":248,"context_line":"    def _list_users_by_multiple_password_expires_at("},{"line_number":249,"context_line":"            self, operator1, time1, operator2, time2):"},{"line_number":250,"context_line":"        url_by_name \u003d \u0027/users?password_expires_at\u003d%s:%s\u0026password_expires_at\u003d%s:%s\u0027 % \\"},{"line_number":251,"context_line":"            (operator1, time1, operator2, time2)"},{"line_number":252,"context_line":"        response \u003d self.get(url_by_name, auth\u003dself.auth)"},{"line_number":253,"context_line":"        return json.loads(response.body.decode(\u0027utf-8\u0027))[\u0027users\u0027]"},{"line_number":254,"context_line":""}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_6af18c94","line":251,"range":{"start_line":250,"start_character":22,"end_line":251,"end_character":48},"updated":"2016-12-17 03:29:03.000000000","message":"we try not to use slashes, you can wrap this in brackets:\n\n  (\u0027/users?password_expires_at\u003d%s:%s\u0026password_expires_at\u003d%s:%s\u0027 % \n            (operator1, time1, operator2, time2))","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6d0e50d57effceafd733fe0a00b23990b9454986","unresolved":false,"context_lines":[{"line_number":258,"context_line":"        Test Plan:"},{"line_number":259,"context_line":""},{"line_number":260,"context_line":"        - Create a user with a password expiration timestamp"},{"line_number":261,"context_line":"        - Ensure we can filter on it successfully"},{"line_number":262,"context_line":""},{"line_number":263,"context_line":"        \"\"\""},{"line_number":264,"context_line":"        created_time \u003d datetime.datetime.utcnow()"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_e17bbf27","line":261,"updated":"2016-12-19 21:59:26.000000000","message":"I don\u0027t think we need to reach in to the sql backends here in order to achieve this. We should be able to leverage the configuration object to set password expiration to something short, use freezegun, and the identity API to test this.\n\nThe advantage will be that it is easier to read and maintain.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":261,"context_line":"        - Ensure we can filter on it successfully"},{"line_number":262,"context_line":""},{"line_number":263,"context_line":"        \"\"\""},{"line_number":264,"context_line":"        created_time \u003d datetime.datetime.utcnow()"},{"line_number":265,"context_line":"        expired_time \u003d created_time + datetime.timedelta(days\u003d1)"},{"line_number":266,"context_line":"        before_time \u003d expired_time - datetime.timedelta(days\u003d2)"},{"line_number":267,"context_line":"        after_time \u003d expired_time + datetime.timedelta(days\u003d2)"},{"line_number":268,"context_line":""},{"line_number":269,"context_line":"        op_assoc \u003d {\u0027eq\u0027: [expired_time, after_time],"},{"line_number":270,"context_line":"                    \u0027neq\u0027: [after_time, expired_time],"},{"line_number":271,"context_line":"                    \u0027lt\u0027: [after_time, before_time],"},{"line_number":272,"context_line":"                    \u0027lte\u0027: [after_time, before_time],"},{"line_number":273,"context_line":"                    \u0027gt\u0027: [before_time, after_time],"},{"line_number":274,"context_line":"                    \u0027gte\u0027: [before_time, after_time]}"},{"line_number":275,"context_line":"        two_op_assoc \u003d {\u0027lt\u0027: \u0027gt\u0027, \u0027lte\u0027: \u0027gte\u0027, \u0027neq\u0027: \u0027gt\u0027}"},{"line_number":276,"context_line":""},{"line_number":277,"context_line":"        self._set_policy({\"identity:list_users\": []})"},{"line_number":278,"context_line":"        user1_ref \u003d self._get_user_ref(self.user1[\u0027id\u0027])"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_8a8d08d5","line":275,"range":{"start_line":264,"start_character":0,"end_line":275,"end_character":62},"updated":"2016-12-17 03:29:03.000000000","message":"This is used in two tests, make it common (in setUp or private function)","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":274,"context_line":"                    \u0027gte\u0027: [before_time, after_time]}"},{"line_number":275,"context_line":"        two_op_assoc \u003d {\u0027lt\u0027: \u0027gt\u0027, \u0027lte\u0027: \u0027gte\u0027, \u0027neq\u0027: \u0027gt\u0027}"},{"line_number":276,"context_line":""},{"line_number":277,"context_line":"        self._set_policy({\"identity:list_users\": []})"},{"line_number":278,"context_line":"        user1_ref \u003d self._get_user_ref(self.user1[\u0027id\u0027])"},{"line_number":279,"context_line":"        user2_ref \u003d self._get_user_ref(self.user2[\u0027id\u0027])"},{"line_number":280,"context_line":""}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_caf38071","line":277,"range":{"start_line":277,"start_character":8,"end_line":277,"end_character":53},"updated":"2016-12-17 03:29:03.000000000","message":"why are we setting policy to nothing? using \"self.get(url)\" should make an admin call.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6d0e50d57effceafd733fe0a00b23990b9454986","unresolved":false,"context_lines":[{"line_number":274,"context_line":"                    \u0027gte\u0027: [before_time, after_time]}"},{"line_number":275,"context_line":"        two_op_assoc \u003d {\u0027lt\u0027: \u0027gt\u0027, \u0027lte\u0027: \u0027gte\u0027, \u0027neq\u0027: \u0027gt\u0027}"},{"line_number":276,"context_line":""},{"line_number":277,"context_line":"        self._set_policy({\"identity:list_users\": []})"},{"line_number":278,"context_line":"        user1_ref \u003d self._get_user_ref(self.user1[\u0027id\u0027])"},{"line_number":279,"context_line":"        user2_ref \u003d self._get_user_ref(self.user2[\u0027id\u0027])"},{"line_number":280,"context_line":""}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_21f137ce","line":277,"in_reply_to":"1a6eadb0_caf38071","updated":"2016-12-19 21:59:26.000000000","message":"Yep - we should make sure we\u0027re testing the default policy in this case. We wouldn\u0027t want to expose an admin-like feature to unprivileged users.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":278,"context_line":"        user1_ref \u003d self._get_user_ref(self.user1[\u0027id\u0027])"},{"line_number":279,"context_line":"        user2_ref \u003d self._get_user_ref(self.user2[\u0027id\u0027])"},{"line_number":280,"context_line":""},{"line_number":281,"context_line":"        with sql.session_for_write() as session:"},{"line_number":282,"context_line":"            user1_ref.password_ref.created_at \u003d created_time"},{"line_number":283,"context_line":"            user1_ref.password_ref.expires_at \u003d expired_time"},{"line_number":284,"context_line":"            session.add(user1_ref, user2_ref)"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":"            for op, times in op_assoc.items():"},{"line_number":287,"context_line":"                user2_ref.password_ref.expires_at \u003d times[1]"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_6ae42cab","line":284,"range":{"start_line":281,"start_character":0,"end_line":284,"end_character":45},"updated":"2016-12-17 03:29:03.000000000","message":"not to be pedantic, but this is something that should go in a private function, it\u0027s used in the other test below -- example of it is already here: https://github.com/openstack/keystone/blob/03ba1324c28e4319f76f0a45ed09e0fd994184bf/keystone/tests/unit/identity/test_backend_sql.py#L532-L540","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":283,"context_line":"            user1_ref.password_ref.expires_at \u003d expired_time"},{"line_number":284,"context_line":"            session.add(user1_ref, user2_ref)"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":"            for op, times in op_assoc.items():"},{"line_number":287,"context_line":"                user2_ref.password_ref.expires_at \u003d times[1]"},{"line_number":288,"context_line":"                self.assertEqual("},{"line_number":289,"context_line":"                    1, len(self._list_users_by_password_expires_at("},{"line_number":290,"context_line":"                        op, times[0])))"},{"line_number":291,"context_line":"                self.assertEqual("},{"line_number":292,"context_line":"                    0, len(self._list_users_by_password_expires_at("},{"line_number":293,"context_line":"                        op, times[1])))"},{"line_number":294,"context_line":"            for op1, op2 in two_op_assoc.items():"},{"line_number":295,"context_line":"                user2_ref.password_ref.expires_at \u003d op_assoc[op2][1]"},{"line_number":296,"context_line":"                self.assertEqual("},{"line_number":297,"context_line":"                    1, len(self._list_users_by_multiple_password_expires_at("},{"line_number":298,"context_line":"                        op1, op_assoc[op1][0], op2, op_assoc[op2][0])))"},{"line_number":299,"context_line":""},{"line_number":300,"context_line":"    def _get_domain_fixture(self):"},{"line_number":301,"context_line":"        domain \u003d unit.new_domain_ref()"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_eacf5c26","line":298,"range":{"start_line":286,"start_character":1,"end_line":298,"end_character":71},"updated":"2016-12-17 03:29:03.000000000","message":"this stuff probably doesn\u0027t need to go in the \"with\" block\n\nedit: oh i see, you\u0027re changing the expires_at on the fly, maybe we can do something cleaner.\n\nedit edit: this whole thing needs a ton of comments.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"5164a1e691be8415f45014b165c836838221ac56","unresolved":false,"context_lines":[{"line_number":283,"context_line":"            user1_ref.password_ref.expires_at \u003d expired_time"},{"line_number":284,"context_line":"            session.add(user1_ref, user2_ref)"},{"line_number":285,"context_line":""},{"line_number":286,"context_line":"            for op, times in op_assoc.items():"},{"line_number":287,"context_line":"                user2_ref.password_ref.expires_at \u003d times[1]"},{"line_number":288,"context_line":"                self.assertEqual("},{"line_number":289,"context_line":"                    1, len(self._list_users_by_password_expires_at("},{"line_number":290,"context_line":"                        op, times[0])))"},{"line_number":291,"context_line":"                self.assertEqual("},{"line_number":292,"context_line":"                    0, len(self._list_users_by_password_expires_at("},{"line_number":293,"context_line":"                        op, times[1])))"},{"line_number":294,"context_line":"            for op1, op2 in two_op_assoc.items():"},{"line_number":295,"context_line":"                user2_ref.password_ref.expires_at \u003d op_assoc[op2][1]"},{"line_number":296,"context_line":"                self.assertEqual("},{"line_number":297,"context_line":"                    1, len(self._list_users_by_multiple_password_expires_at("},{"line_number":298,"context_line":"                        op1, op_assoc[op1][0], op2, op_assoc[op2][0])))"},{"line_number":299,"context_line":""},{"line_number":300,"context_line":"    def _get_domain_fixture(self):"},{"line_number":301,"context_line":"        domain \u003d unit.new_domain_ref()"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_7ebeaaae","line":298,"range":{"start_line":286,"start_character":1,"end_line":298,"end_character":71},"in_reply_to":"1a6eadb0_eacf5c26","updated":"2016-12-17 12:41:49.000000000","message":"Yes, that\u0027s my feeling too, this is not easy to read. In previous patch sets I\u0027ve suggested that there could be many tests instead of one, but much easier to read.\n\ntest_list_users_by_password_expires_at_before\ntest_list_users_by_password_expires_at_after\ntest_list_users_by_password_expires_at_interval\n\nand so on as needed.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"b39ad7979384bfdcd29640ea540866ca4c5925fe","unresolved":false,"context_lines":[{"line_number":297,"context_line":"                    1, len(self._list_users_by_multiple_password_expires_at("},{"line_number":298,"context_line":"                        op1, op_assoc[op1][0], op2, op_assoc[op2][0])))"},{"line_number":299,"context_line":""},{"line_number":300,"context_line":"    def _get_domain_fixture(self):"},{"line_number":301,"context_line":"        domain \u003d unit.new_domain_ref()"},{"line_number":302,"context_line":"        self.resource_api.create_domain(domain[\u0027id\u0027], domain)"},{"line_number":303,"context_line":"        return domain"},{"line_number":304,"context_line":""},{"line_number":305,"context_line":"    def _list_users_in_group_by_password_expires_at(self, groupid,"},{"line_number":306,"context_line":"                                                    operator, time):"}],"source_content_type":"text/x-python","patch_set":26,"id":"1a6eadb0_4a24d00b","line":303,"range":{"start_line":300,"start_character":4,"end_line":303,"end_character":21},"updated":"2016-12-17 03:29:03.000000000","message":"looks like this is only used once? if so do not make it a private function.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"ec593590f8806e957683560b78e6f3b362775a66","unresolved":false,"context_lines":[{"line_number":14,"context_line":"# under the License."},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"import datetime"},{"line_number":17,"context_line":"from six.moves import http_client"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":"import freezegun"},{"line_number":20,"context_line":"from oslo_config import fixture as config_fixture"}],"source_content_type":"text/x-python","patch_set":32,"id":"ba5201f7_3ebf75fc","line":17,"range":{"start_line":17,"start_character":0,"end_line":17,"end_character":33},"updated":"2017-01-11 05:33:53.000000000","message":"group this with the other six import","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":350,"context_line":"                self._format_timestamp(self.starttime + datetime.timedelta("},{"line_number":351,"context_line":"                    days\u003d1)), \u0027gt\u0027,"},{"line_number":352,"context_line":"                self._format_timestamp(self.starttime + datetime.timedelta("},{"line_number":353,"context_line":"                    days\u003d3)), \u0027lt\u0027)))"},{"line_number":354,"context_line":"        self.assertEqual(1, len("},{"line_number":355,"context_line":"            self._list_users_by_multiple_password_expires_at("},{"line_number":356,"context_line":"                self._format_timestamp(self.starttime + datetime.timedelta("}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_06dcfc71","line":353,"updated":"2017-01-11 20:45:13.000000000","message":"These are some intense assertions. We could improve readability (and reviewablility!) by breaking them into their own well-named variables.","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":358,"context_line":"                self._format_timestamp(self.starttime + datetime.timedelta("},{"line_number":359,"context_line":"                    days\u003d2, seconds\u003d1)), \u0027lte\u0027)))"},{"line_number":360,"context_line":""},{"line_number":361,"context_line":"    def test_list_users_by_password_expires_bad_operator(self):"},{"line_number":362,"context_line":"        \"\"\"Test List Users by Password Expires Bad Operator."},{"line_number":363,"context_line":""},{"line_number":364,"context_line":"        GET /users?password_expires_at\u003d{invalid_operator}:{timestamp}"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_a13eba88","line":361,"updated":"2017-01-11 20:45:13.000000000","message":"A personal goal of mine when coming up with unit tests names (which I\u0027m terrible at) is to follow a convention I stumbled across in The Art of Unit Testing [1]. I find it useful because it provides reviewers and readers of the test with a bunch of information, just from the test name, which means it\u0027s available in test output, etc... The only downside is that sometimes the names get a bit long, in which case I find it easier to condense the test name to a simpler version of the same message and provide more detail in a doc string.\n\n  def test_{thing_under_test}_{condition}_{result}:\n  def test_list_users_by_password_expires_with_bad_operator_fails(self):\n\n\n[0] http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html\n[1] https://www.manning.com/books/the-art-of-unit-testing-second-edition","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":372,"context_line":"            expected_status\u003dhttp_client.BAD_REQUEST)"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":"    def test_list_users_by_password_expires_bad_timestamp(self):"},{"line_number":375,"context_line":"        \"\"\"Test List Users by Password Expires Bad Timestamp."},{"line_number":376,"context_line":""},{"line_number":377,"context_line":"        GET /users?password_expires_at\u003d{invalid_timestamp}"},{"line_number":378,"context_line":""}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_c1df3e38","line":375,"updated":"2017-01-11 20:45:13.000000000","message":"We probably don\u0027t need to have a docstring that is exactly the same as the test name, but instead we could use the docstring to explain *why* the behavior in the test is the way it is (if needed of course).","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":396,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":397,"context_line":""},{"line_number":398,"context_line":"    def _list_users_in_group_by_multiple_password_expires_at("},{"line_number":399,"context_line":"            self, time1, operator1, time2, operator2):"},{"line_number":400,"context_line":"        url \u003d (\u0027/groups/\u0027 + self.group_id + \u0027/users\u0027"},{"line_number":401,"context_line":"               \u0027?password_expires_at\u003d%s:%s\u0026password_expires_at\u003d%s:%s\u0027 %"},{"line_number":402,"context_line":"               (operator1, time1, operator2, time2))"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_01cdc684","line":399,"updated":"2017-01-11 20:45:13.000000000","message":"What\u0027s the reason for the integer in the variable name convention here?","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"8cfa2fcd720d3769d3fbd92e93e1272ae5f612c6","unresolved":false,"context_lines":[{"line_number":471,"context_line":"        GET /groups/{groupid}/users?password_expires_at\u003d"},{"line_number":472,"context_line":"        lte:{timestamp}\u0026gte:{timestamp}"},{"line_number":473,"context_line":""},{"line_number":474,"context_line":"        Ensures users in groups can be filtered on time intervals."},{"line_number":475,"context_line":""},{"line_number":476,"context_line":"        \"\"\""},{"line_number":477,"context_line":"        self._set_policy({\"identity:list_users_in_group\": []})"}],"source_content_type":"text/x-python","patch_set":33,"id":"ba5201f7_e17d221d","line":474,"updated":"2017-01-11 20:45:13.000000000","message":"This makes a great docstring title string.","commit_id":"06ef92126debd9d4c330890f1abae15e32bdcd4c"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"82f55c2a6815f52cb2c77cd9abf6c294f54309ed","unresolved":false,"context_lines":[{"line_number":268,"context_line":"        if operator:"},{"line_number":269,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":270,"context_line":"        url +\u003d str(time)"},{"line_number":271,"context_line":"        response \u003d self.get(url, auth\u003dself.auth,"},{"line_number":272,"context_line":"                            expected_status\u003dexpected_status)"},{"line_number":273,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":274,"context_line":"            return response.result.get(\u0027users\u0027)"}],"source_content_type":"text/x-python","patch_set":37,"id":"7a3c09a3_9a47ea4a","line":271,"updated":"2017-01-18 22:13:32.000000000","message":"This should be done by a self.user (which I believe is an admin?). If that\u0027s the case, why do we need to loosen the policy for list_users and list_users_by_group?","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"ae3e0dbd1de1538b469b519bd7a6f83c668be1fb","unresolved":false,"context_lines":[{"line_number":268,"context_line":"        if operator:"},{"line_number":269,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":270,"context_line":"        url +\u003d str(time)"},{"line_number":271,"context_line":"        response \u003d self.get(url, auth\u003dself.auth,"},{"line_number":272,"context_line":"                            expected_status\u003dexpected_status)"},{"line_number":273,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":274,"context_line":"            return response.result.get(\u0027users\u0027)"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a3905b3_690c1c95","line":271,"in_reply_to":"7a3c09a3_9a47ea4a","updated":"2017-01-19 17:26:15.000000000","message":"See below comment.","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"82f55c2a6815f52cb2c77cd9abf6c294f54309ed","unresolved":false,"context_lines":[{"line_number":296,"context_line":"        GET /users?password_expires_at\u003deq:{timestamp}"},{"line_number":297,"context_line":""},{"line_number":298,"context_line":"        \"\"\""},{"line_number":299,"context_line":"        self._set_policy({\"identity:list_users\": []})"},{"line_number":300,"context_line":"        expire_at_users \u003d self._list_users_by_password_expires_at("},{"line_number":301,"context_line":"            self._format_timestamp("},{"line_number":302,"context_line":"                self.starttime + datetime.timedelta(days\u003d2)))"}],"source_content_type":"text/x-python","patch_set":37,"id":"7a3c09a3_3f0b482d","line":299,"updated":"2017-01-18 22:13:32.000000000","message":"We should have a comment that describes why this test needs to modify the policy. I assume it fails if the default policy is in place?","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"ae3e0dbd1de1538b469b519bd7a6f83c668be1fb","unresolved":false,"context_lines":[{"line_number":296,"context_line":"        GET /users?password_expires_at\u003deq:{timestamp}"},{"line_number":297,"context_line":""},{"line_number":298,"context_line":"        \"\"\""},{"line_number":299,"context_line":"        self._set_policy({\"identity:list_users\": []})"},{"line_number":300,"context_line":"        expire_at_users \u003d self._list_users_by_password_expires_at("},{"line_number":301,"context_line":"            self._format_timestamp("},{"line_number":302,"context_line":"                self.starttime + datetime.timedelta(days\u003d2)))"}],"source_content_type":"text/x-python","patch_set":37,"id":"5a3905b3_291214b5","line":299,"in_reply_to":"7a3c09a3_3f0b482d","updated":"2017-01-19 17:26:15.000000000","message":"Moved the test to a new class since the user data is different from normal filter testing. Also removes the need to mess with the policy.","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"d7b89a5e26952ccd6e4b8c7a8d2841bdf41611ce","unresolved":false,"context_lines":[{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def setUp(self):"},{"line_number":43,"context_line":"        \"\"\"Setup for Identity Filter Test Cases.\"\"\""},{"line_number":44,"context_line":"        self.config_fixture \u003d self.useFixture(config_fixture.Config(CONF))"},{"line_number":45,"context_line":"        self.tempfile \u003d self.useFixture(temporaryfile.SecureTempFile())"},{"line_number":46,"context_line":"        self.tmpfilename \u003d self.tempfile.file_name"},{"line_number":47,"context_line":"        super(IdentityTestFilteredCase, self).setUp()"}],"source_content_type":"text/x-python","patch_set":38,"id":"5a3905b3_e9580c59","line":44,"range":{"start_line":44,"start_character":8,"end_line":44,"end_character":74},"updated":"2017-01-19 17:36:12.000000000","message":"Since the logic is moved out to IdentityPasswordExpiryFilteredTestCase, we can remove this.","commit_id":"478f8d7c312e022435524650a28bb6644e194254"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8a9f2f39e0bd6ed1e0685e0d034907b1a572b9f4","unresolved":false,"context_lines":[{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def setUp(self):"},{"line_number":43,"context_line":"        \"\"\"Setup for Identity Filter Test Cases.\"\"\""},{"line_number":44,"context_line":"        self.config_fixture \u003d self.useFixture(config_fixture.Config(CONF))"},{"line_number":45,"context_line":"        self.tempfile \u003d self.useFixture(temporaryfile.SecureTempFile())"},{"line_number":46,"context_line":"        self.tmpfilename \u003d self.tempfile.file_name"},{"line_number":47,"context_line":"        super(IdentityTestFilteredCase, self).setUp()"}],"source_content_type":"text/x-python","patch_set":38,"id":"5a3905b3_82bafedf","line":44,"range":{"start_line":44,"start_character":8,"end_line":44,"end_character":74},"in_reply_to":"5a3905b3_e9580c59","updated":"2017-01-19 18:49:38.000000000","message":"Done","commit_id":"478f8d7c312e022435524650a28bb6644e194254"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"d7b89a5e26952ccd6e4b8c7a8d2841bdf41611ce","unresolved":false,"context_lines":[{"line_number":407,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":408,"context_line":"        url +\u003d str(time)"},{"line_number":409,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":410,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":411,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":412,"context_line":""},{"line_number":413,"context_line":"    def _list_users_by_multiple_password_expires_at("}],"source_content_type":"text/x-python","patch_set":38,"id":"5a3905b3_24ff9b02","line":410,"range":{"start_line":410,"start_character":0,"end_line":410,"end_character":45},"updated":"2017-01-19 17:36:12.000000000","message":"Do we need a check here while L423-L424 do not?","commit_id":"478f8d7c312e022435524650a28bb6644e194254"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"8a9f2f39e0bd6ed1e0685e0d034907b1a572b9f4","unresolved":false,"context_lines":[{"line_number":407,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":408,"context_line":"        url +\u003d str(time)"},{"line_number":409,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":410,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":411,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":412,"context_line":""},{"line_number":413,"context_line":"    def _list_users_by_multiple_password_expires_at("}],"source_content_type":"text/x-python","patch_set":38,"id":"5a3905b3_42f2d6d3","line":410,"range":{"start_line":410,"start_character":0,"end_line":410,"end_character":45},"in_reply_to":"5a3905b3_24ff9b02","updated":"2017-01-19 18:49:38.000000000","message":"Good catch. Originally I didn\u0027t have them because the `multiple` functions didn\u0027t have anything that was suppose to raise an error. Added tests for that case.","commit_id":"478f8d7c312e022435524650a28bb6644e194254"},{"author":{"_account_id":20466,"name":"Tin Lam","email":"tin@lam.wtf","username":"tinlam"},"change_message_id":"d7b89a5e26952ccd6e4b8c7a8d2841bdf41611ce","unresolved":false,"context_lines":[{"line_number":541,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":542,"context_line":"        url +\u003d str(time)"},{"line_number":543,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":544,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":545,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":546,"context_line":""},{"line_number":547,"context_line":"    def _list_users_in_group_by_multiple_password_expires_at("}],"source_content_type":"text/x-python","patch_set":38,"id":"5a3905b3_244ddb72","line":544,"range":{"start_line":544,"start_character":8,"end_line":544,"end_character":45},"updated":"2017-01-19 17:36:12.000000000","message":"Same as comment above w.r.t. L558-559","commit_id":"478f8d7c312e022435524650a28bb6644e194254"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"f96dc45a182a1d38efd0da7914bad064794a3fc8","unresolved":false,"context_lines":[{"line_number":338,"context_line":"        self.config_fixture \u003d self.useFixture(config_fixture.Config(CONF))"},{"line_number":339,"context_line":"        super(IdentityPasswordExpiryFilteredTestCase, self).setUp()"},{"line_number":340,"context_line":""},{"line_number":341,"context_line":"    def load_sample_data(self):"},{"line_number":342,"context_line":"        self._populate_default_domain()"},{"line_number":343,"context_line":"        self.domain \u003d unit.new_domain_ref()"},{"line_number":344,"context_line":"        self.resource_api.create_domain(self.domain[\u0027id\u0027], self.domain)"}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_791225b6","line":341,"updated":"2017-01-19 20:48:59.000000000","message":"nit: This can be done at a separate time, but we could add a description of what we\u0027re doing in this setup. For example:\n\n\"The test environment will consist of a single domain, containing a single project. We\u0027re going to create three users and one group. Each user is going to be given a role assignment on the project and the domain. Two of the three users are going to be placed into the group, which won\u0027t have any role assignments to either the project or the domain.\"","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"cb4f8858e270dde1279500597f58476cf366740c","unresolved":false,"context_lines":[{"line_number":338,"context_line":"        self.config_fixture \u003d self.useFixture(config_fixture.Config(CONF))"},{"line_number":339,"context_line":"        super(IdentityPasswordExpiryFilteredTestCase, self).setUp()"},{"line_number":340,"context_line":""},{"line_number":341,"context_line":"    def load_sample_data(self):"},{"line_number":342,"context_line":"        self._populate_default_domain()"},{"line_number":343,"context_line":"        self.domain \u003d unit.new_domain_ref()"},{"line_number":344,"context_line":"        self.resource_api.create_domain(self.domain[\u0027id\u0027], self.domain)"}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_9eddf244","line":341,"in_reply_to":"5a3905b3_791225b6","updated":"2017-01-19 21:57:39.000000000","message":"Done","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"f96dc45a182a1d38efd0da7914bad064794a3fc8","unresolved":false,"context_lines":[{"line_number":351,"context_line":"        self.group \u003d self.identity_api.create_group(self.group)"},{"line_number":352,"context_line":"        self.group_id \u003d self.group[\u0027id\u0027]"},{"line_number":353,"context_line":"        # Creates three users each with password expiration offset"},{"line_number":354,"context_line":"        # by one day, starting with the current time frozen."},{"line_number":355,"context_line":"        self.starttime \u003d datetime.datetime.utcnow()"},{"line_number":356,"context_line":"        with freezegun.freeze_time(self.starttime):"},{"line_number":357,"context_line":"            self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_8e7ea5e2","line":354,"updated":"2017-01-19 20:48:59.000000000","message":"Can this be accomplished without the context manager? Is the context manager required? Usually when freezegun\u0027s context manager is used we are manually setting the time to specific values during the test. That\u0027s fine if that\u0027s not the case here, I just need to be educated :)\n\n\nhttps://github.com/spulec/freezegun#context-manager","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"cb4f8858e270dde1279500597f58476cf366740c","unresolved":false,"context_lines":[{"line_number":351,"context_line":"        self.group \u003d self.identity_api.create_group(self.group)"},{"line_number":352,"context_line":"        self.group_id \u003d self.group[\u0027id\u0027]"},{"line_number":353,"context_line":"        # Creates three users each with password expiration offset"},{"line_number":354,"context_line":"        # by one day, starting with the current time frozen."},{"line_number":355,"context_line":"        self.starttime \u003d datetime.datetime.utcnow()"},{"line_number":356,"context_line":"        with freezegun.freeze_time(self.starttime):"},{"line_number":357,"context_line":"            self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_be81ce4c","line":354,"in_reply_to":"5a3905b3_8e7ea5e2","updated":"2017-01-19 21:57:39.000000000","message":"Using the context manager ensures the user\u0027s password expiration are exactly 1, 2 and 3 days from starttime. While the time without using freezegun between them would likely be minuscule, this ensure consistency and makes the calls much easier to predict.","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"a48b0dd6d9aba2c8fc77c50d6420e3ca111fe3a3","unresolved":false,"context_lines":[{"line_number":351,"context_line":"        self.group \u003d self.identity_api.create_group(self.group)"},{"line_number":352,"context_line":"        self.group_id \u003d self.group[\u0027id\u0027]"},{"line_number":353,"context_line":"        # Creates three users each with password expiration offset"},{"line_number":354,"context_line":"        # by one day, starting with the current time frozen."},{"line_number":355,"context_line":"        self.starttime \u003d datetime.datetime.utcnow()"},{"line_number":356,"context_line":"        with freezegun.freeze_time(self.starttime):"},{"line_number":357,"context_line":"            self.config_fixture.config(group\u003d\u0027security_compliance\u0027,"}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_1c774a3b","line":354,"in_reply_to":"5a3905b3_be81ce4c","updated":"2017-01-19 22:55:58.000000000","message":"Aha - sure. That makes sense. Thanks!","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"f96dc45a182a1d38efd0da7914bad064794a3fc8","unresolved":false,"context_lines":[{"line_number":459,"context_line":"        \"\"\""},{"line_number":460,"context_line":"        expire_before_users \u003d self._list_users_by_password_expires_at("},{"line_number":461,"context_line":"            self._format_timestamp(self.starttime + datetime.timedelta("},{"line_number":462,"context_line":"                days\u003d2, seconds\u003d1)), \u0027lt\u0027)"},{"line_number":463,"context_line":"        self.assertEqual(2, len(expire_before_users))"},{"line_number":464,"context_line":""},{"line_number":465,"context_line":"        expire_before_users \u003d self._list_users_by_password_expires_at("}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_946318b1","line":462,"updated":"2017-01-19 20:48:59.000000000","message":"These are some pretty gnarly nested calls, we could probably experiment with ways to make these more readable in the future.","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"f96dc45a182a1d38efd0da7914bad064794a3fc8","unresolved":false,"context_lines":[{"line_number":499,"context_line":"                self._format_timestamp(self.starttime + datetime.timedelta("},{"line_number":500,"context_line":"                    days\u003d3)), \u0027lt\u0027, self._format_timestamp("},{"line_number":501,"context_line":"                        self.starttime + datetime.timedelta(days\u003d1)), \u0027gt\u0027))"},{"line_number":502,"context_line":"        self.assertEqual(1, len(expire_interval_users))"},{"line_number":503,"context_line":""},{"line_number":504,"context_line":"        expire_interval_users \u003d ("},{"line_number":505,"context_line":"            self._list_users_by_multiple_password_expires_at("}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_f4961c93","line":502,"updated":"2017-01-19 20:48:59.000000000","message":"We technically know which use this will be since we have them stored in class variables accessible through all the tests. We could do something like: \n\n  self.assertEqual(self.user2[\u0027id\u0027], expire_interval_users[0][\u0027id\u0027])\n\nOr something of that nature. That way we know we\u0027re not only getting the right *number* of entities back in the response, but the ones we expected to get back based on the setup.\n\nThis could be applied to most of these tests.","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"cb4f8858e270dde1279500597f58476cf366740c","unresolved":false,"context_lines":[{"line_number":499,"context_line":"                self._format_timestamp(self.starttime + datetime.timedelta("},{"line_number":500,"context_line":"                    days\u003d3)), \u0027lt\u0027, self._format_timestamp("},{"line_number":501,"context_line":"                        self.starttime + datetime.timedelta(days\u003d1)), \u0027gt\u0027))"},{"line_number":502,"context_line":"        self.assertEqual(1, len(expire_interval_users))"},{"line_number":503,"context_line":""},{"line_number":504,"context_line":"        expire_interval_users \u003d ("},{"line_number":505,"context_line":"            self._list_users_by_multiple_password_expires_at("}],"source_content_type":"text/x-python","patch_set":39,"id":"5a3905b3_cb409a59","line":502,"in_reply_to":"5a3905b3_f4961c93","updated":"2017-01-19 21:57:39.000000000","message":"Done","commit_id":"5475b035500df6bcbe24cdb9e07f8d448baab44a"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"89a882f804ea201bf7990452dea253a86c1dc0fe","unresolved":false,"context_lines":[{"line_number":415,"context_line":"        if operator:"},{"line_number":416,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":417,"context_line":"        url +\u003d str(time)"},{"line_number":418,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":419,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":420,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":421,"context_line":""},{"line_number":422,"context_line":"    def _list_users_by_multiple_password_expires_at("},{"line_number":423,"context_line":"            self, first_time, first_operator, second_time, second_operator,"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_60421c6a","line":420,"range":{"start_line":418,"start_character":8,"end_line":420,"end_character":47},"updated":"2017-01-21 06:01:24.000000000","message":"it might be easier for you to use this function to return the URL, then in the individual test just call the URL and assert whatever you want\n\n  test_foo:\n    url \u003d _list_users_by_password_expires_at(time, op)\n    self.get(url, expected_status\u003d400)\n\n  test_bar:\n    url \u003d _list_users_by_password_expires_at(time, op)\n    resp \u003d self.get(url)\n    assert stuff from resp","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"2b5cd1b4a21b653bde85a1bb5edad61c2aebd4e7","unresolved":false,"context_lines":[{"line_number":415,"context_line":"        if operator:"},{"line_number":416,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":417,"context_line":"        url +\u003d str(time)"},{"line_number":418,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":419,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":420,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":421,"context_line":""},{"line_number":422,"context_line":"    def _list_users_by_multiple_password_expires_at("},{"line_number":423,"context_line":"            self, first_time, first_operator, second_time, second_operator,"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_6b33d0ab","line":420,"range":{"start_line":418,"start_character":8,"end_line":420,"end_character":47},"in_reply_to":"5a3905b3_11736c16","updated":"2017-01-22 04:23:35.000000000","message":"thinking about this more, i\u0027d really like to see this suggestions fixed, so we don\u0027t forget about it. it\u0027s the way we write most of our tests","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":11022,"name":"Rodrigo Duarte Sousa","email":"rodrigodsousa@gmail.com","username":"rodrigods"},"change_message_id":"9877311534d44f67b0e1a75c250c7909c8260fba","unresolved":false,"context_lines":[{"line_number":415,"context_line":"        if operator:"},{"line_number":416,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":417,"context_line":"        url +\u003d str(time)"},{"line_number":418,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":419,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":420,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":421,"context_line":""},{"line_number":422,"context_line":"    def _list_users_by_multiple_password_expires_at("},{"line_number":423,"context_line":"            self, first_time, first_operator, second_time, second_operator,"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_11736c16","line":420,"range":{"start_line":418,"start_character":8,"end_line":420,"end_character":47},"in_reply_to":"5a3905b3_60421c6a","updated":"2017-01-21 11:34:51.000000000","message":"++","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"1ceaa2a907ff144917913101211c11ef15f64375","unresolved":false,"context_lines":[{"line_number":415,"context_line":"        if operator:"},{"line_number":416,"context_line":"            url +\u003d operator + \u0027:\u0027"},{"line_number":417,"context_line":"        url +\u003d str(time)"},{"line_number":418,"context_line":"        response \u003d self.get(url, expected_status\u003dexpected_status)"},{"line_number":419,"context_line":"        if expected_status \u003d\u003d http_client.OK:"},{"line_number":420,"context_line":"            return response.result.get(\u0027users\u0027)"},{"line_number":421,"context_line":""},{"line_number":422,"context_line":"    def _list_users_by_multiple_password_expires_at("},{"line_number":423,"context_line":"            self, first_time, first_operator, second_time, second_operator,"}],"source_content_type":"text/x-python","patch_set":41,"id":"5a3905b3_27ae2f81","line":420,"range":{"start_line":418,"start_character":8,"end_line":420,"end_character":47},"in_reply_to":"5a3905b3_6b33d0ab","updated":"2017-01-23 15:48:42.000000000","message":"Done","commit_id":"e27db9baff16b2f3d813572745ae9c037ebf06d8"}],"releasenotes/notes/pci-dss-query-password-expired-users-a7c96a3843bb9abc.yaml":[{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1f0474a4bf414380bb4e37abe1354446a5065b65","unresolved":false,"context_lines":[{"line_number":1,"context_line":"---"},{"line_number":2,"context_line":"features:"},{"line_number":3,"context_line":"  - Can now filter the /v3/users and /v3/groups/{group_id}/users"},{"line_number":4,"context_line":"    queries with ``password_expires_at``, ``operator``, and a "},{"line_number":5,"context_line":"    ``timestamp``. It will return a list of users based on the "},{"line_number":6,"context_line":"    ``operator`` and ``timestamp`` given. The ``timestamp`` is in"},{"line_number":7,"context_line":"    the format ``YYYY-MM-DDTHH:mm:ssZ``. The ``operator`` will"}],"source_content_type":"text/x-yaml","patch_set":24,"id":"1a6eadb0_c21569e5","line":4,"range":{"start_line":4,"start_character":61,"end_line":4,"end_character":62},"updated":"2016-12-15 18:00:18.000000000","message":"please remove trailing whitespaces","commit_id":"a9c0519a0b4b8d5df2c5002cb8f2e1fcd0163d33"},{"author":{"_account_id":17860,"name":"Samuel de Medeiros Queiroz","email":"samueldmq@gmail.com","username":"samueldmq"},"change_message_id":"1f0474a4bf414380bb4e37abe1354446a5065b65","unresolved":false,"context_lines":[{"line_number":2,"context_line":"features:"},{"line_number":3,"context_line":"  - Can now filter the /v3/users and /v3/groups/{group_id}/users"},{"line_number":4,"context_line":"    queries with ``password_expires_at``, ``operator``, and a "},{"line_number":5,"context_line":"    ``timestamp``. It will return a list of users based on the "},{"line_number":6,"context_line":"    ``operator`` and ``timestamp`` given. The ``timestamp`` is in"},{"line_number":7,"context_line":"    the format ``YYYY-MM-DDTHH:mm:ssZ``. The ``operator`` will"},{"line_number":8,"context_line":"    compare the password expiration to the ``timestamp`` as follows:"}],"source_content_type":"text/x-yaml","patch_set":24,"id":"1a6eadb0_e2d42521","line":5,"range":{"start_line":5,"start_character":62,"end_line":5,"end_character":63},"updated":"2016-12-15 18:00:18.000000000","message":"ditto","commit_id":"a9c0519a0b4b8d5df2c5002cb8f2e1fcd0163d33"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"73b6aae99258329cb855438c9bfc87416a69f499","unresolved":false,"context_lines":[{"line_number":1,"context_line":"---"},{"line_number":2,"context_line":"features:"},{"line_number":3,"context_line":"  - Can now filter the /v3/users and /v3/groups/{group_id}/users"},{"line_number":4,"context_line":"    queries with ``password_expires_at``, ``operator``, and a"},{"line_number":5,"context_line":"    ``timestamp``. It will return a list of users based on the"},{"line_number":6,"context_line":"    ``operator`` and ``timestamp`` given. The ``timestamp`` is in"},{"line_number":7,"context_line":"    the format ``YYYY-MM-DDTHH:mm:ssZ``. The ``operator`` will"},{"line_number":8,"context_line":"    compare the password expiration to the ``timestamp`` are below."},{"line_number":9,"context_line":""},{"line_number":10,"context_line":"    * lt - password expires before the timestamp"},{"line_number":11,"context_line":"    * lte - password expires at or before timestamp"}],"source_content_type":"text/x-yaml","patch_set":26,"id":"1a6eadb0_d06334af","line":8,"range":{"start_line":1,"start_character":0,"end_line":8,"end_character":67},"updated":"2016-12-16 23:21:34.000000000","message":"This is missing the \"\u003e\" so it\u0027 won\u0027t render correctly. How about...\n\n---\nfeatures:\n  - \u003e\n    [`blueprint pci-dss-query-password-expired-users \u003chttps://blueprints.launchpad.net/keystone/+spec/pci-dss-query-password-expired-users\u003e`_]\n    Added a ``password_expires_at`` query to ``/v3/users`` and ``/v3/groups/{group_id}/users``. The ``password_expires_at`` query is comprised of two parts, an ``operator`` (valid choices listed below) and a ``timestamp`` (of form ``YYYY-MM-DDTHH:mm:ssZ``). The APIs will filter the list of users based on the ``operator`` and ``timestamp`` given. \n\n      * lt - password expires before the timestamp\n      * lte - password expires at or before timestamp\n      * gt - password expires after the timestamp\n      * gte - password expires at or after the timestamp\n      * eq - password expires at the timestamp\n      * neq - password expires not at the timestamp\n\nSee http://developer.openstack.org/api-ref/identity/v3/index.html for more details.","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"6d0e50d57effceafd733fe0a00b23990b9454986","unresolved":false,"context_lines":[{"line_number":5,"context_line":"    ``timestamp``. It will return a list of users based on the"},{"line_number":6,"context_line":"    ``operator`` and ``timestamp`` given. The ``timestamp`` is in"},{"line_number":7,"context_line":"    the format ``YYYY-MM-DDTHH:mm:ssZ``. The ``operator`` will"},{"line_number":8,"context_line":"    compare the password expiration to the ``timestamp`` are below."},{"line_number":9,"context_line":""},{"line_number":10,"context_line":"    * lt - password expires before the timestamp"},{"line_number":11,"context_line":"    * lte - password expires at or before timestamp"}],"source_content_type":"text/x-yaml","patch_set":26,"id":"1a6eadb0_a1e627e6","line":8,"in_reply_to":"1a6eadb0_d06334af","updated":"2016-12-19 21:59:26.000000000","message":"++","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"73b6aae99258329cb855438c9bfc87416a69f499","unresolved":false,"context_lines":[{"line_number":7,"context_line":"    the format ``YYYY-MM-DDTHH:mm:ssZ``. The ``operator`` will"},{"line_number":8,"context_line":"    compare the password expiration to the ``timestamp`` are below."},{"line_number":9,"context_line":""},{"line_number":10,"context_line":"    * lt - password expires before the timestamp"},{"line_number":11,"context_line":"    * lte - password expires at or before timestamp"},{"line_number":12,"context_line":"    * gt - password expires after the timestamp"},{"line_number":13,"context_line":"    * gte - password expires at or after the timestamp"},{"line_number":14,"context_line":"    * eq - password expires at the timestamp"},{"line_number":15,"context_line":"    * neq - password expires not at the timestamp"}],"source_content_type":"text/x-yaml","patch_set":26,"id":"1a6eadb0_b03958a6","line":15,"range":{"start_line":10,"start_character":4,"end_line":15,"end_character":49},"updated":"2016-12-16 23:21:34.000000000","message":"bullets need to be bumped in 2 spaces","commit_id":"af15d402b345c7b708478a729e6c2352d675475d"},{"author":{"_account_id":6482,"name":"Steve Martinelli","email":"s.martinelli@gmail.com","username":"stevemar"},"change_message_id":"ec593590f8806e957683560b78e6f3b362775a66","unresolved":false,"context_lines":[{"line_number":6,"context_line":"    ``/v3/groups/{group_id}/users``. The ``password_expires_at`` query is"},{"line_number":7,"context_line":"    comprised of two parts, an ``operator`` (valid choices listed below)"},{"line_number":8,"context_line":"    and a ``timestamp`` (of form ``YYYY-MM-DDTHH:mm:ssZ``). The APIs will"},{"line_number":9,"context_line":"    filter the list of users based on the ``operator`` and ``timestamp`` given. "},{"line_number":10,"context_line":"      * lt - password expires before the timestamp"},{"line_number":11,"context_line":"      * lte - password expires at or before timestamp"},{"line_number":12,"context_line":"      * gt - password expires after the timestamp"}],"source_content_type":"text/x-yaml","patch_set":32,"id":"ba5201f7_beab6588","line":9,"range":{"start_line":9,"start_character":79,"end_line":9,"end_character":80},"updated":"2017-01-11 05:33:53.000000000","message":"random white space","commit_id":"9e479d54b25ce71214db27dfc5ee954730ef4fa1"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"82f55c2a6815f52cb2c77cd9abf6c294f54309ed","unresolved":false,"context_lines":[{"line_number":6,"context_line":"    ``/v3/groups/{group_id}/users``. The ``password_expires_at`` query is"},{"line_number":7,"context_line":"    comprised of two parts, an ``operator`` (valid choices listed below)"},{"line_number":8,"context_line":"    and a ``timestamp`` (of form ``YYYY-MM-DDTHH:mm:ssZ``). The APIs will"},{"line_number":9,"context_line":"    filter the list of users based on the ``operator`` and ``timestamp`` given."},{"line_number":10,"context_line":"      * lt - password expires before the timestamp"},{"line_number":11,"context_line":"      * lte - password expires at or before timestamp"},{"line_number":12,"context_line":"      * gt - password expires after the timestamp"}],"source_content_type":"text/x-yaml","patch_set":37,"id":"7a3c09a3_fa7aae76","line":9,"updated":"2017-01-18 22:13:32.000000000","message":"This will be formally documented in the Identity API, right?","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":5046,"name":"Lance Bragstad","email":"lbragstad@redhat.com","username":"ldbragst"},"change_message_id":"fd2313435be03d573a19645cff5c01edb709100c","unresolved":false,"context_lines":[{"line_number":6,"context_line":"    ``/v3/groups/{group_id}/users``. The ``password_expires_at`` query is"},{"line_number":7,"context_line":"    comprised of two parts, an ``operator`` (valid choices listed below)"},{"line_number":8,"context_line":"    and a ``timestamp`` (of form ``YYYY-MM-DDTHH:mm:ssZ``). The APIs will"},{"line_number":9,"context_line":"    filter the list of users based on the ``operator`` and ``timestamp`` given."},{"line_number":10,"context_line":"      * lt - password expires before the timestamp"},{"line_number":11,"context_line":"      * lte - password expires at or before timestamp"},{"line_number":12,"context_line":"      * gt - password expires after the timestamp"}],"source_content_type":"text/x-yaml","patch_set":37,"id":"5a3905b3_1323c2be","line":9,"in_reply_to":"5a3905b3_695a5c85","updated":"2017-01-19 19:54:18.000000000","message":"Sweet!","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"},{"author":{"_account_id":22604,"name":"Samuel Pilla","email":"sp516w@att.com","username":"samuel.pilla"},"change_message_id":"ae3e0dbd1de1538b469b519bd7a6f83c668be1fb","unresolved":false,"context_lines":[{"line_number":6,"context_line":"    ``/v3/groups/{group_id}/users``. The ``password_expires_at`` query is"},{"line_number":7,"context_line":"    comprised of two parts, an ``operator`` (valid choices listed below)"},{"line_number":8,"context_line":"    and a ``timestamp`` (of form ``YYYY-MM-DDTHH:mm:ssZ``). The APIs will"},{"line_number":9,"context_line":"    filter the list of users based on the ``operator`` and ``timestamp`` given."},{"line_number":10,"context_line":"      * lt - password expires before the timestamp"},{"line_number":11,"context_line":"      * lte - password expires at or before timestamp"},{"line_number":12,"context_line":"      * gt - password expires after the timestamp"}],"source_content_type":"text/x-yaml","patch_set":37,"id":"5a3905b3_695a5c85","line":9,"in_reply_to":"7a3c09a3_fa7aae76","updated":"2017-01-19 17:26:15.000000000","message":"Already merged, see: https://review.openstack.org/#/c/409936/","commit_id":"9855793595e413046c86ec1cb61d25e82b32625b"}]}
