Modified GetID method to not return a pointer
ObjectID type has now a IsValid method
This commit is contained in:
parent
23227e2552
commit
f968addfe0
|
@ -317,8 +317,8 @@ func (a Activity) IsLink() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the Activity object
|
// GetID returns the ObjectID corresponding to the Activity object
|
||||||
func (a Activity) GetID() *ObjectID {
|
func (a Activity) GetID() ObjectID {
|
||||||
return &a.ID
|
return a.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the IRI corresponding to the Activity object
|
// GetLink returns the IRI corresponding to the Activity object
|
||||||
|
|
|
@ -635,15 +635,15 @@ func TestUpdate_Recipients(t *testing.T) {
|
||||||
func TestActivity_GetID(t *testing.T) {
|
func TestActivity_GetID(t *testing.T) {
|
||||||
a := ActivityNew("test", ActivityType, Person{})
|
a := ActivityNew("test", ActivityType, Person{})
|
||||||
|
|
||||||
if *a.GetID() != "test" {
|
if a.GetID() != "test" {
|
||||||
t.Errorf("%T should return an empty %T object. Received %#v", a, a.GetID(), *a.GetID())
|
t.Errorf("%T should return an empty %T object. Received %#v", a, a.GetID(), a.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestActivity_GetIDGetType(t *testing.T) {
|
func TestActivity_GetIDGetType(t *testing.T) {
|
||||||
a := ActivityNew("test", ActivityType, Person{})
|
a := ActivityNew("test", ActivityType, Person{})
|
||||||
|
|
||||||
if *a.GetID() != "test" || a.GetType() != ActivityType {
|
if a.GetID() != "test" || a.GetType() != ActivityType {
|
||||||
t.Errorf("%T should not return an empty %T object. Received %#v", a, a.GetID(), *a.GetID())
|
t.Errorf("%T should not return an empty %T object. Received %#v", a, a.GetID(), a.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestActivity_IsLink(t *testing.T) {
|
func TestActivity_IsLink(t *testing.T) {
|
||||||
|
@ -664,11 +664,11 @@ func TestActivity_IsObject(t *testing.T) {
|
||||||
func checkDedup(list ItemCollection, recIds *[]ObjectID) error {
|
func checkDedup(list ItemCollection, recIds *[]ObjectID) error {
|
||||||
for _, rec := range list {
|
for _, rec := range list {
|
||||||
for _, id := range *recIds {
|
for _, id := range *recIds {
|
||||||
if *rec.GetID() == id {
|
if rec.GetID() == id {
|
||||||
return fmt.Errorf("%T[%s] already stored in recipients list, Deduplication faild", rec, id)
|
return fmt.Errorf("%T[%s] already stored in recipients list, Deduplication faild", rec, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*recIds = append(*recIds, *rec.GetID())
|
*recIds = append(*recIds, rec.GetID())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,8 +109,8 @@ type CollectionPage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the CollectionPage object
|
// GetID returns the ObjectID corresponding to the CollectionPage object
|
||||||
func (c CollectionPage) GetID() *ObjectID {
|
func (c CollectionPage) GetID() ObjectID {
|
||||||
return &c.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType returns the CollectionPage's type
|
// GetType returns the CollectionPage's type
|
||||||
|
|
|
@ -32,7 +32,7 @@ func TestCollectionPage_Append(t *testing.T) {
|
||||||
t.Errorf("Collection page should point to collection %q", c.GetLink())
|
t.Errorf("Collection page should point to collection %q", c.GetLink())
|
||||||
}
|
}
|
||||||
if p.Count() != 1 {
|
if p.Count() != 1 {
|
||||||
t.Errorf("Collection page of %q should have exactly one element", *p.GetID())
|
t.Errorf("Collection page of %q should have exactly one element", p.GetID())
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(p.Items[0], val) {
|
if !reflect.DeepEqual(p.Items[0], val) {
|
||||||
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
||||||
|
|
|
@ -137,8 +137,8 @@ func OrderedCollectionNew(id ObjectID) *OrderedCollection {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the Collection object
|
// GetID returns the ObjectID corresponding to the Collection object
|
||||||
func (c Collection) GetID() *ObjectID {
|
func (c Collection) GetID() ObjectID {
|
||||||
return &c.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType returns the Collection's type
|
// GetType returns the Collection's type
|
||||||
|
|
|
@ -27,7 +27,7 @@ func TestCollection_Append(t *testing.T) {
|
||||||
c.Append(val)
|
c.Append(val)
|
||||||
|
|
||||||
if c.Count() != 1 {
|
if c.Count() != 1 {
|
||||||
t.Errorf("Inbox collection of %q should have one element", *c.GetID())
|
t.Errorf("Inbox collection of %q should have one element", c.GetID())
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(c.Items[0], val) {
|
if !reflect.DeepEqual(c.Items[0], val) {
|
||||||
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
||||||
|
@ -49,8 +49,8 @@ func TestCollection_GetID(t *testing.T) {
|
||||||
|
|
||||||
c := CollectionNew(id)
|
c := CollectionNew(id)
|
||||||
|
|
||||||
if *c.GetID() != id {
|
if c.GetID() != id {
|
||||||
t.Errorf("GetID should return %s, received %s", id, *c.GetID())
|
t.Errorf("GetID should return %s, received %s", id, c.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,8 +145,8 @@ func (i IntransitiveActivity) IsLink() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the IntransitiveActivity object
|
// GetID returns the ObjectID corresponding to the IntransitiveActivity object
|
||||||
func (i IntransitiveActivity) GetID() *ObjectID {
|
func (i IntransitiveActivity) GetID() ObjectID {
|
||||||
return &i.ID
|
return i.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the IRI corresponding to the IntransitiveActivity object
|
// GetLink returns the IRI corresponding to the IntransitiveActivity object
|
||||||
|
|
|
@ -108,14 +108,14 @@ func TestIntransitiveActivityRecipients(t *testing.T) {
|
||||||
func TestIntransitiveActivity_GetLink(t *testing.T) {
|
func TestIntransitiveActivity_GetLink(t *testing.T) {
|
||||||
i := IntransitiveActivityNew("test", QuestionType)
|
i := IntransitiveActivityNew("test", QuestionType)
|
||||||
|
|
||||||
if *i.GetID() != "test" {
|
if i.GetID() != "test" {
|
||||||
t.Errorf("%T should return an empty %T object. Received %#v", i, i, i)
|
t.Errorf("%T should return an empty %T object. Received %#v", i, i, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestIntransitiveActivity_GetObject(t *testing.T) {
|
func TestIntransitiveActivity_GetObject(t *testing.T) {
|
||||||
i := IntransitiveActivityNew("test", QuestionType)
|
i := IntransitiveActivityNew("test", QuestionType)
|
||||||
|
|
||||||
if *i.GetID() != "test" || i.GetType() != QuestionType {
|
if i.GetID() != "test" || i.GetType() != QuestionType {
|
||||||
t.Errorf("%T should not return an empty %T object. Received %#v", i, i, i)
|
t.Errorf("%T should not return an empty %T object. Received %#v", i, i, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,8 +170,8 @@ func TestIntransitiveActivity_Recipients(t *testing.T) {
|
||||||
func TestIntransitiveActivity_GetID(t *testing.T) {
|
func TestIntransitiveActivity_GetID(t *testing.T) {
|
||||||
a := IntransitiveActivityNew("test", IntransitiveActivityType)
|
a := IntransitiveActivityNew("test", IntransitiveActivityType)
|
||||||
|
|
||||||
if *a.GetID() != "test" {
|
if a.GetID() != "test" {
|
||||||
t.Errorf("%T should return an empty %T object. Received %#v", a, a.GetID(), *a.GetID())
|
t.Errorf("%T should return an empty %T object. Received %#v", a, a.GetID(), a.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
iri.go
5
iri.go
|
@ -36,9 +36,8 @@ func (i *IRI) UnmarshalJSON(s []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID
|
// GetID
|
||||||
func (i IRI) GetID() *ObjectID {
|
func (i IRI) GetID() ObjectID {
|
||||||
o := ObjectID(i)
|
return ObjectID(i)
|
||||||
return &o
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType
|
// GetType
|
||||||
|
|
|
@ -20,8 +20,8 @@ func TestIRI_String(t *testing.T) {
|
||||||
|
|
||||||
func TestIRI_GetID(t *testing.T) {
|
func TestIRI_GetID(t *testing.T) {
|
||||||
i := IRI("http://example.com")
|
i := IRI("http://example.com")
|
||||||
if id := i.GetID(); id == nil || *id != ObjectID(i) {
|
if id := i.GetID(); !id.IsValid() || id != ObjectID(i) {
|
||||||
t.Errorf("ObjectID %q (%T) should equal %q (%T)", *id, id, i, ObjectID(i))
|
t.Errorf("ObjectID %q (%T) should equal %q (%T)", id, id, i, ObjectID(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
item.go
9
item.go
|
@ -6,14 +6,17 @@ type ItemCollection []Item
|
||||||
// Item struct
|
// Item struct
|
||||||
type Item ObjectOrLink
|
type Item ObjectOrLink
|
||||||
|
|
||||||
|
const EmptyObjectID = ObjectID("")
|
||||||
|
const EmptyIRI = IRI("")
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to ItemCollection
|
// GetID returns the ObjectID corresponding to ItemCollection
|
||||||
func (i ItemCollection) GetID() *ObjectID {
|
func (i ItemCollection) GetID() ObjectID {
|
||||||
return nil
|
return EmptyObjectID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the empty IRI
|
// GetLink returns the empty IRI
|
||||||
func (i ItemCollection) GetLink() IRI {
|
func (i ItemCollection) GetLink() IRI {
|
||||||
return IRI("")
|
return EmptyIRI
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType returns the ItemCollection's type
|
// GetType returns the ItemCollection's type
|
||||||
|
|
4
link.go
4
link.go
|
@ -80,8 +80,8 @@ func (l Link) IsCollection() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the Link object
|
// GetID returns the ObjectID corresponding to the Link object
|
||||||
func (l Link) GetID() *ObjectID {
|
func (l Link) GetID() ObjectID {
|
||||||
return &l.ID
|
return l.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the IRI corresponding to the current Link
|
// GetLink returns the IRI corresponding to the current Link
|
||||||
|
|
10
object.go
10
object.go
|
@ -98,7 +98,7 @@ type (
|
||||||
// is currently happening, or has already happened
|
// is currently happening, or has already happened
|
||||||
ActivityObject interface {
|
ActivityObject interface {
|
||||||
// GetID returns the dereferenceable ActivityStreams object id
|
// GetID returns the dereferenceable ActivityStreams object id
|
||||||
GetID() *ObjectID
|
GetID() ObjectID
|
||||||
// GetType returns the ActivityStreams type
|
// GetType returns the ActivityStreams type
|
||||||
GetType() ActivityVocabularyType
|
GetType() ActivityVocabularyType
|
||||||
}
|
}
|
||||||
|
@ -479,8 +479,8 @@ func ObjectNew(typ ActivityVocabularyType) *Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the current Object
|
// GetID returns the ObjectID corresponding to the current Object
|
||||||
func (o Object) GetID() *ObjectID {
|
func (o Object) GetID() ObjectID {
|
||||||
return &o.ID
|
return o.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the IRI corresponding to the current Object
|
// GetLink returns the IRI corresponding to the current Object
|
||||||
|
@ -649,6 +649,10 @@ func (i *ObjectID) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *ObjectID) IsValid() bool {
|
||||||
|
return i != nil && len(*i) > 0
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON
|
// UnmarshalJSON
|
||||||
func (c *MimeType) UnmarshalJSON(data []byte) error {
|
func (c *MimeType) UnmarshalJSON(data []byte) error {
|
||||||
*c = MimeType(strings.Trim(string(data), "\""))
|
*c = MimeType(strings.Trim(string(data), "\""))
|
||||||
|
|
|
@ -619,8 +619,8 @@ func TestObject_GetID(t *testing.T) {
|
||||||
a := Object{}
|
a := Object{}
|
||||||
testVal := "crash$"
|
testVal := "crash$"
|
||||||
a.ID = ObjectID(testVal)
|
a.ID = ObjectID(testVal)
|
||||||
if string(*a.GetID()) != testVal {
|
if string(a.GetID()) != testVal {
|
||||||
t.Errorf("%T should return %q, Received %q", a.GetID, testVal, *a.GetID())
|
t.Errorf("%T should return %q, Received %q", a.GetID, testVal, a.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,8 @@ func (o OrderedCollection) IsLink() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the OrderedCollection
|
// GetID returns the ObjectID corresponding to the OrderedCollection
|
||||||
func (o OrderedCollection) GetID() *ObjectID {
|
func (o OrderedCollection) GetID() ObjectID {
|
||||||
return &o.ID
|
return o.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the IRI corresponding to the OrderedCollection object
|
// GetLink returns the IRI corresponding to the OrderedCollection object
|
||||||
|
|
|
@ -113,8 +113,8 @@ type OrderedCollectionPage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the OrderedCollectionPage object
|
// GetID returns the ObjectID corresponding to the OrderedCollectionPage object
|
||||||
func (o OrderedCollectionPage) GetID() *ObjectID {
|
func (o OrderedCollectionPage) GetID() ObjectID {
|
||||||
return &o.ID
|
return o.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetType returns the OrderedCollectionPage's type
|
// GetType returns the OrderedCollectionPage's type
|
||||||
|
|
|
@ -93,7 +93,7 @@ func TestOrderedCollectionPage_Append(t *testing.T) {
|
||||||
t.Errorf("OrderedCollection page should point to OrderedCollection %q", c.GetLink())
|
t.Errorf("OrderedCollection page should point to OrderedCollection %q", c.GetLink())
|
||||||
}
|
}
|
||||||
if p.Count() != 1 {
|
if p.Count() != 1 {
|
||||||
t.Errorf("OrderedCollection page of %q should have exactly one element", *p.GetID())
|
t.Errorf("OrderedCollection page of %q should have exactly one element", p.GetID())
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(p.OrderedItems[0], val) {
|
if !reflect.DeepEqual(p.OrderedItems[0], val) {
|
||||||
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
||||||
|
|
|
@ -27,7 +27,7 @@ func Test_OrderedCollection_Append(t *testing.T) {
|
||||||
c.Append(val)
|
c.Append(val)
|
||||||
|
|
||||||
if c.Count() != 1 {
|
if c.Count() != 1 {
|
||||||
t.Errorf("Inbox collection of %q should have one element", *c.GetID())
|
t.Errorf("Inbox collection of %q should have one element", c.GetID())
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(c.OrderedItems[0], val) {
|
if !reflect.DeepEqual(c.OrderedItems[0], val) {
|
||||||
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
||||||
|
@ -49,7 +49,7 @@ func TestOrderedCollection_Append(t *testing.T) {
|
||||||
t.Errorf("Ordereed collection page should point to ordered collection %q", c.GetLink())
|
t.Errorf("Ordereed collection page should point to ordered collection %q", c.GetLink())
|
||||||
}
|
}
|
||||||
if p.Count() != 1 {
|
if p.Count() != 1 {
|
||||||
t.Errorf("Ordered collection page of %q should have exactly one element", *p.GetID())
|
t.Errorf("Ordered collection page of %q should have exactly one element", p.GetID())
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(p.OrderedItems[0], val) {
|
if !reflect.DeepEqual(p.OrderedItems[0], val) {
|
||||||
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
t.Errorf("First item in Inbox is does not match %q", val.ID)
|
||||||
|
@ -71,8 +71,8 @@ func TestOrderedCollection_GetID(t *testing.T) {
|
||||||
|
|
||||||
c := OrderedCollectionNew(id)
|
c := OrderedCollectionNew(id)
|
||||||
|
|
||||||
if *c.GetID() != id {
|
if c.GetID() != id {
|
||||||
t.Errorf("GetID should return %q, received %q", id, *c.GetID())
|
t.Errorf("GetID should return %q, received %q", id, c.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
place.go
4
place.go
|
@ -134,8 +134,8 @@ func (p Place) GetType() ActivityVocabularyType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ID corresponding to the current Place
|
// GetID returns the ID corresponding to the current Place
|
||||||
func (p Place) GetID() *ObjectID {
|
func (p Place) GetID() ObjectID {
|
||||||
return &p.ID
|
return p.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON
|
// UnmarshalJSON
|
||||||
|
|
|
@ -120,8 +120,8 @@ func (p Profile) GetType() ActivityVocabularyType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ID corresponding to the current Profile
|
// GetID returns the ID corresponding to the current Profile
|
||||||
func (p Profile) GetID() *ObjectID {
|
func (p Profile) GetID() ObjectID {
|
||||||
return &p.ID
|
return p.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON
|
// UnmarshalJSON
|
||||||
|
|
|
@ -120,8 +120,8 @@ type Question struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ObjectID corresponding to the Question object
|
// GetID returns the ObjectID corresponding to the Question object
|
||||||
func (q Question) GetID() *ObjectID {
|
func (q Question) GetID() ObjectID {
|
||||||
return &q.ID
|
return q.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLink returns the IRI corresponding to the Question object
|
// GetLink returns the IRI corresponding to the Question object
|
||||||
|
|
|
@ -18,8 +18,8 @@ func TestQuestionNew(t *testing.T) {
|
||||||
func TestQuestion_GetID(t *testing.T) {
|
func TestQuestion_GetID(t *testing.T) {
|
||||||
a := QuestionNew("test")
|
a := QuestionNew("test")
|
||||||
|
|
||||||
if *a.GetID() != "test" {
|
if a.GetID() != "test" {
|
||||||
t.Errorf("%T should return an empty %T object. Received %#v", a, a.GetID(), *a.GetID())
|
t.Errorf("%T should return an empty %T object. Received %#v", a, a.GetID(), a.GetID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,8 +130,8 @@ func (r Relationship) GetType() ActivityVocabularyType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ID corresponding to the current Relationship
|
// GetID returns the ID corresponding to the current Relationship
|
||||||
func (r Relationship) GetID() *ObjectID {
|
func (r Relationship) GetID() ObjectID {
|
||||||
return &r.ID
|
return r.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON
|
// UnmarshalJSON
|
||||||
|
|
|
@ -122,8 +122,8 @@ func (t Tombstone) GetType() ActivityVocabularyType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the ID corresponding to the current Tombstone
|
// GetID returns the ID corresponding to the current Tombstone
|
||||||
func (t Tombstone) GetID() *ObjectID {
|
func (t Tombstone) GetID() ObjectID {
|
||||||
return &t.ID
|
return t.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON
|
// UnmarshalJSON
|
||||||
|
|
|
@ -19,11 +19,11 @@ var (
|
||||||
|
|
||||||
// ItemTyperFunc will return an instance of a struct that implements activitystreams.Item
|
// ItemTyperFunc will return an instance of a struct that implements activitystreams.Item
|
||||||
// The default for this package is JSONGetItemByType but can be overwritten
|
// The default for this package is JSONGetItemByType but can be overwritten
|
||||||
var ItemTyperFunc TyperFunction
|
var ItemTyperFunc TyperFn
|
||||||
|
|
||||||
// TyperFunction is the type of the function which returns an activitystreams.Item struct instance
|
// TyperFn is the type of the function which returns an activitystreams.Item struct instance
|
||||||
// for a specific ActivityVocabularyType
|
// for a specific ActivityVocabularyType
|
||||||
type TyperFunction func(ActivityVocabularyType) (Item, error)
|
type TyperFn func(ActivityVocabularyType) (Item, error)
|
||||||
|
|
||||||
func JSONGetObjectID(data []byte) ObjectID {
|
func JSONGetObjectID(data []byte) ObjectID {
|
||||||
i, err := jsonparser.GetString(data, "id")
|
i, err := jsonparser.GetString(data, "id")
|
||||||
|
|
Reference in a new issue