datasette-edit-schema 0.8a1__tar.gz → 0.8a3__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.

Potentially problematic release.


This version of datasette-edit-schema might be problematic. Click here for more details.

Files changed (20) hide show
  1. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/PKG-INFO +1 -1
  2. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/__init__.py +19 -14
  3. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/templates/edit_schema_database.html +1 -1
  4. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/templates/edit_schema_table.html +14 -13
  5. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema.egg-info/PKG-INFO +1 -1
  6. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/pyproject.toml +1 -1
  7. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/tests/test_edit_schema.py +50 -9
  8. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/LICENSE +0 -0
  9. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/README.md +0 -0
  10. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/static/draggable.1.0.0-beta.11.bundle.js +0 -0
  11. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/static/draggable.1.0.0-beta.11.bundle.min.js +0 -0
  12. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/templates/edit_schema_create_table.html +0 -0
  13. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/templates/edit_schema_index.html +0 -0
  14. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema/utils.py +0 -0
  15. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema.egg-info/SOURCES.txt +0 -0
  16. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema.egg-info/dependency_links.txt +0 -0
  17. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema.egg-info/entry_points.txt +0 -0
  18. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema.egg-info/requires.txt +0 -0
  19. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/datasette_edit_schema.egg-info/top_level.txt +0 -0
  20. {datasette-edit-schema-0.8a1 → datasette_edit_schema-0.8a3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: datasette-edit-schema
3
- Version: 0.8a1
3
+ Version: 0.8a3
4
4
  Summary: Datasette plugin for modifying table schemas
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -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,7 +40,7 @@ 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
46
  "description": "Rename the table, add and remove columns...",
@@ -216,6 +216,7 @@ async def edit_schema_database(request, datasette):
216
216
  {
217
217
  "database": database,
218
218
  "tables": tables,
219
+ "tilde_encode": tilde_encode,
219
220
  },
220
221
  request=request,
221
222
  )
@@ -307,7 +308,7 @@ async def edit_schema_create_table(request, datasette):
307
308
 
308
309
 
309
310
  async def edit_schema_table(request, datasette):
310
- table = unquote_plus(request.url_vars["table"])
311
+ table = tilde_decode(request.url_vars["table"])
311
312
  databases = get_databases(datasette)
312
313
  database_name = request.url_vars["database"]
313
314
 
@@ -548,16 +549,14 @@ async def edit_schema_table(request, datasette):
548
549
  info["foreign_key"].other_column,
549
550
  ),
550
551
  "value": "{}.{}".format(
551
- info["foreign_key"].other_table,
552
- info["foreign_key"].other_column,
552
+ tilde_encode(info["foreign_key"].other_table),
553
+ tilde_encode(info["foreign_key"].other_column),
553
554
  ),
554
555
  "selected": True,
555
556
  }
556
557
  )
557
558
  seen.add(
558
- "{}:{}".format(
559
- info["foreign_key"].other_table, info["foreign_key"].other_column
560
- )
559
+ (info["foreign_key"].other_table, info["foreign_key"].other_column)
561
560
  )
562
561
  # Now add suggestions
563
562
  for suggested_table, suggested_column in info["suggestions"]:
@@ -570,19 +569,24 @@ async def edit_schema_table(request, datasette):
570
569
  "name": "{}.{} (suggested)".format(
571
570
  suggested_table, suggested_column
572
571
  ),
573
- "value": "{}.{}".format(suggested_table, suggested_column),
572
+ "value": "{}.{}".format(
573
+ tilde_encode(suggested_table),
574
+ tilde_encode(suggested_column),
575
+ ),
574
576
  "selected": False,
575
577
  }
576
578
  )
577
- seen.add("{}:{}".format(suggested_table, suggested_column))
579
+ seen.add((suggested_table, suggested_column))
578
580
  info["suggested"] = "{}.{}".format(suggested_table, suggested_column)
579
581
  # And the rest
580
582
  for rest_table, rest_column in info["options"]:
581
- if "{}:{}".format(rest_table, rest_column) not in seen:
583
+ if (rest_table, rest_column) not in seen:
582
584
  options.append(
583
585
  {
584
586
  "name": "{}.{}".format(rest_table, rest_column),
585
- "value": "{}.{}".format(rest_table, rest_column),
587
+ "value": "{}.{}".format(
588
+ tilde_encode(rest_table), tilde_encode(rest_column)
589
+ ),
586
590
  "selected": False,
587
591
  }
588
592
  )
@@ -619,6 +623,7 @@ async def edit_schema_table(request, datasette):
619
623
  "can_rename_table": await can_rename_table(
620
624
  datasette, request.actor, database_name, table
621
625
  ),
