Skip to content

Commit

Permalink
create-daml-app: Use alias templates for display names (digital-asset…
Browse files Browse the repository at this point in the history
…#12390)

* create-daml-app: Use alias templates for display names

We introduce Alias contracts to the create-daml-app Daml model and use
them to display aliases in the UI instead of party identifier strings.

CHANGELOG_BEGIN
CHANGELOG_END

* fix tests

* addressing review

* Add comments to Setup.daml

* factor out alias to option conversion

CHANGELOG_BEGIN
CHANGELOG_END

* small fix in setup.daml

* replace user dropdown with input in login screen

* fix compatibility tests

* fix alias loading flick in mainscreen

* remove canton incompatible sandbox options from daml.yaml

* fix gsg-trigger.patc
  • Loading branch information
Robin Krom authored Jan 27, 2022
1 parent 2e3ae0d commit 345e267
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 85 deletions.
13 changes: 9 additions & 4 deletions compatibility/bazel_tools/create-daml-app/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ let uiProc: ChildProcess | undefined = undefined;
// Chrome browser that we run in headless mode
let browser: Browser | undefined = undefined;

let publicParty: string | undefined = undefined;

// Function to generate unique party names for us.
// This should be replaced by the party management service once that is exposed
// in the HTTP JSON API.
Expand Down Expand Up @@ -170,6 +172,7 @@ beforeAll(async () => {
);
await waitOn({ resources: [`file:../${JSON_API_PORT_FILE_NAME}`] });

publicParty = getParty();

uiProc =
spawn(npmExeName, ["start"], {
Expand Down Expand Up @@ -206,7 +209,7 @@ test("create and look up user using ledger library", async () => {
const ledger = new Ledger({ token });
const users0 = await ledger.query(User.User);
expect(users0).toEqual([]);
const user = { username: party, following: [] };
const user = { username: party, following: [], public: publicParty };
const userContract1 = await ledger.create(User.User, user);
const userContract2 = await ledger.fetchByKey(User.User, party);
expect(userContract1).toEqual(userContract2);
Expand Down Expand Up @@ -269,9 +272,11 @@ const logout = async (page: Page) => {

// Follow a user using the text input in the follow panel.
const follow = async (page: Page, userToFollow: string) => {
await page.click(".test-select-follow-input");
await page.type(".test-select-follow-input", userToFollow);
await page.click(".test-select-follow-button");
const followInput = await page.waitForSelector('.test-select-follow-input');
await followInput.click();
await followInput.type(userToFollow);
await followInput.press('Tab');
await page.click('.test-select-follow-button');

// Wait for the request to complete, either successfully or after the error
// dialog has been handled.
Expand Down
15 changes: 11 additions & 4 deletions templates/create-daml-app-test-resources/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ let uiProc: ChildProcess | undefined = undefined;
// Chrome browser that we run in headless mode
let browser: Browser | undefined = undefined;

let publicUser: string = '';
let publicParty: string = '';

// Function to generate unique party names for us.
let nextPartyId = 1;
const getParty = async () : [string, string] => {
Expand All @@ -44,7 +47,7 @@ const getParty = async () : [string, string] => {
"id": user,
"primary_party": party,
},
"rights": [{"can_act_as": {"party": party}}]
"rights": [{"can_act_as": {"party": party}}, {"can_read_as": {"party": publicParty}}]
};
const grpcurlUserArgs = [
"-plaintext",
Expand Down Expand Up @@ -107,6 +110,8 @@ beforeAll(async () => {

console.debug("daml start API are running");

[publicUser, publicParty] = await getParty();

// Run `npm start` in another shell.
// Disable automatically opening a browser using the env var described here:
// https://github.com/facebook/create-react-app/issues/873#issuecomment-266318338
Expand Down Expand Up @@ -155,7 +160,7 @@ test('create and look up user using ledger library', async () => {
const ledger = new Ledger({token});
const users0 = await ledger.query(User.User);
expect(users0).toEqual([]);
const userPayload = {username: party, following: []};
const userPayload = {username: party, following: [], public: publicParty};
const userContract1 = await ledger.create(User.User, userPayload);
const userContract2 = await ledger.fetchByKey(User.User, party);
expect(userContract1).toEqual(userContract2);
Expand Down Expand Up @@ -212,8 +217,10 @@ const logout = async (page: Page) => {

// Follow a user using the text input in the follow panel.
const follow = async (page: Page, userToFollow: string) => {
await page.click('.test-select-follow-input');
await page.type('.test-select-follow-input', userToFollow);
const followInput = await page.waitForSelector('.test-select-follow-input');
await followInput.click();
await followInput.type(userToFollow);
await followInput.press('Enter');
await page.click('.test-select-follow-button');

// Wait for the request to complete, either successfully or after the error
Expand Down
18 changes: 13 additions & 5 deletions templates/create-daml-app-test-resources/messaging.patch
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
diff --git templates/create-daml-app/daml/User.daml templates/create-daml-app/daml/User.daml
diff --git templates/create-daml-app/daml/User.daml templates/create-daml-app-test-resources/daml/User.daml
index a1789d5..a007b95 100644
--- templates/create-daml-app/daml/User.daml
+++ templates/create-daml-app/daml/User.daml
@@ -25,3 +25,22 @@ template User with
archive self
+++ templates/create-daml-app-test-resources/daml/User.daml
@@ -27,6 +27,16 @@ template User with
create this with following = userToFollow :: following
-- FOLLOW_END
+
+ -- SENDMESSAGE_BEGIN
+ nonconsuming choice SendMessage: ContractId Message with
+ sender: Party
Expand All @@ -15,6 +15,14 @@ diff --git templates/create-daml-app/daml/User.daml templates/create-daml-app/da
+ assertMsg "Designated user must follow you back to send a message" (elem sender following)
+ create Message with sender, receiver = username, content
+ -- SENDMESSAGE_END
+
-- ALIAS_BEGIN
template Alias with
username: Party
@@ -46,3 +56,12 @@ template Alias with
archive self
create this with alias = newAlias
-- ALIAS_END
+
+-- MESSAGE_BEGIN
+template Message with
Expand Down
3 changes: 0 additions & 3 deletions templates/create-daml-app/daml.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ dependencies:
- daml-prim
- daml-stdlib
- daml-trigger
sandbox-options:
- --wall-clock-time
- --ledgerid=__PROJECT_NAME__-sandbox
start-navigator: false
codegen:
js:
Expand Down
63 changes: 43 additions & 20 deletions templates/create-daml-app/daml/Setup.daml
Original file line number Diff line number Diff line change
@@ -1,33 +1,56 @@
module Setup where

import DA.Foldable (forA_)
import DA.Optional (fromSomeNote)
import qualified DA.Text as T
import Daml.Script

import User (Alias(..))

-- | A test user for the create-daml-app network.
data TestUser = TestUser with
alias : Text
public : Party

-- | Create a public party, then create three test users.
setup : Script ()
setup = do
public <- createPublic
let aliases = ["Alice", "Bob", "Charlie"]
forA_ aliases $ \alias -> createTestUser $ TestUser alias public

-- | Create a test user.
createTestUser : TestUser -> Script Party
createTestUser TestUser{alias, public} = do
u <- getOrCreateUser alias (Some public)
let p = getPrimaryParty u
submit public $ createCmd $ Alias p alias public
pure p

-- | Create the public party.
createPublic : Script Party
createPublic = do
publicUser <- getOrCreateUser "Public" None
pure $ getPrimaryParty publicUser


let displayNames = ["Alice", "Bob", "Charlie"]
forA_ displayNames $ \displayName -> do
userId <- validateUserId $ toUserId displayName
userExists userId >>= \case
True ->
-- user already exists do nothing
pure ()
False -> do
let partyIdHint = toPartyIdHint displayName
p <- allocatePartyWithHint displayName (PartyIdHint partyIdHint)
createUser (User userId (Some p)) [CanActAs p]
pure ()

userExists : UserId -> Script Bool
userExists u = do
try do _ <- getUser u; pure True
catch UserNotFound _ -> pure False
-- | Get a user by their id. If the user doesn't exist, it is created.
getOrCreateUser : Text -> Optional Party -> Script User
getOrCreateUser alias publicM = do
userId <- validateUserId $ toUserId alias
try
getUser userId
catch
UserNotFound _ -> do
p <- allocateParty alias
let u = User userId (Some p)
createUser u $ [CanActAs p] ++ [CanReadAs public | Some public <- [publicM]]
pure u

-- | Convert a text to a valid user id.
toUserId : Text -> Text
toUserId = T.asciiToLower

-- TODO Drop hints once we have aliases.
toPartyIdHint : Text -> Text
toPartyIdHint p = "p" <> toUserId p
-- | Try to get the primary party of a user and fail if the user has no associated primary party.
getPrimaryParty : User -> Party
getPrimaryParty u = fromSomeNote ("User " <> userIdToText u.userId <> " is missing a primary party.") u.primaryParty
20 changes: 20 additions & 0 deletions templates/create-daml-app/daml/User.daml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module User where
-- MAIN_TEMPLATE_BEGIN
template User with
username: Party
public: Party
following: [Party]
where
signatory username
Expand All @@ -25,3 +26,22 @@ template User with
archive self
create this with following = userToFollow :: following
-- FOLLOW_END

-- ALIAS_BEGIN
template Alias with
username: Party
alias: Text
public: Party
where
signatory public

key (username, public): (Party, Party)
maintainer key._2

nonconsuming choice Change: ContractId Alias with
newAlias: Text
controller username
do
archive self
create this with alias = newAlias
-- ALIAS_END
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const LoginScreen: React.FC<Props> = ({onLogin}) => {
const ledger = new Ledger({token: credentials.token});
let userContract = await ledger.fetchByKey(User.User, credentials.party);
if (userContract === null) {
const user = {username: credentials.party, following: []};
const user = {username: credentials.party, following: [], public: 'public'};
userContract = await ledger.create(User.User, user);
}
onLogin(credentials);
Expand Down Expand Up @@ -62,6 +62,7 @@ const LoginScreen: React.FC<Props> = ({onLogin}) => {

const InsecureLogin: React.FC<{auth: Insecure}> = ({auth}) => {
const [username, setUsername] = React.useState('');

const handleLogin = async (event: React.FormEvent) => {
event.preventDefault();
const token = auth.makeToken(username);
Expand All @@ -74,15 +75,15 @@ const LoginScreen: React.FC<Props> = ({onLogin}) => {
await login({party: primaryParty,
token: auth.makeToken(username)});
}

return wrap(<>
{/* FORM_BEGIN */}
<Form.Input fluid
icon='user'
iconPosition='left'
placeholder='Username'
value={username}
className='test-select-username-field'
onChange={e => setUsername(e.currentTarget.value)} />
onChange={(e, {value}) => setUsername(value?.toString() ?? '')}
/>
<Button primary
fluid
className='test-select-login-button'
Expand Down
14 changes: 12 additions & 2 deletions templates/create-daml-app/ui/src/components/MainScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import React from 'react'
import { Image, Menu } from 'semantic-ui-react'
import MainView from './MainView';
import { useParty } from '@daml/react';
import {useLedger} from '@daml/react';
import {useState, useEffect} from 'react'

type Props = {
onLogout: () => void;
Expand All @@ -14,6 +15,15 @@ type Props = {
* React component for the main screen of the `App`.
*/
const MainScreen: React.FC<Props> = ({onLogout}) => {
const ledger = useLedger();
const [user, setUser] = useState('');
useEffect( () =>{
(async () => {
const u = await ledger.getUser()
setUser(u.userId);
} ) ()}
, [ledger]);

return (
<>
<Menu icon borderless>
Expand All @@ -29,7 +39,7 @@ const MainScreen: React.FC<Props> = ({onLogout}) => {
</Menu.Item>
<Menu.Menu position='right' className='test-select-main-menu'>
<Menu.Item position='right'>
You are logged in as {useParty()}.
You are logged in as {user}.
</Menu.Item>
<Menu.Item
position='right'
Expand Down
14 changes: 12 additions & 2 deletions templates/create-daml-app/ui/src/components/MainView.tsx.template
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import PartyListEdit from './PartyListEdit';
const MainView: React.FC = () => {
const username = useParty();
const myUserResult = useStreamFetchByKeys(User.User, () => [username], [username]);
const aliases = useStreamQueries(User.Alias, () => [], []);
const myUser = myUserResult.contracts[0]?.payload;
const allUsers = useStreamQueries(User.User).contracts;
// USERS_END
Expand All @@ -25,6 +26,13 @@ const MainView: React.FC = () => {
.sort((x, y) => x.username.localeCompare(y.username)),
[allUsers, username]);

// Map to translate party identifiers to aliases.
const partyToAlias = useMemo(() =>
new Map<Party, string>(aliases.contracts.map(({payload}) => [payload.username, payload.alias])),
[aliases]
);
const myUserName = aliases.loading ? 'loading ...' : partyToAlias.get(username) ?? username;

// FOLLOW_BEGIN
const ledger = useLedger();

Expand All @@ -45,20 +53,21 @@ const MainView: React.FC = () => {
<Grid.Row stretched>
<Grid.Column>
<Header as='h1' size='huge' color='blue' textAlign='center' style={{padding: '1ex 0em 0ex 0em'}}>
{myUser ? `Welcome, ${myUser.username}!` : 'Loading...'}
{myUserName ? `Welcome, ${myUserName}!` : 'Loading...'}
</Header>

<Segment>
<Header as='h2'>
<Icon name='user' />
<Header.Content>
{myUser?.username ?? 'Loading...'}
{myUserName ?? 'Loading...'}
<Header.Subheader>Users I'm following</Header.Subheader>
</Header.Content>
</Header>
<Divider />
<PartyListEdit
parties={myUser?.following ?? []}
partyToAlias={partyToAlias}
onAddParty={follow}
/>
</Segment>
Expand All @@ -74,6 +83,7 @@ const MainView: React.FC = () => {
{/* USERLIST_BEGIN */}
<UserList
users={followers}
partyToAlias={partyToAlias}
onFollow={follow}
/>
{/* USERLIST_END */}
Expand Down
Loading

0 comments on commit 345e267

Please sign in to comment.