hyperpocket 0.0.3__py3-none-any.whl → 0.1.9__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. hyperpocket/auth/README.md +3 -3
  2. hyperpocket/auth/__init__.py +0 -8
  3. hyperpocket/auth/gumloop/context.py +13 -0
  4. hyperpocket/auth/gumloop/token_context.py +15 -0
  5. hyperpocket/auth/gumloop/token_handler.py +66 -0
  6. hyperpocket/auth/gumloop/token_schema.py +8 -0
  7. hyperpocket/auth/linear/token_context.py +1 -1
  8. hyperpocket/auth/notion/README.md +28 -0
  9. hyperpocket/auth/notion/context.py +15 -0
  10. hyperpocket/auth/notion/token_context.py +14 -0
  11. hyperpocket/auth/notion/token_handler.py +65 -0
  12. hyperpocket/auth/notion/token_schema.py +10 -0
  13. hyperpocket/auth/provider.py +8 -5
  14. hyperpocket/auth/reddit/context.py +15 -0
  15. hyperpocket/auth/reddit/oauth2_context.py +32 -0
  16. hyperpocket/auth/reddit/oauth2_handler.py +151 -0
  17. hyperpocket/auth/reddit/oauth2_schema.py +18 -0
  18. hyperpocket/auth/slack/token_context.py +1 -1
  19. hyperpocket/builtin.py +63 -0
  20. hyperpocket/cli/__main__.py +12 -0
  21. hyperpocket/cli/auth.py +83 -0
  22. hyperpocket/cli/codegen/auth/__init__.py +13 -0
  23. hyperpocket/cli/codegen/auth/auth_context_template.py +16 -0
  24. hyperpocket/cli/codegen/auth/auth_token_context_template.py +16 -0
  25. hyperpocket/cli/codegen/auth/auth_token_handler_template.py +69 -0
  26. hyperpocket/cli/codegen/auth/auth_token_schema_template.py +12 -0
  27. hyperpocket/cli/codegen/auth/server_auth_template.py +18 -0
  28. hyperpocket/cli/eject.py +19 -0
  29. hyperpocket/cli/sync.py +5 -5
  30. hyperpocket/config/settings.py +14 -12
  31. hyperpocket/futures/futurestore.py +0 -1
  32. hyperpocket/pocket_auth.py +25 -5
  33. hyperpocket/pocket_core.py +264 -0
  34. hyperpocket/pocket_main.py +127 -174
  35. hyperpocket/prompts.py +6 -8
  36. hyperpocket/repository/__init__.py +2 -2
  37. hyperpocket/repository/lock.py +71 -1
  38. hyperpocket/repository/lockfile.py +19 -13
  39. hyperpocket/repository/repository.py +26 -1
  40. hyperpocket/server/auth/__init__.py +0 -6
  41. hyperpocket/server/auth/gumloop.py +16 -0
  42. hyperpocket/server/auth/notion.py +19 -0
  43. hyperpocket/server/auth/reddit.py +16 -0
  44. hyperpocket/server/server.py +56 -20
  45. hyperpocket/server/tool/dto/script.py +15 -2
  46. hyperpocket/server/tool/wasm.py +20 -8
  47. hyperpocket/session/README.md +2 -2
  48. hyperpocket/session/in_memory.py +18 -5
  49. hyperpocket/session/interface.py +14 -0
  50. hyperpocket/session/redis.py +29 -5
  51. hyperpocket/tool/README.md +16 -12
  52. hyperpocket/tool/__init__.py +4 -3
  53. hyperpocket/tool/function/README.md +39 -10
  54. hyperpocket/tool/function/__init__.py +2 -0
  55. hyperpocket/tool/function/annotation.py +2 -1
  56. hyperpocket/tool/function/tool.py +108 -29
  57. hyperpocket/tool/tool.py +100 -28
  58. hyperpocket/tool/wasm/README.md +27 -5
  59. hyperpocket/tool/wasm/browser.py +2 -7
  60. hyperpocket/tool/wasm/script.py +40 -1
  61. hyperpocket/tool/wasm/templates/python.py +32 -14
  62. hyperpocket/tool/wasm/tool.py +21 -18
  63. hyperpocket/tool_like.py +5 -0
  64. hyperpocket/util/__init__.py +1 -1
  65. hyperpocket/util/extract_func_param_desc_from_docstring.py +4 -4
  66. hyperpocket/util/function_to_model.py +5 -2
  67. hyperpocket/util/json_schema_to_model.py +47 -26
  68. {hyperpocket-0.0.3.dist-info → hyperpocket-0.1.9.dist-info}/METADATA +107 -88
  69. hyperpocket-0.1.9.dist-info/RECORD +137 -0
  70. {hyperpocket-0.0.3.dist-info → hyperpocket-0.1.9.dist-info}/WHEEL +1 -1
  71. hyperpocket-0.1.9.dist-info/entry_points.txt +2 -0
  72. hyperpocket/auth/README.KR.md +0 -309
  73. hyperpocket/auth/slack/tests/test_oauth2_handler.py +0 -32
  74. hyperpocket/auth/slack/tests/test_token_handler.py +0 -23
  75. hyperpocket/auth/tests/test_google_oauth2_handler.py +0 -147
  76. hyperpocket/auth/tests/test_slack_oauth2_handler.py +0 -147
  77. hyperpocket/auth/tests/test_slack_token_handler.py +0 -66
  78. hyperpocket/external/__init__.py +0 -7
  79. hyperpocket/external/github_client.py +0 -19
  80. hyperpocket/session/README.KR.md +0 -62
  81. hyperpocket/session/tests/test_in_memory.py +0 -145
  82. hyperpocket/session/tests/test_redis.py +0 -151
  83. hyperpocket/tests/test_pocket.py +0 -116
  84. hyperpocket/tests/test_pocket_auth.py +0 -982
  85. hyperpocket/tool/README.KR.md +0 -68
  86. hyperpocket/tool/builtins/__init__.py +0 -0
  87. hyperpocket/tool/builtins/example/__init__.py +0 -0
  88. hyperpocket/tool/builtins/example/add_tool.py +0 -18
  89. hyperpocket/tool/function/README.KR.md +0 -159
  90. hyperpocket/tool/tests/test_function_tool.py +0 -266
  91. hyperpocket/tool/wasm/README.KR.md +0 -144
  92. hyperpocket-0.0.3.dist-info/RECORD +0 -130
  93. hyperpocket-0.0.3.dist-info/entry_points.txt +0 -3
  94. /hyperpocket/auth/{slack/tests → gumloop}/__init__.py +0 -0
  95. /hyperpocket/auth/{tests → notion}/__init__.py +0 -0
  96. /hyperpocket/{session/tests → auth/reddit}/__init__.py +0 -0
  97. /hyperpocket/{tests → cli/codegen}/__init__.py +0 -0