626
+ "tilde_encode": tilde_encode,
622
627
  },
623
628
  request=request,
624
629
  )
@@ -782,8 +787,8 @@ async def update_foreign_keys(request, datasette, database, table, formdata):
782
787
  fks.append(
783
788
  (
784
789
  column,
785
- split[0],
786
- split[1],
790
+ tilde_decode(split[0]),
791
+ tilde_decode(split[1]),
787
792
  )
788
793
  )
789
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
 
@@ -14,13 +14,14 @@ html body input[type="search"] {
14
14
  font-size: 1em;
15
15
  font-family: Helvetica, sans-serif;
16
16
  }
17
- body form .button-red {
18
- background-color: red;
19
- border-color: red;
20
- }
21
- body form .button-small {
17
+ input[type=submit].button-small {
22
18
  font-size: 0.7em;
23
19
  }
20
+ form input[type=submit].button-red {
21
+ background: red;
22
+ border-color: rgb(171, 112, 112);
23
+ }
24
+
24
25
  select {
25
26
  border: 1px solid #ccc;
26
27
  border-radius: 3px;
@@ -86,12 +87,12 @@ html body label {
86
87
  {% endblock %}
87
88
 
88
89
  {% block content %}
89
- <h1>Edit table <a href="{{ base_url }}{{ database.name|quote_plus }}/{{ table|quote_plus }}">{{ database.name }}/{{ table }}</a></h1>
90
+ <h1>Edit table <a href="{{ base_url }}{{ database.name|quote_plus }}/{{ tilde_encode(table) }}">{{ database.name }}/{{ table }}</a></h1>
90
91
 
91
92
  {% if can_rename_table %}
92
93
  <h2>Rename table</h2>
93
94
 
94
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
95
+ <form class="core" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
95
96
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
96
97
  <p><label>New name&nbsp; <input type="text" name="name"></label>
97
98
  <input type="hidden" name="rename_table" value="1">
@@ -99,7 +100,7 @@ html body label {
99
100
  </form>
100
101
  {% endif %}
101
102
 
102
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
103
+ <form class="core" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
103
104
  <h2>Change existing columns</h2>
104
105
  <ul class="sortable-columns">
105
106
  {% for column in columns %}
@@ -134,7 +135,7 @@ html body label {
134
135
 
135
136
  <h2>Add a column</h2>
136
137
 
137
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
138
+ <form class="core" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
138
139
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
139
140
  <input type="hidden" name="add_column" value="1">
140
141
  <p><label>Name &nbsp;<input type="text" name="name"></label>
@@ -156,7 +157,7 @@ table.foreign-key-options td {
156
157
  }
157
158
  </style>
158
159
 
159
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
160
+ <form class="core" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
160
161
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
161
162
  <input type="hidden" name="action" value="update_foreign_keys">
162
163
  <table class="foreign-key-options">
@@ -180,7 +181,7 @@ table.foreign-key-options td {
180
181
 
181
182
  <p>The primary key column uniquely identifies each row in the table.</p>
182
183
 
183
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
184
+ <form class="core" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
184
185
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
185
186
  <input type="hidden" name="action" value="update_primary_key">
186
187
  <label for="primary_key">Primary key column &nbsp;</label>
@@ -201,7 +202,7 @@ table.foreign-key-options td {
201
202
 
202
203
  <p>Indexes can speed up filter and sort operations against indexed columns.</p>
203
204
 
204
- <form action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
205
+ <form class="core" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
205
206
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
206
207
  {% if non_primary_key_columns %}
207
208
  <p><label for="id_add_index_column">
@@ -233,7 +234,7 @@ table.foreign-key-options td {
233
234
  {% if can_drop_table %}
234
235
  <h2>Drop table</h2>
235
236
 
236
- <form id="drop-table-form" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ table|quote_plus }}" method="post">
237
+ <form class="core" id="drop-table-form" action="{{ base_url }}-/edit-schema/{{ database.name|quote_plus }}/{{ tilde_encode(table) }}" method="post">
237
238
  <input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
238
239
  <input type="hidden" name="drop_table" value="1">
239
240
  <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.8a1
3
+ Version: 0.8a3
4
4
  Summary: Datasette plugin for modifying table schemas
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datasette-edit-schema"
3
- version = "0.8a1"
3
+ version = "0.8a3"
4
4
  description = "Datasette plugin for modifying table schemas"
5
5
  readme = "README.md"
6
6
  authors = [{name = "Simon Willison"}]
@@ -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 = '<a href="/-/edit-schema/data/creatures">Edit table schema'
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")