~netlandish/links-dev

links: Adding `imageUrl` to relevant GraphQL types to make it easier to fetch images from their individual hosting environments. v1 APPLIED

Peter Sanchez: 1
 Adding `imageUrl` to relevant GraphQL types to make it easier to fetch images from their individual hosting environments.

 4 files changed, 321 insertions(+), 26 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.code.netlandish.com/~netlandish/links-dev/patches/198/mbox | git am -3
Learn more about email & git

[PATCH links] Adding `imageUrl` to relevant GraphQL types to make it easier to fetch images from their individual hosting environments. Export this patch

Changelog-added: `imageUrl` field to relevant GraphQL types
---
 api/gqlgen.yml                |  12 ++
 api/graph/generated.go        | 289 +++++++++++++++++++++++++++++++---
 api/graph/schema.graphqls     |   5 +-
 api/graph/schema.resolvers.go |  41 ++++-
 4 files changed, 321 insertions(+), 26 deletions(-)

diff --git a/api/gqlgen.yml b/api/gqlgen.yml
index 090a18f..6f9e4a4 100644
--- a/api/gqlgen.yml
+++ b/api/gqlgen.yml
@@ -67,3 +67,15 @@ models:
  AuditLog:
    model:
      - netlandish.com/x/gobwebs-auditlog.AuditLog
  Organization:
    fields:
      imageUrl:
        resolver: true
  Listing:
    fields:
      imageUrl:
        resolver: true
  QRCode:
    fields:
      imageUrl:
        resolver: true
