Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add qr code switcher to the issuer node #596

Merged
merged 18 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions api_ui/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -881,16 +881,6 @@ paths:
summary: Create Authentication Link QRCode
operationId: CreateLinkQrCode
parameters:
- name: type
in: query
required: false
schema:
type: string
enum: [ raw, link ]
description: >
Type:
* `link` - (default value) Return a QR code with a link redirection to the raw content. Easier to scan.
* `raw` - Return the raw QR code. (default value)
- $ref: '#/components/parameters/id'
tags:
- Links
Expand Down Expand Up @@ -1154,19 +1144,21 @@ components:
type: string
example: https://raw.githubusercontent.com/iden3/claim-schema-vocab/main/schemas/json-ld/kyc-v3.json-ld#KYCAgeCredential


CredentialLinkQrCodeResponse:
type: object
required:
- issuer
- qrCode
- qrCodeLink
- qrCodeRaw
- sessionID
- linkID
- linkDetail
properties:
issuer:
$ref: '#/components/schemas/IssuerDescription'
qrCode:
qrCodeRaw:
type: string
qrCodeLink:
type: string
example: iden3comm://?request_uri=https%3A%2F%2Fissuer-demo.polygonid.me%2Fapi%2Fqr-store%3Fid%3Df780a169-8959-4380-9461-f7200e2ed3f4
sessionID:
Expand Down
43 changes: 7 additions & 36 deletions internal/api_ui/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 7 additions & 9 deletions internal/api_ui/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,22 +595,20 @@ func (s *Server) CreateLinkQrCode(ctx context.Context, req CreateLinkQrCodeReque
return CreateLinkQrCode500JSONResponse{N500JSONResponse{"Unexpected error while creating qr code"}}, nil
}

qrContent := createLinkQrCodeResponse.QrCode
// Backward compatibility. If the type is raw, we return the raw qr code
if req.Params.Type != nil && *req.Params.Type == CreateLinkQrCodeParamsTypeRaw {
rawQrCode, err := s.qrService.Find(ctx, createLinkQrCodeResponse.QrID)
if err != nil {
log.Error(ctx, "qr store. Finding qr", "err", err, "id", createLinkQrCodeResponse.QrID)
return CreateLinkQrCode500JSONResponse{N500JSONResponse{"error looking for qr body"}}, nil
}
qrContent = string(rawQrCode)
qrCodeRaw, err := s.qrService.Find(ctx, createLinkQrCodeResponse.QrID)
if err != nil {
log.Error(ctx, "qr store. Finding qr", "err", err, "id", createLinkQrCodeResponse.QrID)
return CreateLinkQrCode500JSONResponse{N500JSONResponse{"error looking for qr body"}}, nil
}

return CreateLinkQrCode200JSONResponse{
Issuer: IssuerDescription{
DisplayName: s.cfg.APIUI.IssuerName,
Logo: s.cfg.APIUI.IssuerLogo,
},
QrCode: qrContent,
QrCodeLink: createLinkQrCodeResponse.QrCode,
QrCodeRaw: string(qrCodeRaw),
SessionID: createLinkQrCodeResponse.SessionID,
LinkDetail: getLinkSimpleResponse(*createLinkQrCodeResponse.Link),
}, nil
Expand Down
58 changes: 12 additions & 46 deletions internal/api_ui/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4298,7 +4298,6 @@ func TestServer_CreateLinkQRCode(t *testing.T) {
type expected struct {
linkDetail Link
httpCode int
qrWithLink bool
message string
}

Expand All @@ -4320,57 +4319,27 @@ func TestServer_CreateLinkQRCode(t *testing.T) {
{
name: "Expired link",
request: CreateLinkQrCodeRequestObject{
Id: linkExpired.ID,
Params: CreateLinkQrCodeParams{Type: nil},
Id: linkExpired.ID,
},
expected: expected{
httpCode: http.StatusNotFound,
message: "error: cannot issue a credential for an expired link",
},
},
{
name: "Happy path without qr type, expecting a qr code with link",
request: CreateLinkQrCodeRequestObject{
Id: link.ID,
Params: CreateLinkQrCodeParams{Type: nil},
},
expected: expected{
linkDetail: linkDetail,
qrWithLink: true,
httpCode: http.StatusOK,
},
},
{
name: "Happy path with qr type == link",
request: CreateLinkQrCodeRequestObject{
Id: link.ID,
Params: CreateLinkQrCodeParams{Type: common.ToPointer(CreateLinkQrCodeParamsTypeLink)},
},
expected: expected{
linkDetail: linkDetail,
qrWithLink: true,
httpCode: http.StatusOK,
},
},
{
name: "Happy path with qr type == raw",
name: "Happy path",
request: CreateLinkQrCodeRequestObject{
Id: link.ID,
Params: CreateLinkQrCodeParams{Type: common.ToPointer(CreateLinkQrCodeParamsTypeRaw)},
Id: link.ID,
},
expected: expected{
linkDetail: linkDetail,
qrWithLink: false,
httpCode: http.StatusOK,
},
},
} {
t.Run(tc.name, func(t *testing.T) {
rr := httptest.NewRecorder()
apiURL := fmt.Sprintf("/v1/credentials/links/%s/qrcode", tc.request.Id.String())
if tc.request.Params.Type != nil {
apiURL = apiURL + "?type=" + string(*tc.request.Params.Type)
}

req, err := http.NewRequest(http.MethodPost, apiURL, tests.JSONBody(t, nil))
require.NoError(t, err)
Expand All @@ -4386,20 +4355,17 @@ func TestServer_CreateLinkQRCode(t *testing.T) {
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response))

realQR := protocol.AuthorizationRequestMessage{}
if tc.expected.qrWithLink {
qrLink := checkQRfetchURL(t, response.QrCode)

// Now let's fetch the original QR using the url
rr := httptest.NewRecorder()
req, err := http.NewRequest(http.MethodGet, qrLink, nil)
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusOK, rr.Code)
qrLink := checkQRfetchURL(t, response.QrCodeLink)

require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &realQR))
} else {
require.NoError(t, json.Unmarshal([]byte(response.QrCode), &realQR))
}
// Now let's fetch the original QR using the url
rr := httptest.NewRecorder()
req, err := http.NewRequest(http.MethodGet, qrLink, nil)
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusOK, rr.Code)