@@ -0,0 +1,137 @@
1
+ hyperpocket/__init__.py,sha256=iaJvrZ0rgHwAndGFVv8m1Iz_DWtWEcphFPL-1D8f9SY,136
2
+ hyperpocket/builtin.py,sha256=FnsASGfieQKvVrFVESjvm73qsxPvCf7iiHGd7HNVpuQ,2371
3
+ hyperpocket/constants.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ hyperpocket/pocket_auth.py,sha256=-d5UvLkjvsSkem5DGrDphowckVCB3NCLt2jOw9k5U9c,16934
5
+ hyperpocket/pocket_core.py,sha256=bEl91BFXL1b-JHGPQoK5pzoy-RgDDcyfv0FUQZn86Ss,10175
6
+ hyperpocket/pocket_main.py,sha256=eSwevmX7vgRQebpNtOGG5jhVGU-DE_hTUJi-zRWAvdo,10231
7
+ hyperpocket/prompts.py,sha256=XAmTTCzCkXWK50zcmWmGA25v9-HKd_4dL5o85uisbGM,472
8
+ hyperpocket/tool_like.py,sha256=ur8oMU5p4DUYBEF5MBP3faQ7CKsOzb0lLOaL6p8JqSw,134
9
+ hyperpocket/auth/README.md,sha256=zn4QqnFZCA_4X3x8Wb6lE3OP5otYxpByZaCiUkBvaNs,11562
10
+ hyperpocket/auth/__init__.py,sha256=YcwSdDbOasFDzz0mYJR3aQi3nYfbqtrCSppDWZHOBGY,588
11
+ hyperpocket/auth/context.py,sha256=r19VtTK5FGcmvCgCOr16oNJ0GMFYmgaMEe2EHKJhtQY,1302
12
+ hyperpocket/auth/handler.py,sha256=9R6uQ7bPwmkZUUyg0Q1iOSwmWEQKnGEe92XiLeaxM0w,6580
13
+ hyperpocket/auth/provider.py,sha256=zBuVvrp6SwGRGcfWy6mqIXabw-bfahGfsxpBi899RHs,503
14
+ hyperpocket/auth/schema.py,sha256=8J6PIXpFIJyuVhkZzz6X_TiKOz7sFGdUW0qmZD8x3MA,529
15
+ hyperpocket/auth/calendly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ hyperpocket/auth/calendly/context.py,sha256=oUQzLy_w5NPvbXjFPOhLD-ggONVSy9VVpFabvX4ZpsY,408
17
+ hyperpocket/auth/calendly/oauth2_context.py,sha256=SiMeZaoWh3aneZVH_MXsT_cZ8qCSVeL-pWHVbfYUS6g,848
18
+ hyperpocket/auth/calendly/oauth2_handler.py,sha256=ulfhIcj_GCGIIZHVV9v42p2miHVsE3fpKODnrwz4qU0,5296
19
+ hyperpocket/auth/calendly/oauth2_schema.py,sha256=9JFogAWFAB7nzCww7NXDYuunY6S28wQZzPX41TWfMig,373
20
+ hyperpocket/auth/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ hyperpocket/auth/github/context.py,sha256=Y0QOCNbtQfNDsmgq47s0YGUxTl1QQC-qq4Hm2aZonJ8,404
22
+ hyperpocket/auth/github/oauth2_context.py,sha256=Ge8rkScRpcAWMMzSWTt2gWrDyi0UehTeZepf39Q6BK0,828
23
+ hyperpocket/auth/github/oauth2_handler.py,sha256=4HP0WE9cd3I6v4s9ZXvwNPhFPNk9b6L_MByTOvFvAIc,5059
24
+ hyperpocket/auth/github/oauth2_schema.py,sha256=XfeSF820natq9kwoDKbY2I9TgdjxP-hPkdoGGrKjSuA,371
25
+ hyperpocket/auth/github/token_context.py,sha256=evstL4mZsWHe9Io1RwBc90VfFOnVAbRk8BI1HseYwg8,446
26
+ hyperpocket/auth/github/token_handler.py,sha256=Vz5CqkGk8a6mb4xpw_Lhidwy4cZD7wTrK6iPKIRb15E,2777
27
+ hyperpocket/auth/github/token_schema.py,sha256=8vbJIYWqX_p4GPAtM5xMx5iwo9oZOtZTuSYcnpFGeio,209
28
+ hyperpocket/auth/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ hyperpocket/auth/google/context.py,sha256=1ziH27dXPsxp4WkdlyKos37lF623IIs4kX8cmat8QrY,426
30
+ hyperpocket/auth/google/oauth2_context.py,sha256=u7TayAfxPUFWuggtlceub22vHIURF0ZV5Do4jUqLWQ0,1023
31
+ hyperpocket/auth/google/oauth2_handler.py,sha256=547NPhFhi7lpmeVgZ99g_O3IizBMKZ9iAd27RrRNhS4,4880
32
+ hyperpocket/auth/google/oauth2_schema.py,sha256=E_hyNrb-Ro2UWs2AjhmFDBHwaTEsHZa0rQBhUcCWWKk,411
33
+ hyperpocket/auth/gumloop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ hyperpocket/auth/gumloop/context.py,sha256=6y-5cpQCSEig6qnCWHu1Rvp35DJAibFxKREey8IYFFw,402
35
+ hyperpocket/auth/gumloop/token_context.py,sha256=P92g9O8sKjrJs6RyPAnAwYQxPB6yG0EuljSyIhLvT04,468
36
+ hyperpocket/auth/gumloop/token_handler.py,sha256=nqsHlLU_F4EfA1P629INH2RrUGyof2mOAfp9QnLmM14,2633
37
+ hyperpocket/auth/gumloop/token_schema.py,sha256=hTWWxvL70L35CWtXbBzMF4OMpUDFQIKbDKkjJEQWN7g,209
38
+ hyperpocket/auth/linear/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ hyperpocket/auth/linear/context.py,sha256=hkxVDYO2jB3FCqIz_bMyB3n6OO74PpLI4LiW-42NFNQ,429
40
+ hyperpocket/auth/linear/token_context.py,sha256=KJQXBRSRC1GuIfaIWVahfb-DDraiodAUAL1nxM0vKCQ,471
41
+ hyperpocket/auth/linear/token_handler.py,sha256=wLOodmgXHH4AQWz1tJEHtjrglP3tP1uEDeMQOgQHrRA,2683
42
+ hyperpocket/auth/linear/token_schema.py,sha256=oTy-gOZvXEmGVStgFfnNmlbkknNepOTI5KIeTQLRxus,209
43
+ hyperpocket/auth/notion/README.md,sha256=-pDvDU0CPoM_ZN6HobREV4yHDP0dF2T9_vVJ1cQQz3s,565
44
+ hyperpocket/auth/notion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ hyperpocket/auth/notion/context.py,sha256=Qa-4fCXK661__lXHf5TGu-4mOFlqYCxuDa4IVIDkncA,426
46
+ hyperpocket/auth/notion/token_context.py,sha256=9U5EjgEumYVPT8wEbWrmK0Zz7MzglDl3vGFaVevTqwQ,470
47
+ hyperpocket/auth/notion/token_handler.py,sha256=eK0lJxsGhngXAAaKz_v7aK9YKyiFuMQsLndLPjaOPNs,2586
48
+ hyperpocket/auth/notion/token_schema.py,sha256=z0Ddy438aB8bsEY69B99Bg2KDNnBw2a-DVrbQgehiwg,210
49
+ hyperpocket/auth/reddit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
+ hyperpocket/auth/reddit/context.py,sha256=WlOvIb4u1PvfrbkYbEq5cSRnXxmIEFuHa4LlKjLEtMc,431
51
+ hyperpocket/auth/reddit/oauth2_context.py,sha256=v1u_uy6O52REp2anteTPNgvb_nX-hI6bM7jNngBZXq4,1015
52
+ hyperpocket/auth/reddit/oauth2_handler.py,sha256=0PLn_A9kkKAXeiEoS11sOaD2p-vyeYFho6_kQhumZJg,5220
53
+ hyperpocket/auth/reddit/oauth2_schema.py,sha256=O93px57D9WMyiY0He-1tAImkCNtorsriRldhlobU6xk,468
54
+ hyperpocket/auth/slack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
+ hyperpocket/auth/slack/context.py,sha256=f8dMs7QtSSyissOlPGw8rCZXsVUckFnZWjZ_LAmNXpA,429
56
+ hyperpocket/auth/slack/oauth2_context.py,sha256=0FJjJIIcVp1-G4EZu85gJeSXor-zOFI4ua2z0R_sFqI,1374
57
+ hyperpocket/auth/slack/oauth2_handler.py,sha256=8v9gSGkKMPoqEFGFyjCI1qdvklpyeml-5qeoUFo54Xg,5902
58
+ hyperpocket/auth/slack/oauth2_schema.py,sha256=eu2jF2thmaz66AKcnivTvIBLTbeF-a-xQFMkR4RQYX8,1056
59
+ hyperpocket/auth/slack/token_context.py,sha256=isoyUzWlUQ_PliXZ9kKsrbWdOw5oGzrq7MNpnqJ-r5I,461
60
+ hyperpocket/auth/slack/token_handler.py,sha256=yaJtlOqJP436ubY2VluGxLuFpPo0jh8Y8V3PHUai4X8,2557
61
+ hyperpocket/auth/slack/token_schema.py,sha256=mAwOtRO2tW5OtDzbllDJtR_uhLD8ShQAjezkAZnAgd0,207
62
+ hyperpocket/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
+ hyperpocket/cli/__main__.py,sha256=4f-ks-2Pzo5QfXEx-JwSpz1C43qTJSBBvnERUwsn6rE,416
64
+ hyperpocket/cli/auth.py,sha256=ypxEpa1L3pqrBdABmRdss23uwE-CnMMNDGHGg4UhBYY,4218
65
+ hyperpocket/cli/eject.py,sha256=Te1YhDONk_VCgZ6l2HjLqONJrn04EfPk19sD6D5SOyY,636
66
+ hyperpocket/cli/pull.py,sha256=1Ku0WKLtlZFnEDvU0m10XsVJcysMxDwdEMzTYgNz0xE,548
67
+ hyperpocket/cli/sync.py,sha256=OO5zFrLOMXk7TSAFEew04ZzW4HLoQ1uVb80UKvIJha0,517
68
+ hyperpocket/cli/codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
+ hyperpocket/cli/codegen/auth/__init__.py,sha256=1R2FwRuReAWyeRe1t3-CpR0Mhvb3xWzF8fEkL2mwuB0,545
70
+ hyperpocket/cli/codegen/auth/auth_context_template.py,sha256=FK8ahapEkx-EyGt5WuS4j3kHnS6-8EcXVhZa9KEmjns,569
71
+ hyperpocket/cli/codegen/auth/auth_token_context_template.py,sha256=sOVeGlt7VKNDuQoB3FPC3S2CaU1qZuHuc6F6aXxb0w4,763
72
+ hyperpocket/cli/codegen/auth/auth_token_handler_template.py,sha256=AMb_uZVBKoQlTPofzO_J1FllLqS99eAPA7t_c7RGpTE,3148
73
+ hyperpocket/cli/codegen/auth/auth_token_schema_template.py,sha256=qXpPcTjnhUizVmBlc6VE6IXwe9NSC9fvYxWPipheuXo,427
74
+ hyperpocket/cli/codegen/auth/server_auth_template.py,sha256=28h8s6-C1AqWhghWHDJieuNufdq1UpPvQQ8OIu_NBGw,620
75
+ hyperpocket/config/__init__.py,sha256=deIffu5NBOlSS1X774QuMlf_yb6P2qBQm6tkcf4s8DU,307
76
+ hyperpocket/config/auth.py,sha256=5Zk7OZmElIqIn7LxsT7H4g3bimBxaKpnbL2v9Fs9MlA,797
77
+ hyperpocket/config/git.py,sha256=CGbY7J1LyN4ccZr9CFGAQwwhjC9kvdU0On_nsAsZ1FQ,362
78
+ hyperpocket/config/logger.py,sha256=Wwl-8lllaCNLZruxXU-bcC74Ciy5SmPOX4AJE5BUzWM,2769
79
+ hyperpocket/config/session.py,sha256=CSXENtWx6Gh4DiPh4u9E-4MFYkM8JRjl2hSPV9PVfrE,801
80
+ hyperpocket/config/settings.py,sha256=yXwZN7eryNWxQCK5aRDHQRTzDLATzmSEZwo-6HCxELU,2017
81
+ hyperpocket/futures/__init__.py,sha256=_pRnYZLbogkYFInA3jokkxrcEVRt6YNaBmkf_dSk3SM,136
82
+ hyperpocket/futures/futurestore.py,sha256=WIJGX-XUdB4gWFu2Trto8vd3nTiLFOrnVzQhQP9bO_U,1316
83
+ hyperpocket/repository/__init__.py,sha256=P4Ge__W5wqNgNILNzHjx7qgS8KRcbwri-nb4IPVVErs,219
84
+ hyperpocket/repository/lock.py,sha256=JhuRB1J1rpqGtN19Qeht-iM0BhFuxgl0EaGW9GzqDJA,8046
85
+ hyperpocket/repository/lockfile.py,sha256=1cNkAv5WHU9o8b6arJn9wcosSp3gHMFe11mbAv72Cgs,2187
86
+ hyperpocket/repository/repository.py,sha256=a0HA6eVA88Xq6MYe3SdqBji0U8_RuiaN2v2OYkT8nZY,1349
87
+ hyperpocket/server/__init__.py,sha256=TLqok_mBeV3VRnbZ_spwrwwbsjJ1q9o375AdBk7tKNA,89
88
+ hyperpocket/server/proxy.py,sha256=OhpAWpilM5ioKAsqInKudtvpYk56nMFeZ-dwoGAYIDA,1905
89
+ hyperpocket/server/server.py,sha256=KPHl9WqRAQsQB-6c1D6nFvdJxxFW0dS__ZpF56oNcNU,7838
90
+ hyperpocket/server/auth/__init__.py,sha256=IMjz9PCzD7qh4QIf2g-gWIdkDeU36jt-9F55vaHvLoM,286
91
+ hyperpocket/server/auth/calendly.py,sha256=mi9_ysn0SffhnEgaoNa2jcHWCcD_yzqkS0rvzpJG2Qg,478
92
+ hyperpocket/server/auth/github.py,sha256=cgUtdCYPhf_e51fEQgiYjyG6yuPfMV5RmltjujuWcBw,759
93
+ hyperpocket/server/auth/google.py,sha256=_C33Bzow7yzjnvf0WUObu8p-dSU5rXqBgSPrENORAmY,470
94
+ hyperpocket/server/auth/gumloop.py,sha256=t_BUQ7RR8_x-ogqXnQ3iNHTVthzJyUTN03iqtRpWUTU,475
95
+ hyperpocket/server/auth/linear.py,sha256=3Ii8jewBCwS2pH3_mSJzBWot1t8IcVghC9K_ohid-H8,448
96
+ hyperpocket/server/auth/notion.py,sha256=CNOLVh2gA2xrVxXgqIAc18dpSTPwM98M-5rFGObBr3k,458
97
+ hyperpocket/server/auth/reddit.py,sha256=UloBQNEdhU1_eXaF8qVSQ5ivq5xtWINsHvBDGwgFSLM,443
98
+ hyperpocket/server/auth/slack.py,sha256=HSfru4_S3B_sEF_keCBLEsNyYeyc92eL5I2a-2TKf64,716
99
+ hyperpocket/server/auth/token.py,sha256=Q38Tt5ly2vLsR0dxjmnJNX2IqS-FkTsm94bO6e2wtYk,1700
100
+ hyperpocket/server/tool/__init__.py,sha256=khNLe3H2W7WXKQlHjXuuvd9R87eHOAZhDsQmjDcbYsg,210
101
+ hyperpocket/server/tool/wasm.py,sha256=N14Sg70cIqTCegmXkrf3HPEwZ_jjKkeiw9G6xJJc-NE,1705
102
+ hyperpocket/server/tool/dto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
+ hyperpocket/server/tool/dto/script.py,sha256=yRBFBz1T4TpssyEqMTEf_E1IdklY0twn1o-FDcXcjDw,847
104
+ hyperpocket/session/README.md,sha256=Wmh-qWJQ-40vU9hP5jNxuM33i0STNfaIjOecba7H-oc,1968
105
+ hyperpocket/session/__init__.py,sha256=-zdfZfXwR336liAYjwcZULIHg_72NxMMcDFz_RQGLGk,260
106
+ hyperpocket/session/in_memory.py,sha256=pD4ZEqsFqLmVOvsDbEcJkUN9nvlJgCXTGapH_PLljYk,3449
107
+ hyperpocket/session/interface.py,sha256=0o5IwXezaR5NKB6F9vfumLnrBSYv7JzY_FAvZB6uuvw,4879
108
+ hyperpocket/session/redis.py,sha256=6SHR2ZJ5E3RS-G_xzeh5ls_cPZ0NarPetlEuaAtqEAk,5467
109
+ hyperpocket/tool/README.md,sha256=vbHvP3fnfihq-H481MiSZEVJNhVoUu0djENb9tiy78c,3026
110
+ hyperpocket/tool/__init__.py,sha256=PlSewsugQ6x4BXszWvmTiW-MfVS87TALvWi4TcIgreY,346
111
+ hyperpocket/tool/tool.py,sha256=9kALM1hk_IE1f9y9DK-_e0Ya5gNuP7oni1pfHU-e2_8,7282
112
+ hyperpocket/tool/function/README.md,sha256=6Y9a8FlFjEdbrVqF0NoQ1j34VoV8Zt6Pf9-xlLIHkTc,3676
113
+ hyperpocket/tool/function/__init__.py,sha256=elshxOWjKUCKSsSSkae8GB65bZ4xG1Xa4o2GCT5QbF4,259
114
+ hyperpocket/tool/function/annotation.py,sha256=UaeawAkX3QNAWLYHCQ-V5UNZyfxpOOvbYs3w5Q8bJq4,1018
115
+ hyperpocket/tool/function/tool.py,sha256=EENaORdCLVz2HM7fdJhDsLCJfixDC4j2Hdf0GnJ-OOg,6034
116
+ hyperpocket/tool/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
+ hyperpocket/tool/wasm/README.md,sha256=QeR7mI8RD6OJ2AXdZCpzNLBW0KUVgYQ0IqCc-wVXgFs,3896
118
+ hyperpocket/tool/wasm/__init__.py,sha256=KGOPPyA1CnbW-T9bgSpfIXf9952l_p5HJDhHGBb3b9o,71
119
+ hyperpocket/tool/wasm/browser.py,sha256=a4TbetonEvClgkfQgTxpIN6a9IxZVB1jjSD3dBF8gNg,1851
120
+ hyperpocket/tool/wasm/invoker.py,sha256=u8ZfGW871IVBaG4_XbOQHfp7QYLS093QdwVa3HOL-_k,1520
121
+ hyperpocket/tool/wasm/script.py,sha256=b0il5tTF8bcMDcfR_ME8V2TppdnPDjNK2rB1GxeH5Pk,4251
122
+ hyperpocket/tool/wasm/tool.py,sha256=GPTxgXEV9DbBbW848TqzNHvVbwk2oMmI__kUhHsuLNw,5199
123
+ hyperpocket/tool/wasm/templates/__init__.py,sha256=cQ-uNsO418xvv54i8bD0IrqcAKUxMuyZoG3ShP72olw,949
124
+ hyperpocket/tool/wasm/templates/node.py,sha256=8ghVQGS9L3IJGdBB8waLK_ej4FS34dCA_bwPKjm8QSU,2782
125
+ hyperpocket/tool/wasm/templates/python.py,sha256=Gi_tn3QQZjolFpbhVDXHLoj4juRLTEGsq_A-iyvTyUk,2733
126
+ hyperpocket/util/__init__.py,sha256=V36_ztskLaKQxOhW2OhrhxRFn4QCxtX3jGjAT4lqNQE,35
127
+ hyperpocket/util/extract_func_param_desc_from_docstring.py,sha256=3bek0BRwDGdlKBATDAhBrSSqzdVshXKD02zNi6KvxO4,4067
128
+ hyperpocket/util/find_all_leaf_class_in_package.py,sha256=afGLqe5s7irOOPh7DI70v-utDL2a0vhNzHjtgSmDeZU,528
129
+ hyperpocket/util/find_all_subclass_in_package.py,sha256=CfsM5sWHHbFZD6M-jbJRN8Zo3m57R1E7FGg_V__HdFU,964
130
+ hyperpocket/util/flatten_json_schema.py,sha256=PXK6I1S2QDxwSGmUVEl5bbSPrjTa38GBllBQ8uKXJNQ,1587
131
+ hyperpocket/util/function_to_model.py,sha256=zPBrxtvfieJearmvJeMOeIGGLn1ymXNvL9PlMoXZbwA,2061
132
+ hyperpocket/util/get_objects_from_subpackage.py,sha256=Aq87PD_H57c2IjLS28Hf0Wu5vLVyoOtDoBvKzvQ1UPw,929
133
+ hyperpocket/util/json_schema_to_model.py,sha256=hmXqiU67WrdHZMGELvHfKxfQ12EqPtTD8x_KezCcEFk,3465
134
+ hyperpocket-0.1.9.dist-info/METADATA,sha256=xXCNAMvXwN0u0ZhkITy943hcfjKj1VE-Rm6my2kFBHM,9961
135
+ hyperpocket-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
136
+ hyperpocket-0.1.9.dist-info/entry_points.txt,sha256=KpBleaYr0SaENXOa-dFvJ_cvFCHYFEQ4LMl11ShAcBI,61
137
+ hyperpocket-0.1.9.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ hyperpocket = hyperpocket.cli.__main__:cli
@@ -1,309 +0,0 @@
1
- ## Auth
2
-
3
- 각 provider에서 유저를 식별하기 위한 프로세스
4
-
5
- ### AuthContext
6
-
7
- 실제 유저의 인증 정보(e.g., access token)을 담고 있는 객체
8
-
9
- ```python
10
- class AuthContext(BaseModel, ABC):
11
- """
12
- This class is used to define the interface of the authentication model.
13
- """
14
- access_token: str = Field(description="user's access token")
15
- description: str = Field(description="description of this authentication context")
16
- expires_at: Optional[datetime] = Field(description="expiration datetime")
17
- detail: Optional[Any] = Field(default=None, description="detailed information")
18
- ```
19
-
20
- 일반적으로 해당 객체에서 access token의 key 정보나 response 객체를 AuthContext로 변환해주는 역할을 수행한다.
21
-
22
- ### AuthenticateRequest
23
-
24
- authentication 수행 시에 필요한 정보를 담고 있는 객체
25
-
26
- ```python
27
- class AuthenticateRequest(BaseModel):
28
- auth_scopes: Optional[list[str]] = Field(default_factory=list,
29
- description="authentication scopes. if the authentication handler is non scoped, it isn't needed")
30
- ```
31
-
32
- - 일반적으로 인증 시에 필요한 client id 및 secret id 등과 같은 정보를 request에 같이 전달한다.
33
-
34
- **examples**
35
-
36
- ```python
37
- class GitHubOAuth2Request(AuthenticateRequest):
38
- client_id: str
39
- client_secret: str
40
- ```
41
-
42
- ### AuthenticateResponse(Optional)
43
-
44
- authentication response에서 필수 정보를 담고 있는 객체
45
-
46
- ```python
47
- class AuthenticateResponse(BaseModel):
48
- """
49
- This class is used to define the interface of the authentication response.
50
- """
51
- pass
52
- ```
53
-
54
- - handler에서 response를 dict 형태로 처리한다면, 구현할 필요가 없다.
55
-
56
- ### AuthHandlerInterface
57
-
58
- 실제 authentication을 수행하는 객체
59
-
60
- ```python
61
- class AuthHandlerInterface(ABC):
62
- name: str = Field(description="name of the authentication handler")
63
- description: str = Field(description="description of the authentication handler")
64
- scoped: bool = Field(description="Indicates whether the handler requires an auth_scope for access control")
65
-
66
-
67
- @staticmethod
68
- def provider() -> AuthProvider:
69
- """
70
- Returns the authentication provider enum.
71
-
72
- This method is used to determine the appropriate authentication handler
73
- based on the authentication provider.
74
- """
75
- raise NotImplementedError()
76
-
77
- @staticmethod
78
- def provider_default() -> bool:
79
- """
80
- Indicates whether this authentication handler is the default handler.
81
-
82
- If no specific handler is designated, the default handler will be used.
83
-
84
- Returns:
85
- bool: True if this handler is the default, False otherwise.
86
- """
87
- return False
88
-
89
- @staticmethod
90
- def recommended_scopes() -> set[str]:
91
- """
92
- Returns the recommended authentication scopes.
93
-
94
- If `use_recommended_scope` is set to True in the `AuthConfig`,
95
- this method should return the proper recommended scopes. Otherwise,
96
- it should return an empty set.
97
-
98
- Returns:
99
- set[str]: A set of recommended scopes, or an empty set if not applicable.
100
-
101
- Examples:
102
- Slack OAuth2 recommended_scopes::
103
-
104
- def recommended_scopes() -> set[str]:
105
- if config.auth.slack.use_recommended_scope:
106
- recommended_scopes = {
107
- "channels:history",
108
- "channels:read",
109
- "chat:write",
110
- "groups:history",
111
- "groups:read",
112
- "im:history",
113
- "mpim:history",
114
- "reactions:read",
115
- "reactions:write",
116
- }
117
- else:
118
- recommended_scopes = {}
119
- return recommended_scopes
120
- """
121
- raise NotImplementedError()
122
-
123
- def make_request(self, auth_scopes: Optional[list[str]] = None, **kwargs) -> AuthenticateRequest:
124
- """
125
- Make an AuthenticationRequest.
126
-
127
- Usually, this method only requires `auth_scopes`.
128
- If additional static information is needed (e.g., clientID, secretID),
129
- retrieve it from the configuration.
130
-
131
- Args:
132
- auth_scopes (Optional[list[str]]): list of auth scopes
133
-
134
- Returns:
135
- AuthenticateRequest: A authentication request object with the necessary details.
136
-
137
- Examples:
138
- Create a Slack OAuth2 request::
139
-
140
- def make_request(self, auth_scopes: Optional[list[str]] = None, **kwargs) -> SlackOAuth2Request:
141
- return SlackOAuth2Request(
142
- auth_scopes=auth_scopes,
143
- client_id=config.auth.slack.client_id,
144
- client_secret=config.auth.slack.client_secret
145
- )
146
- """
147
- raise NotImplementedError()
148
-
149
- def prepare(self, auth_req: AuthenticateRequest, thread_id: str, profile: str,
150
- future_uid: str, *args, **kwargs) -> str:
151
- """
152
- Performs preliminary tasks required for authentication.
153
-
154
- This method typically performs the following actions:
155
- - Creates a future to wait for user authentication completion during the authentication process.
156
- - Issues an authentication URI that the user can access.
157
-
158
- Args:
159
- auth_req (AuthenticateRequest): The authentication request object.
160
- thread_id (str): The thread ID.
161
- profile (str): The profile name.
162
- future_uid (str): A unique identifier for each future.
163
-
164
- Returns:
165
- str: The authentication URI that the user can access.
166
- """
167
- raise NotImplementedError()
168
-
169
- @abstractmethod
170
- async def authenticate(self, auth_req: AuthenticateRequest, future_uid: str, *args, **kwargs) -> AuthContext:
171
- """
172
- Performs the actual authentication process.
173
-
174
- This function assumes that the user has completed the authentication during the `prepare` step,
175
- and the associated future has been resolved. At this point, the result contains the required
176
- values for authentication (e.g., an auth code).
177
-
178
- Typically, this process involves:
179
- - Accessing the resolved future to retrieve the necessary values for authentication.
180
- - Performing the actual authentication using these values.
181
- - Converting the returned response into an appropriate `AuthContext` object and returning it.
182
-
183
- Args:
184
- auth_req (AuthenticateRequest): The authentication request object.
185
- future_uid (str): A unique identifier for the future, used to retrieve the correct
186
- result issued during the `prepare` step.
187
-
188
- Returns:
189
- AuthContext: The authentication context object containing the authentication result.
190
- """
191
- raise NotImplementedError()
192
-
193
- @abstractmethod
194
- async def refresh(self, auth_req: AuthenticateRequest, context: AuthContext, *args, **kwargs) -> AuthContext:
195
- """
196
- Performs re-authentication for an expired session.
197
-
198
- This method is optional and does not need to be implemented for handlers that do not require re-authentication.
199
-
200
- Typically, the information needed for re-authentication (e.g., a refresh token) should be stored
201
- within the `AuthContext` during the previous authentication step.
202
- In the `refresh` step, this method accesses the necessary re-authentication details from the provided `context`,
203
- performs the re-authentication, and returns an updated `AuthContext`.
204
-
205
- Args:
206
- auth_req (AuthenticateRequest): The authentication request object.
207
- context (AuthContext): The current authentication context that it should contain data required for re-authentication.
208
-
209
- Returns:
210
- AuthContext: An updated authentication context object.
211
- """
212
- raise NotImplementedError()
213
- ```
214
-
215
- ## How To Implement Auth
216
-
217
- 1. `AuthenticateRequest`을 상속받은 구현체 구현
218
- - 해당 클래스에서는 실제 prepare 및 authenticate를 할 때 필요한 정보를 저장하고 있어야 합니다.
219
-
220
- 2. `AuthContext`을 상속받은 구현체 구현
221
- - Context 내에는 다음과 같은 정보들이 존재해야 합니다.
222
- - 각 tool에서 접근할 수 있는 access_key
223
- - response를 context로 변환할 수 있는 함수
224
-
225
- 3. `Response`(Optional) 객체 구현
226
- - 필요시 authentication을 완료 후 받은 정보를 parsing할 구현체를 구현
227
-
228
- 4. `AuthHandlerInterface`을 상속받은 구현체 구현
229
- - 실제 prepare, authentication, refresh를 수행할 객체를 구현합니다.
230
-
231
- 5. (신규 auth provider인 경우) `AuthProvider`에 새로운 enum value를 추가
232
- - 만약 새로운 `AuthProvider`가 추가되는 경우라면, `pocket/auth/provider.py` 내에 새로운 AuthProvider Enum을 등록해야 합니다.
233
-
234
- 6. 신규 Auth callback server enpoint 추가
235
- - `pocket/server/auth/` 하위에 유저가 authentication을 완료하면 callback될 server endpoint를 추가해야 합니다.
236
- - 해당 패키지 하위에 적절한 `APIRouter`를 선언해놓으면 pocket 초기화 시에 자동으로 end point를 pocket server에 등록해주게 됩니다.
237
-
238
- 7. Auth Test code 추가(Optional)
239
- - `pocket/auth/tests` 하위에 테스트 코드 추가
240
-
241
- ---
242
-
243
- ## Auth Flow(Advanced)
244
-
245
- ### Session State 정의
246
-
247
- - SKIP_AUTH : Auth가 존재하고, 들어온 요청에 대한 권한도 가지고 있는 상태
248
- - DO_AUTH : Auth가 존재하지만, 들어온 요청에 대한 권한이 없기 때문에 auth가 필요한 상태
249
- - DO_REFRESH : Auth가 존재하지만, 만료가 되어 refresh가 필요한 상태
250
- - NO_SESSION : Auth가 존재하지 않는 상태
251
- - PENDING_RESOLVE : Resource Owner에게 authorization URI를 전달하고 인증을 기다리는 상태
252
- - RESOLVED : Resource Owner가 authorization을 완료해 서버에서 code를 받은 상태
253
-
254
- ### 01. check
255
-
256
- 처음 요청이 들어왔을 때 현재 사용자의 session이 어느 상태인지를 check하게 된다.
257
-
258
- 1. 사용자의 현재 session 조회
259
- - 존재 하지 않으면 NO_SESSION 상태 반환
260
- 2. session의 auth resolve uid가 존재하는지 확인
261
- - auth resolve uid가 아직 존재하는 것은 사용자에게 authentication URI는 전달하였지만 서버에서 access token을 아직 얻지는 못 한 상태
262
- - auth resolve uid가 존재하면서 현재 사용자가 인증을 완료한 상태인지 확인
263
- - 사용자가 인증을 완료한 상태라면 RESOLVED 상태 반환
264
- - 사용자가 인증을 완료하지 못 한 상태라면 PENDING_RESOLVE 상태 반환
265
- 3. 현재 인증된 session이 현재 요청에 적용이 가능한지를 확인
266
- - 현재 session의 auth provider와 필요한 session의 auth provider가 동일한지를 확인
267
- - 다르다면 반드시 재인증이 필요
268
- - 현재 session을 인증한 auto provider가 non scoped인지 확인
269
- - scoped가 아닌 경우에는 모든 권한이 있기 때문에 항상 사용 가능
270
- - scoped인 경우 필요한 scope을 이미 가지고 있는지 확인
271
- - 이미 가지고 있는 경우 그대로 사용하면 된다.
272
- - 가지고 있지 않은 경우 새로운 인증 요청이 필요. 이때 기존 갖고 있던 scope과 새로 필요한 scope의 합집합을 다시 요청하게 된다.
273
- - 위 과정을 통해 현재 session에서 적용이 불가능 하다고 판단되면 DO_AUTH 상태 반환
274
- 4. 현재 인증된 session이 만료되었는지 확인
275
- - 만료되었다면 DO_REFRESH 상태 반환
276
- 5. 위 단계를 모두 통과한 경우에만 SKIP_AUTH 상태 반환
277
-
278
- ### 02. prepare
279
-
280
- 1. auth state가 SKIP_AUTH, DO_REFRESH, RESOLVED인 경우에는 그대로 함수를 종료
281
- 2. DO_AUTH, NO_SESSION인 경우에는 새로운 session과 future를 만들고, 사용자에게 authenticate URI를 반환
282
- 3. PENDING_RESOLVED인 경우에는 기존 session의 authenticate URI를 다시 반환
283
-
284
- ```mermaid
285
- flowchart TD
286
- Check["Prepare Auth"] --> A["Is AuthState<br>SKIP_AUTH, DO_REFRESH or RESOLVED?"]
287
- A -->|Yes| B["Return<br>None"]
288
- A -->|No| C["Is AuthState PENDING_RESOLVED?"]
289
- C -->|YES| D["Return<br>Previous Authorization URI"]
290
- C -->|NO| E["Make New Session"]
291
- E --> F["Return<br>Authorization URI"]
292
- ```
293
-
294
- ### 03. Authenticate
295
-
296
- 1. auth state가 SKIP_AUTH인 경우에는 기존 session을 그대로 반환
297
- 2. auth state가 DO_REFRESH인 경우에는 refresh를 수행하고 기존 session을 교체
298
- 3. auth state가 PENDING_RESOLVE 또는 RESOLVED인 경우에는 사용자로부터 전달받은 code로 authenticate를 수행
299
- 4. 그 외 auth state(NO_SESSION, DO_AUTH)는 해당 단계로 진입하지 않는다.
300
-
301
- ```mermaid
302
- flowchart TD
303
- Check["Authenticate"]
304
- Check -->|Is AuthState<br>SKIP_AUTH?| A2["Return<br>Existing Session"]
305
- Check -->|Is AuthState<br>DO_REFRESH?| B2["Refresh<br>Access Token"]
306
- Check -->|Is AuthState PENDING_RESOLVE<br>or<br>RESOLVED?| C2["Wait for Code<br>And<br>Authenticate"]
307
- Check -->|Is AuthState<br>NO_SESION<br>or<br>DO_AUTH?| D2["Can't be reached<br>while in state"]
308
- ```
309
-
@@ -1,32 +0,0 @@
1
- # import asyncio
2
- # from unittest import TestCase
3
- #
4
- # from pocket.auth.slack.oauth2_handler import SlackOAuth2AuthHandler
5
- # from pocket.auth.slack.oauth2_schema import SlackOAuth2Request
6
- # from pocket.config import config
7
- # from pocket.server.server import get_proxy_server, get_server
8
- #
9
- #
10
- # class TestSlackOAuth2AuthHandler(TestCase):
11
- #
12
- # def test_authenticate(self):
13
- # loop = asyncio.new_event_loop()
14
- # asyncio.set_event_loop(loop)
15
- # server = get_server()
16
- # asyncio.ensure_future(server.serve(), loop=loop)
17
- # proxy_server = get_proxy_server()
18
- # if proxy_server:
19
- # asyncio.ensure_future(proxy_server.serve(), loop=loop)
20
- #
21
- # slack_auth = SlackOAuth2AuthHandler()
22
- # auth_req = SlackOAuth2Request(
23
- # auth_scopes=["channels:history", "im:history", "mpim:history", "groups:history", "reactions:read"],
24
- # client_id=config.auth.slack.client_id,
25
- # client_secret=config.auth.slack.client_secret,
26
- # )
27
- #
28
- # # when
29
- # context = loop.run_until_complete(slack_auth.authenticate(auth_req))
30
- #
31
- # # then
32
- # print("access_token : ", context.access_token)
@@ -1,23 +0,0 @@
1
- # import asyncio
2
- # from unittest import TestCase
3
- # from unittest.mock import patch
4
- #
5
- # from pocket.auth.context import AuthContext
6
- # from pocket.auth.slack.token_handler import SlackTokenAuthHandler
7
- # from pocket.auth.slack.token_schema import SlackTokenRequest
8
- #
9
- #
10
- # class TestSlackTokenAuthHandler(TestCase):
11
- # def test_authenticate(self):
12
- # loop = asyncio.new_event_loop()
13
- # asyncio.set_event_loop(loop)
14
- #
15
- # slack_auth = SlackTokenAuthHandler()
16
- # auth_req = SlackTokenRequest()
17
- #
18
- # # when
19
- # with patch("builtins.input", return_value="test-slack-token"):
20
- # context: AuthContext = loop.run_until_complete(slack_auth.authenticate(auth_req))
21
- #
22
- # # then
23
- # assert context.access_token == "test-slack-token"
@@ -1,147 +0,0 @@
1
- import uuid
2
- from datetime import timezone, datetime
3
- from unittest import IsolatedAsyncioTestCase
4
- from unittest.mock import patch
5
- from urllib.parse import urlparse, parse_qs
6
-
7
- import httpx
8
-
9
- from hyperpocket.auth import GoogleOAuth2AuthContext
10
- from hyperpocket.auth.google.oauth2_handler import GoogleOAuth2AuthHandler
11
- from hyperpocket.auth.google.oauth2_schema import GoogleOAuth2Request, GoogleOAuth2Response
12
- from hyperpocket.config import config
13
- from hyperpocket.config.auth import GoogleAuthConfig
14
- from hyperpocket.futures import FutureStore
15
-
16
-
17
- class TestGoogleOAuth2AuthHandler(IsolatedAsyncioTestCase):
18
-
19
- async def asyncSetUp(self):
20
- config.auth.google = GoogleAuthConfig(
21
- client_id="test-client-id",
22
- client_secret="test-client-secret",
23
- )
24
-
25
- self.handler = GoogleOAuth2AuthHandler()
26
- self.auth_req = GoogleOAuth2Request(
27
- auth_scopes=["https://www.googleapis.com/auth/calendar"],
28
- client_id="test-client-id",
29
- client_secret="test-client-secret",
30
- )
31
-
32
- async def test_make_auth_url(self):
33
- future_uid = str(uuid.uuid4())
34
-
35
- auth_url = self.handler._make_auth_url(
36
- auth_req=self.auth_req,
37
- redirect_uri="http://test-redirect-uri.com",
38
- state=future_uid
39
- )
40
- parsed = urlparse(auth_url)
41
- query_params = parse_qs(parsed.query)
42
- base_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
43
-
44
- # then
45
- self.assertEqual(base_url, self.handler._GOOGLE_AUTH_URL)
46
- self.assertEqual(query_params["state"][0], future_uid)
47
- self.assertEqual(query_params["redirect_uri"][0], "http://test-redirect-uri.com")
48
- self.assertEqual(query_params["client_id"][0], "test-client-id")
49
- self.assertEqual(query_params["scope"][0], "https://www.googleapis.com/auth/calendar")
50
-
51
- async def test_prepare(self):
52
- future_uid = str(uuid.uuid4())
53
-
54
- # when
55
- prepare: str = self.handler.prepare(
56
- auth_req=self.auth_req,
57
- thread_id="test-prepare-thread-id",
58
- profile="test-prepare-profile",
59
- future_uid=future_uid,
60
- )
61
- auth_url = prepare.removeprefix("User needs to authenticate using the following URL:").strip()
62
- future_data = FutureStore.get_future( uid=future_uid)
63
-
64
- # then
65
- self.assertTrue(auth_url.startswith(self.handler._GOOGLE_AUTH_URL))
66
- self.assertIsNotNone(future_data)
67
- self.assertEqual(future_data.data["thread_id"], "test-prepare-thread-id")
68
- self.assertEqual(future_data.data["profile"], "test-prepare-profile")
69
- self.assertFalse(future_data.future.done())
70
-
71
- async def test_authenticate(self):
72
- mock_response = httpx.Response(
73
- status_code=200,
74
- json={
75
- "access_token": "test-token",
76
- "refresh_token": "test-refresh-token",
77
- "expires_in": 3600,
78
- "scope": "https://www.googleapis.com/auth/calendar",
79
- "token_type": "Bearer",
80
-
81
- }
82
- )
83
- future_uid = str(uuid.uuid4())
84
-
85
- self.handler.prepare(
86
- auth_req=self.auth_req,
87
- thread_id="test-thread-id",
88
- profile="test-profile",
89
- future_uid=future_uid
90
- )
91
- future_data = FutureStore.get_future( uid=future_uid)
92
- future_data.future.set_result("test-code")
93
-
94
- with patch("httpx.AsyncClient.post", return_value=mock_response):
95
- response: GoogleOAuth2AuthContext = await self.handler.authenticate(
96
- auth_req=self.auth_req,
97
- future_uid=future_uid
98
- )
99
-
100
- time_diff = (response.expires_at - datetime.now(tz=timezone.utc)).total_seconds()
101
-
102
- self.assertIsInstance(response, GoogleOAuth2AuthContext)
103
- self.assertEqual(response.access_token, "test-token")
104
- self.assertEqual(response.refresh_token, "test-refresh-token")
105
- self.assertTrue(time_diff > 3500)
106
-
107
- async def test_refresh(self):
108
- # given
109
- mock_response = httpx.Response(
110
- status_code=200,
111
- json={
112
- "access_token": "new-test-token",
113
- "expires_in": 3600,
114
- "scope": "https://www.googleapis.com/auth/calendar",
115
- "token_type": "Bearer",
116
- }
117
- )
118
- response = GoogleOAuth2Response(
119
- **{
120
- "access_token": "test-token",
121
- "expires_in": 100,
122
- "scope": "https://www.googleapis.com/auth/calendar",
123
- "refresh_token": "test-refresh-token",
124
- "token_type": "Bearer",
125
- }
126
- )
127
- context = GoogleOAuth2AuthContext.from_google_oauth2_response(response)
128
-
129
- # when
130
- with patch("httpx.AsyncClient.post", return_value=mock_response):
131
- new_context: GoogleOAuth2AuthContext = await self.handler.refresh(
132
- auth_req=self.auth_req,
133
- context=context
134
- )
135
-
136
- old_time_diff = (context.expires_at - datetime.now(tz=timezone.utc))
137
- new_time_diff = (new_context.expires_at - datetime.now(tz=timezone.utc))
138
-
139
- # then
140
- self.assertIsInstance(new_context, GoogleOAuth2AuthContext)
141
- self.assertEqual(context.access_token, "test-token")
142
- self.assertEqual(context.refresh_token, "test-refresh-token")
143
- self.assertTrue(old_time_diff.total_seconds() < 100)
144
-
145
- self.assertEqual(new_context.access_token, "new-test-token")
146
- self.assertEqual(new_context.refresh_token, "test-refresh-token")
147
- self.assertTrue(new_time_diff.total_seconds() > 3500)