fix(extension): restore deprecated request APIs for compatibility (#1628)

This commit is contained in:
Levi
2026-10-31 17:51:56 +08:00
committed by GitHub
parent 8719166dbf
commit fc0a1e0ef9
6 changed files with 131 additions and 3 deletions
+27
View File
@@ -27,6 +27,33 @@ type Request struct {
SkipVerifyCert bool `json:"skipVerifyCert"`
}
// SetLabels replaces all labels on the request.
//
// Deprecated: In onResolve and onDone, assign request labels directly.
// In onStart and onError, use task.meta.req.setLabels instead.
func (r *Request) SetLabels(labels map[string]string) {
r.Labels = labels
}
// PutLabel sets a label on the request.
//
// Deprecated: In onResolve and onDone, assign request labels directly.
// In onStart and onError, use task.meta.req.putLabel instead.
func (r *Request) PutLabel(key, value string) {
if r.Labels == nil {
r.Labels = make(map[string]string)
}
r.Labels[key] = value
}
// DelLabel deletes a label from the request.
//
// Deprecated: In onResolve and onDone, delete request labels directly.
// In onStart and onError, use task.meta.req.delLabel instead.
func (r *Request) DelLabel(key string) {
delete(r.Labels, key)
}
func (r *Request) Validate() error {
if r.URL == "" {
return fmt.Errorf("invalid request url")
+6 -1
View File
@@ -46,7 +46,7 @@ type generationTestManager struct {
func TestExtensionTaskMethodsMatchEventCapabilities(t *testing.T) {
taskType := reflect.TypeOf((*Task)(nil))
for _, typ := range []reflect.Type{taskType, reflect.TypeOf((*ExtensionTask)(nil)), reflect.TypeOf((*OnErrorExtensionTask)(nil))} {
for _, name := range []string{"SetUrl", "SetExtra", "PatchExtra", "PatchHeaders", "SetMethod", "SetBody", "SetHeaders", "PutHeader", "DelHeader", "SetTrackers"} {
for _, name := range []string{"SetExtra", "PatchExtra", "PatchHeaders", "SetMethod", "SetBody", "SetHeaders", "PutHeader", "DelHeader", "SetTrackers"} {
if _, ok := typ.MethodByName(name); ok {
t.Fatalf("%v unexpectedly exposes %s", typ, name)
}
@@ -79,6 +79,11 @@ func TestExtensionTaskMethodsMatchEventCapabilities(t *testing.T) {
}
extensionTaskType := reflect.TypeOf((*ExtensionTask)(nil))
for _, typ := range []reflect.Type{extensionTaskType, reflect.TypeOf((*OnErrorExtensionTask)(nil))} {
if _, ok := typ.MethodByName("SetUrl"); !ok {
t.Fatalf("%v does not expose the deprecated SetUrl alias", typ)
}
}
if _, ok := extensionTaskType.MethodByName("Continue"); ok {
t.Fatal("non-error extension task unexpectedly exposes Continue")
}
+7
View File
@@ -825,6 +825,13 @@ func newOnDoneExtensionTask(task *Task) *Task {
return cloneExtensionTask(task)
}
// SetUrl replaces the task request URL.
//
// Deprecated: Use task.meta.req.setUrl instead.
func (t *ExtensionTask) SetUrl(url string) {
t.Meta.Req.SetUrl(url)
}
func (t *OnErrorExtensionTask) Continue() error {
return t.download.Continue(&TaskFilter{
IDs: []string{t.ID},
+89
View File
@@ -0,0 +1,89 @@
package download
import (
"errors"
"fmt"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/GopeedLab/gopeed/internal/fetcher"
"github.com/GopeedLab/gopeed/pkg/base"
)
func TestExtensionDeprecatedRequestAPIs(t *testing.T) {
for _, event := range []ActivationEvent{EventOnResolve, EventOnStart, EventOnError, EventOnDone} {
for _, withLabels := range []bool{false, true} {
t.Run(fmt.Sprintf("%s/labels=%v", event, withLabels), func(t *testing.T) {
d := NewDownloader(&DownloaderConfig{Storage: NewMemStorage(), StorageDir: t.TempDir()})
if err := d.Setup(); err != nil {
t.Fatal(err)
}
defer d.Clear()
extDir := t.TempDir()
manifest := fmt.Sprintf(`{"name":"legacy-request","title":"Legacy request APIs","version":"0.0.1","scripts":[{"event":%q,"match":{"urls":["*://example.com/*"]},"entry":"index.js"}]}`, event)
request := "ctx.task.meta.req"
urlMutation := ""
if event == EventOnResolve {
request = "ctx.req"
} else if event == EventOnStart || event == EventOnError {
urlMutation = `await ctx.task.setUrl('https://example.com/legacy');
if (req.url !== 'https://example.com/legacy') throw new Error('legacy URL did not write through');
await req.setUrl('https://example.com/new');
if (req.url !== 'https://example.com/new') throw new Error('new URL did not write through');
await ctx.task.setUrl('https://example.com/final');`
}
script := fmt.Sprintf(`gopeed.events.%s(async ctx => {
const req = %s;
await req.delLabel('missing');
await req.putLabel('initial', 'yes');
if (req.labels.initial !== 'yes') throw new Error('putLabel did not initialize labels');
await req.setLabels({keep: 'yes', remove: 'yes'});
if (req.labels.initial !== undefined || req.labels.old !== undefined) throw new Error('setLabels did not replace labels');
await req.putLabel('added', 'yes');
await req.delLabel('remove');
if (req.labels.keep !== 'yes' || req.labels.added !== 'yes' || req.labels.remove !== undefined) throw new Error('label mutation failed');
%s
await req.putLabel('completed', 'yes');
});`, event, request, urlMutation)
for name, content := range map[string]string{"manifest.json": manifest, "index.js": script} {
if err := os.WriteFile(filepath.Join(extDir, name), []byte(content), 0600); err != nil {
t.Fatal(err)
}
}
if _, err := d.InstallExtensionByFolder(extDir, false); err != nil {
t.Fatal(err)
}
req := &base.Request{URL: "https://example.com/original"}
if withLabels {
req.Labels = map[string]string{"old": "yes"}
}
task := &Task{Protocol: "http", Meta: &fetcher.FetcherMeta{Req: req, Opts: &base.Options{}}}
switch event {
case EventOnResolve:
if _, err := d.triggerOnResolve(req); err != nil {
t.Fatal(err)
}
case EventOnStart:
d.triggerOnStart(task)
case EventOnError:
d.triggerOnError(task, errors.New("retry"))
case EventOnDone:
d.triggerOnDone(task)
}
if !reflect.DeepEqual(req.Labels, map[string]string{"keep": "yes", "added": "yes", "completed": "yes"}) {
t.Fatalf("legacy handler did not update the original request: %#v", req.Labels)
}
expectedURL := "https://example.com/original"
if urlMutation != "" {
expectedURL = "https://example.com/final"
}
if req.URL != expectedURL {
t.Fatalf("URL = %q, want %q", req.URL, expectedURL)
}
})
}
}
}
@@ -152,7 +152,7 @@ func TestExtensionOnErrorRequestWritesToOriginalTask(t *testing.T) {
request.setHeaders({Keep:'new'});
request.putHeader('Authorization','Bearer new');
request.setMethod('POST');
if (typeof task.setUrl !== 'undefined' || typeof task.extra !== 'undefined') throw new Error('legacy task API');
if (typeof task.setUrl !== 'function' || typeof task.extra !== 'undefined') throw new Error('unexpected legacy task API');
request.setUrl('https://example.com/new');
request.setLabels({keep:'yes',remove:'yes'});
request.putLabel('new','value');
+1 -1
View File
@@ -1,5 +1,5 @@
gopeed.events.onResolve(async function (ctx) {
for (const method of ['setUrl', 'setLabels', 'putLabel', 'delLabel', 'setMethod', 'putHeader', 'setTrackers']) {
for (const method of ['setUrl', 'setMethod', 'putHeader', 'setTrackers']) {
if (typeof ctx.req[method] !== 'undefined') throw new Error(`onResolve exposes ${method}`);
}
ctx.req.labels = { replaced: "true", removed: "true" };