nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: jeremydaly/dynamodb-toolbox
Pull request #527 (2023-04-30 16:34:37)

Fix issue with mapped fields and defaults on update
There is a bug with attributes that are mapped and use a default value.  
The issue is that you cannot update the field once it's initially set, because the code is looking for the key and not the alias.

ex. 
```js
{ type: 'number', map: 'test_mapped_number', default: 0, onUpdate: false }
```

This fixes this issue.


Patch:
diff --git a/src/classes/Entity/Entity.ts b/src/classes/Entity/Entity.ts
index 7d75333e..a083a614 100644
--- a/src/classes/Entity/Entity.ts
+++ b/src/classes/Entity/Entity.ts
@@ -1178,7 +1178,7 @@ class Entity<Name extends string = string,
             // Push the update to SET
             SET.push(
               // @ts-ignore
-              mapping.default !== undefined && item[field] === undefined && !mapping.onUpdate
+              mapping.default !== undefined && item[mapping.alias || field] === undefined && !mapping.onUpdate
                 ? `#${field} = if_not_exists(#${field},:${field})`
                 : `#${field} = :${field}`,
             )


Test patch:
diff --git a/src/__tests__/entity.update.unit.test.ts b/src/__tests__/entity.update.unit.test.ts
index cf962080..d170c22e 100644
--- a/src/__tests__/entity.update.unit.test.ts
+++ b/src/__tests__/entity.update.unit.test.ts
@@ -90,6 +90,24 @@ const TestEntity3 = new Entity({
   table: TestTable3
 } as const)
 
