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

URO-208: orcidlink main view #210

Merged
merged 21 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0411e83
add tests and light refactoring in response to tests [URO-208]
eapearson May 3, 2024
7a2fdd8
add icons-material dependency for accordion icons [URO-208]
eapearson May 10, 2024
fd3fc8a
add orcid icon [URO-208]
eapearson May 10, 2024
5f98eff
udpate orcidlink api with multi-call endpoints [URO-208]
eapearson May 10, 2024
fe890cd
improve JSON-RPC 2.0 support [URO-208]
eapearson May 10, 2024
13cf808
add support and tests for view for linked and unlinked user [URO-208]
eapearson May 10, 2024
cf8d665
Merge remote-tracking branch 'origin/main' into URO-208
eapearson May 10, 2024
153bd48
remove material icons, use fa chevron icon instead [URO-208]
eapearson May 16, 2024
bc1a3a3
remove upstream code reference (redux-toolkit) comment [URO-208]
eapearson May 16, 2024
77c60a8
remove uiURL [URO-208]
eapearson May 16, 2024
559e0da
remove card content padding top override [URO-208]
eapearson May 16, 2024
97a6a14
remove types - one unused, one replaced with the aliased type [URO-208]
eapearson May 16, 2024
6f18524
replace non-null assertion with thrown exception [URO-208]
eapearson May 17, 2024
bd504a6
remove comments [URO-208]
eapearson May 17, 2024
56a1dc4
updated package-lock [URO-208]
eapearson May 17, 2024
95421b6
remove commented out code [URO-208]
eapearson May 17, 2024
2b511ae
remove responseHandler property [URO-208]
eapearson May 17, 2024
d108f83
remove jsonRpc2Service function [URO-208]
eapearson May 18, 2024
5a5d41f
return react fragment rather than null [URO-208]
eapearson May 18, 2024
decebf5
move orcid link render function to misc components module [URO-208]
eapearson May 18, 2024
04defd8
break render functions into standalone components [URO-208]
eapearson May 20, 2024
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
6 changes: 3 additions & 3 deletions package-lock.json
dauglyon marked this conversation as resolved.
Show resolved Hide resolved

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

17 changes: 17 additions & 0 deletions public/assets/images/ORCID-iD_icon-vector.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions src/common/api/orcidLinkCommon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
export interface ORCIDAuthPublic {
dauglyon marked this conversation as resolved.
Show resolved Hide resolved
expires_in: number;
name: string;
orcid: string;
scope: string;
}

export interface LinkRecordPublic {
created_at: number;
expires_at: number;
retires_at: number;
username: string;
orcid_auth: ORCIDAuthPublic;
}

export interface ORCIDAuthPublicNonOwner {
orcid: string;
name: string;
}

export interface LinkRecordPublicNonOwner {
username: string;
orcid_auth: ORCIDAuthPublicNonOwner;
}

// ORCID User Profile (our version)

export interface Affiliation {
name: string;
role: string;
startYear: string;
endYear: string | null;
}

export interface ORCIDFieldGroupBase {
private: boolean;
}

export interface ORCIDFieldGroupPrivate extends ORCIDFieldGroupBase {
private: true;
fields: null;
}

export interface ORCIDFieldGroupAccessible<T> extends ORCIDFieldGroupBase {
private: false;
fields: T;
}

export type ORCIDFieldGroup<T> =
| ORCIDFieldGroupPrivate
| ORCIDFieldGroupAccessible<T>;

export interface ORCIDNameFieldGroup {
firstName: string;
lastName: string | null;
creditName: string | null;
}

export interface ORCIDBiographyFieldGroup {
bio: string;
}

export interface ORCIDEmailFieldGroup {
emailAddresses: Array<string>;
}

export interface ORCIDProfile {
orcidId: string;
nameGroup: ORCIDFieldGroup<ORCIDNameFieldGroup>;
biographyGroup: ORCIDFieldGroup<ORCIDBiographyFieldGroup>;
emailGroup: ORCIDFieldGroup<ORCIDEmailFieldGroup>;
employment: Array<Affiliation>;
}
152 changes: 104 additions & 48 deletions src/common/api/orcidlinkAPI.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,67 @@
import { baseApi } from '.';
import { jsonRpcService } from './utils/serviceHelpers';
import { LinkRecordPublic, ORCIDProfile } from './orcidLinkCommon';
import { jsonRpc2Service } from './utils/serviceHelpers';

// orcidlink system types
// system info