diff --git a/api/graph/generated.go b/api/graph/generated.go
index b82dd28..1d880ed 100644
--- a/api/graph/generated.go
+++ b/api/graph/generated.go
@@ -46,6 +46,7 @@ type ResolverRoot interface {
	BaseURL() BaseURLResolver
	BillingSettings() BillingSettingsResolver
	Domain() DomainResolver
	Listing() ListingResolver
	Mutation() MutationResolver
	OrgLink() OrgLinkResolver
	Organization() OrganizationResolver
@@ -192,6 +193,7 @@ type ComplexityRoot struct {
		DomainID   func(childComplexity int) int
		ID         func(childComplexity int) int
		Image      func(childComplexity int) int
		ImageURL   func(childComplexity int) int
		IsActive   func(childComplexity int) int
		IsDefault  func(childComplexity int) int
		LookupName func(childComplexity int) int
@@ -319,6 +321,7 @@ type ComplexityRoot struct {
		CreatedOn  func(childComplexity int) int
		ID         func(childComplexity int) int
		Image      func(childComplexity int) int
		ImageURL   func(childComplexity int) int
		IsActive   func(childComplexity int) int
		Name       func(childComplexity int) int
		OrgType    func(childComplexity int) int
@@ -384,6 +387,7 @@ type ComplexityRoot struct {
		HashID    func(childComplexity int) int
		ID        func(childComplexity int) int
		ImagePath func(childComplexity int) int
		ImageURL  func(childComplexity int) int
		OrgID     func(childComplexity int) int
		Title     func(childComplexity int) int
		URL       func(childComplexity int) int
@@ -496,6 +500,9 @@ type DomainResolver interface {
	Level(ctx context.Context, obj *models.Domain) (model.DomainLevel, error)
	Status(ctx context.Context, obj *models.Domain) (model.DomainStatus, error)
}
type ListingResolver interface {
	ImageURL(ctx context.Context, obj *models.Listing) (*string, error)
}
type MutationResolver interface {
	AddOrganization(ctx context.Context, input model.OrganizationInput) (*models.Organization, error)
	UpdateOrganization(ctx context.Context, input *model.UpdateOrganizationInput) (*models.Organization, error)
@@ -540,7 +547,7 @@ type OrgLinkResolver interface {
type OrganizationResolver interface {
	OrgType(ctx context.Context, obj *models.Organization) (model.OrgType, error)

	Image(ctx context.Context, obj *models.Organization) (*graphql.Upload, error)
	ImageURL(ctx context.Context, obj *models.Organization) (*string, error)

	Visibility(ctx context.Context, obj *models.Organization) (model.PublicVisibility, error)
}
@@ -549,6 +556,8 @@ type OrganizationSettingsResolver interface {
}
type QRCodeResolver interface {
	CodeType(ctx context.Context, obj *models.QRCode) (model.QRCodeType, error)

	ImageURL(ctx context.Context, obj *models.QRCode) (*string, error)
}
type QueryResolver interface {
	Version(ctx context.Context) (*model.Version, error)
@@ -1179,6 +1188,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin

		return e.complexity.Listing.Image(childComplexity), true

	case "Listing.imageUrl":
		if e.complexity.Listing.ImageURL == nil {
			break
		}

		return e.complexity.Listing.ImageURL(childComplexity), true

	case "Listing.isActive":
		if e.complexity.Listing.IsActive == nil {
			break
@@ -2007,6 +2023,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin

		return e.complexity.Organization.Image(childComplexity), true

	case "Organization.imageUrl":
		if e.complexity.Organization.ImageURL == nil {
			break
		}

		return e.complexity.Organization.ImageURL(childComplexity), true

	case "Organization.isActive":
		if e.complexity.Organization.IsActive == nil {
			break
@@ -2294,6 +2317,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin

		return e.complexity.QRCode.ImagePath(childComplexity), true

	case "QRCode.imageUrl":
		if e.complexity.QRCode.ImageURL == nil {
			break
		}

		return e.complexity.QRCode.ImageURL(childComplexity), true

	case "QRCode.orgId":
		if e.complexity.QRCode.OrgID == nil {
			break
@@ -4703,6 +4733,8 @@ func (ec *executionContext) fieldContext_Analytics_qrList(_ context.Context, fie
				return ec.fieldContext_QRCode_url(ctx, field)
			case "imagePath":
				return ec.fieldContext_QRCode_imagePath(ctx, field)
			case "imageUrl":
				return ec.fieldContext_QRCode_imageUrl(ctx, field)
			case "hashId":
				return ec.fieldContext_QRCode_hashId(ctx, field)
			case "createdOn":
@@ -8033,6 +8065,47 @@ func (ec *executionContext) fieldContext_Listing_image(_ context.Context, field
	return fc, nil
}

func (ec *executionContext) _Listing_imageUrl(ctx context.Context, field graphql.CollectedField, obj *models.Listing) (ret graphql.Marshaler) {
	fc, err := ec.fieldContext_Listing_imageUrl(ctx, field)
	if err != nil {
		return graphql.Null
	}
	ctx = graphql.WithFieldContext(ctx, fc)
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.Listing().ImageURL(rctx, obj)
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*string)
	fc.Result = res
	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}

func (ec *executionContext) fieldContext_Listing_imageUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
	fc = &graphql.FieldContext{
		Object:     "Listing",
		Field:      field,
		IsMethod:   true,
		IsResolver: true,
		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
			return nil, errors.New("field of type String does not have child fields")
		},
	}
	return fc, nil
}

func (ec *executionContext) _Listing_metadata(ctx context.Context, field graphql.CollectedField, obj *models.Listing) (ret graphql.Marshaler) {
	fc, err := ec.fieldContext_Listing_metadata(ctx, field)
	if err != nil {
@@ -8792,6 +8865,8 @@ func (ec *executionContext) fieldContext_ListingCursor_result(_ context.Context,
				return ec.fieldContext_Listing_slug(ctx, field)
			case "image":
				return ec.fieldContext_Listing_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Listing_imageUrl(ctx, field)
			case "metadata":
				return ec.fieldContext_Listing_metadata(ctx, field)
			case "isActive":
@@ -9480,6 +9555,8 @@ func (ec *executionContext) fieldContext_ListingLinkCursor_result(_ context.Cont
				return ec.fieldContext_Listing_slug(ctx, field)
			case "image":
				return ec.fieldContext_Listing_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Listing_imageUrl(ctx, field)
			case "metadata":
				return ec.fieldContext_Listing_metadata(ctx, field)
			case "isActive":
@@ -9765,6 +9842,8 @@ func (ec *executionContext) fieldContext_Mutation_addOrganization(ctx context.Co
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -9880,6 +9959,8 @@ func (ec *executionContext) fieldContext_Mutation_updateOrganization(ctx context
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -11580,6 +11661,8 @@ func (ec *executionContext) fieldContext_Mutation_addListing(ctx context.Context
				return ec.fieldContext_Listing_slug(ctx, field)
			case "image":
				return ec.fieldContext_Listing_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Listing_imageUrl(ctx, field)
			case "metadata":
				return ec.fieldContext_Listing_metadata(ctx, field)
			case "isActive":
@@ -11806,6 +11889,8 @@ func (ec *executionContext) fieldContext_Mutation_updateListing(ctx context.Cont
				return ec.fieldContext_Listing_slug(ctx, field)
			case "image":
				return ec.fieldContext_Listing_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Listing_imageUrl(ctx, field)
			case "metadata":
				return ec.fieldContext_Listing_metadata(ctx, field)
			case "isActive":
@@ -12226,6 +12311,8 @@ func (ec *executionContext) fieldContext_Mutation_addQRCode(ctx context.Context,
				return ec.fieldContext_QRCode_url(ctx, field)
			case "imagePath":
				return ec.fieldContext_QRCode_imagePath(ctx, field)
			case "imageUrl":
				return ec.fieldContext_QRCode_imageUrl(ctx, field)
			case "hashId":
				return ec.fieldContext_QRCode_hashId(ctx, field)
			case "createdOn":
@@ -12792,6 +12879,8 @@ func (ec *executionContext) fieldContext_Mutation_updateAdminOrg(ctx context.Con
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -15122,7 +15211,7 @@ func (ec *executionContext) _Organization_image(ctx context.Context, field graph
	}()
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.Organization().Image(rctx, obj)
		return obj.Image, nil
	})
	if err != nil {
		ec.Error(ctx, err)
@@ -15131,19 +15220,60 @@ func (ec *executionContext) _Organization_image(ctx context.Context, field graph
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*graphql.Upload)
	res := resTmp.(string)
	fc.Result = res
	return ec.marshalOUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, field.Selections, res)
	return ec.marshalOString2string(ctx, field.Selections, res)
}

func (ec *executionContext) fieldContext_Organization_image(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
	fc = &graphql.FieldContext{
		Object:     "Organization",
		Field:      field,
		IsMethod:   false,
		IsResolver: false,
		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
			return nil, errors.New("field of type String does not have child fields")
		},
	}
	return fc, nil
}

func (ec *executionContext) _Organization_imageUrl(ctx context.Context, field graphql.CollectedField, obj *models.Organization) (ret graphql.Marshaler) {
	fc, err := ec.fieldContext_Organization_imageUrl(ctx, field)
	if err != nil {
		return graphql.Null
	}
	ctx = graphql.WithFieldContext(ctx, fc)
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.Organization().ImageURL(rctx, obj)
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*string)
	fc.Result = res
	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}

func (ec *executionContext) fieldContext_Organization_imageUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
	fc = &graphql.FieldContext{
		Object:     "Organization",
		Field:      field,
		IsMethod:   true,
		IsResolver: true,
		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
			return nil, errors.New("field of type Upload does not have child fields")
			return nil, errors.New("field of type String does not have child fields")
		},
	}
	return fc, nil
@@ -15644,6 +15774,8 @@ func (ec *executionContext) fieldContext_OrganizationCursor_result(_ context.Con
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -17156,6 +17288,47 @@ func (ec *executionContext) fieldContext_QRCode_imagePath(_ context.Context, fie
	return fc, nil
}

func (ec *executionContext) _QRCode_imageUrl(ctx context.Context, field graphql.CollectedField, obj *models.QRCode) (ret graphql.Marshaler) {
	fc, err := ec.fieldContext_QRCode_imageUrl(ctx, field)
	if err != nil {
		return graphql.Null
	}
	ctx = graphql.WithFieldContext(ctx, fc)
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
		ctx = rctx // use context from middleware stack in children
		return ec.resolvers.QRCode().ImageURL(rctx, obj)
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(*string)
	fc.Result = res
	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}

func (ec *executionContext) fieldContext_QRCode_imageUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
	fc = &graphql.FieldContext{
		Object:     "QRCode",
		Field:      field,
		IsMethod:   true,
		IsResolver: true,
		Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
			return nil, errors.New("field of type String does not have child fields")
		},
	}
	return fc, nil
}

func (ec *executionContext) _QRCode_hashId(ctx context.Context, field graphql.CollectedField, obj *models.QRCode) (ret graphql.Marshaler) {
	fc, err := ec.fieldContext_QRCode_hashId(ctx, field)
	if err != nil {
@@ -17865,6 +18038,8 @@ func (ec *executionContext) fieldContext_Query_getOrganizations(ctx context.Cont
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -17977,6 +18152,8 @@ func (ec *executionContext) fieldContext_Query_getOrganization(ctx context.Conte
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -19622,6 +19799,8 @@ func (ec *executionContext) fieldContext_Query_getQRDetail(ctx context.Context,
				return ec.fieldContext_QRCode_url(ctx, field)
			case "imagePath":
				return ec.fieldContext_QRCode_imagePath(ctx, field)
			case "imageUrl":
				return ec.fieldContext_QRCode_imageUrl(ctx, field)
			case "hashId":
				return ec.fieldContext_QRCode_hashId(ctx, field)
			case "createdOn":
@@ -19931,6 +20110,8 @@ func (ec *executionContext) fieldContext_Query_getFeedFollowing(_ context.Contex
				return ec.fieldContext_Organization_slug(ctx, field)
			case "image":
				return ec.fieldContext_Organization_image(ctx, field)
			case "imageUrl":
				return ec.fieldContext_Organization_imageUrl(ctx, field)
			case "timezone":
				return ec.fieldContext_Organization_timezone(ctx, field)
			case "settings":
@@ -27577,72 +27758,105 @@ func (ec *executionContext) _Listing(ctx context.Context, sel ast.SelectionSet,
		case "id":
			out.Values[i] = ec._Listing_id(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "title":
			out.Values[i] = ec._Listing_title(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "slug":
			out.Values[i] = ec._Listing_slug(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "image":
			out.Values[i] = ec._Listing_image(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "imageUrl":
			field := field

			innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
				defer func() {
					if r := recover(); r != nil {
						ec.Error(ctx, ec.Recover(ctx, r))
					}
				}()
				res = ec._Listing_imageUrl(ctx, field, obj)
				return res
			}

			if field.Deferrable != nil {
				dfs, ok := deferred[field.Deferrable.Label]
				di := 0
				if ok {
					dfs.AddField(field)
					di = len(dfs.Values) - 1
				} else {
					dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
					deferred[field.Deferrable.Label] = dfs
				}
				dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
					return innerFunc(ctx, dfs)
				})

				// don't run the out.Concurrently() call below
				out.Values[i] = graphql.Null
				continue
			}

			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
		case "metadata":
			out.Values[i] = ec._Listing_metadata(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "isActive":
			out.Values[i] = ec._Listing_isActive(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "isDefault":
			out.Values[i] = ec._Listing_isDefault(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "tags":
			out.Values[i] = ec._Listing_tags(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "userId":
			out.Values[i] = ec._Listing_userId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "orgId":
			out.Values[i] = ec._Listing_orgId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "domainId":
			out.Values[i] = ec._Listing_domainId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "lookupName":
			out.Values[i] = ec._Listing_lookupName(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "createdOn":
			out.Values[i] = ec._Listing_createdOn(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "updatedOn":
			out.Values[i] = ec._Listing_updatedOn(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				out.Invalids++
				atomic.AddUint32(&out.Invalids, 1)
			}
		default:
			panic("unknown field " + strconv.Quote(field.Name))
@@ -28597,6 +28811,8 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "image":
			out.Values[i] = ec._Organization_image(ctx, field, obj)
		case "imageUrl":
			field := field

			innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
@@ -28605,7 +28821,7 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection
						ec.Error(ctx, ec.Recover(ctx, r))
					}
				}()
				res = ec._Organization_image(ctx, field, obj)
				res = ec._Organization_imageUrl(ctx, field, obj)
				return res
			}

@@ -29169,6 +29385,39 @@ func (ec *executionContext) _QRCode(ctx context.Context, sel ast.SelectionSet, o
			if out.Values[i] == graphql.Null {
				atomic.AddUint32(&out.Invalids, 1)
			}
		case "imageUrl":
			field := field

			innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
				defer func() {
					if r := recover(); r != nil {
						ec.Error(ctx, ec.Recover(ctx, r))
					}
				}()
				res = ec._QRCode_imageUrl(ctx, field, obj)
				return res
			}

			if field.Deferrable != nil {
				dfs, ok := deferred[field.Deferrable.Label]
				di := 0
				if ok {
					dfs.AddField(field)
					di = len(dfs.Values) - 1
				} else {
					dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
					deferred[field.Deferrable.Label] = dfs
				}
				dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
					return innerFunc(ctx, dfs)
				})

				// don't run the out.Concurrently() call below
				out.Values[i] = graphql.Null
				continue
			}

			out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
		case "hashId":
			out.Values[i] = ec._QRCode_hashId(ctx, field, obj)
			if out.Values[i] == graphql.Null {
diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls
index afd08d5..9288fb7 100644
--- a/api/graph/schema.graphqls
+++ b/api/graph/schema.graphqls
@@ -182,7 +182,8 @@ type Organization {
    orgType: OrgType!
    name: String!
    slug: String!
    image: Upload
    image: String
    imageUrl: String
    timezone: String! @access(scope: ORGS, kind: RO)
    settings: OrganizationSettings! @access(scope: ORGS, kind: RO)
    isActive: Boolean! @access(scope: ORGS, kind: RO)
@@ -295,6 +296,7 @@ type Listing {
    title: String!
    slug: String!
    image: String!
    imageUrl: String
    metadata: Metadata!
    isActive: Boolean! @access(scope: LISTS, kind: RO)
    isDefault: Boolean! @access(scope: LISTS, kind: RO)
@@ -460,6 +462,7 @@ type QRCode {
    codeType: QRCodeType!
    url: String!
    imagePath: String!
    imageUrl: String
    hashId: String! @access(scope: QRCODES, kind: RO)
    createdOn: Time!
    clicks: Int!
diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go
index b49dcde..a1584ca 100644
--- a/api/graph/schema.resolvers.go
+++ b/api/graph/schema.resolvers.go
@@ -32,7 +32,6 @@ import (
	"strings"
	"time"

	"github.com/99designs/gqlgen/graphql"
	sq "github.com/Masterminds/squirrel"
	"github.com/segmentio/ksuid"
	qrcode "github.com/yeqown/go-qrcode/v2"
@@ -93,6 +92,16 @@ func (r *domainResolver) Status(ctx context.Context, obj *models.Domain) (model.
	return model.DomainStatus(obj.Status), nil
}

// ImageURL is the resolver for the imageUrl field.
func (r *listingResolver) ImageURL(ctx context.Context, obj *models.Listing) (*string, error) {
	if obj.Image == "" {
		return nil, nil
	}
	srv := server.ForContext(ctx)
	fullURL, _ := url.JoinPath(srv.Config.MediaURL, obj.Image)
	return &fullURL, nil
}

// AddOrganization is the resolver for the addOrganization field.
func (r *mutationResolver) AddOrganization(ctx context.Context, input model.OrganizationInput) (*models.Organization, error) {
	tokenUser := oauth2.ForContext(ctx)
@@ -4987,9 +4996,14 @@ func (r *organizationResolver) OrgType(ctx context.Context, obj *models.Organiza
	return model.OrgType(obj.OrgType), nil
}

// Image is the resolver for the image field.
func (r *organizationResolver) Image(ctx context.Context, obj *models.Organization) (*graphql.Upload, error) {
	panic(fmt.Errorf("not implemented: Image - image"))
// ImageURL is the resolver for the imageUrl field.
func (r *organizationResolver) ImageURL(ctx context.Context, obj *models.Organization) (*string, error) {
	if obj.Image == "" {
		return nil, nil
	}
	srv := server.ForContext(ctx)
	fullURL, _ := url.JoinPath(srv.Config.MediaURL, obj.Image)
	return &fullURL, nil
}

// Visibility is the resolver for the visibility field.
@@ -5007,11 +5021,21 @@ func (r *qRCodeResolver) CodeType(ctx context.Context, obj *models.QRCode) (mode
	return model.QRCodeType(obj.CodeType), nil
}

// ImageURL is the resolver for the imageUrl field.
func (r *qRCodeResolver) ImageURL(ctx context.Context, obj *models.QRCode) (*string, error) {
	if obj.ImagePath == "" {
		return nil, nil
	}
	srv := server.ForContext(ctx)
	fullURL, _ := url.JoinPath(srv.Config.MediaURL, obj.ImagePath)
	return &fullURL, nil
}

// Version is the resolver for the version field.
func (r *queryResolver) Version(ctx context.Context) (*model.Version, error) {
	return &model.Version{
		Major:           0,
		Minor:           8,
		Minor:           9,
		Patch:           0,
		DeprecationDate: nil,
	}, nil
@@ -5026,6 +5050,9 @@ func (r *queryResolver) Me(ctx context.Context) (*models.User, error) {

// GetOrganizations is the resolver for the getOrganizations field.
func (r *queryResolver) GetOrganizations(ctx context.Context, input *model.GetOrganizationsInput) ([]*models.Organization, error) {
	if input == nil {
		input = &model.GetOrganizationsInput{}
	}
	tokenUser := oauth2.ForContext(ctx)
	if tokenUser == nil {
		return nil, valid.ErrAuthorization
@@ -7391,6 +7418,9 @@ func (r *Resolver) BillingSettings() BillingSettingsResolver { return &billingSe
// Domain returns DomainResolver implementation.
func (r *Resolver) Domain() DomainResolver { return &domainResolver{r} }

// Listing returns ListingResolver implementation.
func (r *Resolver) Listing() ListingResolver { return &listingResolver{r} }

// Mutation returns MutationResolver implementation.
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }

@@ -7418,6 +7448,7 @@ type auditLogResolver struct{ *Resolver }
type baseURLResolver struct{ *Resolver }
type billingSettingsResolver struct{ *Resolver }
type domainResolver struct{ *Resolver }
type listingResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type orgLinkResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
-- 
2.49.1
Applied.

To git@git.code.netlandish.com:~netlandish/links
   d4cc8e5..1fdf348  master -> master