forked from digital-asset/daml
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
language: add daml-react package to ts libraries (digital-asset#4259)
* language: add daml-react package to ts libraries This adds the library formerly known as `daml-react-hook` into the monorepo. We renamed it to `@daml/react`. The tests sadly don't work with bazel right now because the local imports aren't resolved correctly. Local testing with `yarn run test` works as usual. CHANGELOG_BEGIN CHANGELOG_END * address moritz comments * get rid of DAVL mentions * fix eslint warnings * Update language-support/ts/daml-react/tsconfig.json Co-Authored-By: Martin Huschenbett <martin.huschenbett@posteo.me> Co-authored-by: Martin Huschenbett <martin.huschenbett@posteo.me>
- Loading branch information
Showing
20 changed files
with
1,129 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
index.d.ts | ||
index.js | ||
index.js.map | ||
lib/ | ||
yarn.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
load("@os_info//:os_info.bzl", "is_windows") | ||
load("@build_bazel_rules_nodejs//:index.bzl", "pkg_npm") | ||
load("@npm_bazel_typescript//:index.bzl", "ts_library") | ||
load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar") | ||
load("//language-support/ts:eslint.bzl", "eslint_test") | ||
load("//language-support/ts:jest.bzl", "jest_test") | ||
load("@sdk_version//:sdk_version.bzl", "sdk_version") | ||
|
||
ts_library( | ||
name = "daml-react", | ||
srcs = glob([ | ||
"**/*.ts", | ||
"**/*.tsx", | ||
]), | ||
data = [ | ||
":LICENSE", | ||
":package.json", | ||
], | ||
module_name = "@daml/react", | ||
node_modules = "@language_support_ts_deps//:node_modules", | ||
tsconfig = ":tsconfig.json", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"//language-support/ts/daml-ledger", | ||
"//language-support/ts/daml-types", | ||
"@language_support_ts_deps//:node_modules", | ||
], | ||
) if not is_windows else None | ||
|
||
# We can't reference any files outside of the directory, hence this rule. | ||
genrule( | ||
name = "license", | ||
srcs = ["//:LICENSE"], | ||
outs = ["LICENSE"], | ||
cmd = """ | ||
cp $(location //:LICENSE) $@ | ||
""", | ||
) | ||
|
||
eslint_test( | ||
name = "lint", | ||
srcs = glob([ | ||
"**/*.ts", | ||
"**/*.tsx", | ||
]), | ||
) | ||
|
||
pkg_npm( | ||
name = "npm_package", | ||
srcs = [ | ||
":package.json", | ||
":tsconfig.json", | ||
], | ||
substitutions = {"0.0.0-SDKVERSION": sdk_version}, | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"daml-react", | ||
":license", | ||
], | ||
) if not is_windows else None | ||
|
||
# This doesn't work currently because imports of local packages aren't resolved correctly. | ||
# jest_test( | ||
# name = "test", | ||
# srcs = glob(["**/*.ts", "**/*.tsx"]), | ||
# jest_config = ":jest.config.js", | ||
# deps = [ | ||
# "//language-support/ts/daml-types", | ||
# "//language-support/ts/daml-ledger", | ||
# ], | ||
# ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import React, { useReducer, useMemo } from 'react'; | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
import { DamlLedgerContext } from './context'; | ||
import Credentials from './credentials'; | ||
import * as LedgerStore from './ledgerStore'; | ||
import Ledger from '@daml/ledger'; | ||
import { reducer } from './reducer'; | ||
|
||
type Props = { | ||
credentials: Credentials; | ||
} | ||
|
||
const DamlLedger: React.FC<Props> = (props) => { | ||
const [store, dispatch] = useReducer(reducer, LedgerStore.empty()); | ||
const state = useMemo(() => ({ | ||
store, | ||
dispatch, | ||
party: props.credentials.party, | ||
ledger: new Ledger(props.credentials.token), | ||
}), [props.credentials, store, dispatch]) | ||
return ( | ||
<DamlLedgerContext.Provider value={state}> | ||
{props.children} | ||
</DamlLedgerContext.Provider> | ||
); | ||
} | ||
|
||
export default DamlLedger; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
//SPDX-License-Identifier: Apache-2.0 | ||
|
||
import Ledger from '@daml/ledger'; | ||
import * as LedgerStore from './ledgerStore'; | ||
import React from "react"; | ||
import { Action } from "./reducer"; | ||
import { Party } from '@daml/types'; | ||
|
||
export type DamlLedgerState = { | ||
store: LedgerStore.Store; | ||
dispatch: React.Dispatch<Action>; | ||
party: Party; | ||
ledger: Ledger; | ||
} | ||
|
||
export const DamlLedgerContext = React.createContext(null as DamlLedgerState | null); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { decode } from 'jwt-simple'; | ||
|
||
export type Credentials = { | ||
party: string; | ||
token: string; | ||
ledgerId: string; | ||
} | ||
|
||
/** | ||
* Check that the party in the token matches the party of the credentials and | ||
* that the ledger ID in the token matches the given ledger id. | ||
*/ | ||
export const preCheckCredentials = ({party, token, ledgerId}: Credentials): string | null => { | ||
const decoded = decode(token, '', true); | ||
if (!decoded.ledgerId || decoded.ledgerId !== ledgerId) { | ||
return 'The password is not valid for the given ledger id.'; | ||
} | ||
if (!decoded.party || decoded.party !== party) { | ||
return 'The password is not valid for this user.'; | ||
} | ||
return null; | ||
} | ||
|
||
export default Credentials; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { Template, Choice, ContractId } from "@daml/types"; | ||
import { Query, CreateEvent } from '@daml/ledger'; | ||
import { useEffect, useMemo, useState, useContext } from "react"; | ||
import * as LedgerStore from './ledgerStore'; | ||
import * as TemplateStore from './templateStore'; | ||
import { setQueryLoading, setQueryResult, setFetchByKeyLoading, setFetchByKeyResult, addEvents } from "./reducer"; | ||
import { DamlLedgerState, DamlLedgerContext } from './context'; | ||
|
||
export const useDamlState = (): DamlLedgerState => { | ||
const state = useContext(DamlLedgerContext); | ||
if (!state) { | ||
throw Error("Trying to use DamlLedgerContext before initializing.") | ||
} | ||
return state; | ||
} | ||
|
||
export const useParty = () => { | ||
const state = useDamlState(); | ||
return state.party; | ||
} | ||
|
||
const loadQuery = async <T extends object>(state: DamlLedgerState, template: Template<T>, query: Query<T>) => { | ||
state.dispatch(setQueryLoading(template, query)); | ||
const contracts = await state.ledger.query(template, query); | ||
state.dispatch(setQueryResult(template, query, contracts)); | ||
} | ||
|
||
const emptyQueryFactory = <T extends object>(): Query<T> => ({} as Query<T>); | ||
|
||
export type QueryResult<T extends object, K> = { | ||
contracts: CreateEvent<T, K>[]; | ||
loading: boolean; | ||
} | ||
|
||
/// React Hook for a query against the `/contracts/search` endpoint of the JSON API. | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const useQuery = <T extends object, K>(template: Template<T, K>, queryFactory: () => Query<T> = emptyQueryFactory, queryDeps?: readonly any[]): QueryResult<T, K> => { | ||
const state = useDamlState(); | ||
const query = useMemo(queryFactory, queryDeps); | ||
const contracts = LedgerStore.getQueryResult(state.store, template, query); | ||
useEffect(() => { | ||
if (contracts === undefined) { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
loadQuery(state, template, query); | ||
} | ||
}, [state, template, query, contracts]); | ||
return contracts ?? TemplateStore.emptyQueryResult(); | ||
} | ||
|
||
const loadFetchByKey = async <T extends object, K>(state: DamlLedgerState, template: Template<T, K>, key: K) => { | ||
state.dispatch(setFetchByKeyLoading(template, key)); | ||
let contract; | ||
if (key === undefined) { | ||
console.error(`Calling useFetchByKey on template without a contract key: ${template}`); | ||
contract = null; | ||
} else { | ||
contract = await state.ledger.lookupByKey(template, key as K extends undefined ? never : K); | ||
} | ||
state.dispatch(setFetchByKeyResult(template, key, contract)); | ||
} | ||
|
||
export type FetchResult<T extends object, K> = { | ||
contract: CreateEvent<T, K> | null; | ||
loading: boolean; | ||
} | ||
|
||
/// React Hook for a lookup by key against the `/contracts/lookup` endpoint of the JSON API. | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export const useFetchByKey = <T extends object, K>(template: Template<T, K>, keyFactory: () => K, keyDeps?: readonly any[]): FetchResult<T, K> => { | ||
const state = useDamlState(); | ||
const key = useMemo(keyFactory, keyDeps); | ||
const contract = LedgerStore.getFetchByKeyResult(state.store, template, key); | ||
useEffect(() => { | ||
if (contract === undefined) { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
loadFetchByKey(state, template, key); | ||
} | ||
}, [state, template, key, contract]); | ||
return contract ?? TemplateStore.emptyFetchResult(); | ||
} | ||
|
||
const reloadTemplate = async <T extends object, K>(state: DamlLedgerState, template: Template<T, K>) => { | ||
const templateStore = state.store.templateStores.get(template) as TemplateStore.Store<T, K> | undefined; | ||
if (templateStore) { | ||
const queries: Query<T>[] = Array.from(templateStore.queryResults.keys()); | ||
const keys: K[] = Array.from(templateStore.fetchByKeyResults.keys()); | ||
await Promise.all([ | ||
Promise.all(queries.map(async (query) => await loadQuery(state, template, query))), | ||
Promise.all(keys.map(async (key) => await loadFetchByKey(state, template, key))), | ||
]); | ||
} | ||
} | ||
|
||
/// React Hook that returns a function to exercise a choice and a boolean | ||
/// indicator whether the exercise is currently running. | ||
export const useExercise = <T extends object, C, R>(choice: Choice<T, C, R>): [(cid: ContractId<T>, argument: C) => Promise<R>, boolean] => { | ||
const [loading, setLoading] = useState(false); | ||
const state = useDamlState(); | ||
|
||
const exercise = async (cid: ContractId<T>, argument: C) => { | ||
setLoading(true); | ||
const [result, events] = await state.ledger.exercise(choice, cid, argument); | ||
state.dispatch(addEvents(events)); | ||
setLoading(false); | ||
return result; | ||
} | ||
return [exercise, loading]; | ||
} | ||
|
||
/// React Hook that returns a function to exercise a choice and a boolean | ||
/// indicator whether the exercise is currently running. | ||
export const usePseudoExerciseByKey = <T extends object, C, R>(choice: Choice<T, C, R>): [(key: Query<T>, argument: C) => Promise<R>, boolean] => { | ||
const [loading, setLoading] = useState(false); | ||
const state = useDamlState(); | ||
|
||
const exercise = async (key: Query<T>, argument: C) => { | ||
setLoading(true); | ||
const [result, events] = await state.ledger.exerciseByKey(choice, key, argument); | ||
state.dispatch(addEvents(events)); | ||
setLoading(false); | ||
return result; | ||
} | ||
return [exercise, loading]; | ||
} | ||
|
||
/// React Hook to reload all queries currently present in the store. | ||
export const useReload = (): () => Promise<void> => { | ||
const state = useDamlState(); | ||
return async () => { | ||
const templates = Array.from(state.store.templateStores.keys()); | ||
await Promise.all(templates.map((template) => reloadTemplate(state, template))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import DamlLedger from './DamlLedger'; | ||
|
||
export default DamlLedger; | ||
|
||
export * from './hooks'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright (c) 2020 The DAML Authors. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
module.exports = { | ||
testEnvironment: "node", | ||
testMatch: [ | ||
"**/__tests__/**/*.+(ts|tsx|js)", | ||
"**/?(*.)+(spec|test).+(ts|tsx|js)" | ||
], | ||
transform: { | ||
"^.+\\.(ts|tsx)$": "ts-jest" | ||
} | ||
} | ||
|
Oops, something went wrong.