Skip to content

Commit

Permalink
[MDS-5553] (and 5542) Request a connection invitation for a permitee (#…
Browse files Browse the repository at this point in the history
…2743)

* [MDS-5553] (and 5542) Request a connection invitation for a permitee (#2732)

* better help text.

* API serves current_permittee_guid

* untested action creator

* permitee column, invitation request made successfully on modal load

* show invitation url on the screen

actionCreator is being called over and over again.

* sync common folders

* convert action creator to typescript

* only show button if mine is major mine

* add get vc invtitations endpoint, permit must be open\

* fixes

* typescript updates by Tara!

* skeleton loading by Tara

* add feature flag

* api now returns connection status with current_permitee

* pass state up to form

* pass connectionState and remove logging

* don't show button if active

* remove not working button

* don't allow null, replace with string

* common folder diff

* feature flag and remove unused import

Signed-off-by: Jason Syrotuck <[email protected]>

---------

Signed-off-by: Jason Syrotuck <[email protected]>

* make tests more likely to pass- Henry's changes

---------

Signed-off-by: Jason Syrotuck <[email protected]>
Co-authored-by: Jason Syrotuck <[email protected]>
  • Loading branch information
taraepp and Jsyro authored Oct 25, 2023
1 parent f85b86b commit 73715a6
Show file tree
Hide file tree
Showing 35 changed files with 609 additions and 83 deletions.
7 changes: 7 additions & 0 deletions services/common/src/constants/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ export enum ActivityTypeEnum {
major_mine_app_submitted = "major_mine_app_submitted",
major_mine_desc_submitted = "major_mine_desc_submitted",
}

export enum LOADING_STATUS {
none,
sent,
success,
error,
}
1 change: 1 addition & 0 deletions services/common/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ export * from "./now";
export * from "./noticeOfWork.interface";
export * from "./noticeOfWorkDraftPermit.interface";
export * from "./search/searchResult.interface";
export * from "./verifiableCredentials";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./verifiableCredentialInvitation.interface";
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IVCInvitation {
invitation: any[];
invitation_url: string;
}
1 change: 1 addition & 0 deletions services/common/src/utils/featureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum Feature {
ESUP_PERMIT_AMENDMENT = "esup_permit_amendment",
FLAGSMITH = "flagsmith",
TSF_V2 = "tsf_v2",
VERIFIABLE_CREDENTIALS = "verifiable_credentials",
}

export const initializeFlagsmith = async (flagsmithUrl, flagsmithKey) => {
Expand Down
14 changes: 14 additions & 0 deletions services/core-api/app/api/mines/permits/permit/models/permit.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ def current_permittee(self):
else:
return ""

@hybrid_property
def current_permittee_guid(self):
if len(self.permittee_appointments) > 0:
return self.permittee_appointments[0].party.party_guid
else:
return ""

@hybrid_property
def current_permittee_digital_wallet_connection_state(self):
if len(self.permittee_appointments) > 0:
return self.permittee_appointments[0].party.digital_wallet_connection_status
else:
return ""

@hybrid_property
def permit_amendments(self):
if not self._context_mine:
Expand Down
2 changes: 2 additions & 0 deletions services/core-api/app/api/mines/response_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ def format(self, value):
'permit_no': fields.String,
'permit_status_code': fields.String,
'current_permittee': fields.String,
'current_permittee_guid': fields.String,
'current_permittee_digital_wallet_connection_state': fields.String,
'project_id': fields.String,
'permit_amendments': fields.List(fields.Nested(PERMIT_AMENDMENT_MODEL)),
'remaining_static_liability': fields.Float,
Expand Down
2 changes: 2 additions & 0 deletions services/core-api/app/api/notice_of_departure/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
'permit_no': fields.String,
'permit_status_code': fields.String,
'current_permittee': fields.String,
'current_permittee_guid': fields.String,
'current_permittee_digital_wallet_connection_state':fields.String
})),
'nod_status':
fields.String(enum=NodStatus, attribute='nod_status.name'),
Expand Down
14 changes: 14 additions & 0 deletions services/core-api/app/api/parties/party/models/party.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ class Party(SoftDeleteMixin, AuditMixin, Base):
uselist=False,
remote_side=[party_guid],
foreign_keys=[organization_guid])

digital_wallet_invitations = db.relationship(
'PartyVerifiableCredentialConnection',
lazy='select',
uselist=True,
remote_side=[party_guid],
order_by='desc(PartyVerifiableCredentialConnection.connection_state)',)