require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &realQR))

assert.NotNil(t, realQR.Body)
assert.Equal(t, "authentication", realQR.Body.Reason)
Expand Down
32 changes: 22 additions & 10 deletions ui/src/adapters/api/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import { z } from "zod";
import { Response, buildErrorResponse, buildSuccessResponse } from "src/adapters";
import { Message, buildAuthorizationHeader, messageParser } from "src/adapters/api";
import { credentialParser } from "src/adapters/api/credentials";
import { datetimeParser, getListParser, getStrictParser } from "src/adapters/parsers";
import {
datetimeParser,
getListParser,
getResourceParser,
getStrictParser,
} from "src/adapters/parsers";
import { Connection, Env } from "src/domain";
import { API_VERSION, QUERY_SEARCH_PARAM } from "src/utils/constants";
import { List } from "src/utils/types";
import { Resource } from "src/utils/types";

type ConnectionInput = Omit<Connection, "credentials" | "createdAt"> & {
createdAt: string;
Expand Down Expand Up @@ -52,16 +57,18 @@ export async function getConnection({
export async function getConnections({
credentials,
env,
params,
params: { max_results, page, query },
signal,
}: {
credentials: boolean;
env: Env;
params?: {
params: {
max_results?: number;
page?: number;
query?: string;
};
signal?: AbortSignal;
}): Promise<Response<List<Connection>>> {
}): Promise<Response<Resource<Connection>>> {
try {
const response = await axios({
baseURL: env.api.url,
Expand All @@ -70,17 +77,22 @@ export async function getConnections({
},
method: "GET",
params: new URLSearchParams({
...(params?.query !== undefined ? { [QUERY_SEARCH_PARAM]: params?.query } : {}),
...(query !== undefined ? { [QUERY_SEARCH_PARAM]: query } : {}),
...(credentials ? { credentials: "true" } : {}),
...(max_results !== undefined ? { max_results: max_results.toString() } : {}),
...(page !== undefined ? { page: page.toString() } : {}),
}),
signal,
url: `${API_VERSION}/connections`,
});
return buildSuccessResponse(
getListParser(connectionParser)
.transform(({ failed, successful }) => ({
failed,
successful: successful.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()),
getResourceParser(connectionParser)
.transform(({ items: { failed, successful }, meta }) => ({
items: {
failed,
successful: successful.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()),
},
meta,
}))
.parse(response.data)
);
Expand Down
Loading