datasette-edit-schema 0.8a0__tar.gz → 0.8a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/PKG-INFO +2 -2
  2. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/__init__.py +21 -14
  3. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_database.html +1 -1
  4. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_table.html +8 -8
  5. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/PKG-INFO +2 -2
  6. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/requires.txt +1 -1
  7. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/pyproject.toml +2 -2
  8. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/tests/test_edit_schema.py +50 -9
  9. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/LICENSE +0 -0
  10. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/README.md +0 -0
  11. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/static/draggable.1.0.0-beta.11.bundle.js +0 -0
  12. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/static/draggable.1.0.0-beta.11.bundle.min.js +0 -0
  13. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_create_table.html +0 -0
  14. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/templates/edit_schema_index.html +0 -0
  15. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema/utils.py +0 -0
  16. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/SOURCES.txt +0 -0
  17. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/dependency_links.txt +0 -0
  18. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/entry_points.txt +0 -0
  19. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/datasette_edit_schema.egg-info/top_level.txt +0 -0
  20. {datasette-edit-schema-0.8a0 → datasette_edit_schema-0.8a2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datasette-edit-schema
3
- Version: 0.8a0
3
+ Version: 0.8a2
4
4
  Summary: Datasette plugin for modifying table schemas
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -13,7 +13,7 @@ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: datasette>=1.0a9
16
+ Requires-Dist: datasette>=1.0a13
17
17
  Requires-Dist: sqlite-utils>=3.35
18
18
  Provides-Extra: test
19
19
  Requires-Dist: pytest; extra == "test"
@@ -1,7 +1,7 @@
1
1
  from datasette import hookimpl
2
2
  from datasette.events import CreateTableEvent, AlterTableEvent, DropTableEvent
3
3
  from datasette.utils.asgi import Response, NotFound, Forbidden
4
- from datasette.utils import sqlite3
4
+ from datasette.utils import sqlite3, tilde_decode, tilde_encode
5
5
  from urllib.parse import quote_plus, unquote_plus
6
6
  import sqlite_utils
7
7
  import textwrap
@@ -40,9 +40,10 @@ def table_actions(datasette, actor, database, table):
40
40
  return [
41
41
  {
42
42
  "href": datasette.urls.path(
43
- "/-/edit-schema/{}/{}".format(database, quote_plus(table))
43
+ "/-/edit-schema/{}/{}".format(database, tilde_encode(table))
44
44
  ),
45
45
  "label": "Edit table schema",
46
+ "description": "Rename the table, add and remove columns...",
46
47
  }
47
48
  ]
48
49
 
@@ -106,6 +107,7 @@ def database_actions(datasette, actor, database):
106
107
  "/-/edit-schema/{}/-/create".format(database)
107
108
  ),
108
109
  "label": "Create a table",
110
+ "description": "Define a new table with specified columns",
109
111
  }
110
112
  ]
111
113
 
@@ -214,6 +216,7 @@ async def edit_schema_database(request, datasette):
214
216
  {
215
217
  "database": database,
216
218
  "tables": tables,
219
+ "tilde_encode": tilde_encode,
217
220
  },
218
221
  request=request,
219
222
  )
@@ -305,7 +308,7 @@ async def edit_schema_create_table(request, datasette):
305
308
 
306
309
 
307
310
  async def edit_schema_table(request, datasette):
308
- table = unquote_plus(request.url_vars["table"])
311
+ table = tilde_decode(request.url_vars["table"])
309
312
  databases = get_databases(datasette)
310
313
  database_name = request.url_vars["database"]
311
314
 
@@ -546,16 +549,14 @@ async def edit_schema_table(request, datasette):
546
549
  info["foreign_key"].other_column,
547
550
  ),
548
551
  "value": "{}.{}".format(
549
- info["foreign_key"].other_table,
550
- info["foreign_key"].other_column,
552
+ tilde_encode(info["foreign_key"].other_table),
553
+ tilde_encode(info["foreign_key"].other_column),
551
554
  ),
552
555
  "selected": True,
553
556
  }
554
557
  )