+const TestTable4 = new Table({
+  name: 'test-table4',
+  partitionKey: 'pk',
+  entityField: false,
+  DocumentClient
+})
+
+const TestEntity4 = new Entity({
+  name: 'TestEntity4',
+  autoExecute: false,
+  attributes: {
+    email: { type: 'string', partitionKey: true },
+    test_number_default_with_map: { type: 'number', map: 'test_mapped_number', default: 0, onUpdate: false },
+  },
+  timestamps: false,
+  table: TestTable4
+} as const)
+
 const TestEntityGSI = new Entity({
   name: 'TestEntityGSI',
   autoExecute: false,
@@ -204,6 +222,26 @@ describe('update', () => {
     )
   })
 
+  it('allows overriding default field values that use mapping', () => {
+    const {
+      UpdateExpression,
+      ExpressionAttributeNames,
+      ExpressionAttributeValues
+    } = TestEntity4.updateParams({
+      email: 'test-pk',
+      test_number_default_with_map: 111
+    })
+
+    expect(ExpressionAttributeNames?.['#test_mapped_number']).toBe('test_mapped_number')
+    expect(ExpressionAttributeValues?.[':test_mapped_number']).toBe(111)
+
+    expect(UpdateExpression).toBe(
+      'SET #test_mapped_number = :test_mapped_number'
+    )
+
+  })
+
+
   it('fails when removing fields with default values', () => {
     expect(() =>
       TestEntity.updateParams({ email: 'test-pk', sort: 'test-sk', $remove: 'test_string' })
Repository: honeydipper/honeydipper
Pull request #559 (2023-07-10 17:27:20)

fix: run hooks in parallel to avoid interference
<!--
Thanks for contributing!

See https://github.com/honeydipper/honeydipper/blob/master/CODE_OF_CONDUCT.md for information on our contributor code of conduct, and https://github.com/honeydipper/honeydipper/tree/master/docs for more information on developing on Honeydipper.

If possible, please follow these guidelines for commit messages:
https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines

-->
#### Description
<!--
Add a description of your pull request.
-->

The hook execution return may override the parent workflow return causing following hook to receive wrong data. Making the hook execution in parallel avoids the problem and also make it faster.

#### This PR fixes the following issues
<!--
Replace this comment with fixed issues in the following format:
Fixes #123
Fixes #124
-->



Patch:
diff --git a/internal/workflow/hooks.go b/internal/workflow/hooks.go
index 5458c11..07df3c5 100644
--- a/internal/workflow/hooks.go
+++ b/internal/workflow/hooks.go
@@ -62,7 +62,7 @@ func (w *Session) executeHook(msg *dipper.Message, hookBlock interface{}) {
 			Context: SessionContextHooks,
 		}
 		for i := 0; i < v.Len(); i++ {
-			childWf.Steps = append(childWf.Steps, config.Workflow{Workflow: v.Index(i).Interface().(string)})
+			childWf.Threads = append(childWf.Threads, config.Workflow{Workflow: v.Index(i).Interface().(string)})
 		}
 		child = w.createChildSession(&childWf, msg)
 	}


Test patch:
diff --git a/internal/workflow/session_hook_test.go b/internal/workflow/session_hook_test.go
index b49dc0d..b7864e4 100644
--- a/internal/workflow/session_hook_test.go
+++ b/internal/workflow/session_hook_test.go
@@ -308,6 +308,7 @@ func TestOnFailureHook(t *testing.T) {
 				},
 				"ctx": map[string]interface{}{},
 				"asserts": func() {
+					mockHelper.EXPECT().GetDaemonID().Return("")
 					mockHelper.EXPECT().SendMessage(gomock.Eq(&dipper.Message{
 						Channel: "eventbus",
 						Subject: "command",
@@ -316,10 +317,10 @@ func TestOnFailureHook(t *testing.T) {
 						},
 						Payload: map[string]interface{}{
 							"ctx": map[string]interface{}{
-								"_meta_desc":   "",
-								"_meta_name":   "foo.failure",
-								"resume_token": "//0",
-								"step_number":  int32(0),
+								"_meta_desc":    "",
+								"_meta_name":    "foo.failure",
+								"resume_token":  "//2",
+								"thread_number": 0,
 							},
 							"data":  map[string]interface{}{},
 							"event": map[string]interface{}{},
Repository: sissbruecker/linkding
Pull request #684 (2024-04-07 11:50:26)

Add option to disable SSL verification for OIDC
Adds the setting OIDC_VERIFY_SSL which allows to accept ssl certificates from untrusted (e.g. self-signed or created using a custom certificate authority) sources.

Without this setting, every OIDC login attempt fails with:
`requests.exceptions.SSLError: HTTPSConnectionPool(host='auth.example.com', port=443): Max retries exceeded with url: /api/oidc/token (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1006)')))`


Patch:
diff --git a/docs/Options.md b/docs/Options.md
index 06a6bb0..9d2bb63 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -118,6 +118,7 @@ The following options can be configured:
 - `OIDC_RP_CLIENT_SECRET` - The client secret of the application.
 - `OIDC_RP_SIGN_ALGO` - The algorithm the OIDC provider uses to sign ID tokens. Default is `RS256`.
 - `OIDC_USE_PKCE` - Whether to use PKCE for the OIDC flow. Default is `True`.
+- `OIDC_VERIFY_SSL` - Whether to verify the SSL certificate of the OIDC provider. Set to `False` if using self-signed certificates or custom certificate authority. Default is `True`.
 
 <details>
 
diff --git a/siteroot/settings/base.py b/siteroot/settings/base.py
index b9f2d7f..b0116a6 100644
--- a/siteroot/settings/base.py
+++ b/siteroot/settings/base.py
@@ -212,6 +212,7 @@ if LD_ENABLE_OIDC:
     OIDC_RP_CLIENT_SECRET = os.getenv("OIDC_RP_CLIENT_SECRET")
     OIDC_RP_SIGN_ALGO = os.getenv("OIDC_RP_SIGN_ALGO", "RS256")
     OIDC_USE_PKCE = os.getenv("OIDC_USE_PKCE", True) in (True, "True", "1")
+    OIDC_VERIFY_SSL = os.getenv("OIDC_VERIFY_SSL", True) in (True, "True", "1")
 
 # Enable authentication proxy support if configured
 LD_ENABLE_AUTH_PROXY = os.getenv("LD_ENABLE_AUTH_PROXY", False) in (True, "True", "1")


Test patch:
diff --git a/bookmarks/tests/test_oidc_support.py b/bookmarks/tests/test_oidc_support.py
index 0c937d2..b3525a5 100644
--- a/bookmarks/tests/test_oidc_support.py
+++ b/bookmarks/tests/test_oidc_support.py
@@ -49,3 +49,15 @@ class OidcSupportTest(TestCase):
             base_settings.AUTHENTICATION_BACKENDS,
         )
         del os.environ["LD_ENABLE_OIDC"]  # Remove the temporary environment variable
+
+    def test_default_settings(self):
+        os.environ["LD_ENABLE_OIDC"] = "True"
+        base_settings = importlib.import_module("siteroot.settings.base")
+        importlib.reload(base_settings)
+
+        self.assertEqual(
+            True,
+            base_settings.OIDC_VERIFY_SSL,
+        )
+
+        del os.environ["LD_ENABLE_OIDC"]
Repository: sissbruecker/linkding
Pull request #687 (2024-04-07 16:08:43)

Truncate snapshot filename for long URLs
Fixes https://github.com/sissbruecker/linkding/issues/681


Patch:
diff --git a/bookmarks/services/tasks.py b/bookmarks/services/tasks.py
index 2f1caad..6d24ba2 100644
--- a/bookmarks/services/tasks.py
+++ b/bookmarks/services/tasks.py
@@ -239,6 +239,9 @@ def create_html_snapshot(bookmark: Bookmark):
     asset.save()
 
 
+MAX_SNAPSHOT_FILENAME_LENGTH = 192
+
+
 def _generate_snapshot_filename(asset: BookmarkAsset) -> str:
     def sanitize_char(char):
         if char.isalnum() or char in ("-", "_", "."):
@@ -249,6 +252,13 @@ def _generate_snapshot_filename(asset: BookmarkAsset) -> str:
     formatted_datetime = asset.date_created.strftime("%Y-%m-%d_%H%M%S")
     sanitized_url = "".join(sanitize_char(char) for char in asset.bookmark.url)
 
+    # Calculate the length of the non-URL parts of the filename
+    non_url_length = len(f"{asset.asset_type}{formatted_datetime}__.html.gz")
+    # Calculate the maximum length for the URL part
+    max_url_length = MAX_SNAPSHOT_FILENAME_LENGTH - non_url_length
+    # Truncate the URL if necessary
+    sanitized_url = sanitized_url[:max_url_length]
+
     return f"{asset.asset_type}_{formatted_datetime}_{sanitized_url}.html.gz"
 
 


Test patch:
diff --git a/bookmarks/tests/test_bookmarks_tasks.py b/bookmarks/tests/test_bookmarks_tasks.py
index e36183e..5040caa 100644
--- a/bookmarks/tests/test_bookmarks_tasks.py
+++ b/bookmarks/tests/test_bookmarks_tasks.py
@@ -556,6 +556,21 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
             self.assertEqual(asset.file, expected_filename)
             self.assertTrue(asset.gzip)
 
+    @override_settings(LD_ENABLE_SNAPSHOTS=True)
+    def test_create_html_snapshot_truncate_filename(self):
+        # Create a bookmark with a very long URL
+        long_url = "http://" + "a" * 300 + ".com"
+        bookmark = self.setup_bookmark(url=long_url)
+
+        tasks.create_html_snapshot(bookmark)
+        BookmarkAsset.objects.get(bookmark=bookmark)
+
+        # Run periodic task to process the snapshot
+        tasks._schedule_html_snapshots_task()
+
+        asset = BookmarkAsset.objects.get(bookmark=bookmark)
+        self.assertEqual(len(asset.file), 192)
+
     @override_settings(LD_ENABLE_SNAPSHOTS=True)
     def test_create_html_snapshot_should_handle_error(self):
         bookmark = self.setup_bookmark(url="https://example.com")
Repository: sissbruecker/linkding
Pull request #812 (2024-08-31 21:14:41)

Allow pre-filling notes in new bookmark form
Closes https://github.com/sissbruecker/linkding/issues/779


Patch:
diff --git a/bookmarks/models.py b/bookmarks/models.py
index f4db7d5..42acfaa 100644
--- a/bookmarks/models.py
+++ b/bookmarks/models.py
@@ -169,7 +169,9 @@ class BookmarkForm(forms.ModelForm):
 
     @property
     def has_notes(self):
-        return self.instance and self.instance.notes
+        return self.initial.get("notes", None) or (
+            self.instance and self.instance.notes
+        )
 
 
 class BookmarkSearch:
diff --git a/bookmarks/views/bookmarks.py b/bookmarks/views/bookmarks.py
index d214652..2bcb653 100644
--- a/bookmarks/views/bookmarks.py
+++ b/bookmarks/views/bookmarks.py
@@ -192,6 +192,7 @@ def new(request):
     initial_url = request.GET.get("url")
     initial_title = request.GET.get("title")
     initial_description = request.GET.get("description")
+    initial_notes = request.GET.get("notes")
     initial_auto_close = "auto_close" in request.GET
     initial_mark_unread = request.user.profile.default_mark_unread
 
@@ -214,6 +215,8 @@ def new(request):
             form.initial["title"] = initial_title
         if initial_description:
             form.initial["description"] = initial_description
+        if initial_notes:
+            form.initial["notes"] = initial_notes
         if initial_auto_close:
             form.initial["auto_close"] = "true"
         if initial_mark_unread:


Test patch:
diff --git a/bookmarks/tests/test_bookmark_new_view.py b/bookmarks/tests/test_bookmark_new_view.py
index cef1812..7980bca 100644
--- a/bookmarks/tests/test_bookmark_new_view.py
+++ b/bookmarks/tests/test_bookmark_new_view.py
@@ -100,6 +100,29 @@ class BookmarkNewViewTestCase(TestCase, BookmarkFactoryMixin):
             html,
         )
 
+    def test_should_prefill_notes_from_url_parameter(self):
+        response = self.client.get(
+            reverse("bookmarks:new")
+            + "?notes=%2A%2AFind%2A%2A%20more%20info%20%5Bhere%5D%28http%3A%2F%2Fexample.com%29"
+        )
+        html = response.content.decode()
+
+        self.assertInHTML(
+            """
+            <details class="notes" open="">
+                <summary>
+                    <span class="form-label d-inline-block">Notes</span>
+                </summary>
+                <label for="id_notes" class="text-assistive">Notes</label>
+                <textarea name="notes" cols="40" rows="8" class="form-input" id="id_notes">**Find** more info [here](http://example.com)</textarea>
+                <div class="form-input-hint">
+                Additional notes, supports Markdown.
+                </div>
+            </details>
+            """,
+            html,
+        )
+
     def test_should_enable_auto_close_when_specified_in_url_parameter(self):
         response = self.client.get(reverse("bookmarks:new") + "?auto_close")
         html = response.content.decode()
Repository: sissbruecker/linkding
Pull request #819 (2024-09-10 18:47:03)

Fix inconsistent tag order in bookmarks
Fixes https://github.com/sissbruecker/linkding/issues/818


Patch:
diff --git a/bookmarks/models.py b/bookmarks/models.py
index 42acfaa..a8d5a81 100644
--- a/bookmarks/models.py
+++ b/bookmarks/models.py
@@ -84,7 +84,8 @@ class Bookmark(models.Model):
 
     @property
     def tag_names(self):
-        return [tag.name for tag in self.tags.all()]
+        names = [tag.name for tag in self.tags.all()]
+        return sorted(names)
 
     def __str__(self):
         return self.resolved_title + " (" + self.url[:30] + "...)"


Test patch:
diff --git a/bookmarks/tests/test_bookmarks_list_template.py b/bookmarks/tests/test_bookmarks_list_template.py
index a8658f4..c6c4d75 100644
--- a/bookmarks/tests/test_bookmarks_list_template.py
+++ b/bookmarks/tests/test_bookmarks_list_template.py
@@ -457,6 +457,22 @@ class BookmarkListTemplateTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
         style = bookmark_list["style"]
         self.assertIn("--ld-bookmark-description-max-lines:3;", style)
 
+    def test_bookmark_tag_ordering(self):
+        bookmark = self.setup_bookmark()
+        tag3 = self.setup_tag(name="tag3")
+        tag1 = self.setup_tag(name="tag1")
+        tag2 = self.setup_tag(name="tag2")
+        bookmark.tags.add(tag3, tag1, tag2)
+
+        html = self.render_template()
+        soup = self.make_soup(html)
+        tags = soup.select_one(".tags")
+        tag_links = tags.find_all("a")
+        self.assertEqual(len(tag_links), 3)
+        self.assertEqual(tag_links[0].text, "#tag1")
+        self.assertEqual(tag_links[1].text, "#tag2")
+        self.assertEqual(tag_links[2].text, "#tag3")
+
     def test_should_render_web_archive_link_with_absolute_date_setting(self):
         bookmark = self.setup_date_format_test(
             UserProfile.BOOKMARK_DATE_DISPLAY_ABSOLUTE,
Repository: sissbruecker/linkding
Pull request #847 (2024-09-23 14:16:25)

Add basic fail2ban support
Failed login attempts now return a `401` status code, resulting in uwsgi logs like:
```
2024-09-23 14:12:50,557 WARNING Unauthorized: /login/
[pid: 20|app: 0|req: 3/9] ::ffff:192.168.65.1 () {54 vars in 1728 bytes} [Mon Sep 23 14:12:50 2024] POST /login/ => generated 5956 bytes in 276 msecs (HTTP/1.1 401) 11 headers in 519 bytes (1 switches on core 1)
```

Closes https://github.com/sissbruecker/linkding/issues/489


Patch:
diff --git a/siteroot/urls.py b/siteroot/urls.py
index 5431f02..4b14f4f 100644
--- a/siteroot/urls.py
+++ b/siteroot/urls.py
@@ -34,6 +34,11 @@ class LinkdingLoginView(auth_views.LoginView):
         context["enable_oidc"] = settings.LD_ENABLE_OIDC
         return context
 
+    def form_invalid(self, form):
+        response = super().form_invalid(form)
+        response.status_code = 401
+        return response
+
 
 urlpatterns = [
     path("admin/", linkding_admin_site.urls),


Test patch:
diff --git a/bookmarks/tests/test_login_view.py b/bookmarks/tests/test_login_view.py
index 3eb25bd..5aa55a6 100644
--- a/bookmarks/tests/test_login_view.py
+++ b/bookmarks/tests/test_login_view.py
@@ -1,7 +1,7 @@
 from django.test import TestCase, override_settings
 from django.urls import path, include
 
-from bookmarks.tests.helpers import HtmlTestMixin
+from bookmarks.tests.helpers import BookmarkFactoryMixin, HtmlTestMixin
 from siteroot.urls import urlpatterns as base_patterns
 
 # Register OIDC urls for this test, otherwise login template can not render when OIDC is enabled
@@ -9,7 +9,19 @@ urlpatterns = base_patterns + [path("oidc/", include("mozilla_django_oidc.urls")
 
 
 @override_settings(ROOT_URLCONF=__name__)
-class LoginViewTestCase(TestCase, HtmlTestMixin):
+class LoginViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
+
+    def test_failed_login_should_return_401(self):
+        response = self.client.post("/login/", {"username": "test", "password": "test"})
+        self.assertEqual(response.status_code, 401)
+
+    def test_successful_login_should_redirect(self):
+        user = self.setup_user(name="test")
+        user.set_password("test")
+        user.save()
+
+        response = self.client.post("/login/", {"username": "test", "password": "test"})
+        self.assertEqual(response.status_code, 302)
 
     def test_should_not_show_oidc_login_by_default(self):
         response = self.client.get("/login/")
Repository: sissbruecker/linkding
Pull request #855 (2024-09-24 15:17:21)

Improve error handling for auto tagging


Patch:
diff --git a/bookmarks/services/auto_tagging.py b/bookmarks/services/auto_tagging.py
index 2608761..840745a 100644
--- a/bookmarks/services/auto_tagging.py
+++ b/bookmarks/services/auto_tagging.py
@@ -7,6 +7,9 @@ def get_tags(script: str, url: str):
     parsed_url = urlparse(url.lower())
     result = set()
 
+    if not parsed_url.hostname:
+        return result
+
     for line in script.lower().split("\n"):
         if "#" in line:
             i = line.index("#")


Test patch:
diff --git a/bookmarks/tests/test_auto_tagging.py b/bookmarks/tests/test_auto_tagging.py
index 320003f..b4213a1 100644
--- a/bookmarks/tests/test_auto_tagging.py
+++ b/bookmarks/tests/test_auto_tagging.py
@@ -14,6 +14,20 @@ class AutoTaggingTestCase(TestCase):
 
         self.assertEqual(tags, {"example"})
 
+    def test_auto_tag_by_domain_handles_invalid_urls(self):
+        script = """
+            example.com example
+            test.com test
+        """
+
+        url = "https://"
+        tags = auto_tagging.get_tags(script, url)
+        self.assertEqual(tags, set([]))
+
+        url = "example.com"
+        tags = auto_tagging.get_tags(script, url)
+        self.assertEqual(tags, set([]))
+
     def test_auto_tag_by_domain_works_with_port(self):
         script = """
             example.com example
Repository: sissbruecker/linkding
Pull request #1101 (2025-06-20 03:26:53)

Add date and time to HTML export filename
Closes https://github.com/sissbruecker/linkding/issues/1047


Patch:
diff --git a/bookmarks/views/settings.py b/bookmarks/views/settings.py
index abce0ce..7e6714e 100644
--- a/bookmarks/views/settings.py
+++ b/bookmarks/views/settings.py
@@ -11,6 +11,7 @@ from django.db.models import prefetch_related_objects
 from django.http import HttpResponseRedirect, HttpResponse
 from django.shortcuts import render
 from django.urls import reverse
+from django.utils import timezone
 from rest_framework.authtoken.models import Token
 
 from bookmarks.models import (
@@ -239,8 +240,12 @@ def bookmark_export(request: HttpRequest):
         prefetch_related_objects(bookmarks, "tags")
         file_content = exporter.export_netscape_html(list(bookmarks))
 
+        # Generate filename with current date and time
+        current_time = timezone.now()
+        filename = current_time.strftime("bookmarks_%Y-%m-%d_%H-%M-%S.html")
+
         response = HttpResponse(content_type="text/plain; charset=UTF-8")
-        response["Content-Disposition"] = 'attachment; filename="bookmarks.html"'
+        response["Content-Disposition"] = f'attachment; filename="{filename}"'
         response.write(file_content)
 
         return response


Test patch:
diff --git a/bookmarks/tests/test_settings_export_view.py b/bookmarks/tests/test_settings_export_view.py
index 303f3d9..4837025 100644
--- a/bookmarks/tests/test_settings_export_view.py
+++ b/bookmarks/tests/test_settings_export_view.py
@@ -1,3 +1,4 @@
+import datetime
 from unittest.mock import patch
 
 from django.test import TestCase
@@ -29,9 +30,6 @@ class SettingsExportViewTestCase(TestCase, BookmarkFactoryMixin):
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response["content-type"], "text/plain; charset=UTF-8")
-        self.assertEqual(
-            response["Content-Disposition"], 'attachment; filename="bookmarks.html"'
-        )
 
         for bookmark in Bookmark.objects.all():
             self.assertContains(response, bookmark.url)
@@ -78,3 +76,16 @@ class SettingsExportViewTestCase(TestCase, BookmarkFactoryMixin):
             self.assertFormErrorHint(
                 response, "An error occurred during bookmark export."
             )
+
+    def test_filename_includes_date_and_time(self):
+        self.setup_bookmark()
+        
+        # Mock timezone.now to return a fixed datetime for predictable filename
+        fixed_time = datetime.datetime(2023, 5, 15, 14, 30, 45, tzinfo=datetime.timezone.utc)
+        
+        with patch("bookmarks.views.settings.timezone.now", return_value=fixed_time):
+            response = self.client.get(reverse("linkding:settings.export"), follow=True)
+
+        self.assertEqual(response.status_code, 200)
+        expected_filename = 'attachment; filename="bookmarks_2023-05-15_14-30-45.html"'
+        self.assertEqual(response["Content-Disposition"], expected_filename)