@hybrid_property
def name(self):
Expand Down Expand Up @@ -105,6 +112,13 @@ def business_roles_codes(self):
if (not x.end_date or x.end_date > datetime.utcnow().date())
]

@hybrid_property
def digital_wallet_connection_status(self):
if self.digital_wallet_invitations:
return self.digital_wallet_invitations[0].connection_state # active >> invitation
else:
return None

def __repr__(self):
return '<Party %r>' % self.party_guid

Expand Down
2 changes: 1 addition & 1 deletion services/core-api/app/api/services/traction_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def create_oob_connection_invitation(self,party: Party):
"handshake_protocols": [
"did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0"
],
"my_label": f"Invitation to {str(party.party_guid)}",
"my_label": f"BC Mines - Chief Permitting Officer {Config.ENVIRONMENT_NAME}",
"use_public_did": False
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class PartyVerifiableCredentialConnection(AuditMixin, Base):


def __repr__(self):
return '<PartyVerifiableCredentialConnection party_guid=%r, connection_state=%r>' % self.party_guid, self.connection_state or "UNKNOWN"
return '<PartyVerifiableCredentialConnection party_guid=%r, connection_state=%r>' % (self.party_guid, self.connection_state or "UNKNOWN")

@classmethod
def find_by_party_guid(cls, party_guid) -> "PartyVerifiableCredentialConnection":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
from flask_restplus import Resource
from werkzeug.exceptions import NotFound
from app.extensions import api
from app.api.utils.access_decorators import requires_any_of, VIEW_ALL, MINESPACE_PROPONENT

from app.api.parties.party.models.party import Party
from app.api.verifiable_credentials.models.connection import PartyVerifiableCredentialConnection
from app.api.services.traction_service import TractionService

from app.api.verifiable_credentials.response_models import PARTY_VERIFIABLE_CREDENTIAL_CONNECTION
from app.api.utils.resources_mixins import UserMixin

class VerifiableCredentialConnectionResource(Resource, UserMixin):
@api.doc(description='Create a connection invitation for a party by guid', params={})
@requires_any_of([VIEW_ALL, MINESPACE_PROPONENT])
def post(self, party_guid: str):
#mine_guid will be param. just easy this way for development
party = Party.find_by_party_guid(party_guid)
Expand All @@ -23,4 +26,12 @@ def post(self, party_guid: str):
invitation = traction_svc.create_oob_connection_invitation(party)

return invitation


@api.doc(description='Create a connection invitation for a party by guid', params={})
@requires_any_of([VIEW_ALL, MINESPACE_PROPONENT])
@api.marshal_with(PARTY_VERIFIABLE_CREDENTIAL_CONNECTION, code=200, envelope='records')
def get(self, party_guid: str):
party_vc_conn = PartyVerifiableCredentialConnection.find_by_party_guid(party_guid=party_guid)
return party_vc_conn

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from flask_restplus import fields
from app.extensions import api