555
558
  seen.add(
556
- "{}:{}".format(
557
- info["foreign_key"].other_table, info["foreign_key"].other_column
558
- )
559
+ (info["foreign_key"].other_table, info["foreign_key"].other_column)
559
560
  )
560
561
  # Now add suggestions
561
562
  for suggested_table, suggested_column in info["suggestions"]:
@@ -568,19 +569,24 @@ async def edit_schema_table(request, datasette):
568
569
  "name": "{}.{} (suggested)".format(
569
570
  suggested_table, suggested_column
570
571
  ),
571
- "value": "{}.{}".format(suggested_table, suggested_column),
572
+ "value": "{}.{}".format(
573
+ tilde_encode(suggested_table),
574
+ tilde_encode(suggested_column),
575
+ ),
572
576
  "selected": False,
573
577
  }
574
578
  )
575
- seen.add("{}:{}".format(suggested_table, suggested_column))
579
+ seen.add((suggested_table, suggested_column))
576
580
  info["suggested"] = "{}.{}".format(suggested_table, suggested_column)
577
581
  # And the rest
578
582
  for rest_table, rest_column in info["options"]:
579
- if "{}:{}".format(rest_table, rest_column) not in seen:
583
+ if (rest_table, rest_column) not in seen:
580
584
  options.append(
581
585
  {
582
586
  "name": "{}.{}".format(rest_table, rest_column),
583
- "value": "{}.{}".format(rest_table, rest_column),
587
+ "value": "{}.{}".format(
588
+ tilde_encode(rest_table), tilde_encode(rest_column)
589
+ ),
584
590
  "selected": False,
585
591
  }
586
592
  )
@@ -617,6 +623,7 @@ async def edit_schema_table(request, datasette):
617
623
  "can_rename_table": await can_rename_table(
618
624
  datasette, request.actor, database_name, table
619
625
  ),
626
+ "tilde_encode": tilde_encode,
620
627
  },
621
628
  request=request,
622
629
  )
@@ -780,8 +787,8 @@ async def update_foreign_keys(request, datasette, database, table, formdata):
780
787
  fks.append(
781
788
  (
782
789
  column,
783
- split[0],
784
- split[1],
790
+ tilde_decode(split[0]),
791
+ tilde_decode(split[1]),
785
792
  )
786
793
  )
787
794
 
@@ -10,7 +10,7 @@
10
10
  <h1>Edit tables in {{ database.name }}.db</h1>
11
11
 
12
12
  {% for table in tables %}
13
- <h2><a href="/-/edit-schema/{{ database.name|quote_plus }}/{{ table.name|quote_plus }}">{{ table.name }}</a></h2>
13
+ <h2><a href="/-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table.name) }}">{{ table.name }}</a></h2>
14
14
  <p>{% for column in table.columns %}{{ column.name }}{% if not loop.last %}, {% endif %}{% endfor %}</p>
15
15
  {% endfor %}
16
16
 