export interface ORCIDAuthPublic {
expires_in: number;
export interface ServiceDescription {
name: string;
orcid: string;
scope: string;
title: string;
version: string;
language: string;
description: string;
repoURL: string;
}

export interface LinkRecordPublic {
created_at: number;
expires_at: number;
retires_at: number;
username: string;
orcid_auth: ORCIDAuthPublic;
export interface GitInfo {
commit_hash: string;
commit_hash_abbreviated: string;
author_name: string;
committer_name: string;
committer_date: number;
url: string;
branch: string;
tag: string | null;
}

// Method types

export interface StatusResult {
status: string;
export interface RuntimeInfo {
current_time: number;
start_time: number;
orcid_api_url: string;
orcid_oauth_url: string;
orcid_site_url: string;
}

// TODO: normalize to either kebab or underscore. Pref underscore.
dauglyon marked this conversation as resolved.
Show resolved Hide resolved
export interface InfoResult {
'service-description': {
name: string;
title: string;
version: string;
};
'service-description': ServiceDescription;
'git-info': GitInfo;
runtime_info: RuntimeInfo;
}

// is-linked
// combined api calls for initial view

export interface ORCIDLinkInitialStateResult {
isLinked: boolean;
info: InfoResult;
}

export interface IsLinkedParams {
export interface ORCIDLinkInitialStateParams {
username: string;
}

export type IsLinkedResult = boolean;
// combined api call for linked user info

// owner-link
export interface OwnerLinkParams {
username: string;
export interface ORCIDLinkLinkedUserInfoResult {
linkRecord: LinkRecordPublic;
profile: ORCIDProfile;
}

export type OwnerLinkResult = LinkRecordPublic;
export interface ORCIDLinkLinkedUserInfoParams {
username: string;
}

// It is mostly a JSONRPC 2.0 service, although the oauth flow is rest-ish.
const orcidlinkService = jsonRpcService({
const orcidlinkService = jsonRpc2Service({
url: '/services/orcidlink/api/v1',
version: '2.0',
dauglyon marked this conversation as resolved.
Show resolved Hide resolved
});
Expand All @@ -62,31 +73,76 @@ export const orcidlinkAPI = baseApi
.enhanceEndpoints({ addTagTypes: ['ORCIDLink'] })
.injectEndpoints({
endpoints: ({ query }) => ({
orcidlinkStatus: query<StatusResult, {}>({
query: () => {
return orcidlinkService({
method: 'status',
});
},
}),
orcidlinkIsLinked: query<IsLinkedResult, IsLinkedParams>({
query: ({ username }) => {
return orcidlinkService({
method: 'is-linked',
params: {
username,
orcidlinkInitialState: query<
dauglyon marked this conversation as resolved.
Show resolved Hide resolved
ORCIDLinkInitialStateResult,
ORCIDLinkInitialStateParams
>({
async queryFn({ username }, _queryApi, _extraOptions, fetchWithBQ) {
const [isLinked, info] = await Promise.all([
fetchWithBQ(
orcidlinkService({
method: 'is-linked',
params: {
username,
},
})
),
fetchWithBQ(
orcidlinkService({
method: 'info',
})
),
]);
if (isLinked.error) {
return { error: isLinked.error };
}
if (info.error) {
return { error: info.error };
}
return {
data: {
isLinked: isLinked.data as boolean,
info: info.data as InfoResult,
},
});
};
},
}),
orcidlinkOwnerLink: query<OwnerLinkResult, OwnerLinkParams>({
query: ({ username }) => {
return orcidlinkService({
method: 'owner-link',
orcidlinkLinkedUserInfo: query<
ORCIDLinkLinkedUserInfoResult,
ORCIDLinkLinkedUserInfoParams
>({
async queryFn({ username }, _queryApi, _extraOptions, fetchWithBQ) {
const profileQuery = orcidlinkService({
method: 'get-orcid-profile',
params: {
username,
},
});

const [linkRecord, profile] = await Promise.all([
fetchWithBQ(
orcidlinkService({
method: 'owner-link',
params: {
username,
},
})
),
fetchWithBQ(profileQuery),
]);
if (linkRecord.error) {
return { error: linkRecord.error };
}

if (profile.error) {
return { error: profile.error };
}
return {
data: {
linkRecord: linkRecord.data as LinkRecordPublic,
profile: profile.data as ORCIDProfile,
},
};
},
}),
}),
Expand Down
23 changes: 23 additions & 0 deletions src/common/api/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@ export const isJsonRpcError = (obj: unknown): obj is JsonRpcError => {
return false;
};

export const isJsonRpc20Error = (obj: unknown): obj is JsonRpcError => {
if (
typeof obj === 'object' &&
obj !== null &&
['jsonrpc', 'error', 'id'].every((k) => k in obj)
) {
const { jsonrpc, error } = obj as { jsonrpc: string; error: unknown };
if (jsonrpc !== '2.0') {
return false;
}
// const versionsSupported = new Set(['1.1', '2.0']);
dauglyon marked this conversation as resolved.
Show resolved Hide resolved
// if (!versionsSupported.has(version)) return false;
if (
typeof error === 'object' &&
error !== null &&
['code', 'message'].every((k) => k in error)
) {
return true;
}
}
return false;
};

/**
* Type predicate to narrow an unknown error to `FetchBaseQueryError`
*/
Expand Down
Loading
Loading