PARTY_VERIFIABLE_CREDENTIAL_CONNECTION = api.model(
'PartyVerifiableCredentialConnection', {
'invitation_id': fields.String,
'party_guid': fields.String,
'connection_id': fields.String,
'connection_state': fields.String,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ENVIRONMENT } from "@mds/common";
import { request, success, error } from "../actions/genericActions";
import * as reducerTypes from "../constants/reducerTypes";
import * as verfiableCredentialActions from "../actions/verfiableCredentialActions";
import { createRequestHeader } from "../utils/RequestHeaders";
import { showLoading, hideLoading } from "react-redux-loading-bar";
import CustomAxios from "../customAxios";
import { AppThunk } from "@/store/appThunk.type";
import { IVCInvitation } from "@mds/common";
import { AxiosResponse } from "axios";

export const createVCWalletInvitation = (
partyGuid: string
): AppThunk<Promise<AxiosResponse<IVCInvitation>>> => (
dispatch
): Promise<AxiosResponse<IVCInvitation>> => {
dispatch(showLoading("modal"));
dispatch(request(reducerTypes.CREATE_VC_WALLET_CONNECTION_INVITATION));
return CustomAxios()
.post(
`${ENVIRONMENT.apiUrl}/verifiable-credentials/oob-invitation/${partyGuid}`,
null,
createRequestHeader()
)
.then((response) => {
dispatch(success(reducerTypes.CREATE_VC_WALLET_CONNECTION_INVITATION));
dispatch(verfiableCredentialActions.storeVCConnectionInvitation(response.data));
dispatch(hideLoading("modal"));
return response;
})
.catch((err) => {
dispatch(error(reducerTypes.CREATE_VC_WALLET_CONNECTION_INVITATION));
dispatch(hideLoading("modal"));
throw new Error(err);
});
};

export const fetchVCWalletInvitations = (
partyGuid: string
): AppThunk<Promise<AxiosResponse<IVCInvitation>>> => (
dispatch
): Promise<AxiosResponse<IVCInvitation>> => {
dispatch(showLoading("modal"));
dispatch(request(reducerTypes.FETCH_VC_WALLET_CONNECTION_INVITATIONS));
return CustomAxios()
.get(
`${ENVIRONMENT.apiUrl}/verifiable-credentials/oob-invitation/${partyGuid}`,
createRequestHeader()
)
.then((response) => {
dispatch(success(reducerTypes.FETCH_VC_WALLET_CONNECTION_INVITATIONS));
dispatch(verfiableCredentialActions.storeVCConnectionInvitation(response.data));
dispatch(hideLoading("modal"));
return response;
})
.catch((err) => {
dispatch(error(reducerTypes.FETCH_VC_WALLET_CONNECTION_INVITATIONS));
dispatch(hideLoading("modal"));
throw new Error(err);
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as ActionTypes from "../constants/actionTypes";

export const storeVCConnectionInvitation = (payload) => ({
type: ActionTypes.STORE_VC_WALLET_CONNECTION_INVITATION,
payload,
});
3 changes: 3 additions & 0 deletions services/core-web/common/constants/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,6 @@ export const CLEAR_TAILINGS_STORAGE_FACILITY = "CLEAR_TAILINGS_STORAGE_FACILITY"

// Dams
export const STORE_DAM = "STORE_DAM";

// Verifiable Credentials
export const STORE_VC_WALLET_CONNECTION_INVITATION = "STORE_VC_WALLET_CONNECTION_INVITATION";
5 changes: 5 additions & 0 deletions services/core-web/common/constants/reducerTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,8 @@ export const GET_DAM = "GET_DAM";

// Alerts
export const GET_GLOBAL_ALERTS = "GET_GLOBAL_ALERTS";

//Verficable Credentials
export const VERIFIABLE_CREDENTIALS = "VERIFIABLE_CREDENTIALS";
export const CREATE_VC_WALLET_CONNECTION_INVITATION = "CREATE_VC_WALLET_CONNECTION_INVITATION";
export const FETCH_VC_WALLET_CONNECTION_INVITATIONS = "FETCH_VC_WALLET_CONNECTION_INVITATIONS";
2 changes: 2 additions & 0 deletions services/core-web/common/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import tailingsReducerObject from "./reducers/tailingsReducer";
import userReducerObject from "./reducers/userReducer";
import varianceReducerObject from "./reducers/varianceReducer";
import workInformationReducerObject from "./reducers/workInformationReducer";
import verifiableCredentialReducerObject from "./reducers/verifiableCredentialReducer";

export const complianceReducer = complianceReducerObject;
export const authenticationReducer = authenticationReducerObject;
Expand All @@ -50,3 +51,4 @@ export const noticeOfDepartureReducer = noticeOfDepartureReducerObject;
export const activityReducer = activityReducerObject;
export const tailingsReducer = tailingsReducerObject;
export const damReducer = damReducerObject;
export const verifiableCredentialReducer = verifiableCredentialReducerObject;
32 changes: 32 additions & 0 deletions services/core-web/common/reducers/verifiableCredentialReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as actionTypes from "../constants/actionTypes";
import { VERIFIABLE_CREDENTIALS } from "../constants/reducerTypes";

/**
* @file verifiableCredentialReducer.js
* all data associated with verificable credential records.
*/

const initialState = {
vcWalletConnectionInvitation: {},
};

const verifiableCredentialReducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.STORE_VC_WALLET_CONNECTION_INVITATION:
return {
...state,
vcWalletConnectionInvitation: action.payload,
};
default:
return state;
}
};

const verifiableCredentialReducerObject = {
[VERIFIABLE_CREDENTIALS]: verifiableCredentialReducer,
};

export const getVCWalletConnectionInvitation = (state) =>
state[VERIFIABLE_CREDENTIALS].vcWalletConnectionInvitation;

export default verifiableCredentialReducerObject;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as verifiableCredentialReducer from "../reducers/verifiableCredentialReducer";

export const { getVCWalletConnectionInvitation } = verifiableCredentialReducer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ENVIRONMENT } from "@mds/common";
import { request, success, error } from "../actions/genericActions";
import * as reducerTypes from "../constants/reducerTypes";
import * as verfiableCredentialActions from "../actions/verfiableCredentialActions";
import { createRequestHeader } from "../utils/RequestHeaders";
import { showLoading, hideLoading } from "react-redux-loading-bar";
import CustomAxios from "../customAxios";
import { AppThunk } from "@/store/appThunk.type";
import { IVCInvitation } from "@mds/common";
import { AxiosResponse } from "axios";

export const createVCWalletInvitation = (
partyGuid: string
): AppThunk<Promise<AxiosResponse<IVCInvitation>>> => (
dispatch
): Promise<AxiosResponse<IVCInvitation>> => {
dispatch(showLoading("modal"));
dispatch(request(reducerTypes.CREATE_VC_WALLET_CONNECTION_INVITATION));
return CustomAxios()
.post(
`${ENVIRONMENT.apiUrl}/verifiable-credentials/oob-invitation/${partyGuid}`,
null,
createRequestHeader()
)
.then((response) => {
dispatch(success(reducerTypes.CREATE_VC_WALLET_CONNECTION_INVITATION));
dispatch(verfiableCredentialActions.storeVCConnectionInvitation(response.data));
dispatch(hideLoading("modal"));
return response;
})
.catch((err) => {
dispatch(error(reducerTypes.CREATE_VC_WALLET_CONNECTION_INVITATION));
dispatch(hideLoading("modal"));
throw new Error(err);
});
};

export const fetchVCWalletInvitations = (
partyGuid: string
): AppThunk<Promise<AxiosResponse<IVCInvitation>>> => (
dispatch
): Promise<AxiosResponse<IVCInvitation>> => {
dispatch(showLoading("modal"));
dispatch(request(reducerTypes.FETCH_VC_WALLET_CONNECTION_INVITATIONS));
return CustomAxios()
.get(
`${ENVIRONMENT.apiUrl}/verifiable-credentials/oob-invitation/${partyGuid}`,
createRequestHeader()
)
.then((response) => {
dispatch(success(reducerTypes.FETCH_VC_WALLET_CONNECTION_INVITATIONS));
dispatch(verfiableCredentialActions.storeVCConnectionInvitation(response.data));
dispatch(hideLoading("modal"));
return response;
})
.catch((err) => {
dispatch(error(reducerTypes.FETCH_VC_WALLET_CONNECTION_INVITATIONS));
dispatch(hideLoading("modal"));
throw new Error(err);
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as ActionTypes from "../constants/actionTypes";

export const storeVCConnectionInvitation = (payload) => ({
type: ActionTypes.STORE_VC_WALLET_CONNECTION_INVITATION,
payload,
});
3 changes: 3 additions & 0 deletions services/minespace-web/common/constants/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,6 @@ export const CLEAR_TAILINGS_STORAGE_FACILITY = "CLEAR_TAILINGS_STORAGE_FACILITY"

// Dams
export const STORE_DAM = "STORE_DAM";

// Verifiable Credentials
export const STORE_VC_WALLET_CONNECTION_INVITATION = "STORE_VC_WALLET_CONNECTION_INVITATION";
5 changes: 5 additions & 0 deletions services/minespace-web/common/constants/reducerTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,8 @@ export const GET_DAM = "GET_DAM";

// Alerts
export const GET_GLOBAL_ALERTS = "GET_GLOBAL_ALERTS";

//Verficable Credentials
export const VERIFIABLE_CREDENTIALS = "VERIFIABLE_CREDENTIALS";
export const CREATE_VC_WALLET_CONNECTION_INVITATION = "CREATE_VC_WALLET_CONNECTION_INVITATION";
export const FETCH_VC_WALLET_CONNECTION_INVITATIONS = "FETCH_VC_WALLET_CONNECTION_INVITATIONS";
2 changes: 2 additions & 0 deletions services/minespace-web/common/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import tailingsReducerObject from "./reducers/tailingsReducer";
import userReducerObject from "./reducers/userReducer";
import varianceReducerObject from "./reducers/varianceReducer";
import workInformationReducerObject from "./reducers/workInformationReducer";
import verifiableCredentialReducerObject from "./reducers/verifiableCredentialReducer";

export const complianceReducer = complianceReducerObject;
export const authenticationReducer = authenticationReducerObject;
Expand All @@ -50,3 +51,4 @@ export const noticeOfDepartureReducer = noticeOfDepartureReducerObject;
export const activityReducer = activityReducerObject;
export const tailingsReducer = tailingsReducerObject;
export const damReducer = damReducerObject;
export const verifiableCredentialReducer = verifiableCredentialReducerObject;
Loading

0 comments on commit 73715a6

Please sign in to comment.