@@ -86,12 +86,12 @@ html body label {
86
86
  {% endblock %}
87
87
 
88
88
  {% block content %}
89
- <h1>Edit table <a href="{{ base_url }}{{ database.name|quote_plus }}/{{ table|quote_plus }}">{{ database.name }}/{{ table }}</a></h1>
89
+ <h1>Edit table <a href="{{ base_url }}{{ database.name|quote_plus }}/{{ tilde_encode(table) }}">{{ database.name }}/{{ table }}</a></h1>
90
90
 
91
91
  {% if can_rename_table %}
92
92
  <h2>Rename table</h2>
93
93
 
94
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
94
+ <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
95
95
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
96
96
  <p><label>New name&nbsp; <input type="text" name="name"></label>
97
97
  <input type="hidden" name="rename_table" value="1">
@@ -99,7 +99,7 @@ html body label {
99
99
  </form>
100
100
  {% endif %}
101
101
 
102
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
102
+ <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
103
103
  <h2>Change existing columns</h2>
104
104
  <ul class="sortable-columns">
105
105
  {% for column in columns %}
@@ -134,7 +134,7 @@ html body label {
134
134
 
135
135
  <h2>Add a column</h2>
136
136
 
137
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
137
+ <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
138
138
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
139
139
  <input type="hidden" name="add_column" value="1">
140
140
  <p><label>Name &nbsp;<input type="text" name="name"></label>
@@ -156,7 +156,7 @@ table.foreign-key-options td {
156
156
  }
157
157
  </style>
158
158
 
159
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
159
+ <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
160
160
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
161
161
  <input type="hidden" name="action" value="update_foreign_keys">
162
162
  <table class="foreign-key-options">
@@ -180,7 +180,7 @@ table.foreign-key-options td {
180
180
 
181
181
  <p>The primary key column uniquely identifies each row in the table.</p>
182
182
 
183
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
183
+ <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
184
184
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
185
185
  <input type="hidden" name="action" value="update_primary_key">
186
186
  <label for="primary_key">Primary key column &nbsp;</label>
@@ -201,7 +201,7 @@ table.foreign-key-options td {
201
201
 
202
202
  <p>Indexes can speed up filter and sort operations against indexed columns.</p>
203
203
 
204
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
204
+ <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
205
205
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
206
206
  {% if non_primary_key_columns %}
207
207
  <p><label for="id_add_index_column">
@@ -233,7 +233,7 @@ table.foreign-key-options td {
233
233
  {% if can_drop_table %}
234
234
  <h2>Drop table</h2>
235
235
 
236
- <form id="drop-table-form" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
236
+ <form id="drop-table-form" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
237
237
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
238
238
  <input type="hidden" name="drop_table" value="1">
239
239
  <input type="submit" class="button-red" value="Drop this table">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datasette-edit-schema
3
- Version: 0.8a0
3
+ Version: 0.8a2
4
4
  Summary: Datasette plugin for modifying table schemas
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -13,7 +13,7 @@ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: datasette>=1.0a9
16
+ Requires-Dist: datasette>=1.0a13
17
17
  Requires-Dist: sqlite-utils>=3.35
18
18
  Provides-Extra: test
19
19
  Requires-Dist: pytest; extra == "test"
@@ -1,4 +1,4 @@
1
- datasette>=1.0a9
1
+ datasette>=1.0a13
2
2
  sqlite-utils>=3.35
3
3
 
4
4
  [test]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datasette-edit-schema"
3
- version = "0.8a0"
3
+ version = "0.8a2"
4
4
  description = "Datasette plugin for modifying table schemas"
5
5
  readme = "README.md"
6
6
  authors = [{name = "Simon Willison"}]
@@ -11,7 +11,7 @@ classifiers=[
11
11
  ]
12
12
  requires-python = ">=3.8"
13
13
  dependencies = [
14
- "datasette>=1.0a9",
14
+ "datasette>=1.0a13",
15
15
  "sqlite-utils>=3.35",
16
16
  ]
17
17
 
@@ -1,4 +1,5 @@
1
1
  from datasette.app import Datasette
2
+ from datasette.utils import tilde_encode
2
3
  from datasette_edit_schema.utils import (
3
4
  potential_foreign_keys,
4
5
  get_primary_keys,
@@ -41,8 +42,9 @@ async def test_csrf_required(db_path):
41
42
  ("user_with_no_perms", False),
42
43
  ),
43
44
  )
45
+ @pytest.mark.parametrize("table", ("creatures", "animal.name/with/slashes"))
44
46
  @pytest.mark.asyncio
45
- async def test_table_actions(permission_plugin, ds, actor_id, should_allow):
47
+ async def test_table_actions(permission_plugin, ds, actor_id, should_allow, table):
46
48
  ds._rules_allow = [
47
49
  Rule(
48
50
  actor_id="user_with_edit_schema",
@@ -60,9 +62,13 @@ async def test_table_actions(permission_plugin, ds, actor_id, should_allow):
60
62
  cookies = None
61
63
  if actor_id:
62
64
  cookies = {"ds_actor": ds.sign({"a": {"id": actor_id}}, "actor")}
63
- response = await ds.client.get("/data/creatures", cookies=cookies)
65
+ response = await ds.client.get(
66
+ ds.urls.table(database="data", table=table), cookies=cookies
67
+ )
64
68
  assert response.status_code == 200
65
- fragment = '<li><a href="/-/edit-schema/data/creatures">Edit table schema</a></li>'
69
+ fragment = '<a href="/-/edit-schema/data/{}">Edit table schema'.format(
70
+ tilde_encode(table)
71
+ )
66
72
  if should_allow:
67
73
  # Should have table action
68
74
  assert fragment in response.text
@@ -814,6 +820,24 @@ async def test_edit_form_for_empty_table(db_path):
814
820
  ["id"],
815
821
  "Foreign keys updated to distraction_id → cities.id",
816
822
  ),
823
+ # Same again for tables with weird characters in their names
824
+ (
825
+ "animal.name/with/slashes",
826
+ {
827
+ "action": "update_foreign_keys",
828
+ "fk.species": "table~2Ename~2Fwith~2Fslashes~2Ecategories.id",
829
+ },
830
+ [
831
+ (
832
+ "animal.name/with/slashes",
833
+ "species",
834
+ "table.name/with/slashes.categories",
835
+ "id",
836
+ )
837
+ ],
838
+ ["id"],
839
+ "Foreign keys updated to species → table.name/with/slashes.categories.id",
840
+ ),
817
841
  # Change primary key in a way that works
818
842
  (
819
843
  "museums",
@@ -839,13 +863,13 @@ async def test_edit_keys(
839
863
  # Grab a csrftoken
840
864
  cookies = {"ds_actor": ds.sign({"a": {"id": "root"}}, "actor")}
841
865
  csrftoken_r = await ds.client.get(
842
- "/-/edit-schema/data/{}".format(table), cookies=cookies
866
+ "/-/edit-schema/data/{}".format(tilde_encode(table)), cookies=cookies
843
867
  )
844
868
  csrftoken = csrftoken_r.cookies["ds_csrftoken"]
845
869
  cookies["ds_csrftoken"] = csrftoken
846
870
  post_data["csrftoken"] = csrftoken
847
871
  response = await ds.client.post(
848
- "/-/edit-schema/data/{}".format(table),
872
+ "/-/edit-schema/data/{}".format(tilde_encode(table)),
849
873
  data=post_data,
850
874
  cookies=cookies,
851
875
  )
@@ -1065,6 +1089,19 @@ def test_potential_primary_keys_primary_key_only_table():
1065
1089
  "Index dropped: name_unique_index",
1066
1090
  [{"columns": ["name"], "name": "name_index", "unique": 0}],
1067
1091
  ),
1092
+ # Test for table with surprising characters in its name
1093
+ (
1094
+ "animal.name/with/slashes",
1095
+ {"add_index": "1", "add_index_column": "species"},
1096
+ "Index added on species",
1097
+ [
1098
+ {
1099
+ "name": "idx_animal.name/with/slashes_species",
1100
+ "columns": ["species"],
1101
+ "unique": 0,
1102
+ }
1103
+ ],
1104
+ ),
1068
1105
  ),
1069
1106
  )
1070
1107
  async def test_add_remove_index(
@@ -1072,13 +1109,17 @@ async def test_add_remove_index(
1072
1109
  ):
1073
1110
  ds = Datasette([db_path])
1074
1111
  cookies = {"ds_actor": ds.sign({"a": {"id": "root"}}, "actor")}
1075
- csrftoken = (
1076
- await ds.client.get("/-/edit-schema/data/{}".format(table), cookies=cookies)
1077
- ).cookies["ds_csrftoken"]
1112
+ get_response = await ds.client.get(
1113
+ "/-/edit-schema/data/{}".format(tilde_encode(table)), cookies=cookies
1114
+ )
1115
+ assert get_response.status_code == 200
1116
+ csrftoken = get_response.cookies["ds_csrftoken"]
1078
1117
  cookies["ds_csrftoken"] = csrftoken
1079
1118
  post_data["csrftoken"] = csrftoken
1080
1119
  response = await ds.client.post(
1081
- "/-/edit-schema/data/{}".format(table), cookies=cookies, data=post_data
1120
+ "/-/edit-schema/data/{}".format(tilde_encode(table)),
1121
+ cookies=cookies,
1122
+ data=post_data,
1082
1123
  )
1083
1124
  assert response.status_code == 302
1084
1125
  messages = ds.unsign(response.cookies["ds_messages"], "messages")