Merge branch 'develop' into main
This commit is contained in:
commit
0213d193e8
@ -87,6 +87,7 @@ jobs:
|
||||
REDIS_PORT: 0
|
||||
REDIS_PASSWORD: xxxxxxxxxxxx
|
||||
ADB2C_CACHE_TTL: 0
|
||||
STAGE: local
|
||||
- task: Docker@0
|
||||
displayName: build
|
||||
inputs:
|
||||
|
||||
4
dictation_client/.vscode/settings.json
vendored
4
dictation_client/.vscode/settings.json
vendored
@ -27,8 +27,8 @@
|
||||
"debug.javascript.usePreview": false,
|
||||
"editor.copyWithSyntaxHighlighting": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.fixAll.stylelint": true
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"build": "tsc && vite build --mode development",
|
||||
"build:stg": "tsc && vite build --mode staging",
|
||||
"build:prod": "tsc && vite build --mode production",
|
||||
"build:stg": "tsc && vite build --mode staging --sourcemap false",
|
||||
"build:prod": "tsc && vite build --mode production --sourcemap false",
|
||||
"build:local": "tsc && vite build --mode development && sh localdeploy.sh",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
15
dictation_client/src/common/convertUtcToLocal.ts
Normal file
15
dictation_client/src/common/convertUtcToLocal.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// UTCの日付に対してローカルのロケール+タイムゾーンを考慮して表示形式と時刻補正を行った文字列を返却する
|
||||
export const convertUtcToLocal = (
|
||||
utcDateString: string,
|
||||
formatOptions?: Intl.DateTimeFormatOptions
|
||||
): string => {
|
||||
if (Number.isNaN(Date.parse(utcDateString))) {
|
||||
// 日付文字列が未定義または無効な場合は 変換を行わない
|
||||
return utcDateString;
|
||||
}
|
||||
|
||||
const utcDate = new Date(utcDateString);
|
||||
return formatOptions
|
||||
? utcDate.toLocaleString(undefined, formatOptions)
|
||||
: utcDate.toLocaleString();
|
||||
};
|
||||
@ -51,7 +51,7 @@ export const HEADER_MENUS: {
|
||||
},
|
||||
];
|
||||
|
||||
export const HEADER_NAME = getTranslationID("common.label.headerName");
|
||||
export const HEADER_NAME = "ODMS Cloud";
|
||||
|
||||
/**
|
||||
* adminのみに表示するヘッダータブ
|
||||
|
||||
@ -74,7 +74,7 @@ const LoginedHeader: React.FC<HeaderProps> = (props: HeaderProps) => {
|
||||
<div className={styles.headerLogo}>
|
||||
<img src={logo} alt="OM System" />
|
||||
</div>
|
||||
<div className={styles.headerSub}>{t(HEADER_NAME)}</div>
|
||||
<div className={styles.headerSub}>{HEADER_NAME}</div>
|
||||
<div className={styles.headerMenu}>
|
||||
<ul>
|
||||
{filterMenus.map((x) => (
|
||||
|
||||
@ -19,7 +19,7 @@ const NotLoginHeader: React.FC<NotLoginHeaderProps> = (
|
||||
<div className={`${styles.headerLogo}`}>
|
||||
<img src={logo} alt="OM System" />
|
||||
</div>
|
||||
<p className={`${styles.headerSub}`}>{t(HEADER_NAME)}</p>
|
||||
<p className={`${styles.headerSub}`}>{HEADER_NAME}</p>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,9 +1,50 @@
|
||||
import { RootState } from "app/store";
|
||||
import { convertUtcToLocal } from "common/convertUtcToLocal";
|
||||
import { ceil, floor } from "lodash";
|
||||
import { BACKUP_POPUP_LIST_SIZE } from "./constants";
|
||||
|
||||
export const selectTasks = (state: RootState) => state.dictation.domain.tasks;
|
||||
// ミリ秒の数値をhh:mm:ss形式に変換する
|
||||
const formatMillisecondsToHHMMSS = (milliseconds: number): string => {
|
||||
const seconds = Math.floor(milliseconds / 1000);
|
||||
if (seconds < 0) {
|
||||
return "00:00:00";
|
||||
}
|
||||
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const remainingSeconds = Math.floor(seconds % 60);
|
||||
|
||||
// "0x"となるように0埋めする
|
||||
const formattedHours = String(hours).padStart(2, "0");
|
||||
const formattedMinutes = String(minutes).padStart(2, "0");
|
||||
const formattedSeconds = String(remainingSeconds).padStart(2, "0");
|
||||
|
||||
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
|
||||
};
|
||||
|
||||
export const selectTasks = (state: RootState) => {
|
||||
const { tasks } = state.dictation.domain;
|
||||
|
||||
const tasksWithLocalDate = tasks.map((task) => ({
|
||||
...task,
|
||||
|
||||
// UTCからローカルタイムゾーンに変換して詰めなおす
|
||||
audioCreatedDate: convertUtcToLocal(task.audioCreatedDate),
|
||||
audioFinishedDate: convertUtcToLocal(task.audioFinishedDate),
|
||||
audioUploadedDate: convertUtcToLocal(task.audioUploadedDate),
|
||||
transcriptionStartedDate:
|
||||
task.transcriptionStartedDate &&
|
||||
convertUtcToLocal(task.transcriptionStartedDate),
|
||||
transcriptionFinishedDate:
|
||||
task.transcriptionFinishedDate &&
|
||||
convertUtcToLocal(task.transcriptionFinishedDate),
|
||||
|
||||
// ミリ秒からhh:mm:ss形式に変換して詰めなおす
|
||||
audioDuration: formatMillisecondsToHHMMSS(parseInt(task.audioDuration, 10)),
|
||||
}));
|
||||
|
||||
return tasksWithLocalDate;
|
||||
};
|
||||
export const selectTotal = (state: RootState) => state.dictation.domain.total;
|
||||
|
||||
export const seletctLimit = (state: RootState) => state.dictation.domain.limit;
|
||||
|
||||
@ -1,8 +1,27 @@
|
||||
import { RootState } from "app/store";
|
||||
import { ceil, floor } from "lodash";
|
||||
import { convertUtcToLocal } from "common/convertUtcToLocal";
|
||||
|
||||
export const selectOrderHisory = (state: RootState) =>
|
||||
state.licenseOrderHistory.domain.orderHistories;
|
||||
export const selectOrderHisory = (state: RootState) => {
|
||||
const { orderHistories } = state.licenseOrderHistory.domain;
|
||||
|
||||
const dateOptions: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
};
|
||||
|
||||
const orderHistoriesWithLocalDate = orderHistories.map((orderHistory) => ({
|
||||
...orderHistory,
|
||||
// UTCからローカルタイムゾーンに変換して詰めなおす
|
||||
orderDate: convertUtcToLocal(orderHistory.orderDate, dateOptions),
|
||||
issueDate:
|
||||
orderHistory.issueDate &&
|
||||
convertUtcToLocal(orderHistory.issueDate, dateOptions),
|
||||
}));
|
||||
|
||||
return orderHistoriesWithLocalDate;
|
||||
};
|
||||
|
||||
export const selectCompanyName = (state: RootState) =>
|
||||
state.licenseOrderHistory.domain.companyName;
|
||||
|
||||
@ -8,8 +8,11 @@ export const selectInputValidationErrors = (state: RootState) => {
|
||||
const hasErrorEmptyAdminName = state.partner.apps.addPartner.adminName === "";
|
||||
const hasErrorEmptyEmail = state.partner.apps.addPartner.email === "";
|
||||
|
||||
const emailPattern =
|
||||
/^[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]+@[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*\.[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*[a-zA-Z]$/;
|
||||
|
||||
const hasErrorIncorrectEmail =
|
||||
(state.partner.apps.addPartner.email as string).match(/^[^@]+@[^@]+$/)
|
||||
(state.partner.apps.addPartner.email as string).match(emailPattern)
|
||||
?.length !== 1;
|
||||
|
||||
return {
|
||||
|
||||
@ -16,8 +16,10 @@ export const selectInputValidationErrors = (state: RootState) => {
|
||||
state.signup.apps.password
|
||||
);
|
||||
|
||||
const emailPattern =
|
||||
/^[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]+@[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*\.[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*[a-zA-Z]$/;
|
||||
const hasErrorIncorrectEmail =
|
||||
(state.signup.apps.email as string).match(/^[^@]+@[^@]+$/)?.length !== 1;
|
||||
(state.signup.apps.email as string).match(emailPattern)?.length !== 1;
|
||||
|
||||
return {
|
||||
hasErrorEmptyEmail,
|
||||
|
||||
@ -22,3 +22,6 @@ export const selectTermVersions = (state: RootState) => {
|
||||
};
|
||||
|
||||
export const selectTier = (state: RootState) => state.terms.domain.tier;
|
||||
|
||||
export const selectIsLoading = (state: RootState) =>
|
||||
state.terms.apps.isLoading === true;
|
||||
|
||||
@ -39,16 +39,9 @@ export const termsSlice = createSlice({
|
||||
builder.addCase(getAccountInfoMinimalAccessAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(getTermsInfoAsync.pending, (state) => {
|
||||
state.apps.isLoading = true;
|
||||
});
|
||||
builder.addCase(getTermsInfoAsync.fulfilled, (state, actions) => {
|
||||
state.apps.isLoading = false;
|
||||
state.domain.termsInfo = actions.payload.termsInfo;
|
||||
});
|
||||
builder.addCase(getTermsInfoAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(updateAcceptedVersionAsync.pending, (state) => {
|
||||
state.apps.isLoading = true;
|
||||
});
|
||||
|
||||
@ -26,7 +26,10 @@ export const selectInputValidationErrors = (state: RootState) => {
|
||||
role
|
||||
);
|
||||
|
||||
const hasErrorIncorrectEmail = email.match(/^[^@]+@[^@]+$/)?.length !== 1;
|
||||
const emailPattern =
|
||||
/^[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]+@[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*\.[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*[a-zA-Z]$/;
|
||||
|
||||
const hasErrorIncorrectEmail = email.match(emailPattern)?.length !== 1;
|
||||
|
||||
const hasErrorIncorrectEncryptionPassword =
|
||||
checkErrorIncorrectEncryptionPassword(encryptionPassword, role, encryption);
|
||||
|
||||
@ -38,7 +38,7 @@ export const FilePropertyPopup: React.FC<FilePropertyPopupProps> = (props) => {
|
||||
</p>
|
||||
<dl className={`${styles.formList} ${styles.property} ${styles.hasbg}`}>
|
||||
<dt className={styles.formTitle}>
|
||||
{t(getTranslationID("dictationPage.label.general"))}
|
||||
{t(getTranslationID("filePropertyPopup.label.general"))}
|
||||
</dt>
|
||||
<dt>{t(getTranslationID("dictationPage.label.fileName"))}</dt>
|
||||
<dd>{selectedFileTask?.fileName.replace(".zip", "") ?? ""}</dd>
|
||||
@ -93,7 +93,7 @@ export const FilePropertyPopup: React.FC<FilePropertyPopupProps> = (props) => {
|
||||
<dt>{t(getTranslationID("dictationPage.label.comment"))}</dt>
|
||||
<dd>{selectedFileTask?.comment ?? ""}</dd>
|
||||
<dt className={styles.formTitle}>
|
||||
{t(getTranslationID("dictationPage.label.job"))}
|
||||
{t(getTranslationID("filePropertyPopup.label.job"))}
|
||||
</dt>
|
||||
<dt>{t(getTranslationID("dictationPage.label.jobNumber"))}</dt>
|
||||
<dd>{selectedFileTask?.jobNumber ?? ""}</dd>
|
||||
@ -114,9 +114,9 @@ export const FilePropertyPopup: React.FC<FilePropertyPopupProps> = (props) => {
|
||||
<dt>{t(getTranslationID("dictationPage.label.transcriptionist"))}</dt>
|
||||
<dd>{selectedFileTask?.typist?.name ?? ""}</dd>
|
||||
<dd className={`${styles.full} ${styles.alignRight}`}>
|
||||
<a href="" className={`${styles.buttonText}`}>
|
||||
<a onClick={closePopup} className={`${styles.buttonText}`}>
|
||||
<img src={close} className={styles.modalTitleIcon} alt="close" />
|
||||
{t(getTranslationID("dictationPage.label.close"))}
|
||||
{t(getTranslationID("filePropertyPopup.label.close"))}
|
||||
</a>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
@ -121,9 +121,9 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
|
||||
</h1>
|
||||
</div>
|
||||
<section className={styles.license}>
|
||||
<div>
|
||||
<div className={styles.boxFlex}>
|
||||
<h2 className="">{companyName}</h2>
|
||||
<ul className={styles.menuAction}>
|
||||
<ul className={`${styles.menuAction} ${styles.box100}`}>
|
||||
<li>
|
||||
{/* 他アカウントのライセンス情報を見ている場合は、前画面に戻る用のreturnボタンを表示 */}
|
||||
{selectedRow && (
|
||||
@ -194,99 +194,133 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
<dl className={`${styles.listVertical} ${styles.marginBtm5}`}>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.totalLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.totalLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.allocatedLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.allocatedLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.reusableLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.reusableLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID("LicenseSummaryPage.label.freeLicense")
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.freeLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.expiringWithin14daysLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.expiringWithin14daysLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.issueRequesting"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.issueRequesting}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.numberOfRequesting"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.numberOfRequesting}</dd>
|
||||
<dt>
|
||||
{t(getTranslationID("LicenseSummaryPage.label.shortage"))}
|
||||
</dt>
|
||||
<dd>
|
||||
<span
|
||||
className={
|
||||
licenseSummaryInfo.shortage > 0 ? styles.isAlert : ""
|
||||
}
|
||||
>
|
||||
{licenseSummaryInfo.shortage}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID("LicenseSummaryPage.label.storageSize")
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.storageSize}GB</dd>
|
||||
<dt>
|
||||
{t(getTranslationID("LicenseSummaryPage.label.usedSize"))}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.usedSize}GB</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.storageAvailable"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>
|
||||
{licenseSummaryInfo.isStorageAvailable && (
|
||||
<img src={block} alt="" className={styles.icInTable} />
|
||||
)}
|
||||
{!licenseSummaryInfo.isStorageAvailable && (
|
||||
<img src={circle} alt="" className={styles.icInTable} />
|
||||
)}
|
||||
</dd>
|
||||
</dl>
|
||||
<div className={styles.marginRgt3}>
|
||||
<dl
|
||||
className={`${styles.listVertical} ${styles.marginBtm3}`}
|
||||
>
|
||||
<h4 className={styles.listHeader}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.licenseLabel"
|
||||
)
|
||||
)}
|
||||
</h4>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.totalLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.totalLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.freeLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.freeLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.reusableLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.reusableLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.allocatedLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.allocatedLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.expiringWithin14daysLicense"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.expiringWithin14daysLicense}</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID("LicenseSummaryPage.label.shortage")
|
||||
)}
|
||||
</dt>
|
||||
<dd>
|
||||
<span
|
||||
className={
|
||||
licenseSummaryInfo.shortage > 0
|
||||
? styles.isAlert
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{licenseSummaryInfo.shortage}
|
||||
</span>
|
||||
</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.issueRequesting"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.issueRequesting}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<dl
|
||||
className={`${styles.listVertical} ${styles.marginBtm3}`}
|
||||
>
|
||||
<h4 className={styles.listHeader}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.storageLabel"
|
||||
)
|
||||
)}
|
||||
</h4>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.storageSize"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.storageSize}GB</dd>
|
||||
<dt>
|
||||
{t(
|
||||
getTranslationID("LicenseSummaryPage.label.usedSize")
|
||||
)}
|
||||
</dt>
|
||||
<dd>{licenseSummaryInfo.usedSize}GB</dd>
|
||||
<dt className={styles.overLine}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"LicenseSummaryPage.label.storageAvailable"
|
||||
)
|
||||
)}
|
||||
</dt>
|
||||
<dd>
|
||||
{licenseSummaryInfo.isStorageAvailable && (
|
||||
<img
|
||||
src={block}
|
||||
alt=""
|
||||
className={styles.icInTable}
|
||||
/>
|
||||
)}
|
||||
{!licenseSummaryInfo.isStorageAvailable && (
|
||||
<img
|
||||
src={circle}
|
||||
alt=""
|
||||
className={styles.icInTable}
|
||||
/>
|
||||
)}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
getTermsInfoAsync,
|
||||
updateAcceptedVersionAsync,
|
||||
selectTier,
|
||||
selectIsLoading,
|
||||
selectTermVersions,
|
||||
} from "features//terms";
|
||||
import { selectLocalStorageKeyforIdToken } from "features/login";
|
||||
@ -27,7 +28,7 @@ const TermsPage: React.FC = (): JSX.Element => {
|
||||
selectLocalStorageKeyforIdToken
|
||||
);
|
||||
const tier = useSelector(selectTier);
|
||||
|
||||
const isLoading = useSelector(selectIsLoading);
|
||||
const [isCheckedEula, setIsCheckedEula] = useState(false);
|
||||
const [isCheckedPrivacyNotice, setIsCheckedPrivacyNotice] = useState(false);
|
||||
const [isCheckedDpa, setIsCheckedDpa] = useState(false);
|
||||
@ -88,7 +89,15 @@ const TermsPage: React.FC = (): JSX.Element => {
|
||||
tier,
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<h3>loading ...</h3>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
<Header />
|
||||
@ -117,7 +126,7 @@ const TermsPage: React.FC = (): JSX.Element => {
|
||||
>
|
||||
{t(getTranslationID("termsPage.label.linkOfEula"))}
|
||||
</a>
|
||||
{` ${t(getTranslationID("termsPage.label.forOdds"))}`}
|
||||
{` ${t(getTranslationID("termsPage.label.forOdms"))}`}
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
@ -150,7 +159,7 @@ const TermsPage: React.FC = (): JSX.Element => {
|
||||
getTranslationID("termsPage.label.linkOfPrivacyNotice")
|
||||
)}
|
||||
</a>
|
||||
{` ${t(getTranslationID("termsPage.label.forOdds"))}`}
|
||||
{` ${t(getTranslationID("termsPage.label.forOdms"))}`}
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
@ -185,7 +194,7 @@ const TermsPage: React.FC = (): JSX.Element => {
|
||||
>
|
||||
{t(getTranslationID("termsPage.label.linkOfDpa"))}
|
||||
</a>
|
||||
{` ${t(getTranslationID("termsPage.label.forOdds"))}`}
|
||||
{` ${t(getTranslationID("termsPage.label.forOdms"))}`}
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
"common": {
|
||||
"message": {
|
||||
"inputEmptyError": "Pflichtfeld",
|
||||
"passwordIncorrectError": "(de)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
|
||||
"emailIncorrectError": "(de)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
|
||||
"passwordIncorrectError": "Das von Ihnen eingegebene Passwort entspricht nicht den Spezifikationen. Bitte geben Sie das korrekte Passwort ein, wie in den Spezifikationen unten beschrieben.",
|
||||
"emailIncorrectError": "Das Format der E-Mail-Adresse ist ungültig. Bitte geben Sie eine gültige E-Mail-Adresse ein.",
|
||||
"internalServerError": "Verarbeitung fehlgeschlagen. Bitte versuchen Sie es später noch einmal.",
|
||||
"listEmpty": "Es gibt 0 Suchergebnisse.",
|
||||
"dialogConfirm": "Möchten Sie die Operation durchführen?",
|
||||
"success": "Erfolgreich verarbeitet",
|
||||
"displayDialog": "(de)サインアウトしてもよろしいですか?"
|
||||
"displayDialog": "Möchten Sie sich wirklich abmelden?"
|
||||
},
|
||||
"label": {
|
||||
"cancel": "Abbrechen",
|
||||
@ -17,22 +17,21 @@
|
||||
"save": "Speichern",
|
||||
"delete": "Löschen",
|
||||
"return": "zurückkehren",
|
||||
"operationInsteadOf": "(de)Operation instead of:",
|
||||
"headerName": "(de)ODMS Cloud",
|
||||
"headerAccount": "(de)Account",
|
||||
"headerUser": "(de)User",
|
||||
"headerLicense": "(de)License",
|
||||
"headerDictations": "(de)Dictations",
|
||||
"headerWorkflow": "(de)Workflow",
|
||||
"headerPartners": "(de)Partners",
|
||||
"operationInsteadOf": "Betrieb der ODMS Cloud im Auftrag von:",
|
||||
"headerAccount": "Konto",
|
||||
"headerUser": "Benutzer",
|
||||
"headerLicense": "Abonnement",
|
||||
"headerDictations": "Diktate",
|
||||
"headerWorkflow": "Arbeitsablauf",
|
||||
"headerPartners": "Partner",
|
||||
"headerSupport": "(de)Support",
|
||||
"tier1": "(de)Admin",
|
||||
"tier2": "(de)BC",
|
||||
"tier3": "(de)Distributor",
|
||||
"tier4": "(de)Dealer",
|
||||
"tier5": "(de)Customer",
|
||||
"notSelected": "(de)None",
|
||||
"signOutButton": "(de)Sign out"
|
||||
"tier1": "Admin",
|
||||
"tier2": "BC",
|
||||
"tier3": "Verteiler",
|
||||
"tier4": "Händler",
|
||||
"tier5": "Kunde",
|
||||
"notSelected": "Keine",
|
||||
"signOutButton": "Abmelden"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -49,7 +48,7 @@
|
||||
"signInButton": "Anmelden",
|
||||
"newUser": "Neuer Benutzer",
|
||||
"signUpButton": "Benutzerkonto erstellen",
|
||||
"logoAlt": "(de)OM Dictation Management System in the Cloud"
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
}
|
||||
},
|
||||
"signupPage": {
|
||||
@ -75,7 +74,7 @@
|
||||
"email": "E-Mail-Addresse",
|
||||
"password": "Passwort",
|
||||
"termsLink": "Klicken Sie hier, um die Nutzungsbedingungen zu lesen.",
|
||||
"termsLinkFor": "(de)for ODDS.",
|
||||
"termsLinkFor": "für ODMS Cloud.",
|
||||
"termsCheckBox": "Ja, ich stimme den Nutzungsbedingungen zu.",
|
||||
"createAccountButton": "Einreichen"
|
||||
}
|
||||
@ -176,11 +175,12 @@
|
||||
"freeLicense": "Anzahl ungenutzter Lizenzen",
|
||||
"expiringWithin14daysLicense": "Anzahl der Lizenzen, die innerhalb von 14 Tagen ablaufen",
|
||||
"issueRequesting": "Gesamtzahl der bestellten Lizenzen",
|
||||
"numberOfRequesting": "Gesamtzahl der Bestellungen",
|
||||
"shortage": "Mangel",
|
||||
"storageSize": "Lagerung verfügbar",
|
||||
"usedSize": "Gebrauchter Lagerung",
|
||||
"storageAvailable": "Speicher nicht verfügbar (Menge überschritten)"
|
||||
"storageAvailable": "Speicher nicht verfügbar (Menge überschritten)",
|
||||
"licenseLabel": "(de)License",
|
||||
"storageLabel": "(de)Storage"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
@ -252,10 +252,7 @@
|
||||
"fileBackup": "(de)File Backup",
|
||||
"downloadForBackup": "(de)Download for backup",
|
||||
"applications": "(de)Applications",
|
||||
"cancelDictation": "(de)Cancel Dictation",
|
||||
"general": "(de)General",
|
||||
"job": "(de)Job",
|
||||
"close": "(de)Close"
|
||||
"cancelDictation": "Transkription abbrechen"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
@ -377,29 +374,29 @@
|
||||
"workflowPage": {
|
||||
"label": {
|
||||
"title": "Arbeitsablauf",
|
||||
"addRoutingRule": "(de)Add Routing Rule",
|
||||
"editRoutingRule": "(de)Edit Routing Rule",
|
||||
"templateSetting": "(de)Template Setting",
|
||||
"worktypeIdSetting": "(de)WorktypeID Setting",
|
||||
"typistGroupSetting": "(de)Transcriptionist Group Setting",
|
||||
"addRoutingRule": "Routing-Regel hinzufügen",
|
||||
"editRoutingRule": "Routing-Regel bearbeiten",
|
||||
"templateSetting": "Vorlageneinstellung",
|
||||
"worktypeIdSetting": "Einstellung der Aufgabentypkennung",
|
||||
"typistGroupSetting": "Gruppeneinstellung für Transkriptionisten",
|
||||
"authorID": "Autoren-ID",
|
||||
"worktype": "Aufgabentypkennung",
|
||||
"worktypeOptional": "(de)Worktype ID (Optional)",
|
||||
"worktypeOptional": "Aufgabentypkennung (Optional)",
|
||||
"transcriptionist": "Transkriptionist",
|
||||
"template": "(de)Template",
|
||||
"templateOptional": "(de)Template (Optional)",
|
||||
"editRule": "(de)Edit Rule",
|
||||
"template": "Vorlage",
|
||||
"templateOptional": "Vorlage (Optional)",
|
||||
"editRule": "Regel bearbeiten",
|
||||
"selected": "Ausgewählter transkriptionist",
|
||||
"pool": "Transkriptionsliste",
|
||||
"selectAuthor": "(de)Select Author ID",
|
||||
"selectWorktypeId": "(de)Select Worktype ID",
|
||||
"selectTemplate": "(de)Select Template"
|
||||
"selectAuthor": "Autoren-ID auswählen",
|
||||
"selectWorktypeId": "Aufgabentypkennung auswählen",
|
||||
"selectTemplate": "Vorlage auswählen"
|
||||
},
|
||||
"message": {
|
||||
"selectedTypistEmptyError": "(de)Transcriptionist,TranscriptionistGroupがいないルーティングルールは保存できません。ルーティング先を1つ以上選択してください。",
|
||||
"workflowConflictError": "(de)指定したAuthorIDとWorktypeIDの組み合わせで既にルーティングルールが登録されています。他の組み合わせで登録してください。",
|
||||
"selectedTypistEmptyError": "Transkriptionist oder Transkriptionistgruppe wurde nicht ausgewählt. Bitte wählen Sie eine oder mehrere aus der Transkriptionsliste aus.",
|
||||
"workflowConflictError": "Eine Routing-Regel wurde bereits mit der angegebenen Kombination aus AuthorID und WorktypeID registriert. Bitte registrieren Sie sich mit einer anderen Kombination.",
|
||||
"inputEmptyError": "Pflichtfeld",
|
||||
"saveFailedError": "(de)ルーティングルールの保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"saveFailedError": "Die Routing-Regel konnte nicht gespeichert werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut."
|
||||
}
|
||||
},
|
||||
"typistGroupSetting": {
|
||||
@ -433,38 +430,38 @@
|
||||
"addWorktype": "Aufgabentyp hinzufügen",
|
||||
"editWorktypeId": "Aufgabentypkennung bearbeiten",
|
||||
"saveChange": "Änderungen speichern",
|
||||
"editOptionItems": "(de)Option Item",
|
||||
"itemLabel": "(de)Item label",
|
||||
"defaultValue": "(de)Default value",
|
||||
"initialValue": "(de)Initial value",
|
||||
"default": "(de)Default",
|
||||
"blank": "(de)Blank",
|
||||
"lastInput": "(de)Last Input",
|
||||
"optionItemTerms": "(de)The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
|
||||
"editOptionItems": "Optionales Attribut",
|
||||
"itemLabel": "Artikeletiketten",
|
||||
"defaultValue": "Standardwert",
|
||||
"initialValue": "Anfangswert",
|
||||
"default": "Standard",
|
||||
"blank": "Leer",
|
||||
"lastInput": "Letzte Werteingabe",
|
||||
"optionItemTerms": "Die Artikeletiketten und der Anfangswert können alphanumerische Zeichen und Symbole enthalten. Die folgenden Symbole können nicht verwendet werden:\\/:*?\"<>|."
|
||||
},
|
||||
"message": {
|
||||
"worktypeIdIncorrectError": "Die von Ihnen eingegebene Aufgabentypkennung entspricht nicht den Spezifikationen. Bitte geben Sie den korrekten Arbeitstyp ein, wie in den Spezifikationen unten beschrieben.",
|
||||
"alreadyWorktypeIdExistError": "Diese Aufgabentypkennung ist derzeit registriert. Bitte registrieren Sie sich mit einer anderen Worktype-ID.",
|
||||
"worktypeIDLimitError": "Die Aufgabentypkennung kann nicht hinzugefügt werden, da die maximale Anzahl an Registrierungen erreicht wurde.",
|
||||
"optionItemInvalidError": "(de)Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
|
||||
"worktypeIdAlreadyDeletedError": "(de)WorktypeIDは既に削除されています。画面を更新し、再度ご確認ください",
|
||||
"optionItemSaveFailedError": "(de)オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"optionItemIncorrectError": "(de)入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
|
||||
"updateActiveWorktypeFailedError": "(de)Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"worktypeInUseError": "(de)このWorktype IDはルーティングルールで使用されているため削除できません。",
|
||||
"updateWorktypeFailedError": "(de)WorktypeIDの保存に失敗しました。画面を更新し、再度ご確認ください"
|
||||
"optionItemInvalidError": "Die Einstellung des Anfangswerts wurde nicht abgeschlossen. Bitte legen Sie den Anfangswert fest oder ändern Sie den Standardwert.",
|
||||
"worktypeIdAlreadyDeletedError": "Aufgabentypkennung wurde bereits gelöscht. Bitte aktualisieren Sie Ihren Bildschirm und überprüfen Sie es erneut.",
|
||||
"optionItemSaveFailedError": "Optionales Attribut konnte nicht gespeichert werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut.",
|
||||
"optionItemIncorrectError": "Die eingegebene Artikeletiketten oder der Anfangswert entspricht nicht den Regeln. Bitte geben Sie einen Wert ein, der den folgenden Regeln entspricht.",
|
||||
"updateActiveWorktypeFailedError": "Die aktive Aufgabentypkennung konnte nicht gespeichert werden. Bitte aktualisieren Sie Ihren Bildschirm und überprüfen Sie es erneut.",
|
||||
"worktypeInUseError": "Diese Aufgabentypkennung kann nicht gelöscht werden, da sie derzeit in einer Routing-Regel verwendet wird.",
|
||||
"updateWorktypeFailedError": "Aufgabentypkennung konnte nicht gespeichert werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut."
|
||||
}
|
||||
},
|
||||
"templateFilePage": {
|
||||
"label": {
|
||||
"title": "(de)Template List",
|
||||
"addTemplate": "(de)Add Template",
|
||||
"fileName": "(de)Flie Name",
|
||||
"chooseFile": "(de)Choose File",
|
||||
"notFileChosen": "(de)- Not file chosen -",
|
||||
"fileSizeTerms": "(de)ファイルサイズは5MBまでです。",
|
||||
"fileSizeError": "(de)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "(de)ファイル選択は必須です。ファイルを選択してください。"
|
||||
"title": "Vorlagenliste",
|
||||
"addTemplate": "Vorlage hinzufügen",
|
||||
"fileName": "Dateiname",
|
||||
"chooseFile": "Datei aussuchen",
|
||||
"notFileChosen": "- Keine Datei ausgewählt -",
|
||||
"fileSizeTerms": "Die maximale Dateigröße, die gespeichert werden kann, beträgt 5 MB.",
|
||||
"fileSizeError": "Die ausgewählte Dateigröße ist zu groß. Bitte wählen Sie eine Datei mit einer Größe von 5 MB oder weniger aus.",
|
||||
"fileEmptyError": "Dateiauswahl ist erforderlich. Bitte wählen Sie eine Datei aus."
|
||||
}
|
||||
},
|
||||
"partnerPage": {
|
||||
@ -473,7 +470,7 @@
|
||||
"addAccount": "Konto hinzufügen",
|
||||
"name": "Name der Firma",
|
||||
"category": "Kontoebene",
|
||||
"accountId": "Autoren-ID",
|
||||
"accountId": "Konto-ID",
|
||||
"country": "Land",
|
||||
"primaryAdmin": "Hauptadministrator",
|
||||
"email": "Email",
|
||||
@ -482,61 +479,60 @@
|
||||
"deleteAccount": "Konto löschen"
|
||||
},
|
||||
"message": {
|
||||
"delegateNotAllowedError": "(de)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
|
||||
"deleteFailedError": "(de)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
|
||||
"delegateCancelError": "(de)代行操作の許可が取り消されたため、代行操作を終了しました。"
|
||||
"delegateNotAllowedError": "Aktionen im Namen des Partners sind nicht zulässig. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.",
|
||||
"deleteFailedError": "Der Delegierungsvorgang ist fehlgeschlagen. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.",
|
||||
"delegateCancelError": "Der delegierte Vorgang wurde beendet, da die Berechtigung für den delegierten Vorgang widerrufen wurde."
|
||||
}
|
||||
},
|
||||
"accountPage": {
|
||||
"label": {
|
||||
"title": "(de)Account",
|
||||
"fileDeleteSetting": "(de)File Delete Setting",
|
||||
"accountInformation": "(de)Account Information",
|
||||
"companyName": "(de)Company Name",
|
||||
"accountID": "(de)Account ID",
|
||||
"yourCategory": "(de)Your Category",
|
||||
"yourCountry": "(de)Your Country",
|
||||
"yourDealer": "(de)Your Dealer(Upper layer)",
|
||||
"selectDealer": "(de)Select Dealer",
|
||||
"dealerManagement": "(de)Dealer Management",
|
||||
"administratorInformation": "(de)Administrator Information",
|
||||
"primaryAdministrator": "(de)Primary Administrator",
|
||||
"secondaryAdministrator": "(de)Secondary Administrator",
|
||||
"emailAddress": "(de)E-mail address",
|
||||
"selectSecondaryAdministrator": "(de)Select Secondary Administrator",
|
||||
"saveChanges": "(de)Save Changes",
|
||||
"deleteAccount": "(de)Delete Account"
|
||||
"title": "Konto",
|
||||
"fileDeleteSetting": "Einstellung zum Löschen von Dateien",
|
||||
"accountInformation": "Kontoinformationen",
|
||||
"companyName": "Name der Firma",
|
||||
"accountID": "Konto-ID",
|
||||
"yourCategory": "Konto Typ",
|
||||
"yourCountry": "Land",
|
||||
"yourDealer": "Händler",
|
||||
"selectDealer": "Händler auswählen",
|
||||
"dealerManagement": "Erlauben Sie dem Händler, Änderungen vorzunehmen",
|
||||
"administratorInformation": "Administratorinformationen",
|
||||
"primaryAdministrator": "Hauptadministrator",
|
||||
"secondaryAdministrator": "Zweiter Administrator",
|
||||
"emailAddress": "E-Mail-Addresse",
|
||||
"selectSecondaryAdministrator": "Sekundäradministrator auswählen",
|
||||
"saveChanges": "Änderungen speichern",
|
||||
"deleteAccount": "Konto löschen"
|
||||
},
|
||||
"message": {
|
||||
"updateAccountFailedError": "(de)アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"updateAccountFailedError": "Kontoinformationen konnten nicht gespeichert werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut."
|
||||
}
|
||||
},
|
||||
"deleteAccountPopup": {
|
||||
"label": {
|
||||
"title": "(de)Delete Account",
|
||||
"subTitle": "(de)Delete your account?",
|
||||
"cautionOfDeleteingAccountData": "(de)Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
|
||||
"deleteButton": "(de)Delete account",
|
||||
"cancelButton": "(de)Cancel"
|
||||
"title": "Konto löschen",
|
||||
"subTitle": "Lösche deinen Konto?",
|
||||
"cautionOfDeleteingAccountData": "Durch das Löschen Ihres Kontos werden alle Benutzerinformationen, Lizenzen und Diktatdateien aus dem System entfernt. Gelöschte Informationen können nicht wiederhergestellt werden.",
|
||||
"deleteButton": "Konto löschen",
|
||||
"cancelButton": "Abbrechen"
|
||||
}
|
||||
},
|
||||
"accountDeleteSuccess": {
|
||||
"label": {
|
||||
"title": "(de)Account Delete Success",
|
||||
"message": "(de)Your account has been deleted. Thank you for using our services.",
|
||||
"backToTopPageLink": "(de)Back to TOP Page"
|
||||
"title": "Kontolöschung erfolgreich",
|
||||
"message": "Dein Account wurde gelöscht. Vielen Dank, dass Sie die ODMS Cloud nutzen.",
|
||||
"backToTopPageLink": "Zurück zur Startseite"
|
||||
}
|
||||
},
|
||||
"termsPage": {
|
||||
"label": {
|
||||
"title": "(de)Terms of Use has updated. Please confirm again.",
|
||||
"linkOfEula": "(de)Click here to read the terms of use.",
|
||||
"linkOfDpa": "(de)Click here to read the terms of use.",
|
||||
"linkOfPrivacyNotice": "(de)Click here to read the terms of use.",
|
||||
"checkBoxForConsent": "(de)Yes, I agree to the terms of use.",
|
||||
"forOdds": "(de)for ODDS.",
|
||||
"button": "(de)Continue",
|
||||
"linkOfPrivacyNotice": "(de)Click here to read the terms of use."
|
||||
"title": "Die Nutzungsbedingungen wurden aktualisiert. Für die weitere Nutzung der ODMS Cloud ist eine erneute Bestätigung erforderlich.",
|
||||
"linkOfEula": "Klicken Sie hier, um die Endbenutzer-Lizenzvereinbarung zu lesen.",
|
||||
"linkOfDpa": "Klicken Sie hier, um die Datenverarbeitungsvereinbarung zu lesen.",
|
||||
"checkBoxForConsent": "Ja, ich stimme den Nutzungsbedingungen zu.",
|
||||
"forOdms": "für ODMS Cloud.",
|
||||
"button": "Fortsetzen",
|
||||
"linkOfPrivacyNotice": "Klicken Sie hier, um die Datenschutzerklärung zu lesen."
|
||||
}
|
||||
},
|
||||
"supportPage": {
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
"common": {
|
||||
"message": {
|
||||
"inputEmptyError": "Mandatory Field",
|
||||
"passwordIncorrectError": "入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
|
||||
"emailIncorrectError": "メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
|
||||
"passwordIncorrectError": "The password you entered does not meet specifications. Please enter the correct password as outlined in the specifications below.",
|
||||
"emailIncorrectError": "The format of the e-mail address is not valid. Please enter a valid email address.",
|
||||
"internalServerError": "Processing failed. Please try again later. ",
|
||||
"listEmpty": "There are 0 search results.",
|
||||
"dialogConfirm": "Do you want to perform the operation?",
|
||||
"success": "Successfully Processed",
|
||||
"displayDialog": "サインアウトしてもよろしいですか?"
|
||||
"displayDialog": "Are you sure you want to sign out?"
|
||||
},
|
||||
"label": {
|
||||
"cancel": "Cancel",
|
||||
@ -17,11 +17,10 @@
|
||||
"save": "Save",
|
||||
"delete": "Delete",
|
||||
"return": "Return",
|
||||
"operationInsteadOf": "Operation instead of:",
|
||||
"headerName": "ODMS Cloud",
|
||||
"operationInsteadOf": "Operating the ODMS Cloud on behalf of:",
|
||||
"headerAccount": "Account",
|
||||
"headerUser": "User",
|
||||
"headerLicense": "License",
|
||||
"headerLicense": "Subscription",
|
||||
"headerDictations": "Dictations",
|
||||
"headerWorkflow": "Workflow",
|
||||
"headerPartners": "Partners",
|
||||
@ -75,7 +74,7 @@
|
||||
"email": "Email Address",
|
||||
"password": "Password",
|
||||
"termsLink": "Click here to read the terms of use",
|
||||
"termsLinkFor": "for ODDS.",
|
||||
"termsLinkFor": "for OMDS Cloud.",
|
||||
"termsCheckBox": "Yes, I agree to the terms of use.",
|
||||
"createAccountButton": "Submit"
|
||||
}
|
||||
@ -176,11 +175,12 @@
|
||||
"freeLicense": "Number of unused licenses",
|
||||
"expiringWithin14daysLicense": "Number of licenses expiring within 14 days",
|
||||
"issueRequesting": "Total number of licenses on order",
|
||||
"numberOfRequesting": "Total number of orders",
|
||||
"shortage": "Shortage",
|
||||
"storageSize": "Storage Available",
|
||||
"usedSize": "Storage Used",
|
||||
"storageAvailable": "Storage Unavailable (Exceeded Amount)"
|
||||
"storageAvailable": "Storage Unavailable (Exceeded Amount)",
|
||||
"licenseLabel": "License",
|
||||
"storageLabel": "Storage"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
@ -252,10 +252,7 @@
|
||||
"fileBackup": "File Backup",
|
||||
"downloadForBackup": "Download for backup",
|
||||
"applications": "Applications",
|
||||
"cancelDictation": "Cancel Dictation",
|
||||
"general": "General",
|
||||
"job": "Job",
|
||||
"close": "Close"
|
||||
"cancelDictation": "Cancel Transcription"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
@ -380,7 +377,7 @@
|
||||
"addRoutingRule": "Add Routing Rule",
|
||||
"editRoutingRule": "Edit Routing Rule",
|
||||
"templateSetting": "Template Setting",
|
||||
"worktypeIdSetting": "WorktypeID Setting",
|
||||
"worktypeIdSetting": "Worktype ID Setting",
|
||||
"typistGroupSetting": "Transcriptionist Group Setting",
|
||||
"authorID": "Author ID",
|
||||
"worktype": "Worktype ID",
|
||||
@ -396,10 +393,10 @@
|
||||
"selectTemplate": "Select Template"
|
||||
},
|
||||
"message": {
|
||||
"selectedTypistEmptyError": "Transcriptionist,TranscriptionistGroupがいないルーティングルールは保存できません。ルーティング先を1つ以上選択してください。",
|
||||
"workflowConflictError": "指定したAuthorIDとWorktypeIDの組み合わせで既にルーティングルールが登録されています。他の組み合わせで登録してください。",
|
||||
"selectedTypistEmptyError": "Transcriptionist, or Transcriptionist Group has not been selected. Please select one or more from the Transcription List.",
|
||||
"workflowConflictError": "A routing rule has already been registered with the specified AuthorID and WorktypeID combination. Please register with different combination.",
|
||||
"inputEmptyError": "Mandatory Field",
|
||||
"saveFailedError": "ルーティングルールの保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"saveFailedError": "Failed to save the routing rule. Please refresh the screen and try again."
|
||||
}
|
||||
},
|
||||
"typistGroupSetting": {
|
||||
@ -434,37 +431,37 @@
|
||||
"editWorktypeId": "Edit Worktype ID",
|
||||
"saveChange": "Save Changes",
|
||||
"editOptionItems": "Option Item",
|
||||
"itemLabel": "Item label",
|
||||
"itemLabel": "Item labels",
|
||||
"defaultValue": "Default value",
|
||||
"initialValue": "Initial value",
|
||||
"default": "Default",
|
||||
"blank": "Blank",
|
||||
"lastInput": "Last Input",
|
||||
"optionItemTerms": "The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
|
||||
"lastInput": "Last input value",
|
||||
"optionItemTerms": "The Item label and Initial value can contain alphanumeric and symbols. The following symbols cannot be used:\\/:*?\"<>|."
|
||||
},
|
||||
"message": {
|
||||
"worktypeIdIncorrectError": "The Worktype ID you entered does not meet specifications. Please enter the correct Worktype as outlined in the specifications below.",
|
||||
"alreadyWorktypeIdExistError": "This Worktype ID is currently registered. Please register with a different Worktype ID.",
|
||||
"worktypeIDLimitError": "Worktype ID cannot be added because it has reached the maximum number of registrations.",
|
||||
"optionItemInvalidError": "Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
|
||||
"worktypeIdAlreadyDeletedError": "WorktypeIDは既に削除されています。画面を更新し、再度ご確認ください",
|
||||
"optionItemSaveFailedError": "オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"optionItemIncorrectError": "入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
|
||||
"updateActiveWorktypeFailedError": "Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"worktypeInUseError": "このWorktype IDはルーティングルールで使用されているため削除できません。",
|
||||
"updateWorktypeFailedError": "WorktypeIDの保存に失敗しました。画面を更新し、再度ご確認ください"
|
||||
"optionItemInvalidError": "Initial value setting has not been completed. Please set the Initial value or change the Default value.",
|
||||
"worktypeIdAlreadyDeletedError": "WorktypeID has already been deleted. Please refresh your screen and check again.",
|
||||
"optionItemSaveFailedError": "Failed to save Option Item. Please refresh the screen and try again.",
|
||||
"optionItemIncorrectError": "The entered Item label or Initial value does not meet the rules. Please enter a value that meets the rules below.",
|
||||
"updateActiveWorktypeFailedError": "Failed to save Active WorktypeID. Please refresh your screen and check again.",
|
||||
"worktypeInUseError": "This Worktype ID cannot be deleted because it is currently being used in a routing rule.",
|
||||
"updateWorktypeFailedError": "Failed to save WorktypeID. Please refresh the screen and try again."
|
||||
}
|
||||
},
|
||||
"templateFilePage": {
|
||||
"label": {
|
||||
"title": "Template List",
|
||||
"addTemplate": "Add Template",
|
||||
"fileName": "Flie Name",
|
||||
"chooseFile": "Choose File",
|
||||
"notFileChosen": "- Not file chosen -",
|
||||
"fileSizeTerms": "ファイルサイズは5MBまでです。",
|
||||
"fileSizeError": "選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "ファイル選択は必須です。ファイルを選択してください。"
|
||||
"fileName": "File Name",
|
||||
"chooseFile": "Select file",
|
||||
"notFileChosen": "- No file selected -",
|
||||
"fileSizeTerms": "The maximum file size that can be saved is 5MB.",
|
||||
"fileSizeError": "The selected file size is too large. Please select a file that is 5MB or less in size.",
|
||||
"fileEmptyError": "File selection is required. Please select a file."
|
||||
}
|
||||
},
|
||||
"partnerPage": {
|
||||
@ -482,9 +479,9 @@
|
||||
"deleteAccount": "Delete Account"
|
||||
},
|
||||
"message": {
|
||||
"delegateNotAllowedError": "パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
|
||||
"deleteFailedError": "代行操作に失敗しました。画面を更新し、再度ご確認ください。",
|
||||
"delegateCancelError": "代行操作の許可が取り消されたため、代行操作を終了しました。"
|
||||
"delegateNotAllowedError": "Actions on behalf of partner are not allowed. Please refresh the screen and check again.",
|
||||
"deleteFailedError": "Delegate operation failed. Please refresh the screen and check again.",
|
||||
"delegateCancelError": "The delegated operation has been terminated because permission for the delegated operation has been revoked."
|
||||
}
|
||||
},
|
||||
"accountPage": {
|
||||
@ -494,49 +491,48 @@
|
||||
"accountInformation": "Account Information",
|
||||
"companyName": "Company Name",
|
||||
"accountID": "Account ID",
|
||||
"yourCategory": "Your Category",
|
||||
"yourCountry": "Your Country",
|
||||
"yourDealer": "Your Dealer(Upper layer)",
|
||||
"yourCategory": "Account Type",
|
||||
"yourCountry": "Country",
|
||||
"yourDealer": "Dealer",
|
||||
"selectDealer": "Select Dealer",
|
||||
"dealerManagement": "Dealer Management",
|
||||
"administratorInformation": "Administrator Information",
|
||||
"primaryAdministrator": "Primary Administrator",
|
||||
"primaryAdministrator": "Primary administrator",
|
||||
"secondaryAdministrator": "Secondary Administrator",
|
||||
"emailAddress": "E-mail address",
|
||||
"emailAddress": "Email Address",
|
||||
"selectSecondaryAdministrator": "Select Secondary Administrator",
|
||||
"saveChanges": "Save Changes",
|
||||
"deleteAccount": "Delete Account"
|
||||
},
|
||||
"message": {
|
||||
"updateAccountFailedError": "アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"updateAccountFailedError": "Failed to save account information. Please refresh the screen and try again."
|
||||
}
|
||||
},
|
||||
"deleteAccountPopup": {
|
||||
"label": {
|
||||
"title": "Delete Account",
|
||||
"subTitle": "Delete your account?",
|
||||
"cautionOfDeleteingAccountData": "Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
|
||||
"deleteButton": "Delete account",
|
||||
"cautionOfDeleteingAccountData": "Deleting your account will remove all user information, licenses, and dictation files from the system. Deleted information cannot be restored.",
|
||||
"deleteButton": "Delete Account",
|
||||
"cancelButton": "Cancel"
|
||||
}
|
||||
},
|
||||
"accountDeleteSuccess": {
|
||||
"label": {
|
||||
"title": "Account Delete Success",
|
||||
"message": "Your account has been deleted. Thank you for using our services.",
|
||||
"message": "Your account has been deleted. Thank you for using the ODMS Cloud.",
|
||||
"backToTopPageLink": "Back to TOP Page"
|
||||
}
|
||||
},
|
||||
"termsPage": {
|
||||
"label": {
|
||||
"title": "Terms of Use has updated. Please confirm again.",
|
||||
"linkOfEula": "Click here to read the terms of use.",
|
||||
"linkOfDpa": "Click here to read the terms of use.",
|
||||
"linkOfPrivacyNotice": "Click here to read the terms of use.",
|
||||
"title": "Terms of Use have been updated. Reconfirmation is required to continue using the ODMS Cloud.",
|
||||
"linkOfEula": "Click here to read the End User License Agreement.",
|
||||
"linkOfDpa": "Click here to read the Data Processing Agreement.",
|
||||
"checkBoxForConsent": "Yes, I agree to the terms of use.",
|
||||
"forOdds": "for ODDS.",
|
||||
"forOdms": "for ODMS Cloud.",
|
||||
"button": "Continue",
|
||||
"linkOfPrivacyNotice": "Click here to read the terms of use."
|
||||
"linkOfPrivacyNotice": "Click here to read the Privacy Notice."
|
||||
}
|
||||
},
|
||||
"supportPage": {
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
"common": {
|
||||
"message": {
|
||||
"inputEmptyError": "Campo obligatorio",
|
||||
"passwordIncorrectError": "(es)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
|
||||
"emailIncorrectError": "(es)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
|
||||
"passwordIncorrectError": "La contraseña que ingresó no cumple con las especificaciones. Ingrese la contraseña correcta como se describe en las especificaciones a continuación.",
|
||||
"emailIncorrectError": "El formato de la dirección de correo electrónico no es válido. Por favor, introduce una dirección de correo electrónico válida.",
|
||||
"internalServerError": "El procesamiento falló. Por favor, inténtelo de nuevo más tarde.",
|
||||
"listEmpty": "Hay 0 resultados de búsqueda.",
|
||||
"dialogConfirm": "¿Quieres realizar la operación?",
|
||||
"success": "Procesado con éxito",
|
||||
"displayDialog": "(es)サインアウトしてもよろしいですか?"
|
||||
"displayDialog": "¿Estás seguro de que quieres cerrar sesión?"
|
||||
},
|
||||
"label": {
|
||||
"cancel": "Cancelar",
|
||||
@ -17,22 +17,21 @@
|
||||
"save": "Ahorrar",
|
||||
"delete": "Delete",
|
||||
"return": "Devolver",
|
||||
"operationInsteadOf": "(es)Operation instead of:",
|
||||
"headerName": "(es)ODMS Cloud",
|
||||
"headerAccount": "(es)Account",
|
||||
"headerUser": "(es)User",
|
||||
"headerLicense": "(es)License",
|
||||
"headerDictations": "(es)Dictations",
|
||||
"headerWorkflow": "(es)Workflow",
|
||||
"headerPartners": "(es)Partners",
|
||||
"operationInsteadOf": "Operar la nube ODMS en nombre de:",
|
||||
"headerAccount": "Cuenta",
|
||||
"headerUser": "Usuario",
|
||||
"headerLicense": "Suscripción",
|
||||
"headerDictations": "Dictado",
|
||||
"headerWorkflow": "flujo de trabajo",
|
||||
"headerPartners": "Socios",
|
||||
"headerSupport": "(es)Support",
|
||||
"tier1": "(es)Admin",
|
||||
"tier2": "(es)BC",
|
||||
"tier3": "(es)Distributor",
|
||||
"tier4": "(es)Dealer",
|
||||
"tier5": "(es)Customer",
|
||||
"notSelected": "(es)None",
|
||||
"signOutButton": "(es)Sign out"
|
||||
"tier1": "Admin",
|
||||
"tier2": "BC",
|
||||
"tier3": "Distribuidor",
|
||||
"tier4": "Concesionario",
|
||||
"tier5": "Cliente",
|
||||
"notSelected": "Ninguno",
|
||||
"signOutButton": "cerrar sesión"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -49,7 +48,7 @@
|
||||
"signInButton": "Iniciar sesión",
|
||||
"newUser": "Nuevo usuario",
|
||||
"signUpButton": "Crear una cuenta",
|
||||
"logoAlt": "(es)OM Dictation Management System in the Cloud"
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
}
|
||||
},
|
||||
"signupPage": {
|
||||
@ -75,7 +74,7 @@
|
||||
"email": "Dirección de correo electrónico",
|
||||
"password": "Contraseña",
|
||||
"termsLink": "Haga clic aquí para leer el término de uso.",
|
||||
"termsLinkFor": "(es)for ODDS.",
|
||||
"termsLinkFor": "para la nube ODMS.",
|
||||
"termsCheckBox": "Sí, estoy de acuerdo con los términos de uso.",
|
||||
"createAccountButton": "Entregar"
|
||||
}
|
||||
@ -176,11 +175,12 @@
|
||||
"freeLicense": "Número de licencias sin usar",
|
||||
"expiringWithin14daysLicense": "Número de licencias que vencen en 14 días",
|
||||
"issueRequesting": "Número total de licencias en pedido",
|
||||
"numberOfRequesting": "Número total de pedidos",
|
||||
"shortage": "Escasez",
|
||||
"storageSize": "Almacenamiento disponible",
|
||||
"usedSize": "Almacenamiento utilizado",
|
||||
"storageAvailable": "Almacenamiento no disponible (cantidad excedida)"
|
||||
"storageAvailable": "Almacenamiento no disponible (cantidad excedida)",
|
||||
"licenseLabel": "(es)License",
|
||||
"storageLabel": "(es)Storage"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
@ -252,10 +252,7 @@
|
||||
"fileBackup": "(es)File Backup",
|
||||
"downloadForBackup": "(es)Download for backup",
|
||||
"applications": "(es)Applications",
|
||||
"cancelDictation": "(es)Cancel Dictation",
|
||||
"general": "(es)General",
|
||||
"job": "(es)Job",
|
||||
"close": "(es)Close"
|
||||
"cancelDictation": "Cancelar transcripción"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
@ -377,29 +374,29 @@
|
||||
"workflowPage": {
|
||||
"label": {
|
||||
"title": "flujo de trabajo",
|
||||
"addRoutingRule": "(es)Add Routing Rule",
|
||||
"editRoutingRule": "(es)Edit Routing Rule",
|
||||
"templateSetting": "(es)Template Setting",
|
||||
"worktypeIdSetting": "(es)WorktypeID Setting",
|
||||
"typistGroupSetting": "(es)Transcriptionist Group Setting",
|
||||
"addRoutingRule": "Agregar regla de enrutamiento",
|
||||
"editRoutingRule": "Editar regla de enrutamiento",
|
||||
"templateSetting": "Configuración de plantilla",
|
||||
"worktypeIdSetting": "Configuración de ID de tipo de trabajo",
|
||||
"typistGroupSetting": "Configuración del grupo transcriptor",
|
||||
"authorID": "ID de autor",
|
||||
"worktype": "ID de tipo de trabajo",
|
||||
"worktypeOptional": "(es)Worktype ID (Optional)",
|
||||
"worktypeOptional": "ID de tipo de trabajo (Opcional)",
|
||||
"transcriptionist": "Transcriptor",
|
||||
"template": "(es)Template",
|
||||
"templateOptional": "(es)Template (Optional)",
|
||||
"editRule": "(es)Edit Rule",
|
||||
"template": "Plantilla",
|
||||
"templateOptional": "Plantilla (Opcional)",
|
||||
"editRule": "Editar regla",
|
||||
"selected": "Transcriptor seleccionado",
|
||||
"pool": "Lista de transcriptor",
|
||||
"selectAuthor": "(es)Select Author ID",
|
||||
"selectWorktypeId": "(es)Select Worktype ID",
|
||||
"selectTemplate": "(es)Select Template"
|
||||
"selectAuthor": "Seleccionar ID de autor",
|
||||
"selectWorktypeId": "Seleccionar ID de tipo de trabajo",
|
||||
"selectTemplate": "Seleccionar Plantilla"
|
||||
},
|
||||
"message": {
|
||||
"selectedTypistEmptyError": "(es)Transcriptionist,TranscriptionistGroupがいないルーティングルールは保存できません。ルーティング先を1つ以上選択してください。",
|
||||
"workflowConflictError": "(es)指定したAuthorIDとWorktypeIDの組み合わせで既にルーティングルールが登録されています。他の組み合わせで登録してください。",
|
||||
"selectedTypistEmptyError": "No se ha seleccionado el transcriptor o el grupo de transcriptores. Seleccione uno o más de la lista de transcripción.",
|
||||
"workflowConflictError": "Ya se ha registrado una regla de enrutamiento con la combinación AuthorID y WorktypeID especificada. Regístrese con una combinación diferente.",
|
||||
"inputEmptyError": "Campo obligatorio",
|
||||
"saveFailedError": "(es)ルーティングルールの保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"saveFailedError": "No se pudo guardar la regla de enrutamiento. Actualice la pantalla e inténtelo de nuevo."
|
||||
}
|
||||
},
|
||||
"typistGroupSetting": {
|
||||
@ -433,38 +430,38 @@
|
||||
"addWorktype": "Agregar tipo de trabajo",
|
||||
"editWorktypeId": "Editar ID de tipo de trabajo",
|
||||
"saveChange": "Guardar cambios",
|
||||
"editOptionItems": "(es)Option Item",
|
||||
"itemLabel": "(es)Item label",
|
||||
"defaultValue": "(es)Default value",
|
||||
"initialValue": "(es)Initial value",
|
||||
"default": "(es)Default",
|
||||
"blank": "(es)Blank",
|
||||
"lastInput": "(es)Last Input",
|
||||
"optionItemTerms": "(es)The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
|
||||
"editOptionItems": "Elemento opcional",
|
||||
"itemLabel": "Etiquetas de elementos",
|
||||
"defaultValue": "Valor por defecto",
|
||||
"initialValue": "Valor inicial",
|
||||
"default": "Por defecto",
|
||||
"blank": "Vacío",
|
||||
"lastInput": "Última introducción de valor",
|
||||
"optionItemTerms": "La Etiquetas de elementos y el valor inicial pueden contener símbolos y alfanuméricos. No se pueden utilizar los siguientes símbolos:\\/:*?\"<>|."
|
||||
},
|
||||
"message": {
|
||||
"worktypeIdIncorrectError": "El ID de tipo de trabajo que ingresó no cumple con las especificaciones. Ingrese el tipo de trabajo correcto como se describe en las especificaciones a continuación.",
|
||||
"alreadyWorktypeIdExistError": "Este ID de tipo de trabajo está registrado actualmente. Regístrese con una ID de tipo de trabajo diferente.",
|
||||
"worktypeIDLimitError": "No se puede agregar el ID de tipo de trabajo porque ha alcanzado el número máximo de registros.",
|
||||
"optionItemInvalidError": "(es)Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
|
||||
"worktypeIdAlreadyDeletedError": "(es)WorktypeIDは既に削除されています。画面を更新し、再度ご確認ください",
|
||||
"optionItemSaveFailedError": "(es)オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"optionItemIncorrectError": "(es)入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
|
||||
"updateActiveWorktypeFailedError": "(es)Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"worktypeInUseError": "(es)このWorktype IDはルーティングルールで使用されているため削除できません。",
|
||||
"updateWorktypeFailedError": "(es)WorktypeIDの保存に失敗しました。画面を更新し、再度ご確認ください"
|
||||
"optionItemInvalidError": "La configuración del valor inicial no se ha completado. Establezca el valor inicial o cambie el valor predeterminado.",
|
||||
"worktypeIdAlreadyDeletedError": "El ID de tipo de trabajo ya se ha eliminado. Actualice su pantalla y verifique nuevamente.",
|
||||
"optionItemSaveFailedError": "No se pudo guardar el Elemento opcional. Actualice la pantalla e inténtelo de nuevo.",
|
||||
"optionItemIncorrectError": "La Etiquetas de elementos o el valor inicial no cumple con las reglas. Ingrese un valor que cumpla con las reglas a continuación.",
|
||||
"updateActiveWorktypeFailedError": "No se pudo guardar el ID de tipo de trabajo activo. Actualice su pantalla y verifique nuevamente.",
|
||||
"worktypeInUseError": "Este ID de tipo de trabajo no se puede eliminar porque actualmente se está utilizando en una regla de enrutamiento.",
|
||||
"updateWorktypeFailedError": "No se pudo guardar el ID de tipo de trabajo. Actualice la pantalla e inténtelo de nuevo."
|
||||
}
|
||||
},
|
||||
"templateFilePage": {
|
||||
"label": {
|
||||
"title": "(es)Template List",
|
||||
"addTemplate": "(es)Add Template",
|
||||
"fileName": "(es)Flie Name",
|
||||
"chooseFile": "(es)Choose File",
|
||||
"notFileChosen": "(es)- Not file chosen -",
|
||||
"fileSizeTerms": "(es)ファイルサイズは5MBまでです。",
|
||||
"fileSizeError": "(es)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "(es)ファイル選択は必須です。ファイルを選択してください。"
|
||||
"title": "Lista de plantillas",
|
||||
"addTemplate": "Agregar plantilla",
|
||||
"fileName": "Nombre del archivo",
|
||||
"chooseFile": "Seleccione Archivo",
|
||||
"notFileChosen": "- Ningún archivo seleccionado -",
|
||||
"fileSizeTerms": "El tamaño máximo de archivo que se puede guardar es de 5 MB.",
|
||||
"fileSizeError": "El tamaño del archivo seleccionado es demasiado grande. Seleccione un archivo que tenga un tamaño de 5 MB o menos.",
|
||||
"fileEmptyError": "Se requiere selección de archivos. Por favor seleccione un archivo."
|
||||
}
|
||||
},
|
||||
"partnerPage": {
|
||||
@ -473,70 +470,69 @@
|
||||
"addAccount": "Añadir cuenta",
|
||||
"name": "Nombre de empresa",
|
||||
"category": "Nivel de cuenta",
|
||||
"accountId": "ID de autor",
|
||||
"accountId": "ID de la cuenta",
|
||||
"country": "País",
|
||||
"primaryAdmin": "Administrador primario",
|
||||
"email": "Email",
|
||||
"dealerManagement": "Permitir que el distribuidor realice los cambios",
|
||||
"dealerManagement": "Permitir que el concesionario realice los cambios",
|
||||
"partners": "Socios",
|
||||
"deleteAccount": "Borrar cuenta"
|
||||
},
|
||||
"message": {
|
||||
"delegateNotAllowedError": "(es)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
|
||||
"deleteFailedError": "(es)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
|
||||
"delegateCancelError": "(es)代行操作の許可が取り消されたため、代行操作を終了しました。"
|
||||
"delegateNotAllowedError": "No se permiten acciones en nombre del socio. Actualice la pantalla y verifique nuevamente.",
|
||||
"deleteFailedError": "La operación del delegado falló. Actualice la pantalla y verifique nuevamente.",
|
||||
"delegateCancelError": "La operación delegada finalizó porque se revocó el permiso para la operación delegada."
|
||||
}
|
||||
},
|
||||
"accountPage": {
|
||||
"label": {
|
||||
"title": "(es)Account",
|
||||
"fileDeleteSetting": "(es)File Delete Setting",
|
||||
"accountInformation": "(es)Account Information",
|
||||
"companyName": "(es)Company Name",
|
||||
"accountID": "(es)Account ID",
|
||||
"yourCategory": "(es)Your Category",
|
||||
"yourCountry": "(es)Your Country",
|
||||
"yourDealer": "(es)Your Dealer(Upper layer)",
|
||||
"selectDealer": "(es)Select Dealer",
|
||||
"dealerManagement": "(es)Dealer Management",
|
||||
"administratorInformation": "(es)Administrator Information",
|
||||
"primaryAdministrator": "(es)Primary Administrator",
|
||||
"secondaryAdministrator": "(es)Secondary Administrator",
|
||||
"emailAddress": "(es)E-mail address",
|
||||
"selectSecondaryAdministrator": "(es)Select Secondary Administrator",
|
||||
"saveChanges": "(es)Save Changes",
|
||||
"deleteAccount": "(es)Delete Account"
|
||||
"title": "Cuenta",
|
||||
"fileDeleteSetting": "Configuración de eliminación de archivos",
|
||||
"accountInformation": "Información de la cuenta",
|
||||
"companyName": "Nombre de empresa",
|
||||
"accountID": "ID de la cuenta",
|
||||
"yourCategory": "Tipo de cuenta",
|
||||
"yourCountry": "País",
|
||||
"yourDealer": "Concesionario",
|
||||
"selectDealer": "Seleccionar Concesionario",
|
||||
"dealerManagement": "Permitir que el concesionario realice los cambios",
|
||||
"administratorInformation": "Información del administrador",
|
||||
"primaryAdministrator": "Administrador primario",
|
||||
"secondaryAdministrator": "Administrador secundario",
|
||||
"emailAddress": "Dirección de correo electrónico",
|
||||
"selectSecondaryAdministrator": "Seleccionar administrador secundario",
|
||||
"saveChanges": "Guardar cambios",
|
||||
"deleteAccount": "Borrar cuenta"
|
||||
},
|
||||
"message": {
|
||||
"updateAccountFailedError": "(es)アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"updateAccountFailedError": "No se pudo guardar la información de la cuenta. Actualice la pantalla e inténtelo de nuevo."
|
||||
}
|
||||
},
|
||||
"deleteAccountPopup": {
|
||||
"label": {
|
||||
"title": "(es)Delete Account",
|
||||
"subTitle": "(es)Delete your account?",
|
||||
"cautionOfDeleteingAccountData": "(es)Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
|
||||
"deleteButton": "(es)Delete account",
|
||||
"cancelButton": "(es)Cancel"
|
||||
"title": "Borrar cuenta",
|
||||
"subTitle": "¿Eliminar tu cuenta?",
|
||||
"cautionOfDeleteingAccountData": "Al eliminar su cuenta, se eliminará toda la información del usuario, las licencias y los archivos de dictado del sistema. La información eliminada no se puede restaurar.",
|
||||
"deleteButton": "Borrar cuenta",
|
||||
"cancelButton": "Cancelar"
|
||||
}
|
||||
},
|
||||
"accountDeleteSuccess": {
|
||||
"label": {
|
||||
"title": "(es)Account Delete Success",
|
||||
"message": "(es)Your account has been deleted. Thank you for using our services.",
|
||||
"backToTopPageLink": "(es)Back to TOP Page"
|
||||
"title": "Eliminación exitosa de la cuenta",
|
||||
"message": "Tu cuenta ha sido eliminada. Gracias por utilizar la nube ODMS.",
|
||||
"backToTopPageLink": "Volver a la página superior"
|
||||
}
|
||||
},
|
||||
"termsPage": {
|
||||
"label": {
|
||||
"title": "(es)Terms of Use has updated. Please confirm again.",
|
||||
"linkOfEula": "(es)Click here to read the terms of use.",
|
||||
"linkOfDpa": "(es)Click here to read the terms of use.",
|
||||
"linkOfPrivacyNotice": "(es)Click here to read the terms of use.",
|
||||
"checkBoxForConsent": "(es)Yes, I agree to the terms of use.",
|
||||
"forOdds": "(es)for ODDS.",
|
||||
"button": "(es)Continue",
|
||||
"linkOfPrivacyNotice": "(es)Click here to read the terms of use."
|
||||
"title": "Los Términos de uso han sido actualizados. Se requiere reconfirmación para continuar usando ODMS Cloud.",
|
||||
"linkOfEula": "Haga clic aquí para leer el Acuerdo de licencia de usuario final.",
|
||||
"linkOfDpa": "Haga clic aquí para leer el Acuerdo de procesamiento de datos.",
|
||||
"checkBoxForConsent": "Sí, acepto los términos de uso.",
|
||||
"forOdms": "para la nube ODMS.",
|
||||
"button": "Continuar",
|
||||
"linkOfPrivacyNotice": "Haga clic aquí para leer el Aviso de Privacidad."
|
||||
}
|
||||
},
|
||||
"supportPage": {
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
"common": {
|
||||
"message": {
|
||||
"inputEmptyError": "Champ obligatoire",
|
||||
"passwordIncorrectError": "(fr)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
|
||||
"emailIncorrectError": "(fr)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
|
||||
"passwordIncorrectError": "Le mot de passe que vous avez entré ne répond pas aux spécifications. Veuillez saisir le mot de passe correct, comme indiqué dans les spécifications ci-dessous.",
|
||||
"emailIncorrectError": "Le format de l'adresse e-mail n'est pas valide. S'il vous plaît, mettez une adresse email valide.",
|
||||
"internalServerError": "Le traitement a échoué. Veuillez réessayer plus tard.",
|
||||
"listEmpty": "Il y a 0 résultats de recherche.",
|
||||
"dialogConfirm": "Voulez-vous effectuer l'opération?",
|
||||
"success": "Traité avec succès",
|
||||
"displayDialog": "(fr)サインアウトしてもよろしいですか?"
|
||||
"displayDialog": "Êtes-vous certain de vouloir vous déconnecter?"
|
||||
},
|
||||
"label": {
|
||||
"cancel": "Annuler",
|
||||
@ -17,22 +17,21 @@
|
||||
"save": "Sauvegarder",
|
||||
"delete": "Delete",
|
||||
"return": "Retour",
|
||||
"operationInsteadOf": "(fr)Operation instead of:",
|
||||
"headerName": "(fr)ODMS Cloud",
|
||||
"headerAccount": "(fr)Account",
|
||||
"headerUser": "(fr)User",
|
||||
"headerLicense": "(fr)License",
|
||||
"headerDictations": "(fr)Dictations",
|
||||
"headerWorkflow": "(fr)Workflow",
|
||||
"headerPartners": "(fr)Partners",
|
||||
"operationInsteadOf": "Exploiter le Cloud ODMS pour le compte de :",
|
||||
"headerAccount": "Compte",
|
||||
"headerUser": "Utilisateur",
|
||||
"headerLicense": "Abonnement",
|
||||
"headerDictations": "Dictées",
|
||||
"headerWorkflow": "Flux de travail",
|
||||
"headerPartners": "Partenaires",
|
||||
"headerSupport": "(fr)Support",
|
||||
"tier1": "(fr)Admin",
|
||||
"tier2": "(fr)BC",
|
||||
"tier3": "(fr)Distributor",
|
||||
"tier4": "(fr)Dealer",
|
||||
"tier5": "(fr)Customer",
|
||||
"notSelected": "(fr)None",
|
||||
"signOutButton": "(fr)Sign out"
|
||||
"tier1": "Admin",
|
||||
"tier2": "BC",
|
||||
"tier3": "Distributeur",
|
||||
"tier4": "Concessionnaire",
|
||||
"tier5": "Client",
|
||||
"notSelected": "Aucune",
|
||||
"signOutButton": "se déconnecter"
|
||||
}
|
||||
},
|
||||
"topPage": {
|
||||
@ -49,7 +48,7 @@
|
||||
"signInButton": "S'identifier",
|
||||
"newUser": "Nouvel utilisateur",
|
||||
"signUpButton": "Créer un compte",
|
||||
"logoAlt": "(fr)OM Dictation Management System in the Cloud"
|
||||
"logoAlt": "OM Dictation Management System in the Cloud"
|
||||
}
|
||||
},
|
||||
"signupPage": {
|
||||
@ -75,7 +74,7 @@
|
||||
"email": "Adresse e-mail",
|
||||
"password": "Mot de passe",
|
||||
"termsLink": "Cliquez ici pour lire les conditions d'utilisation.",
|
||||
"termsLinkFor": "(fr)for ODDS.",
|
||||
"termsLinkFor": "pour ODMS Cloud.",
|
||||
"termsCheckBox": "Oui, j'accepte les conditions d'utilisation.",
|
||||
"createAccountButton": "Soumettre"
|
||||
}
|
||||
@ -176,11 +175,12 @@
|
||||
"freeLicense": "Nombre de licences inutilisées",
|
||||
"expiringWithin14daysLicense": "Nombre de licences expirant dans les 14 jours",
|
||||
"issueRequesting": "Nombre total de licences commandées",
|
||||
"numberOfRequesting": "Nombre total de commandes",
|
||||
"shortage": "Pénurie",
|
||||
"storageSize": "Stockage disponible",
|
||||
"usedSize": "Stockage utilisé",
|
||||
"storageAvailable": "Stockage indisponible (montant dépassée)"
|
||||
"storageAvailable": "Stockage indisponible (montant dépassée)",
|
||||
"licenseLabel": "(fr)License",
|
||||
"storageLabel": "(fr)Storage"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
@ -252,10 +252,7 @@
|
||||
"fileBackup": "(fr)File Backup",
|
||||
"downloadForBackup": "(fr)Download for backup",
|
||||
"applications": "(fr)Applications",
|
||||
"cancelDictation": "(fr)Cancel Dictation",
|
||||
"general": "(fr)General",
|
||||
"job": "(fr)Job",
|
||||
"close": "(fr)Close"
|
||||
"cancelDictation": "Annuler la transcription"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
@ -377,29 +374,29 @@
|
||||
"workflowPage": {
|
||||
"label": {
|
||||
"title": "Flux de travail",
|
||||
"addRoutingRule": "(fr)Add Routing Rule",
|
||||
"editRoutingRule": "(fr)Edit Routing Rule",
|
||||
"templateSetting": "(fr)Template Setting",
|
||||
"worktypeIdSetting": "(fr)WorktypeID Setting",
|
||||
"typistGroupSetting": "(fr)Transcriptionist Group Setting",
|
||||
"addRoutingRule": "Ajouter une règle de routage",
|
||||
"editRoutingRule": "Modifier la règle de routage",
|
||||
"templateSetting": "Paramètre de Masque",
|
||||
"worktypeIdSetting": "Paramètre d'ID du type de travail",
|
||||
"typistGroupSetting": "Paramètre de groupe de transcriptionniste",
|
||||
"authorID": "Identifiant Auteur",
|
||||
"worktype": "Identifiant du Type de travail",
|
||||
"worktypeOptional": "(fr)Worktype ID (Optional)",
|
||||
"worktypeOptional": "Identifiant du Type de travail (Facultatif)",
|
||||
"transcriptionist": "Transcriptionniste",
|
||||
"template": "(fr)Template",
|
||||
"templateOptional": "(fr)Template (Optional)",
|
||||
"editRule": "(fr)Edit Rule",
|
||||
"template": "Masque",
|
||||
"templateOptional": "Masque (Facultatif)",
|
||||
"editRule": "Modifier la règle",
|
||||
"selected": "Transcriptionniste sélectionné",
|
||||
"pool": "Liste de transcriptionniste",
|
||||
"selectAuthor": "(fr)Select Author ID",
|
||||
"selectWorktypeId": "(fr)Select Worktype ID",
|
||||
"selectTemplate": "(fr)Select Template"
|
||||
"selectAuthor": "Sélectionner le Identifiant Auteur",
|
||||
"selectWorktypeId": "Sélectionner le Identifiant du Type de travail",
|
||||
"selectTemplate": "Sélectionner le Masque"
|
||||
},
|
||||
"message": {
|
||||
"selectedTypistEmptyError": "(fr)Transcriptionist,TranscriptionistGroupがいないルーティングルールは保存できません。ルーティング先を1つ以上選択してください。",
|
||||
"workflowConflictError": "(fr)指定したAuthorIDとWorktypeIDの組み合わせで既にルーティングルールが登録されています。他の組み合わせで登録してください。",
|
||||
"selectedTypistEmptyError": "Transcriptionist ou Transcriptionist Group n’a pas été sélectionné. Veuillez en sélectionner un ou plusieurs dans la liste de transcription.",
|
||||
"workflowConflictError": "Une règle de routage a déjà été enregistrée avec la combinaison AuthorID et WorktypeID spécifiée. Veuillez vous inscrire avec une combinaison différente.",
|
||||
"inputEmptyError": "Champ obligatoire",
|
||||
"saveFailedError": "(fr)ルーティングルールの保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"saveFailedError": "Échec de l'enregistrement de la règle de routage. Veuillez actualiser l'écran et réessayer."
|
||||
}
|
||||
},
|
||||
"typistGroupSetting": {
|
||||
@ -433,38 +430,38 @@
|
||||
"addWorktype": "Ajouter type de travail",
|
||||
"editWorktypeId": "Modifier l'ID du type de travail",
|
||||
"saveChange": "Sauvegarder les modifications",
|
||||
"editOptionItems": "(fr)Option Item",
|
||||
"itemLabel": "(fr)Item label",
|
||||
"defaultValue": "(fr)Default value",
|
||||
"initialValue": "(fr)Initial value",
|
||||
"default": "(fr)Default",
|
||||
"blank": "(fr)Blank",
|
||||
"lastInput": "(fr)Last Input",
|
||||
"optionItemTerms": "(fr)The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
|
||||
"editOptionItems": "Elément d’option",
|
||||
"itemLabel": "Étiquettes d'élément",
|
||||
"defaultValue": "Valeur par défaut",
|
||||
"initialValue": "Valeur initiale",
|
||||
"default": "Défaut",
|
||||
"blank": "Vide",
|
||||
"lastInput": "Valeur saisie la dernière fois",
|
||||
"optionItemTerms": "Étiquettes d'élément et la valeur initiale peuvent contenir des caractères alphanumériques et des symboles. Les symboles suivants ne peuvent pas être utilisés :\\/:*?\"<>|."
|
||||
},
|
||||
"message": {
|
||||
"worktypeIdIncorrectError": "L'ID du type de travail que vous avez saisi ne répond pas aux spécifications. Veuillez saisir le type de travail correct, comme indiqué dans les spécifications ci-dessous.",
|
||||
"alreadyWorktypeIdExistError": "Cet ID de type de travail est actuellement enregistré. Veuillez vous inscrire avec un identifiant de type de travail différent.",
|
||||
"worktypeIDLimitError": "L'ID du type de travail ne peut pas être ajouté car il a atteint le nombre maximum d'enregistrements.",
|
||||
"optionItemInvalidError": "(fr)Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
|
||||
"worktypeIdAlreadyDeletedError": "(fr)WorktypeIDは既に削除されています。画面を更新し、再度ご確認ください",
|
||||
"optionItemSaveFailedError": "(fr)オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"optionItemIncorrectError": "(fr)入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
|
||||
"updateActiveWorktypeFailedError": "(fr)Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください",
|
||||
"worktypeInUseError": "(fr)このWorktype IDはルーティングルールで使用されているため削除できません。",
|
||||
"updateWorktypeFailedError": "(fr)WorktypeIDの保存に失敗しました。画面を更新し、再度ご確認ください"
|
||||
"optionItemInvalidError": "Le réglage de la valeur initiale n'est pas terminé. Veuillez définir la valeur initiale ou modifier la valeur par défaut.",
|
||||
"worktypeIdAlreadyDeletedError": "Identifiant du Type de travail a déjà été supprimé. Veuillez actualiser votre écran et vérifier à nouveau.",
|
||||
"optionItemSaveFailedError": "Échec de l'enregistrement de l'élément d'option. Veuillez actualiser l'écran et réessayer.",
|
||||
"optionItemIncorrectError": "Le Elément d’option ou la valeur initiale saisi ne respecte pas les règles. Veuillez saisir une valeur qui répond aux règles ci-dessous.",
|
||||
"updateActiveWorktypeFailedError": "Échec de l'enregistrement de l'Active Identifiant du Type de travail. Veuillez actualiser votre écran et vérifier à nouveau.",
|
||||
"worktypeInUseError": "Cet Identifiant du Type de travail ne peut pas être supprimé car il est actuellement utilisé dans une règle de routage.",
|
||||
"updateWorktypeFailedError": "Échec de l'enregistrement de Identifiant du Type de travail. Veuillez actualiser l'écran et réessayer."
|
||||
}
|
||||
},
|
||||
"templateFilePage": {
|
||||
"label": {
|
||||
"title": "(fr)Template List",
|
||||
"addTemplate": "(fr)Add Template",
|
||||
"fileName": "(fr)Flie Name",
|
||||
"chooseFile": "(fr)Choose File",
|
||||
"notFileChosen": "(fr)- Not file chosen -",
|
||||
"fileSizeTerms": "(fr)ファイルサイズは5MBまでです。",
|
||||
"fileSizeError": "(fr)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
|
||||
"fileEmptyError": "(fr)ファイル選択は必須です。ファイルを選択してください。"
|
||||
"title": "Liste des modèles",
|
||||
"addTemplate": "Ajouter un modèle",
|
||||
"fileName": "Nom de fichier",
|
||||
"chooseFile": "Choisir le fichier",
|
||||
"notFileChosen": "- Aucun fichier sélectionné -",
|
||||
"fileSizeTerms": "La taille maximale du fichier pouvant être enregistré est de 5 Mo.",
|
||||
"fileSizeError": "La taille du fichier sélectionné est trop grande. Veuillez sélectionner un fichier d'une taille maximale de 5 Mo.",
|
||||
"fileEmptyError": "La sélection de fichiers est requise. Veuillez sélectionner un fichier."
|
||||
}
|
||||
},
|
||||
"partnerPage": {
|
||||
@ -473,70 +470,69 @@
|
||||
"addAccount": "Ajouter compte",
|
||||
"name": "Nom de l'entreprise",
|
||||
"category": "Niveau compte",
|
||||
"accountId": "Identifiant Auteur",
|
||||
"accountId": "identifiant de compte",
|
||||
"country": "Pays",
|
||||
"primaryAdmin": "Administrateur principal",
|
||||
"email": "Email",
|
||||
"dealerManagement": "Autoriser le revendeur à modifier les paramètres",
|
||||
"dealerManagement": "Autoriser le concessionnaire à modifier les paramètres",
|
||||
"partners": "Partenaires",
|
||||
"deleteAccount": "Supprimer le compte"
|
||||
},
|
||||
"message": {
|
||||
"delegateNotAllowedError": "(fr)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
|
||||
"deleteFailedError": "(fr)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
|
||||
"delegateCancelError": "(fr)代行操作の許可が取り消されたため、代行操作を終了しました。"
|
||||
"delegateNotAllowedError": "Les actions au nom du partenaire ne sont pas autorisées. Veuillez actualiser l'écran et vérifier à nouveau.",
|
||||
"deleteFailedError": "L’opération de délégation a échoué. Veuillez actualiser l'écran et vérifier à nouveau.",
|
||||
"delegateCancelError": "L'opération déléguée a été interrompue car l'autorisation pour l'opération déléguée a été révoquée."
|
||||
}
|
||||
},
|
||||
"accountPage": {
|
||||
"label": {
|
||||
"title": "(fr)Account",
|
||||
"fileDeleteSetting": "(fr)File Delete Setting",
|
||||
"accountInformation": "(fr)Account Information",
|
||||
"companyName": "(fr)Company Name",
|
||||
"accountID": "(fr)Account ID",
|
||||
"yourCategory": "(fr)Your Category",
|
||||
"yourCountry": "(fr)Your Country",
|
||||
"yourDealer": "(fr)Your Dealer(Upper layer)",
|
||||
"selectDealer": "(fr)Select Dealer",
|
||||
"dealerManagement": "(fr)Dealer Management",
|
||||
"administratorInformation": "(fr)Administrator Information",
|
||||
"primaryAdministrator": "(fr)Primary Administrator",
|
||||
"secondaryAdministrator": "(fr)Secondary Administrator",
|
||||
"emailAddress": "(fr)E-mail address",
|
||||
"selectSecondaryAdministrator": "(fr)Select Secondary Administrator",
|
||||
"saveChanges": "(fr)Save Changes",
|
||||
"deleteAccount": "(fr)Delete Account"
|
||||
"title": "Compte",
|
||||
"fileDeleteSetting": "Paramètre de suppression de fichier",
|
||||
"accountInformation": "Information sur le compte",
|
||||
"companyName": "Nom de l'entreprise",
|
||||
"accountID": "identifiant de compte",
|
||||
"yourCategory": "Type de compte",
|
||||
"yourCountry": "Pays",
|
||||
"yourDealer": "Concessionnaire",
|
||||
"selectDealer": "Sélectionner le Concessionnaire",
|
||||
"dealerManagement": "Autoriser le concessionnaire à modifier les paramètres",
|
||||
"administratorInformation": "Informations sur l'administrateur",
|
||||
"primaryAdministrator": "Administrateur principal",
|
||||
"secondaryAdministrator": "Administrateur secondaire",
|
||||
"emailAddress": "Adresse e-mail",
|
||||
"selectSecondaryAdministrator": "Sélectionner le administrateur secondaire",
|
||||
"saveChanges": "Sauvegarder les modifications",
|
||||
"deleteAccount": "Supprimer le compte"
|
||||
},
|
||||
"message": {
|
||||
"updateAccountFailedError": "(fr)アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
|
||||
"updateAccountFailedError": "Échec de l'enregistrement des informations du compte. Veuillez actualiser l'écran et réessayer."
|
||||
}
|
||||
},
|
||||
"deleteAccountPopup": {
|
||||
"label": {
|
||||
"title": "(fr)Delete Account",
|
||||
"subTitle": "(fr)Delete your account?",
|
||||
"cautionOfDeleteingAccountData": "(fr)Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
|
||||
"deleteButton": "(fr)Delete account",
|
||||
"cancelButton": "(fr)Cancel"
|
||||
"title": "Supprimer le compte",
|
||||
"subTitle": "Supprimer votre compte?",
|
||||
"cautionOfDeleteingAccountData": "La suppression de votre compte supprimera toutes les informations utilisateur, licences et fichiers de dictée du système. Les informations supprimées ne peuvent pas être restaurées.",
|
||||
"deleteButton": "Supprimer le compte",
|
||||
"cancelButton": "Annuler"
|
||||
}
|
||||
},
|
||||
"accountDeleteSuccess": {
|
||||
"label": {
|
||||
"title": "(fr)Account Delete Success",
|
||||
"message": "(fr)Your account has been deleted. Thank you for using our services.",
|
||||
"backToTopPageLink": "(fr)Back to TOP Page"
|
||||
"title": "Succès de la suppression du compte",
|
||||
"message": "Votre compte a été supprimé. Merci d'utiliser le Cloud ODMS.",
|
||||
"backToTopPageLink": "Retour à la page supérieure"
|
||||
}
|
||||
},
|
||||
"termsPage": {
|
||||
"label": {
|
||||
"title": "(fr)Terms of Use has updated. Please confirm again.",
|
||||
"linkOfEula": "(fr)Click here to read the terms of use.",
|
||||
"linkOfDpa": "(fr)Click here to read the terms of use.",
|
||||
"linkOfPrivacyNotice": "(fr)Click here to read the terms of use.",
|
||||
"checkBoxForConsent": "(fr)Yes, I agree to the terms of use.",
|
||||
"forOdds": "(fr)for ODDS.",
|
||||
"button": "(fr)Continue",
|
||||
"linkOfPrivacyNotice": "(fr)Click here to read the terms of use."
|
||||
"title": "Les conditions d'utilisation ont été mises à jour. Une reconfirmation est requise pour continuer à utiliser le cloud ODMS.",
|
||||
"linkOfEula": "Cliquez ici pour lire le contrat de licence utilisateur final.",
|
||||
"linkOfDpa": "Cliquez ici pour lire l'accord de traitement des données.",
|
||||
"checkBoxForConsent": "Oui, j'accepte les conditions d'utilisation.",
|
||||
"forOdms": "pour ODMS Cloud.",
|
||||
"button": "Continuer",
|
||||
"linkOfPrivacyNotice": "Cliquez ici pour lire l'avis de confidentialité."
|
||||
}
|
||||
},
|
||||
"supportPage": {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {
|
||||
LICENSE_EXPIRATION_DAYS,
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
LICENSE_EXPIRATION_TIME_WITH_TIMEZONE,
|
||||
TRIAL_LICENSE_EXPIRATION_DAYS,
|
||||
} from "../../constants";
|
||||
|
||||
@ -28,6 +29,19 @@ export class DateWithDayEndTime extends Date {
|
||||
}
|
||||
}
|
||||
|
||||
// 翌日の日付を取得する
|
||||
export class DateWithNextDayEndTime extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setDate(this.getDate() + 1);
|
||||
this.setHours(23, 59, 59, 999); // 時分秒を"23:59:59.999"に固定
|
||||
}
|
||||
}
|
||||
|
||||
// ライセンスの算出用に、閾値となる時刻(23:59:59.999)の日付を取得する
|
||||
export class ExpirationThresholdDate extends Date {
|
||||
constructor(...args: any[]) {
|
||||
@ -49,6 +63,8 @@ export class NewTrialLicenseExpirationDate extends Date {
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
// タイムゾーンをカバーするために現在時刻に8時間を加算してから、30日後の日付を取得する
|
||||
this.setHours(this.getHours() + LICENSE_EXPIRATION_TIME_WITH_TIMEZONE);
|
||||
this.setDate(this.getDate() + TRIAL_LICENSE_EXPIRATION_DAYS);
|
||||
this.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
this.setMilliseconds(0);
|
||||
@ -63,6 +79,8 @@ export class NewAllocatedLicenseExpirationDate extends Date {
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
// タイムゾーンをカバーするために現在時刻に8時間を加算してから、365日後の日付を取得する
|
||||
this.setHours(this.getHours() + LICENSE_EXPIRATION_TIME_WITH_TIMEZONE);
|
||||
this.setDate(this.getDate() + LICENSE_EXPIRATION_DAYS);
|
||||
this.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
this.setMilliseconds(0);
|
||||
|
||||
@ -19,54 +19,54 @@ export const TIERS = {
|
||||
* 音声ファイルをEast USに保存する国リスト
|
||||
* @const {number}
|
||||
*/
|
||||
export const BLOB_STORAGE_REGION_US = ['CA', 'KY', 'US'];
|
||||
export const BLOB_STORAGE_REGION_US = ["CA", "KY", "US"];
|
||||
|
||||
/**
|
||||
* 音声ファイルをAustralia Eastに保存する国リスト
|
||||
* @const {number}
|
||||
*/
|
||||
export const BLOB_STORAGE_REGION_AU = ['AU', 'NZ'];
|
||||
export const BLOB_STORAGE_REGION_AU = ["AU", "NZ"];
|
||||
|
||||
/**
|
||||
* 音声ファイルをNorth Europeに保存する国リスト
|
||||
* @const {number}
|
||||
*/
|
||||
export const BLOB_STORAGE_REGION_EU = [
|
||||
'AT',
|
||||
'BE',
|
||||
'BG',
|
||||
'HR',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DK',
|
||||
'EE',
|
||||
'FI',
|
||||
'FR',
|
||||
'DE',
|
||||
'GR',
|
||||
'HU',
|
||||
'IS',
|
||||
'IE',
|
||||
'IT',
|
||||
'LV',
|
||||
'LI',
|
||||
'LT',
|
||||
'LU',
|
||||
'MT',
|
||||
'NL',
|
||||
'NO',
|
||||
'PL',
|
||||
'PT',
|
||||
'RO',
|
||||
'RS',
|
||||
'SK',
|
||||
'SI',
|
||||
'ZA',
|
||||
'ES',
|
||||
'SE',
|
||||
'CH',
|
||||
'TR',
|
||||
'GB',
|
||||
"AT",
|
||||
"BE",
|
||||
"BG",
|
||||
"HR",
|
||||
"CY",
|
||||
"CZ",
|
||||
"DK",
|
||||
"EE",
|
||||
"FI",
|
||||
"FR",
|
||||
"DE",
|
||||
"GR",
|
||||
"HU",
|
||||
"IS",
|
||||
"IE",
|
||||
"IT",
|
||||
"LV",
|
||||
"LI",
|
||||
"LT",
|
||||
"LU",
|
||||
"MT",
|
||||
"NL",
|
||||
"NO",
|
||||
"PL",
|
||||
"PT",
|
||||
"RO",
|
||||
"RS",
|
||||
"SK",
|
||||
"SI",
|
||||
"ZA",
|
||||
"ES",
|
||||
"SE",
|
||||
"CH",
|
||||
"TR",
|
||||
"GB",
|
||||
];
|
||||
|
||||
/**
|
||||
@ -74,8 +74,8 @@ export const BLOB_STORAGE_REGION_EU = [
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const ADMIN_ROLES = {
|
||||
ADMIN: 'admin',
|
||||
STANDARD: 'standard',
|
||||
ADMIN: "admin",
|
||||
STANDARD: "standard",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -83,9 +83,9 @@ export const ADMIN_ROLES = {
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const USER_ROLES = {
|
||||
NONE: 'none',
|
||||
AUTHOR: 'author',
|
||||
TYPIST: 'typist',
|
||||
NONE: "none",
|
||||
AUTHOR: "author",
|
||||
TYPIST: "typist",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -93,9 +93,9 @@ export const USER_ROLES = {
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const LICENSE_ISSUE_STATUS = {
|
||||
ISSUE_REQUESTING: 'Issue Requesting',
|
||||
ISSUED: 'Issued',
|
||||
CANCELED: 'Order Canceled',
|
||||
ISSUE_REQUESTING: "Issue Requesting",
|
||||
ISSUED: "Issued",
|
||||
CANCELED: "Order Canceled",
|
||||
};
|
||||
|
||||
/**
|
||||
@ -103,28 +103,28 @@ export const LICENSE_ISSUE_STATUS = {
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const LICENSE_TYPE = {
|
||||
TRIAL: 'TRIAL',
|
||||
NORMAL: 'NORMAL',
|
||||
CARD: 'CARD',
|
||||
TRIAL: "TRIAL",
|
||||
NORMAL: "NORMAL",
|
||||
CARD: "CARD",
|
||||
} as const;
|
||||
/**
|
||||
* ライセンス状態
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const LICENSE_ALLOCATED_STATUS = {
|
||||
UNALLOCATED: 'Unallocated',
|
||||
ALLOCATED: 'Allocated',
|
||||
REUSABLE: 'Reusable',
|
||||
DELETED: 'Deleted',
|
||||
UNALLOCATED: "Unallocated",
|
||||
ALLOCATED: "Allocated",
|
||||
REUSABLE: "Reusable",
|
||||
DELETED: "Deleted",
|
||||
} as const;
|
||||
/**
|
||||
* 切り替え元種別
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const SWITCH_FROM_TYPE = {
|
||||
NONE: 'NONE',
|
||||
CARD: 'CARD',
|
||||
TRIAL: 'TRIAL',
|
||||
NONE: "NONE",
|
||||
CARD: "CARD",
|
||||
TRIAL: "TRIAL",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -139,6 +139,12 @@ export const LICENSE_EXPIRATION_THRESHOLD_DAYS = 14;
|
||||
*/
|
||||
export const LICENSE_EXPIRATION_DAYS = 365;
|
||||
|
||||
/**
|
||||
* タイムゾーンを加味したライセンスの有効期間(8時間)
|
||||
* @const {number}
|
||||
*/
|
||||
export const LICENSE_EXPIRATION_TIME_WITH_TIMEZONE = 8;
|
||||
|
||||
/**
|
||||
* カードライセンスの桁数
|
||||
* @const {number}
|
||||
@ -156,36 +162,36 @@ export const OPTION_ITEM_NUM = 10;
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const TASK_STATUS = {
|
||||
UPLOADED: 'Uploaded',
|
||||
PENDING: 'Pending',
|
||||
IN_PROGRESS: 'InProgress',
|
||||
FINISHED: 'Finished',
|
||||
BACKUP: 'Backup',
|
||||
UPLOADED: "Uploaded",
|
||||
PENDING: "Pending",
|
||||
IN_PROGRESS: "InProgress",
|
||||
FINISHED: "Finished",
|
||||
BACKUP: "Backup",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* タスク一覧でソート可能な属性の一覧
|
||||
*/
|
||||
export const TASK_LIST_SORTABLE_ATTRIBUTES = [
|
||||
'JOB_NUMBER',
|
||||
'STATUS',
|
||||
'ENCRYPTION',
|
||||
'AUTHOR_ID',
|
||||
'WORK_TYPE',
|
||||
'FILE_NAME',
|
||||
'FILE_LENGTH',
|
||||
'FILE_SIZE',
|
||||
'RECORDING_STARTED_DATE',
|
||||
'RECORDING_FINISHED_DATE',
|
||||
'UPLOAD_DATE',
|
||||
'TRANSCRIPTION_STARTED_DATE',
|
||||
'TRANSCRIPTION_FINISHED_DATE',
|
||||
"JOB_NUMBER",
|
||||
"STATUS",
|
||||
"ENCRYPTION",
|
||||
"AUTHOR_ID",
|
||||
"WORK_TYPE",
|
||||
"FILE_NAME",
|
||||
"FILE_LENGTH",
|
||||
"FILE_SIZE",
|
||||
"RECORDING_STARTED_DATE",
|
||||
"RECORDING_FINISHED_DATE",
|
||||
"UPLOAD_DATE",
|
||||
"TRANSCRIPTION_STARTED_DATE",
|
||||
"TRANSCRIPTION_FINISHED_DATE",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* タスク一覧のソート条件(昇順・降順)
|
||||
*/
|
||||
export const SORT_DIRECTIONS = ['ASC', 'DESC'] as const;
|
||||
export const SORT_DIRECTIONS = ["ASC", "DESC"] as const;
|
||||
|
||||
/**
|
||||
* 通知タグの最大個数
|
||||
@ -198,18 +204,18 @@ export const TAG_MAX_COUNT = 20;
|
||||
* 通知のプラットフォーム種別文字列
|
||||
*/
|
||||
export const PNS = {
|
||||
WNS: 'wns',
|
||||
APNS: 'apns',
|
||||
WNS: "wns",
|
||||
APNS: "apns",
|
||||
};
|
||||
|
||||
/**
|
||||
* ユーザーのライセンス状態
|
||||
*/
|
||||
export const USER_LICENSE_STATUS = {
|
||||
NORMAL: 'Normal',
|
||||
NO_LICENSE: 'NoLicense',
|
||||
ALERT: 'Alert',
|
||||
RENEW: 'Renew',
|
||||
NORMAL: "Normal",
|
||||
NO_LICENSE: "NoLicense",
|
||||
ALERT: "Alert",
|
||||
RENEW: "Renew",
|
||||
};
|
||||
|
||||
/**
|
||||
@ -234,9 +240,9 @@ export const WORKTYPE_MAX_COUNT = 20;
|
||||
* worktypeのDefault値の取りうる値
|
||||
**/
|
||||
export const OPTION_ITEM_VALUE_TYPE = {
|
||||
DEFAULT: 'Default',
|
||||
BLANK: 'Blank',
|
||||
LAST_INPUT: 'LastInput',
|
||||
DEFAULT: "Default",
|
||||
BLANK: "Blank",
|
||||
LAST_INPUT: "LastInput",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -244,20 +250,46 @@ export const OPTION_ITEM_VALUE_TYPE = {
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const ADB2C_SIGN_IN_TYPE = {
|
||||
EMAILADDRESS: 'emailAddress',
|
||||
EMAILADDRESS: "emailAddress",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* MANUAL_RECOVERY_REQUIRED
|
||||
* @const {string}
|
||||
*/
|
||||
export const MANUAL_RECOVERY_REQUIRED = '[MANUAL_RECOVERY_REQUIRED]';
|
||||
export const MANUAL_RECOVERY_REQUIRED = "[MANUAL_RECOVERY_REQUIRED]";
|
||||
|
||||
/**
|
||||
* 利用規約種別
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const TERM_TYPE = {
|
||||
EULA: 'EULA',
|
||||
DPA: 'DPA',
|
||||
EULA: "EULA",
|
||||
DPA: "DPA",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* HTTPメソッド
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const HTTP_METHODS = {
|
||||
POST: "POST",
|
||||
GET: "GET",
|
||||
DELETE: "DELETE",
|
||||
HEAD: "HEAD",
|
||||
PATCH: "PATCH",
|
||||
PUT: "PUT",
|
||||
OPTIONS: "OPTIONS",
|
||||
TRACE: "TRACE",
|
||||
CONNECT: "CONNECT",
|
||||
};
|
||||
|
||||
/**
|
||||
* HTTPステータスコード
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const HTTP_STATUS_CODES = {
|
||||
OK: 200,
|
||||
BAD_REQUEST: 400,
|
||||
INTERNAL_SERVER_ERROR: 500,
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
UpdateDateColumn,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity({ name: "accounts" })
|
||||
@ -73,4 +74,7 @@ export class Account {
|
||||
@OneToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "secondary_admin_user_id" })
|
||||
secondaryAdminUser: User | null;
|
||||
|
||||
@OneToMany(() => User, (user) => user.id)
|
||||
user: User[] | null;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
UpdateDateColumn,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
ManyToOne,
|
||||
} from "typeorm";
|
||||
import { bigintTransformer } from "../common/entity";
|
||||
import { User } from "./user.entity";
|
||||
@ -61,3 +62,54 @@ export class License {
|
||||
@JoinColumn({ name: "allocated_user_id" })
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
@Entity({ name: "license_allocation_history" })
|
||||
export class LicenseAllocationHistory {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
user_id: number;
|
||||
|
||||
@Column()
|
||||
license_id: number;
|
||||
|
||||
@Column()
|
||||
is_allocated: boolean;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
executed_at: Date;
|
||||
|
||||
@Column()
|
||||
switch_from_type: string;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
deleted_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
updated_at: Date;
|
||||
|
||||
@ManyToOne(() => License, (licenses) => licenses.id, {
|
||||
createForeignKeyConstraints: false,
|
||||
}) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
|
||||
@JoinColumn({ name: "license_id" })
|
||||
license: License | null;
|
||||
}
|
||||
|
||||
@ -5,8 +5,11 @@ import {
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
} from "typeorm";
|
||||
import { License } from "./license.entity";
|
||||
import { Account } from "./account.entity";
|
||||
|
||||
@Entity({ name: "users" })
|
||||
export class User {
|
||||
@ -73,6 +76,12 @@ export class User {
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.user, {
|
||||
createForeignKeyConstraints: false,
|
||||
}) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
|
||||
@JoinColumn({ name: "account_id" })
|
||||
account: Account | null;
|
||||
|
||||
@OneToOne(() => License, (license) => license.user)
|
||||
license: License | null;
|
||||
}
|
||||
|
||||
387
dictation_function/src/functions/licenseAutoAllocation.ts
Normal file
387
dictation_function/src/functions/licenseAutoAllocation.ts
Normal file
@ -0,0 +1,387 @@
|
||||
import { app, InvocationContext, Timer } from "@azure/functions";
|
||||
import { Between, DataSource, In, MoreThan, Repository } from "typeorm";
|
||||
import { User } from "../entity/user.entity";
|
||||
import { Account } from "../entity/account.entity";
|
||||
import { License, LicenseAllocationHistory } from "../entity/license.entity";
|
||||
import * as dotenv from "dotenv";
|
||||
import {
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_TYPE,
|
||||
SWITCH_FROM_TYPE,
|
||||
TIERS,
|
||||
USER_ROLES,
|
||||
} from "../constants";
|
||||
import {
|
||||
DateWithDayEndTime,
|
||||
DateWithZeroTime,
|
||||
NewAllocatedLicenseExpirationDate,
|
||||
} from "../common/types/types";
|
||||
|
||||
export async function licenseAutoAllocationProcessing(
|
||||
context: InvocationContext,
|
||||
datasource: DataSource,
|
||||
dateToTrigger?: Date
|
||||
): Promise<void> {
|
||||
try {
|
||||
context.log("[IN]licenseAutoAllocationProcessing");
|
||||
|
||||
// ライセンスの有効期間判定用
|
||||
let currentDateZeroTime = new DateWithZeroTime();
|
||||
let currentDateEndTime = new DateWithDayEndTime();
|
||||
if (dateToTrigger) {
|
||||
currentDateZeroTime = new DateWithZeroTime(dateToTrigger);
|
||||
currentDateEndTime = new DateWithDayEndTime(dateToTrigger);
|
||||
}
|
||||
// 自動更新対象の候補となるアカウントを取得
|
||||
const accountRepository = datasource.getRepository(Account);
|
||||
const targetAccounts = await accountRepository.find({
|
||||
where: {
|
||||
tier: TIERS.TIER5,
|
||||
},
|
||||
});
|
||||
|
||||
// 自動更新対象となるアカウント・ユーザーを取得
|
||||
const autoAllocationLists = await findTargetUser(
|
||||
context,
|
||||
datasource,
|
||||
targetAccounts,
|
||||
currentDateZeroTime,
|
||||
currentDateEndTime
|
||||
);
|
||||
|
||||
// 対象となるアカウント数分ループ
|
||||
for (const autoAllocationList of autoAllocationLists) {
|
||||
// ライセンスを割り当てる
|
||||
await allocateLicense(
|
||||
context,
|
||||
datasource,
|
||||
autoAllocationList,
|
||||
currentDateZeroTime,
|
||||
currentDateEndTime
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
context.log("licenseAutoAllocationProcessing failed.");
|
||||
context.error(e);
|
||||
throw e;
|
||||
} finally {
|
||||
context.log("[OUT]licenseAutoAllocationProcessing");
|
||||
}
|
||||
}
|
||||
|
||||
export async function licenseAutoAllocation(
|
||||
myTimer: Timer,
|
||||
context: InvocationContext
|
||||
): Promise<void> {
|
||||
try {
|
||||
context.log("[IN]licenseAutoAllocation");
|
||||
dotenv.config({ path: ".env" });
|
||||
dotenv.config({ path: ".env.local", override: true });
|
||||
let datasource: DataSource;
|
||||
try {
|
||||
datasource = new DataSource({
|
||||
type: "mysql",
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT),
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
entities: [User, Account, License, LicenseAllocationHistory],
|
||||
});
|
||||
await datasource.initialize();
|
||||
} catch (e) {
|
||||
context.log("database initialize failed.");
|
||||
context.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
await licenseAutoAllocationProcessing(context, datasource);
|
||||
} catch (e) {
|
||||
context.log("licenseAutoAllocation failed.");
|
||||
context.error(e);
|
||||
throw e;
|
||||
} finally {
|
||||
context.log("[OUT]licenseAutoAllocation");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自動更新対象のアカウント・ユーザーを取得する
|
||||
* @param context
|
||||
* @param datasource
|
||||
* @param targetAccounts 自動更新対象候補のアカウント
|
||||
* @param currentDateZeroTime
|
||||
* @param currentDateEndTime
|
||||
* @returns autoAllocationList[] 自動更新対象のアカウント・ユーザーのIDリスト
|
||||
*/
|
||||
export async function findTargetUser(
|
||||
context: InvocationContext,
|
||||
datasource: DataSource,
|
||||
targetAccounts: Account[],
|
||||
currentDateZeroTime: DateWithZeroTime,
|
||||
currentDateEndTime: DateWithDayEndTime
|
||||
): Promise<autoAllocationList[]> {
|
||||
try {
|
||||
context.log("[IN]findTargetUser");
|
||||
|
||||
const autoAllocationList = [] as autoAllocationList[];
|
||||
// ライセンス期限が今日で自動更新対象のユーザーを取得
|
||||
const userRepository = datasource.getRepository(User);
|
||||
for (const account of targetAccounts) {
|
||||
// Author→Typist→Noneの優先度で割り当てたいので、roleごとに個別で取得
|
||||
const targetAuthorUsers = await userRepository.find({
|
||||
where: {
|
||||
account_id: account.id,
|
||||
auto_renew: true,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
license: {
|
||||
expiry_date: Between(currentDateZeroTime, currentDateEndTime),
|
||||
},
|
||||
},
|
||||
relations: {
|
||||
license: true,
|
||||
},
|
||||
});
|
||||
|
||||
const targetTypistUsers = await userRepository.find({
|
||||
where: {
|
||||
account_id: account.id,
|
||||
auto_renew: true,
|
||||
role: USER_ROLES.TYPIST,
|
||||
license: {
|
||||
expiry_date: Between(currentDateZeroTime, currentDateEndTime),
|
||||
},
|
||||
},
|
||||
relations: {
|
||||
license: true,
|
||||
},
|
||||
});
|
||||
|
||||
const targetNoneUsers = await userRepository.find({
|
||||
where: {
|
||||
account_id: account.id,
|
||||
auto_renew: true,
|
||||
role: USER_ROLES.NONE,
|
||||
license: {
|
||||
expiry_date: Between(currentDateZeroTime, currentDateEndTime),
|
||||
},
|
||||
},
|
||||
relations: {
|
||||
license: true,
|
||||
},
|
||||
});
|
||||
// Author→Typist→Noneの順で配列に格納
|
||||
const userIds = [] as number[];
|
||||
for (const user of targetAuthorUsers) {
|
||||
userIds.push(Number(user.id));
|
||||
}
|
||||
for (const user of targetTypistUsers) {
|
||||
userIds.push(Number(user.id));
|
||||
}
|
||||
for (const user of targetNoneUsers) {
|
||||
userIds.push(Number(user.id));
|
||||
}
|
||||
// 対象ユーザーが0件なら自動更新リストには含めない
|
||||
if (userIds.length !== 0) {
|
||||
autoAllocationList.push({
|
||||
accountId: account.id,
|
||||
userIds: userIds,
|
||||
});
|
||||
}
|
||||
}
|
||||
return autoAllocationList;
|
||||
} catch (e) {
|
||||
context.error(e);
|
||||
context.log("findTargetUser failed.");
|
||||
throw e;
|
||||
} finally {
|
||||
context.log("[OUT]findTargetUser");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 割り当て可能なライセンスを取得する
|
||||
* @param context
|
||||
* @param licenseRepository
|
||||
* @param accountId 自動更新対象のアカウントID
|
||||
* @returns License 割り当て可能なライセンス
|
||||
*/
|
||||
export async function getAutoAllocatableLicense(
|
||||
context: InvocationContext,
|
||||
licenseRepository: Repository<License>,
|
||||
accountId: number,
|
||||
currentDateEndTime: DateWithDayEndTime
|
||||
): Promise<License | undefined> {
|
||||
try {
|
||||
context.log("[IN]getAutoAllocatableLicense");
|
||||
// 割り当て可能なライセンスを取得
|
||||
const license = await licenseRepository.findOne({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
]),
|
||||
expiry_date: MoreThan(currentDateEndTime) || null,
|
||||
},
|
||||
order: {
|
||||
expiry_date: "ASC",
|
||||
},
|
||||
});
|
||||
if (!license) {
|
||||
// 割り当て可能なライセンスが存在しない場合でもエラーとはしたくないので、undifinedを返却する
|
||||
return undefined;
|
||||
}
|
||||
return license;
|
||||
} catch (e) {
|
||||
context.error(e);
|
||||
context.log("getAutoAllocatableLicense failed.");
|
||||
throw e;
|
||||
} finally {
|
||||
context.log("[OUT]getAutoAllocatableLicense");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ライセンスを割り当てる
|
||||
* @param context
|
||||
* @param datasource
|
||||
* @param account アカウント・ユーザーID
|
||||
* @param currentDateZeroTime
|
||||
* @param currentDateEndTime
|
||||
*/
|
||||
export async function allocateLicense(
|
||||
context: InvocationContext,
|
||||
datasource: DataSource,
|
||||
autoAllocationList: autoAllocationList,
|
||||
currentDateZeroTime: DateWithZeroTime,
|
||||
currentDateEndTime: DateWithDayEndTime
|
||||
): Promise<void> {
|
||||
try {
|
||||
context.log("[IN]allocateLicense");
|
||||
|
||||
// 自動更新対象ユーザーにライセンスを割り当てる
|
||||
let hasAllocatebleLicense = true;
|
||||
for (const userId of autoAllocationList.userIds) {
|
||||
await datasource.transaction(async (entityManager) => {
|
||||
const licenseRepository = entityManager.getRepository(License);
|
||||
const licenseAllocationHistoryRepo = entityManager.getRepository(
|
||||
LicenseAllocationHistory
|
||||
);
|
||||
// 割り当て可能なライセンスを取得する(自動割り当て用)
|
||||
const autoAllocatableLicense = await getAutoAllocatableLicense(
|
||||
context,
|
||||
licenseRepository,
|
||||
autoAllocationList.accountId,
|
||||
currentDateEndTime
|
||||
);
|
||||
|
||||
// 割り当て可能なライセンスが存在しなければreturnし、その後ループ終了
|
||||
if (!autoAllocatableLicense) {
|
||||
context.log(`allocatable license not exist.`);
|
||||
hasAllocatebleLicense = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// ライセンスが直前で手動割り当てされていたら、自動割り当てしない
|
||||
const allocatedLicense = await licenseRepository.findOne({
|
||||
where: {
|
||||
allocated_user_id: userId,
|
||||
expiry_date: Between(currentDateZeroTime, currentDateEndTime),
|
||||
},
|
||||
});
|
||||
if (!allocatedLicense) {
|
||||
context.log(`skip auto allocation. userID:${userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 古いライセンスの割り当て解除
|
||||
allocatedLicense.status = LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
allocatedLicense.allocated_user_id = null;
|
||||
await licenseRepository.save(allocatedLicense);
|
||||
|
||||
// ライセンス割り当て履歴テーブルへ登録
|
||||
const deallocationHistory = new LicenseAllocationHistory();
|
||||
deallocationHistory.user_id = userId;
|
||||
deallocationHistory.license_id = allocatedLicense.id;
|
||||
deallocationHistory.account_id = autoAllocationList.accountId;
|
||||
deallocationHistory.is_allocated = false;
|
||||
deallocationHistory.executed_at = new Date();
|
||||
deallocationHistory.switch_from_type = SWITCH_FROM_TYPE.NONE;
|
||||
await licenseAllocationHistoryRepo.save(deallocationHistory);
|
||||
|
||||
// 新規ライセンス割り当て
|
||||
autoAllocatableLicense.status = LICENSE_ALLOCATED_STATUS.ALLOCATED;
|
||||
autoAllocatableLicense.allocated_user_id = userId;
|
||||
// 有効期限が未設定なら365日後に設定
|
||||
if (!autoAllocatableLicense.expiry_date) {
|
||||
autoAllocatableLicense.expiry_date =
|
||||
new NewAllocatedLicenseExpirationDate();
|
||||
}
|
||||
await licenseRepository.save(autoAllocatableLicense);
|
||||
context.log(
|
||||
`license allocated. userID:${userId}, licenseID:${autoAllocatableLicense.id}`
|
||||
);
|
||||
|
||||
// ライセンス割り当て履歴テーブルを更新するための処理
|
||||
// 直近割り当てたライセンス種別を取得
|
||||
const oldLicenseType = await licenseAllocationHistoryRepo.findOne({
|
||||
relations: {
|
||||
license: true,
|
||||
},
|
||||
where: { user_id: userId, is_allocated: true },
|
||||
order: { executed_at: "DESC" },
|
||||
});
|
||||
|
||||
let switchFromType = "";
|
||||
if (oldLicenseType && oldLicenseType.license) {
|
||||
switch (oldLicenseType.license.type) {
|
||||
case LICENSE_TYPE.CARD:
|
||||
switchFromType = SWITCH_FROM_TYPE.CARD;
|
||||
break;
|
||||
case LICENSE_TYPE.TRIAL:
|
||||
switchFromType = SWITCH_FROM_TYPE.TRIAL;
|
||||
break;
|
||||
default:
|
||||
switchFromType = SWITCH_FROM_TYPE.NONE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switchFromType = SWITCH_FROM_TYPE.NONE;
|
||||
}
|
||||
|
||||
// ライセンス割り当て履歴テーブルへ登録
|
||||
const allocationHistory = new LicenseAllocationHistory();
|
||||
allocationHistory.user_id = userId;
|
||||
allocationHistory.license_id = autoAllocatableLicense.id;
|
||||
allocationHistory.account_id = autoAllocationList.accountId;
|
||||
allocationHistory.is_allocated = true;
|
||||
allocationHistory.executed_at = new Date();
|
||||
// TODO switchFromTypeの値については「PBI1234: 第一階層として、ライセンス数推移情報をCSV出力したい」で正式対応
|
||||
allocationHistory.switch_from_type = switchFromType;
|
||||
await licenseAllocationHistoryRepo.save(allocationHistory);
|
||||
});
|
||||
// 割り当て可能なライセンスが存在しなければループ終了
|
||||
if (!hasAllocatebleLicense) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// エラーが発生しても次のアカウントへの処理は継続させるため、例外をthrowせずにreturnだけする
|
||||
context.error(e);
|
||||
context.log("allocateLicense failed.");
|
||||
return;
|
||||
} finally {
|
||||
context.log("[OUT]allocateLicense");
|
||||
}
|
||||
}
|
||||
|
||||
app.timer("licenseAutoAllocation", {
|
||||
schedule: "0 0 16 * * *",
|
||||
handler: licenseAutoAllocation,
|
||||
});
|
||||
|
||||
class autoAllocationList {
|
||||
accountId: number;
|
||||
userIds: number[];
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpResponseInit,
|
||||
InvocationContext,
|
||||
app,
|
||||
HttpMethod,
|
||||
} from "@azure/functions";
|
||||
import { licenseAutoAllocationProcessing } from "./licenseAutoAllocation";
|
||||
import * as dotenv from "dotenv";
|
||||
import { DataSource } from "typeorm";
|
||||
import { User } from "../entity/user.entity";
|
||||
import { Account } from "../entity/account.entity";
|
||||
import { License, LicenseAllocationHistory } from "../entity/license.entity";
|
||||
import { HTTP_METHODS, HTTP_STATUS_CODES } from "../constants";
|
||||
|
||||
export async function licenseAutoAllocationManualRetry(
|
||||
req: HttpRequest,
|
||||
context: InvocationContext
|
||||
): Promise<HttpResponseInit> {
|
||||
context.log(req);
|
||||
try {
|
||||
if (req.method === HTTP_METHODS.POST) {
|
||||
const queryParams = new URLSearchParams(req.url.split("&")[1]); // クエリパラメータを取得
|
||||
const requestedDate = queryParams.get("date");
|
||||
context.log("requestedDate: " + requestedDate);
|
||||
let dateToTrigger: Date;
|
||||
if (requestedDate) {
|
||||
// パラメータのチェックを行う(YYYY-MM-DD形式)
|
||||
if (!requestedDate.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
||||
context.log("Invalid date format.");
|
||||
return {
|
||||
status: HTTP_STATUS_CODES.BAD_REQUEST,
|
||||
body: "Invalid date format.",
|
||||
};
|
||||
}
|
||||
dateToTrigger = new Date(requestedDate);
|
||||
} else {
|
||||
dateToTrigger = new Date();
|
||||
}
|
||||
context.log("[IN]licenseAutoAllocationManualRetry");
|
||||
dotenv.config({ path: ".env" });
|
||||
dotenv.config({ path: ".env.local", override: true });
|
||||
let datasource: DataSource;
|
||||
try {
|
||||
datasource = new DataSource({
|
||||
type: "mysql",
|
||||
host: process.env.DB_HOST,
|
||||
port: Number(process.env.DB_PORT),
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
entities: [User, Account, License, LicenseAllocationHistory],
|
||||
});
|
||||
await datasource.initialize();
|
||||
} catch (e) {
|
||||
context.log("database initialize failed.");
|
||||
context.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
await licenseAutoAllocationProcessing(context, datasource, dateToTrigger);
|
||||
context.log("Automatic license allocation has been triggered.");
|
||||
return {
|
||||
status: HTTP_STATUS_CODES.OK,
|
||||
body: "Automatic license allocation has been triggered.",
|
||||
};
|
||||
} else {
|
||||
context.log("Please use the POST method.");
|
||||
return {
|
||||
status: HTTP_STATUS_CODES.BAD_REQUEST,
|
||||
body: "Please use the POST method.",
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
context.log("licenseAutoAllocationManualRetry failed.");
|
||||
context.error(e);
|
||||
return {
|
||||
status: HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR,
|
||||
body: "licenseAutoAllocationManualRetry failed.",
|
||||
};
|
||||
} finally {
|
||||
context.log("[OUT]licenseAutoAllocationManualRetry");
|
||||
}
|
||||
}
|
||||
|
||||
// httpトリガは定時処理licenseAutoAllocationの異常時の手動再実行用
|
||||
app.http("licenseAutoAllocationManualRetry", {
|
||||
methods: [HTTP_METHODS.POST as HttpMethod],
|
||||
authLevel: "function",
|
||||
handler: licenseAutoAllocationManualRetry,
|
||||
});
|
||||
@ -58,26 +58,6 @@ Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Ma
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
|
||||
|
||||
|
||||
<Español>
|
||||
Estimado(a) ${companyName},
|
||||
|
||||
Una o más de sus licencias de ODMS Cloud asignadas caducarán en un plazo de 14 días. No hay una cantidad suficiente de licencias no asignadas en su inventario para emitirlas a usuarios con licencias vencidas.
|
||||
|
||||
Recuento de licencias insuficiente: ${shortage}
|
||||
|
||||
Solicite licencias anuales adicionales a su ${dealer} para asegurarse de tener suficiente inventario.
|
||||
|
||||
Puede asignar licencias a los usuarios de forma automática o manual. A los usuarios con la opción Asignación automática habilitada (predeterminada) se les asignará su licencia automáticamente desde su inventario de licencias en la fecha de vencimiento. Si desactiva la opción Asignar automáticamente, deberá asignar licencias manualmente.
|
||||
|
||||
Inicie sesión en ODMS Cloud para configurar su configuración de usuario y verificar la fecha de vencimiento de la licencia.
|
||||
URL: https://odmscloud.omsystem.com/
|
||||
|
||||
Si necesita ayuda con respecto a ODMS Cloud, comuníquese con ${dealer}.
|
||||
|
||||
Si recibió este correo electrónico por error, elimínelo de su sistema.
|
||||
Este es un correo electrónico generado automáticamente y este buzón no está monitoreado. Por favor, no responda.
|
||||
|
||||
|
||||
<Français>
|
||||
Chère/Cher ${companyName},
|
||||
|
||||
@ -137,26 +117,6 @@ URL: <a href="https://odmscloud.omsystem.com/">https://odmscloud.omsystem.com/</
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.</p>
|
||||
|
||||
|
||||
<h3><Español></h3>
|
||||
<p>Estimado(a) ${companyName},</p>
|
||||
|
||||
<p>Una o más de sus licencias de ODMS Cloud asignadas caducarán en un plazo de 14 días. No hay una cantidad suficiente de licencias no asignadas en su inventario para emitirlas a usuarios con licencias vencidas.</p>
|
||||
|
||||
<p>Recuento de licencias insuficiente: ${shortage}</p>
|
||||
|
||||
<p>Solicite licencias anuales adicionales a su ${dealer} para asegurarse de tener suficiente inventario.</p>
|
||||
|
||||
<p>Puede asignar licencias a los usuarios de forma automática o manual. A los usuarios con la opción Asignación automática habilitada (predeterminada) se les asignará su licencia automáticamente desde su inventario de licencias en la fecha de vencimiento. Si desactiva la opción Asignar automáticamente, deberá asignar licencias manualmente.</p>
|
||||
|
||||
<p>Inicie sesión en ODMS Cloud para configurar su configuración de usuario y verificar la fecha de vencimiento de la licencia.<br>
|
||||
URL: <a href="https://odmscloud.omsystem.com/">https://odmscloud.omsystem.com/</a></p>
|
||||
|
||||
<p>Si necesita ayuda con respecto a ODMS Cloud, comuníquese con ${dealer}.</p>
|
||||
|
||||
<p>Si recibió este correo electrónico por error, elimínelo de su sistema.<br>
|
||||
Este es un correo electrónico generado automáticamente y este buzón no está monitoreado. Por favor, no responda.</p>
|
||||
|
||||
|
||||
<h3><Français></h3>
|
||||
<p>Chère/Cher ${companyName},</p>
|
||||
|
||||
|
||||
@ -52,23 +52,6 @@ Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Ma
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
|
||||
|
||||
|
||||
<Español>
|
||||
Estimado(a) ${companyName},
|
||||
|
||||
Una o más de sus licencias de ODMS Cloud asignadas caducarán hoy.
|
||||
Número de licencias que vencen: ${ExpiringSoonUserCount}
|
||||
|
||||
Si no tiene una cantidad suficiente de licencias, deberá solicitar licencias anuales a su ${dealer} y asignarlas a los usuarios cuyas licencias están por vencer.
|
||||
|
||||
Inicie sesión en ODMS Cloud para configurar su configuración de usuario y verificar la fecha de vencimiento de la licencia.
|
||||
URL: https://odmscloud.omsystem.com/
|
||||
|
||||
Si necesita ayuda con respecto a ODMS Cloud, comuníquese con ${dealer}.
|
||||
|
||||
Si recibió este correo electrónico por error, elimínelo de su sistema.
|
||||
Este es un correo electrónico generado automáticamente y este buzón no está monitoreado. Por favor, no responda.
|
||||
|
||||
|
||||
<Français>
|
||||
Chère/Cher ${companyName},
|
||||
|
||||
@ -119,23 +102,6 @@ URL: <a href="https://odmscloud.omsystem.com/">https://odmscloud.omsystem.com/</
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.</p>
|
||||
|
||||
|
||||
<h3><Español></h3>
|
||||
<p>Estimado(a) ${companyName},</p>
|
||||
|
||||
<p>Una o más de sus licencias de ODMS Cloud asignadas caducarán hoy.<br>
|
||||
Número de licencias que vencen: ${ExpiringSoonUserCount}</p>
|
||||
|
||||
<p>Si no tiene una cantidad suficiente de licencias, deberá solicitar licencias anuales a su ${dealer} y asignarlas a los usuarios cuyas licencias están por vencer.</p>
|
||||
|
||||
<p>Inicie sesión en ODMS Cloud para configurar su configuración de usuario y verificar la fecha de vencimiento de la licencia.<br>
|
||||
URL: <a href="https://odmscloud.omsystem.com/">https://odmscloud.omsystem.com/</a></p>
|
||||
|
||||
<p>Si necesita ayuda con respecto a ODMS Cloud, comuníquese con ${dealer}.</p>
|
||||
|
||||
<p>Si recibió este correo electrónico por error, elimínelo de su sistema.<br>
|
||||
Este es un correo electrónico generado automáticamente y este buzón no está monitoreado. Por favor, no responda.</p>
|
||||
|
||||
|
||||
<h3><Français></h3>
|
||||
<p>Chère/Cher ${companyName},</p>
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { DataSource } from "typeorm";
|
||||
import { User } from "../../entity/user.entity";
|
||||
import { Account } from "../../entity/account.entity";
|
||||
import { ADMIN_ROLES, USER_ROLES } from "../../constants";
|
||||
import { License } from "../../entity/license.entity";
|
||||
import { License, LicenseAllocationHistory } from "../../entity/license.entity";
|
||||
|
||||
type InitialTestDBState = {
|
||||
tier1Accounts: { account: Account; users: User[] }[];
|
||||
@ -196,3 +196,34 @@ export const createLicense = async (
|
||||
});
|
||||
identifiers.pop() as License;
|
||||
};
|
||||
|
||||
export const selectLicenseByAllocatedUser = async (
|
||||
datasource: DataSource,
|
||||
userId: number
|
||||
): Promise<{ license: License | null }> => {
|
||||
const license = await datasource.getRepository(License).findOne({
|
||||
where: {
|
||||
allocated_user_id: userId,
|
||||
},
|
||||
});
|
||||
return { license };
|
||||
};
|
||||
|
||||
export const selectLicenseAllocationHistory = async (
|
||||
datasource: DataSource,
|
||||
userId: number,
|
||||
licence_id: number
|
||||
): Promise<{ licenseAllocationHistory: LicenseAllocationHistory | null }> => {
|
||||
const licenseAllocationHistory = await datasource
|
||||
.getRepository(LicenseAllocationHistory)
|
||||
.findOne({
|
||||
where: {
|
||||
user_id: userId,
|
||||
license_id: licence_id,
|
||||
},
|
||||
order: {
|
||||
executed_at: "DESC",
|
||||
},
|
||||
});
|
||||
return { licenseAllocationHistory };
|
||||
};
|
||||
|
||||
658
dictation_function/src/test/licenseAutoAllocation.spec.ts
Normal file
658
dictation_function/src/test/licenseAutoAllocation.spec.ts
Normal file
@ -0,0 +1,658 @@
|
||||
import { DataSource } from "typeorm";
|
||||
import { licenseAutoAllocationProcessing } from "../functions/licenseAutoAllocation";
|
||||
import {
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_TYPE,
|
||||
USER_ROLES,
|
||||
} from "../constants";
|
||||
import { DateWithDayEndTime } from "../common/types/types";
|
||||
import {
|
||||
makeTestAccount,
|
||||
createLicense,
|
||||
makeTestUser,
|
||||
selectLicenseByAllocatedUser,
|
||||
selectLicenseAllocationHistory,
|
||||
} from "./common/utility";
|
||||
import * as dotenv from "dotenv";
|
||||
import { InvocationContext } from "@azure/functions";
|
||||
|
||||
describe("licenseAutoAllocation", () => {
|
||||
dotenv.config({ path: ".env" });
|
||||
dotenv.config({ path: ".env.local", override: true });
|
||||
let source: DataSource | null = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: "sqlite",
|
||||
database: ":memory:",
|
||||
logging: false,
|
||||
entities: [__dirname + "/../../**/*.entity{.ts,.js}"],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!source) return;
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it("有効期限が本日のライセンスが自動更新されること", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
|
||||
const currentDateEndTime = new DateWithDayEndTime();
|
||||
|
||||
// アカウント
|
||||
const account1 = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ role: `${USER_ROLES.NONE}` }
|
||||
);
|
||||
const account2 = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ role: `${USER_ROLES.NONE}` }
|
||||
);
|
||||
|
||||
// 更新対象のユーザー(3role分)
|
||||
const user1 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
const user2 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.AUTHOR}`,
|
||||
});
|
||||
const user3 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.TYPIST}`,
|
||||
});
|
||||
|
||||
// 更新対象ではないユーザー(まだ有効期限が残っている)
|
||||
const user4 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
|
||||
// 更新対象ではないユーザー(auto_renewがfalse)
|
||||
const user5 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
auto_renew: false,
|
||||
});
|
||||
// 更新対象のユーザー(Author二人目)
|
||||
const user6 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.AUTHOR}`,
|
||||
});
|
||||
// 更新対象のユーザー(ただしライセンスが足りない)
|
||||
const user7 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
|
||||
// 割り当て済みで有効期限が本日のライセンス
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user1.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
2,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user2.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
3,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user3.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
20,
|
||||
currentDateEndTime,
|
||||
account2.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
account2.admin.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
5,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user5.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
6,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user6.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
7,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user7.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 割り当て済みの更新対象ではないライセンス
|
||||
const nextDate = new Date();
|
||||
nextDate.setDate(nextDate.getDate() + 1);
|
||||
nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
nextDate.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
4,
|
||||
nextDate,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user4.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 有効期限が先の未割当ライセンスを作成
|
||||
// idが100のものは有効期限が当日なので自動割り当て対象外
|
||||
// idが101のものから割り当てられる
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + i);
|
||||
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
date.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
i + 100,
|
||||
date,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 30);
|
||||
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
date.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
200,
|
||||
date,
|
||||
account2.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAutoAllocationProcessing(context, source);
|
||||
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
|
||||
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
|
||||
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
|
||||
const user4Allocated = await selectLicenseByAllocatedUser(source, user4.id);
|
||||
const user5Allocated = await selectLicenseByAllocatedUser(source, user5.id);
|
||||
const user6Allocated = await selectLicenseByAllocatedUser(source, user6.id);
|
||||
const user7Allocated = await selectLicenseByAllocatedUser(source, user7.id);
|
||||
const admin2Allocated = await selectLicenseByAllocatedUser(
|
||||
source,
|
||||
account2.admin.id
|
||||
);
|
||||
const licenseAllocationHistory = await selectLicenseAllocationHistory(
|
||||
source,
|
||||
user1.id,
|
||||
104
|
||||
);
|
||||
// Author、Typist、Noneの優先順位で割り当てられていることを確認
|
||||
expect(user1Allocated.license?.id).toBe(104);
|
||||
expect(user2Allocated.license?.id).toBe(101);
|
||||
expect(user3Allocated.license?.id).toBe(103);
|
||||
// 有効期限がまだあるので、ライセンスが更新されていないことを確認
|
||||
expect(user4Allocated.license?.id).toBe(4);
|
||||
// auto_renewがfalseなので、ライセンスが更新されていないことを確認
|
||||
expect(user5Allocated.license?.id).toBe(5);
|
||||
// 複数Authorがいる場合、それぞれに割り当てられていることを確認
|
||||
expect(user6Allocated.license?.id).toBe(102);
|
||||
// ライセンスが足りない場合、ライセンスが更新されていないことを確認
|
||||
expect(user7Allocated.license?.id).toBe(7);
|
||||
// 複数アカウント分の処理が正常に行われていることの確認
|
||||
expect(admin2Allocated.license?.id).toBe(200);
|
||||
|
||||
// ライセンス割り当て履歴テーブルが更新されていることを確認
|
||||
expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe(
|
||||
user1.id
|
||||
);
|
||||
expect(
|
||||
licenseAllocationHistory.licenseAllocationHistory?.is_allocated
|
||||
).toBe(true);
|
||||
expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
|
||||
account1.account.id
|
||||
);
|
||||
});
|
||||
|
||||
it("有効期限が指定日のライセンスが自動更新されること(リトライ用)", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
// 11/22の日付を作成
|
||||
const dateSeptember22 = new Date();
|
||||
dateSeptember22.setMonth(11);
|
||||
dateSeptember22.setDate(22);
|
||||
dateSeptember22.setHours(23, 59, 59);
|
||||
const currentDateEndTime = new DateWithDayEndTime(dateSeptember22);
|
||||
|
||||
// アカウント
|
||||
const account1 = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ role: `${USER_ROLES.NONE}` }
|
||||
);
|
||||
const account2 = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ role: `${USER_ROLES.NONE}` }
|
||||
);
|
||||
|
||||
// 更新対象のユーザー(3role分)
|
||||
const user1 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
const user2 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.AUTHOR}`,
|
||||
});
|
||||
const user3 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.TYPIST}`,
|
||||
});
|
||||
|
||||
// 更新対象ではないユーザー(まだ有効期限が残っている)
|
||||
const user4 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
|
||||
// 更新対象ではないユーザー(auto_renewがfalse)
|
||||
const user5 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
auto_renew: false,
|
||||
});
|
||||
|
||||
// 割り当て済みで有効期限が12/31のライセンス
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user1.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
2,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user2.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
3,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user3.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
20,
|
||||
currentDateEndTime,
|
||||
account2.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
account2.admin.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
5,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user5.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 割り当て済みの更新対象ではないライセンス
|
||||
const nextDate = new Date();
|
||||
nextDate.setDate(dateSeptember22.getDate() + 1);
|
||||
nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
nextDate.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
4,
|
||||
nextDate,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user4.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 有効期限が先の未割当ライセンスを作成
|
||||
// idが100のものは有効期限が当日なので自動割り当て対象外
|
||||
// idが101のものから割り当てられる
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const date = new Date();
|
||||
date.setDate(dateSeptember22.getDate() + i);
|
||||
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
date.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
i + 100,
|
||||
date,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
const dateMarch31 = new Date();
|
||||
dateMarch31.setMonth(12);
|
||||
dateMarch31.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
dateMarch31.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
200,
|
||||
dateMarch31,
|
||||
account2.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAutoAllocationProcessing(context, source, dateSeptember22);
|
||||
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
|
||||
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
|
||||
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
|
||||
const user4Allocated = await selectLicenseByAllocatedUser(source, user4.id);
|
||||
const user5Allocated = await selectLicenseByAllocatedUser(source, user5.id);
|
||||
const admin2Allocated = await selectLicenseByAllocatedUser(
|
||||
source,
|
||||
account2.admin.id
|
||||
);
|
||||
const licenseAllocationHistory = await selectLicenseAllocationHistory(
|
||||
source,
|
||||
user1.id,
|
||||
103
|
||||
);
|
||||
// Author、Typist、Noneの優先順位で割り当てられていることを確認
|
||||
expect(user1Allocated.license?.id).toBe(103);
|
||||
expect(user2Allocated.license?.id).toBe(101);
|
||||
expect(user3Allocated.license?.id).toBe(102);
|
||||
// 有効期限がまだあるので、ライセンスが更新されていないことを確認
|
||||
expect(user4Allocated.license?.id).toBe(4);
|
||||
// auto_renewがfalseなので、ライセンスが更新されていないことを確認
|
||||
expect(user5Allocated.license?.id).toBe(5);
|
||||
// 複数アカウント分の処理が正常に行われていることの確認
|
||||
expect(admin2Allocated.license?.id).toBe(200);
|
||||
|
||||
// ライセンス割り当て履歴テーブルが更新されていることを確認
|
||||
expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe(
|
||||
user1.id
|
||||
);
|
||||
expect(
|
||||
licenseAllocationHistory.licenseAllocationHistory?.is_allocated
|
||||
).toBe(true);
|
||||
expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
|
||||
account1.account.id
|
||||
);
|
||||
});
|
||||
it("新たに割り当てられるライセンスが存在しないため、ライセンスが自動更新されない(エラーではない)", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
|
||||
const currentDateEndTime = new DateWithDayEndTime();
|
||||
|
||||
// アカウント
|
||||
const account1 = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 5 },
|
||||
{ role: `${USER_ROLES.NONE}` }
|
||||
);
|
||||
|
||||
// 更新対象のユーザー(3role分)
|
||||
const user1 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
const user2 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.AUTHOR}`,
|
||||
});
|
||||
const user3 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.TYPIST}`,
|
||||
});
|
||||
|
||||
// 割り当て済みで有効期限が本日のライセンス
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user1.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
2,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user2.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
3,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user3.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
await licenseAutoAllocationProcessing(context, source);
|
||||
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
|
||||
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
|
||||
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
|
||||
// ライセンスが更新されていないことを確認
|
||||
expect(user1Allocated.license?.id).toBe(1);
|
||||
expect(user2Allocated.license?.id).toBe(2);
|
||||
expect(user3Allocated.license?.id).toBe(3);
|
||||
});
|
||||
|
||||
it("tier4のアカウントのため、ライセンスが自動更新されない", async () => {
|
||||
if (!source) fail();
|
||||
const context = new InvocationContext();
|
||||
|
||||
const currentDateEndTime = new DateWithDayEndTime();
|
||||
|
||||
// アカウント
|
||||
const account1 = await makeTestAccount(
|
||||
source,
|
||||
{ tier: 4 },
|
||||
{ role: `${USER_ROLES.NONE}` }
|
||||
);
|
||||
|
||||
// 更新対象のユーザー(3role分)
|
||||
const user1 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.NONE}`,
|
||||
});
|
||||
const user2 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.AUTHOR}`,
|
||||
});
|
||||
const user3 = await makeTestUser(source, {
|
||||
account_id: account1.account.id,
|
||||
role: `${USER_ROLES.TYPIST}`,
|
||||
});
|
||||
|
||||
// 割り当て済みで有効期限が本日のライセンス
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user1.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
2,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user2.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
await createLicense(
|
||||
source,
|
||||
3,
|
||||
currentDateEndTime,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
user3.id,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// 有効期限が先の未割当ライセンスを作成
|
||||
// idが100,101のものは有効期限が当日、翌日なので自動割り当て対象外
|
||||
// idが102のものから割り当てられる
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + i);
|
||||
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
date.setMilliseconds(0);
|
||||
await createLicense(
|
||||
source,
|
||||
i + 100,
|
||||
date,
|
||||
account1.account.id,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
await licenseAutoAllocationProcessing(context, source);
|
||||
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
|
||||
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
|
||||
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
|
||||
// ライセンスが更新されていないことを確認
|
||||
expect(user1Allocated.license?.id).toBe(1);
|
||||
expect(user2Allocated.license?.id).toBe(2);
|
||||
expect(user3Allocated.license?.id).toBe(3);
|
||||
});
|
||||
});
|
||||
2
dictation_server/.vscode/settings.json
vendored
2
dictation_server/.vscode/settings.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"eslint.format.enable": false,
|
||||
"[javascript]": {
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
-- +migrate Up
|
||||
ALTER TABLE `license_orders` ADD INDEX `idx_from_account_id_and_po_number` (from_account_id,po_number);
|
||||
|
||||
-- +migrate Down
|
||||
ALTER TABLE `license_orders` DROP INDEX `idx_from_account_id_and_po_number`;
|
||||
@ -1,5 +1,9 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"assets": ["templates/**/*.html", "templates/**/*.txt"],
|
||||
"watchAssets": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,8 @@ export const ErrorCodes = [
|
||||
'E000107', // トークン不足エラー
|
||||
'E000108', // トークン権限エラー
|
||||
'E000301', // ADB2Cへのリクエスト上限超過エラー
|
||||
'E000401', // IPアドレス未設定エラー
|
||||
'E000501', // リクエストID未設定エラー
|
||||
'E010001', // パラメータ形式不正エラー
|
||||
'E010201', // 未認証ユーザエラー
|
||||
'E010202', // 認証済ユーザエラー
|
||||
|
||||
@ -12,6 +12,8 @@ export const errors: Errors = {
|
||||
E000107: 'Token is not exist Error.',
|
||||
E000108: 'Token authority failed Error.',
|
||||
E000301: 'ADB2C request limit exceeded Error',
|
||||
E000401: 'IP address not found Error.',
|
||||
E000501: 'Request ID not found Error.',
|
||||
E010001: 'Param invalid format Error.',
|
||||
E010201: 'Email not verified user Error.',
|
||||
E010202: 'Email already verified user Error.',
|
||||
|
||||
@ -1,8 +1,32 @@
|
||||
import { Request } from 'express';
|
||||
import { Context } from './types';
|
||||
|
||||
export const makeContext = (
|
||||
externalId: string,
|
||||
requestId: string,
|
||||
delegationId?: string,
|
||||
): Context => {
|
||||
return new Context(externalId, delegationId);
|
||||
return new Context(externalId, requestId, delegationId);
|
||||
};
|
||||
|
||||
// リクエストヘッダーからrequestIdを取得する
|
||||
export const retrieveRequestId = (req: Request): string | undefined => {
|
||||
return req.header('x-request-id');
|
||||
};
|
||||
|
||||
/**
|
||||
* リクエストのIPアドレスを取得します
|
||||
* @param {Request}
|
||||
* @return {string | undefined}
|
||||
*/
|
||||
export const retrieveIp = (req: Request): string | undefined => {
|
||||
// ローカル環境では直近の送信元IPを取得する
|
||||
if (process.env.STAGE === 'local') {
|
||||
return req.ip;
|
||||
}
|
||||
const ip = req.header('x-forwarded-for');
|
||||
if (typeof ip === 'string') {
|
||||
return ip;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Context } from './types';
|
||||
import { makeContext } from './context';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from './context';
|
||||
|
||||
export { Context, makeContext };
|
||||
export { Context, makeContext, retrieveRequestId, retrieveIp };
|
||||
|
||||
@ -3,23 +3,32 @@ export class Context {
|
||||
* APIの操作ユーザーを追跡するためのID
|
||||
*/
|
||||
trackingId: string;
|
||||
/**
|
||||
* APIの操作ユーザーのIPアドレス
|
||||
*/
|
||||
ip: string;
|
||||
/**
|
||||
* ユーザーの操作を一意に識別するためのID
|
||||
*/
|
||||
requestId: string;
|
||||
/**
|
||||
* APIの代行操作ユーザーを追跡するためのID
|
||||
*/
|
||||
delegationId?: string | undefined;
|
||||
|
||||
constructor(externalId: string, delegationId?: string) {
|
||||
constructor(externalId: string, requestId: string, delegationId?: string) {
|
||||
this.trackingId = externalId;
|
||||
this.delegationId = delegationId;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
/**
|
||||
* ログにユーザーを特定する情報を出力する
|
||||
*/
|
||||
getTrackingId(): string {
|
||||
if (this.delegationId) {
|
||||
return `${this.trackingId} by ${this.delegationId}`;
|
||||
return `${this.requestId}_${this.trackingId} by ${this.delegationId}`;
|
||||
} else {
|
||||
return this.trackingId;
|
||||
return `${this.requestId}_${this.trackingId}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
export class LoggerMiddleware implements NestMiddleware {
|
||||
private readonly logger = new Logger(LoggerMiddleware.name);
|
||||
|
||||
use(req: Request, res: Response, next: () => void): void {
|
||||
// ここで一意のリクエストIDを生成して、リクエストヘッダーに設定する
|
||||
const requestId = uuidv4();
|
||||
req.headers['x-request-id'] = requestId;
|
||||
|
||||
this.logger.log(this.createReqMsg(req));
|
||||
|
||||
res.on('close', () => {
|
||||
@ -15,13 +20,17 @@ export class LoggerMiddleware implements NestMiddleware {
|
||||
}
|
||||
|
||||
private createReqMsg(req: Request): string {
|
||||
const message = `Request [url=${req.url}, method=${req.method}]`;
|
||||
const message = `[${req.header('x-request-id')}] Request [url=${
|
||||
req.url
|
||||
}, method=${req.method}]`;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private createResMsg(res: Response): string {
|
||||
const message = `Response [statusCode=${res.statusCode}, message=${res.statusMessage}]`;
|
||||
const message = `[${res.req.header('x-request-id')}] Response [statusCode=${
|
||||
res.statusCode
|
||||
}, message=${res.statusMessage}]`;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
143
dictation_server/src/common/repository/index.ts
Normal file
143
dictation_server/src/common/repository/index.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import {
|
||||
ObjectLiteral,
|
||||
Repository,
|
||||
EntityTarget,
|
||||
UpdateResult,
|
||||
DeleteResult,
|
||||
UpdateQueryBuilder,
|
||||
Brackets,
|
||||
FindOptionsWhere,
|
||||
} from 'typeorm';
|
||||
import { Context } from '../log';
|
||||
|
||||
/**
|
||||
* VS Code上で型解析エラーが発生するため、typeorm内の型定義と同一の型定義をここに記述する
|
||||
*/
|
||||
type QueryDeepPartialEntity<T> = _QueryDeepPartialEntity<
|
||||
ObjectLiteral extends T ? unknown : T
|
||||
>;
|
||||
type _QueryDeepPartialEntity<T> = {
|
||||
[P in keyof T]?:
|
||||
| (T[P] extends Array<infer U>
|
||||
? Array<_QueryDeepPartialEntity<U>>
|
||||
: T[P] extends ReadonlyArray<infer U>
|
||||
? ReadonlyArray<_QueryDeepPartialEntity<U>>
|
||||
: _QueryDeepPartialEntity<T[P]>)
|
||||
| (() => string);
|
||||
};
|
||||
|
||||
interface InsertEntityOptions {
|
||||
id: number;
|
||||
}
|
||||
|
||||
const insertEntity = async <T extends InsertEntityOptions & ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
repository: Repository<T>,
|
||||
value: QueryDeepPartialEntity<T>,
|
||||
isCommentOut: boolean,
|
||||
context: Context,
|
||||
): Promise<T> => {
|
||||
let query = repository.createQueryBuilder().insert().into(entity);
|
||||
if (isCommentOut) {
|
||||
query = query.comment(
|
||||
`${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
);
|
||||
}
|
||||
const result = await query.values(value).execute();
|
||||
// result.identifiers[0].idがnumber型でない場合はエラー
|
||||
if (typeof result.identifiers[0].id !== 'number') {
|
||||
throw new Error('Failed to insert entity');
|
||||
}
|
||||
const where: FindOptionsWhere<T> = { id: result.identifiers[0].id } as T;
|
||||
|
||||
// 結果をもとにセレクトする
|
||||
const inserted = await repository.findOne({
|
||||
where,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!inserted) {
|
||||
throw new Error('Failed to insert entity');
|
||||
}
|
||||
return inserted;
|
||||
};
|
||||
|
||||
const insertEntities = async <T extends InsertEntityOptions & ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
repository: Repository<T>,
|
||||
values: QueryDeepPartialEntity<T>[],
|
||||
isCommentOut: boolean,
|
||||
context: Context,
|
||||
): Promise<T[]> => {
|
||||
let query = repository.createQueryBuilder().insert().into(entity);
|
||||
if (isCommentOut) {
|
||||
query = query.comment(
|
||||
`${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
);
|
||||
}
|
||||
const result = await query.values(values).execute();
|
||||
|
||||
// 挿入するレコードが0で、結果も0であれば、からの配列を返す
|
||||
if (values.length === 0 && result.identifiers.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 挿入するレコード数と挿入されたレコード数が一致しない場合はエラー
|
||||
if (result.identifiers.length !== values.length) {
|
||||
throw new Error('Failed to insert entities');
|
||||
}
|
||||
const where: FindOptionsWhere<T>[] = result.identifiers.map((i) => {
|
||||
// idがnumber型でない場合はエラー
|
||||
if (typeof i.id !== 'number') {
|
||||
throw new Error('Failed to insert entities');
|
||||
}
|
||||
return { id: i.id } as T;
|
||||
});
|
||||
|
||||
// 結果をもとにセレクトする
|
||||
const inserted = await repository.find({
|
||||
where,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!inserted) {
|
||||
throw new Error('Failed to insert entity');
|
||||
}
|
||||
return inserted;
|
||||
};
|
||||
|
||||
const updateEntity = async <T extends ObjectLiteral>(
|
||||
repository: Repository<T>,
|
||||
criteria:
|
||||
| string
|
||||
| ((qb: UpdateQueryBuilder<T>) => string)
|
||||
| Brackets
|
||||
| ObjectLiteral
|
||||
| ObjectLiteral[],
|
||||
values: QueryDeepPartialEntity<T>,
|
||||
isCommentOut: boolean,
|
||||
context: Context,
|
||||
): Promise<UpdateResult> => {
|
||||
let query = repository.createQueryBuilder().update();
|
||||
if (isCommentOut) {
|
||||
query = query.comment(
|
||||
`${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
);
|
||||
}
|
||||
return await query.set(values).where(criteria).execute();
|
||||
};
|
||||
|
||||
const deleteEntity = async <T extends ObjectLiteral>(
|
||||
repository: Repository<T>,
|
||||
criteria: string | Brackets | ObjectLiteral | ObjectLiteral[],
|
||||
isCommentOut: boolean,
|
||||
context: Context,
|
||||
): Promise<DeleteResult> => {
|
||||
let query = repository.createQueryBuilder().delete();
|
||||
if (isCommentOut) {
|
||||
query = query.comment(
|
||||
`${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
);
|
||||
}
|
||||
return await query.where(criteria).execute();
|
||||
};
|
||||
|
||||
export { insertEntity, insertEntities, updateEntity, deleteEntity };
|
||||
@ -34,6 +34,7 @@ export const overrideAdB2cService = <TService>(
|
||||
context: Context,
|
||||
externalIds: string[],
|
||||
) => Promise<AdB2cUser[]>;
|
||||
getUser?: (context: Context, externalId: string) => Promise<AdB2cUser>;
|
||||
},
|
||||
): void => {
|
||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||
@ -62,6 +63,12 @@ export const overrideAdB2cService = <TService>(
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.getUser) {
|
||||
Object.defineProperty(obj, obj.getUser.name, {
|
||||
value: overrides.getUser,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -73,12 +80,6 @@ export const overrideAdB2cService = <TService>(
|
||||
export const overrideSendgridService = <TService>(
|
||||
service: TService,
|
||||
overrides: {
|
||||
createMailContentFromEmailConfirm?: (
|
||||
context: Context,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
email: string,
|
||||
) => Promise<{ subject: string; text: string; html: string }>;
|
||||
createMailContentFromEmailConfirmForNormalUser?: (
|
||||
accountId: number,
|
||||
userId: number,
|
||||
@ -113,13 +114,6 @@ export const overrideSendgridService = <TService>(
|
||||
});
|
||||
}
|
||||
|
||||
if (overrides.createMailContentFromEmailConfirm) {
|
||||
Object.defineProperty(obj, obj.createMailContentFromEmailConfirm.name, {
|
||||
value: overrides.createMailContentFromEmailConfirm,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (overrides.createMailContentFromEmailConfirmForNormalUser) {
|
||||
Object.defineProperty(
|
||||
obj,
|
||||
|
||||
@ -295,3 +295,9 @@ export const TERM_TYPE = {
|
||||
* @const {string}
|
||||
*/
|
||||
export const USER_AUDIO_FORMAT = 'DS2(QP)';
|
||||
|
||||
/**
|
||||
* ユニットテスト実行をしている場合のNODE_ENVの値
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const NODE_ENV_TEST = 'test';
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
Param,
|
||||
Query,
|
||||
HttpException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiOperation,
|
||||
@ -77,14 +78,14 @@ import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@ApiTags('accounts')
|
||||
@Controller('accounts')
|
||||
export class AccountsController {
|
||||
private readonly logger = new Logger(AccountsController.name);
|
||||
constructor(
|
||||
private readonly accountService: AccountsService, //private readonly cryptoService: CryptoService,
|
||||
private readonly authService: AuthService,
|
||||
@ -109,6 +110,7 @@ export class AccountsController {
|
||||
@ApiOperation({ operationId: 'createAccount' })
|
||||
async createAccount(
|
||||
@Body() body: CreateAccountRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<CreateAccountResponse> {
|
||||
const {
|
||||
companyName,
|
||||
@ -123,7 +125,24 @@ export class AccountsController {
|
||||
} = body;
|
||||
const role = USER_ROLES.NONE;
|
||||
|
||||
const context = makeContext(uuidv4());
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.createAccount(
|
||||
context,
|
||||
@ -178,6 +197,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -186,7 +221,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
const response = await this.accountService.getLicenseSummary(
|
||||
context,
|
||||
body.accountId,
|
||||
@ -232,6 +269,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -240,7 +293,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
//アカウントID取得処理
|
||||
const accountInfo = await this.accountService.getAccountInfo(
|
||||
context,
|
||||
@ -283,6 +338,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -291,7 +362,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const authors = await this.accountService.getAuthors(context, userId);
|
||||
|
||||
@ -330,6 +403,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -338,7 +427,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const typists = await this.accountService.getTypists(context, userId);
|
||||
|
||||
@ -377,6 +468,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -385,7 +492,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const typistGroups = await this.accountService.getTypistGroups(
|
||||
context,
|
||||
@ -441,6 +550,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -450,7 +575,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const typistGroup = await this.accountService.getTypistGroup(
|
||||
context,
|
||||
@ -506,6 +632,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -514,7 +656,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.accountService.createTypistGroup(
|
||||
context,
|
||||
userId,
|
||||
@ -572,6 +716,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -581,7 +741,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.updateTypistGroup(
|
||||
context,
|
||||
@ -637,6 +798,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -646,7 +823,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId, tier } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.createPartnerAccount(
|
||||
context,
|
||||
@ -699,6 +877,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -708,7 +902,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const getPartnerLicensesResponse =
|
||||
await this.accountService.getPartnerLicenses(
|
||||
@ -759,6 +954,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -768,7 +979,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const getOrderHistoriesResponse =
|
||||
await this.accountService.getOrderHistories(
|
||||
@ -825,6 +1037,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -834,7 +1062,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId, tier } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.accountService.issueLicense(
|
||||
context,
|
||||
orderedAccountId,
|
||||
@ -857,8 +1086,25 @@ export class AccountsController {
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'getDealers' })
|
||||
async getDealers(): Promise<GetDealersResponse> {
|
||||
const context = makeContext(uuidv4());
|
||||
async getDealers(@Req() req: Request): Promise<GetDealersResponse> {
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
return await this.accountService.getDealers(context);
|
||||
}
|
||||
|
||||
@ -907,6 +1153,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -916,7 +1178,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.cancelIssue(
|
||||
context,
|
||||
@ -957,6 +1220,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -966,7 +1245,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
const worktypes = await this.accountService.getWorktypes(context, userId);
|
||||
|
||||
return worktypes;
|
||||
@ -1012,6 +1292,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1021,7 +1317,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.accountService.createWorktype(
|
||||
context,
|
||||
userId,
|
||||
@ -1074,6 +1371,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1083,7 +1396,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.updateWorktype(
|
||||
context,
|
||||
@ -1136,6 +1450,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1145,7 +1475,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.deleteWorktype(context, userId, id);
|
||||
return {};
|
||||
@ -1191,6 +1522,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1200,7 +1547,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const optionItems = await this.accountService.getOptionItems(
|
||||
context,
|
||||
@ -1253,6 +1601,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1262,7 +1626,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.updateOptionItems(
|
||||
context,
|
||||
@ -1314,6 +1679,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1323,7 +1704,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.updateActiveWorktype(context, userId, id);
|
||||
return {};
|
||||
@ -1372,6 +1754,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1381,7 +1779,8 @@ export class AccountsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
const response = await this.accountService.getPartners(
|
||||
context,
|
||||
userId,
|
||||
@ -1439,6 +1838,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1447,7 +1862,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId, tier } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.updateAccountInfo(
|
||||
context,
|
||||
@ -1499,6 +1916,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1507,7 +1940,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.accountService.deleteAccountAndData(context, userId, accountId);
|
||||
return {};
|
||||
@ -1532,8 +1967,25 @@ export class AccountsController {
|
||||
@ApiOperation({ operationId: 'getAccountInfoMinimalAccess' })
|
||||
async getAccountInfoMinimalAccess(
|
||||
@Body() body: GetAccountInfoMinimalAccessRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<GetAccountInfoMinimalAccessResponse> {
|
||||
const context = makeContext(uuidv4());
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
// IDトークンの検証
|
||||
const idToken = await this.authService.getVerifiedIdToken(
|
||||
@ -1591,6 +2043,22 @@ export class AccountsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -1599,7 +2067,9 @@ export class AccountsController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
const companyName = await this.accountService.getCompanyName(
|
||||
context,
|
||||
body.accountId,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -13,9 +13,9 @@ import { User } from '../../repositories/users/entity/user.entity';
|
||||
import {
|
||||
TIERS,
|
||||
USER_ROLES,
|
||||
ADB2C_SIGN_IN_TYPE,
|
||||
OPTION_ITEM_VALUE_TYPE,
|
||||
MANUAL_RECOVERY_REQUIRED,
|
||||
LICENSE_ISSUE_STATUS,
|
||||
} from '../../constants';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import {
|
||||
@ -71,6 +71,7 @@ import {
|
||||
WorktypeIdMaxCountError,
|
||||
WorktypeIdNotFoundError,
|
||||
} from '../../repositories/worktypes/errors/types';
|
||||
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||
|
||||
@Injectable()
|
||||
export class AccountsService {
|
||||
@ -111,6 +112,7 @@ export class AccountsService {
|
||||
|
||||
const { licenseSummary, isStorageAvailable } =
|
||||
await this.accountRepository.getLicenseSummaryInfo(
|
||||
context,
|
||||
accountId,
|
||||
currentDate,
|
||||
expiringSoonDate,
|
||||
@ -228,6 +230,7 @@ export class AccountsService {
|
||||
// アカウントと管理者をセットで作成
|
||||
const { newAccount, adminUser } =
|
||||
await this.accountRepository.createAccount(
|
||||
context,
|
||||
companyName,
|
||||
country,
|
||||
dealerAccountId,
|
||||
@ -284,23 +287,11 @@ export class AccountsService {
|
||||
}
|
||||
|
||||
try {
|
||||
// メールの内容を構成
|
||||
const { subject, text, html } =
|
||||
await this.sendgridService.createMailContentFromEmailConfirm(
|
||||
context,
|
||||
account.id,
|
||||
user.id,
|
||||
email,
|
||||
);
|
||||
|
||||
// メールを送信
|
||||
await this.sendgridService.sendMail(
|
||||
await this.sendgridService.sendMailWithU102(
|
||||
context,
|
||||
email,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
account.id,
|
||||
user.id,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -376,7 +367,7 @@ export class AccountsService {
|
||||
} | params: { accountId: ${accountId}, userId: ${userId} };`,
|
||||
);
|
||||
try {
|
||||
await this.accountRepository.deleteAccount(accountId, userId);
|
||||
await this.accountRepository.deleteAccount(context, accountId, userId);
|
||||
this.logger.log(
|
||||
`[${context.getTrackingId()}] delete account: ${accountId}, user: ${userId}`,
|
||||
);
|
||||
@ -439,17 +430,20 @@ export class AccountsService {
|
||||
} | params: { ` + `externalId: ${externalId}, };`,
|
||||
);
|
||||
try {
|
||||
let userInfo: User;
|
||||
userInfo = await this.usersRepository.findUserByExternalId(externalId);
|
||||
const userInfo = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
let accountInfo: Account;
|
||||
accountInfo = await this.accountRepository.findAccountById(
|
||||
const accountInfo = await this.accountRepository.findAccountById(
|
||||
context,
|
||||
userInfo.account_id,
|
||||
);
|
||||
|
||||
let parentInfo: Account | undefined;
|
||||
if (accountInfo.parent_account_id) {
|
||||
parentInfo = await this.accountRepository.findAccountById(
|
||||
context,
|
||||
accountInfo.parent_account_id,
|
||||
);
|
||||
}
|
||||
@ -505,8 +499,12 @@ export class AccountsService {
|
||||
|
||||
// TypistGroup取得
|
||||
try {
|
||||
const user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
const userGroups = await this.userGroupsRepository.getUserGroups(
|
||||
context,
|
||||
user.account_id,
|
||||
);
|
||||
|
||||
@ -543,10 +541,12 @@ export class AccountsService {
|
||||
|
||||
try {
|
||||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
const { name, userGroupMembers } =
|
||||
await this.userGroupsRepository.getTypistGroup(
|
||||
context,
|
||||
account_id,
|
||||
typistGroupId,
|
||||
);
|
||||
@ -602,16 +602,13 @@ export class AccountsService {
|
||||
// Typist取得
|
||||
try {
|
||||
const typistUsers = await this.usersRepository.findTypistUsers(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
const externalIds = typistUsers.map((x) => x.external_id);
|
||||
|
||||
// B2Cからユーザー名を取得する
|
||||
const trackingId = new Context(context.trackingId);
|
||||
const adb2cUsers = await this.adB2cService.getUsers(
|
||||
trackingId,
|
||||
externalIds,
|
||||
);
|
||||
const adb2cUsers = await this.adB2cService.getUsers(context, externalIds);
|
||||
|
||||
const typists = typistUsers.map((x) => {
|
||||
const user = adb2cUsers.find((adb2c) => adb2c.id === x.external_id);
|
||||
@ -655,6 +652,7 @@ export class AccountsService {
|
||||
|
||||
try {
|
||||
const { account } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
@ -665,6 +663,7 @@ export class AccountsService {
|
||||
}
|
||||
|
||||
const authorUsers = await this.usersRepository.findAuthorUsers(
|
||||
context,
|
||||
account.id,
|
||||
);
|
||||
|
||||
@ -737,7 +736,10 @@ export class AccountsService {
|
||||
try {
|
||||
// アクセストークンからユーザーIDを取得する
|
||||
myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(creatorUserId)
|
||||
await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
creatorUserId,
|
||||
)
|
||||
).account_id;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -787,6 +789,7 @@ export class AccountsService {
|
||||
// アカウントと管理者をセットで作成
|
||||
const { newAccount, adminUser } =
|
||||
await this.accountRepository.createAccount(
|
||||
context,
|
||||
companyName,
|
||||
country,
|
||||
myAccountId,
|
||||
@ -848,7 +851,8 @@ export class AccountsService {
|
||||
);
|
||||
await this.sendgridService.sendMail(
|
||||
context,
|
||||
email,
|
||||
[email],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
@ -913,6 +917,7 @@ export class AccountsService {
|
||||
|
||||
const getPartnerLicenseResult =
|
||||
await this.accountRepository.getPartnerLicense(
|
||||
context,
|
||||
accountId,
|
||||
currentDate,
|
||||
expiringSoonDate,
|
||||
@ -941,7 +946,7 @@ export class AccountsService {
|
||||
for (const childPartnerLicenseFromRepository of getPartnerLicenseResult.childPartnerLicensesFromRepository) {
|
||||
const { allocatableLicenseWithMargin, expiringSoonLicense } =
|
||||
childPartnerLicenseFromRepository;
|
||||
let childShortage: number = 0;
|
||||
let childShortage = 0;
|
||||
if (childPartnerLicenseFromRepository.tier === TIERS.TIER5) {
|
||||
if (
|
||||
allocatableLicenseWithMargin === undefined ||
|
||||
@ -1012,6 +1017,7 @@ export class AccountsService {
|
||||
try {
|
||||
const licenseHistoryInfo =
|
||||
await this.licensesRepository.getLicenseOrderHistoryInfo(
|
||||
context,
|
||||
accountId,
|
||||
offset,
|
||||
limit,
|
||||
@ -1023,16 +1029,10 @@ export class AccountsService {
|
||||
const returnLicenseOrder: LicenseOrder = {
|
||||
issueDate:
|
||||
licenseOrder.issued_at !== null
|
||||
? new Date(licenseOrder.issued_at)
|
||||
.toISOString()
|
||||
.substring(0, 10)
|
||||
.replace(/-/g, '/')
|
||||
? new Date(licenseOrder.issued_at).toISOString()
|
||||
: undefined,
|
||||
numberOfOrder: licenseOrder.quantity,
|
||||
orderDate: new Date(licenseOrder.ordered_at)
|
||||
.toISOString()
|
||||
.substring(0, 10)
|
||||
.replace(/-/g, '/'),
|
||||
orderDate: new Date(licenseOrder.ordered_at).toISOString(),
|
||||
poNumber: licenseOrder.po_number,
|
||||
status: licenseOrder.status,
|
||||
};
|
||||
@ -1083,14 +1083,57 @@ export class AccountsService {
|
||||
try {
|
||||
// アクセストークンからユーザーIDを取得する
|
||||
const myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(userId)
|
||||
await this.usersRepository.findUserByExternalId(context, userId)
|
||||
).account_id;
|
||||
await this.licensesRepository.issueLicense(
|
||||
const { issuedOrderId } = await this.licensesRepository.issueLicense(
|
||||
context,
|
||||
orderedAccountId,
|
||||
myAccountId,
|
||||
tier,
|
||||
poNumber,
|
||||
);
|
||||
|
||||
try {
|
||||
// 発行済みの注文をID指定して取得する
|
||||
const orderLicense = await this.licensesRepository.getLicenseOrder(
|
||||
context,
|
||||
orderedAccountId,
|
||||
poNumber,
|
||||
issuedOrderId,
|
||||
);
|
||||
|
||||
if (orderLicense == null) {
|
||||
throw new Error(
|
||||
`issue target order not found. fromAccountId: ${orderedAccountId}, poNumber:${poNumber}`,
|
||||
);
|
||||
}
|
||||
// 未発行の注文の場合、エラー
|
||||
if (orderLicense.status !== LICENSE_ISSUE_STATUS.ISSUED) {
|
||||
throw new AlreadyIssuedError(
|
||||
`An order for PONumber:${poNumber} has not been issued.`,
|
||||
);
|
||||
}
|
||||
|
||||
// 注文したアカウントと自分のアカウントの情報を取得
|
||||
const customer = await this.getAccountInformation(
|
||||
context,
|
||||
orderedAccountId,
|
||||
);
|
||||
const dealer = await this.getAccountInformation(context, myAccountId);
|
||||
|
||||
await this.sendgridService.sendMailWithU107(
|
||||
context,
|
||||
customer.adminEmails,
|
||||
customer.companyName,
|
||||
orderLicense.quantity,
|
||||
poNumber,
|
||||
dealer.adminEmails,
|
||||
dealer.companyName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -1131,7 +1174,9 @@ export class AccountsService {
|
||||
);
|
||||
|
||||
try {
|
||||
const dealerAccounts = await this.accountRepository.findDealerAccounts();
|
||||
const dealerAccounts = await this.accountRepository.findDealerAccounts(
|
||||
context,
|
||||
);
|
||||
|
||||
const dealers: GetDealersResponse = {
|
||||
dealers: dealerAccounts.map((dealerAccount): Dealer => {
|
||||
@ -1181,10 +1226,12 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
// API実行ユーザーのアカウントIDでタイピストグループを作成し、タイピストグループとtypistIdsのユーザーを紐付ける
|
||||
await this.userGroupsRepository.createTypistGroup(
|
||||
context,
|
||||
typistGroupName,
|
||||
typistIds,
|
||||
account_id,
|
||||
@ -1244,11 +1291,13 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
// タイピストグループと所属するタイピストを更新する
|
||||
await this.userGroupsRepository.updateTypistGroup(
|
||||
context,
|
||||
account_id,
|
||||
typistGroupId,
|
||||
typistGroupName,
|
||||
@ -1314,7 +1363,7 @@ export class AccountsService {
|
||||
try {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(extarnalId)
|
||||
await this.usersRepository.findUserByExternalId(context, extarnalId)
|
||||
).account_id;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -1333,6 +1382,7 @@ export class AccountsService {
|
||||
|
||||
// 注文元アカウントIDの親世代を取得
|
||||
const parentAccountIds = await this.accountRepository.getHierarchyParents(
|
||||
context,
|
||||
orderedAccountId,
|
||||
);
|
||||
// 自身が存在しない場合、エラー
|
||||
@ -1348,7 +1398,42 @@ export class AccountsService {
|
||||
|
||||
try {
|
||||
// 発行キャンセル処理
|
||||
await this.accountRepository.cancelIssue(orderedAccountId, poNumber);
|
||||
const { canceledIssueLicenseOrderId } =
|
||||
await this.accountRepository.cancelIssue(
|
||||
context,
|
||||
orderedAccountId,
|
||||
poNumber,
|
||||
);
|
||||
try {
|
||||
// 発行キャンセルされ、発行済状態から注文中状態に戻った注文を取得する
|
||||
const order = await this.licensesRepository.getLicenseOrder(
|
||||
context,
|
||||
orderedAccountId,
|
||||
poNumber,
|
||||
canceledIssueLicenseOrderId,
|
||||
);
|
||||
if (order == null) {
|
||||
throw new Error('order not found.');
|
||||
}
|
||||
const { quantity, from_account_id, to_account_id } = order;
|
||||
const customer = await this.getAccountInformation(
|
||||
context,
|
||||
from_account_id,
|
||||
);
|
||||
const dealer = await this.getAccountInformation(context, to_account_id);
|
||||
await this.sendgridService.sendMailWithU109(
|
||||
context,
|
||||
dealer.adminEmails,
|
||||
dealer.companyName,
|
||||
quantity,
|
||||
poNumber,
|
||||
customer.adminEmails,
|
||||
customer.companyName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信の例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
switch (e.constructor) {
|
||||
@ -1398,11 +1483,11 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// ワークタイプ一覧とActiveWorktypeIDを取得する
|
||||
const { worktypes, active_worktype_id } =
|
||||
await this.worktypesRepository.getWorktypes(accountId);
|
||||
await this.worktypesRepository.getWorktypes(context, accountId);
|
||||
|
||||
return {
|
||||
worktypes: worktypes.map((x) => ({
|
||||
@ -1461,9 +1546,10 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
await this.worktypesRepository.createWorktype(
|
||||
context,
|
||||
accountId,
|
||||
worktypeId,
|
||||
description,
|
||||
@ -1531,10 +1617,11 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// ワークタイプを更新する
|
||||
await this.worktypesRepository.updateWorktype(
|
||||
context,
|
||||
accountId,
|
||||
id,
|
||||
worktypeId,
|
||||
@ -1597,7 +1684,7 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account, account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
if (!account) {
|
||||
throw new AccountNotFoundError(
|
||||
@ -1606,7 +1693,7 @@ export class AccountsService {
|
||||
}
|
||||
|
||||
// ワークタイプを削除する
|
||||
await this.worktypesRepository.deleteWorktype(accountId, id);
|
||||
await this.worktypesRepository.deleteWorktype(context, accountId, id);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -1673,10 +1760,11 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// オプションアイテム一覧を取得する
|
||||
const optionItems = await this.worktypesRepository.getOptionItems(
|
||||
context,
|
||||
accountId,
|
||||
id,
|
||||
);
|
||||
@ -1742,10 +1830,11 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// オプションアイテムを更新する
|
||||
await this.worktypesRepository.updateOptionItems(
|
||||
context,
|
||||
accountId,
|
||||
id,
|
||||
// initialValueはdefaultValueTypeがDEFAULTの場合以外は空文字を設定する
|
||||
@ -1808,10 +1897,14 @@ export class AccountsService {
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// ActiveWorktypeを更新する
|
||||
await this.accountRepository.updateActiveWorktypeId(accountId, id);
|
||||
await this.accountRepository.updateActiveWorktypeId(
|
||||
context,
|
||||
accountId,
|
||||
id,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -1865,9 +1958,10 @@ export class AccountsService {
|
||||
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
const partnersRecords = await this.accountRepository.getPartners(
|
||||
context,
|
||||
accountId,
|
||||
limit,
|
||||
offset,
|
||||
@ -1891,10 +1985,8 @@ export class AccountsService {
|
||||
);
|
||||
}
|
||||
|
||||
const primaryAdmin = adb2cUser.displayName;
|
||||
const mail = adb2cUser.identities?.find(
|
||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
)?.issuerAssignedId;
|
||||
const { displayName: primaryAdmin, emailAddress: mail } =
|
||||
getUserNameAndMailAddress(adb2cUser);
|
||||
if (!mail) {
|
||||
throw new Error(
|
||||
`adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`,
|
||||
@ -1962,9 +2054,10 @@ export class AccountsService {
|
||||
);
|
||||
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
const { account_id: accountId, account } =
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
await this.accountRepository.updateAccountInfo(
|
||||
context,
|
||||
accountId,
|
||||
tier,
|
||||
delegationPermission,
|
||||
@ -1972,6 +2065,52 @@ export class AccountsService {
|
||||
parentAccountId,
|
||||
secondryAdminUserId,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
if (account === null) {
|
||||
throw new Error(`account not found. accountId: ${accountId}`);
|
||||
}
|
||||
|
||||
let dealerName: string | null = null;
|
||||
if (parentAccountId !== undefined) {
|
||||
const dealer = await this.accountRepository.findAccountById(
|
||||
context,
|
||||
parentAccountId,
|
||||
);
|
||||
dealerName = dealer.company_name;
|
||||
}
|
||||
|
||||
const { external_id: externalId } =
|
||||
await this.usersRepository.findUserById(context, primaryAdminUserId);
|
||||
|
||||
const primaryAdmin = await this.adB2cService.getUser(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
const {
|
||||
displayName: primaryAdminName,
|
||||
emailAddress: primaryAdminEmail,
|
||||
} = getUserNameAndMailAddress(primaryAdmin);
|
||||
|
||||
if (!primaryAdminEmail) {
|
||||
throw new Error(
|
||||
`adb2c user mail not found. externalId: ${externalId}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.sendgridService.sendMailWithU112(
|
||||
context,
|
||||
primaryAdminName,
|
||||
primaryAdminEmail,
|
||||
account.company_name,
|
||||
dealerName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -2020,10 +2159,15 @@ export class AccountsService {
|
||||
);
|
||||
let country: string;
|
||||
let dbUsers: User[];
|
||||
|
||||
// メール送信に必要な情報
|
||||
let companyName: string | null = null;
|
||||
let primaryAdminName: string | null = null;
|
||||
let primaryAdminEmail: string | null = null;
|
||||
try {
|
||||
// パラメータとトークンから取得したアカウントIDの突き合わせ
|
||||
const { account_id: myAccountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
if (myAccountId !== accountId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000108'),
|
||||
@ -2033,10 +2177,41 @@ export class AccountsService {
|
||||
|
||||
// アカウント削除前に必要な情報を退避する
|
||||
const targetAccount = await this.accountRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// メール送信に必要な情報を取得する
|
||||
try {
|
||||
companyName = targetAccount.company_name;
|
||||
|
||||
if (!targetAccount.primary_admin_user_id) {
|
||||
throw new Error(
|
||||
`primary_admin_user_id not found. accountId: ${accountId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const primaryAdmin = await this.usersRepository.findUserById(
|
||||
context,
|
||||
targetAccount.primary_admin_user_id,
|
||||
);
|
||||
|
||||
const adb2cAdmin = await this.adB2cService.getUser(
|
||||
context,
|
||||
primaryAdmin.external_id,
|
||||
);
|
||||
const { displayName, emailAddress } =
|
||||
getUserNameAndMailAddress(adb2cAdmin);
|
||||
primaryAdminName = displayName;
|
||||
primaryAdminEmail = emailAddress ?? null;
|
||||
} catch (e) {
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
}
|
||||
|
||||
// 削除対象アカウントを削除する
|
||||
dbUsers = await this.accountRepository.deleteAccountAndInsertArchives(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
this.logger.log(
|
||||
@ -2090,6 +2265,29 @@ export class AccountsService {
|
||||
);
|
||||
}
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
if (companyName === null) {
|
||||
throw new Error('companyName is null');
|
||||
}
|
||||
if (primaryAdminName === null) {
|
||||
throw new Error('primaryAdminName is null');
|
||||
}
|
||||
if (primaryAdminEmail === null) {
|
||||
throw new Error('primaryAdminEmail is null');
|
||||
}
|
||||
|
||||
await this.sendgridService.sendMailWithU111(
|
||||
context,
|
||||
primaryAdminName,
|
||||
primaryAdminEmail,
|
||||
companyName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.deleteAccountAndData.name}`,
|
||||
);
|
||||
@ -2113,6 +2311,7 @@ export class AccountsService {
|
||||
|
||||
try {
|
||||
const { account } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
if (!account) {
|
||||
@ -2172,6 +2371,7 @@ export class AccountsService {
|
||||
|
||||
try {
|
||||
const { company_name } = await this.accountRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
@ -2202,4 +2402,51 @@ export class AccountsService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウントIDを指定して、アカウント情報と管理者情報を取得する
|
||||
* @param context
|
||||
* @param accountId 対象アカウントID
|
||||
* @returns 企業名/管理者メールアドレス
|
||||
*/
|
||||
private async getAccountInformation(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<{
|
||||
companyName: string;
|
||||
adminEmails: string[];
|
||||
}> {
|
||||
// アカウントIDから企業名を取得する
|
||||
const { company_name } = await this.accountRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// 管理者一覧を取得
|
||||
const admins = await this.usersRepository.findAdminUsers(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
const adminExternalIDs = admins.map((x) => x.external_id);
|
||||
|
||||
// ADB2Cから管理者IDを元にメールアドレスを取得する
|
||||
const usersInfo = await this.adB2cService.getUsers(
|
||||
context,
|
||||
adminExternalIDs,
|
||||
);
|
||||
|
||||
// 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する
|
||||
const adminEmails = usersInfo.map((x) => {
|
||||
const { emailAddress } = getUserNameAndMailAddress(x);
|
||||
if (emailAddress == null) {
|
||||
throw new Error(`admin email-address is not found. id=${x.id}`);
|
||||
}
|
||||
return emailAddress;
|
||||
});
|
||||
|
||||
return {
|
||||
companyName: company_name,
|
||||
adminEmails: adminEmails,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,11 +44,6 @@ export type AdB2cMockValue = {
|
||||
getUsers: AdB2cUser[] | Error;
|
||||
};
|
||||
export type SendGridMockValue = {
|
||||
createMailContentFromEmailConfirm: {
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
};
|
||||
createMailContentFromEmailConfirmForNormalUser: {
|
||||
subject: string;
|
||||
text: string;
|
||||
@ -242,20 +237,8 @@ export const makeConfigMock = (value: ConfigMockValue) => {
|
||||
};
|
||||
};
|
||||
export const makeSendGridServiceMock = (value: SendGridMockValue) => {
|
||||
const {
|
||||
createMailContentFromEmailConfirm,
|
||||
createMailContentFromEmailConfirmForNormalUser,
|
||||
sendMail,
|
||||
} = value;
|
||||
const { createMailContentFromEmailConfirmForNormalUser, sendMail } = value;
|
||||
return {
|
||||
createMailContentFromEmailConfirm:
|
||||
createMailContentFromEmailConfirm instanceof Error
|
||||
? jest
|
||||
.fn<Promise<void>, []>()
|
||||
.mockRejectedValue(createMailContentFromEmailConfirm)
|
||||
: jest
|
||||
.fn<Promise<{ subject: string; text: string; html: string }>, []>()
|
||||
.mockResolvedValue(createMailContentFromEmailConfirm),
|
||||
createMailContentFromEmailConfirmForNormalUser:
|
||||
createMailContentFromEmailConfirmForNormalUser instanceof Error
|
||||
? jest
|
||||
@ -475,7 +458,6 @@ export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
|
||||
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
|
||||
return {
|
||||
sendMail: undefined,
|
||||
createMailContentFromEmailConfirm: { subject: '', text: '', html: '' },
|
||||
createMailContentFromEmailConfirmForNormalUser: {
|
||||
subject: 'Verify your new account',
|
||||
text: `The verification URL.`,
|
||||
|
||||
@ -56,13 +56,14 @@ export const createLicenseSetExpiryDateAndStatus = async (
|
||||
accountId: number,
|
||||
expiryDate: Date | null,
|
||||
status: string,
|
||||
allocated_user_id?: number | null,
|
||||
): Promise<void> => {
|
||||
const { identifiers } = await datasource.getRepository(License).insert({
|
||||
expiry_date: expiryDate,
|
||||
account_id: accountId,
|
||||
type: 'NORMAL',
|
||||
status: status,
|
||||
allocated_user_id: null,
|
||||
allocated_user_id: allocated_user_id,
|
||||
order_id: null,
|
||||
deleted_at: null,
|
||||
delete_order_id: null,
|
||||
|
||||
@ -24,51 +24,336 @@ import { OPTION_ITEM_VALUE_TYPE } from '../../../constants';
|
||||
|
||||
export class CreateAccountRequest {
|
||||
@ApiProperty()
|
||||
@MaxLength(255)
|
||||
companyName: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: '国名(ISO 3166-1 alpha-2)',
|
||||
minLength: 2,
|
||||
maxLength: 2,
|
||||
})
|
||||
@MinLength(2)
|
||||
@MaxLength(2)
|
||||
country: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
dealerAccountId?: number;
|
||||
|
||||
@ApiProperty()
|
||||
@MaxLength(256) // AzureAdB2Cの仕様上、256文字まで[https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/user-profile-attributes]
|
||||
adminName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsEmail({ blacklisted_chars: '*' })
|
||||
adminMail: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsAdminPasswordvalid()
|
||||
adminPassword: string;
|
||||
|
||||
@ApiProperty({ description: '同意済み利用規約のバージョン(EULA)' })
|
||||
@MaxLength(255)
|
||||
acceptedEulaVersion: string;
|
||||
|
||||
@ApiProperty({ description: '同意済みプライバシーポリシーのバージョン' })
|
||||
@MaxLength(255)
|
||||
acceptedPrivacyNoticeVersion: string;
|
||||
|
||||
@ApiProperty({ description: '同意済み利用規約のバージョン(DPA)' })
|
||||
@MaxLength(255)
|
||||
acceptedDpaVersion: string;
|
||||
|
||||
@ApiProperty({ description: 'reCAPTCHA Token' })
|
||||
token: string;
|
||||
}
|
||||
|
||||
export class GetLicenseSummaryRequest {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class GetTypistGroupRequest {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
typistGroupId: number;
|
||||
}
|
||||
|
||||
export class CreateTypistGroupRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 50 })
|
||||
@MinLength(1)
|
||||
@MaxLength(50)
|
||||
typistGroupName: string;
|
||||
@ApiProperty({ minItems: 1, isArray: true, type: 'integer' })
|
||||
@ArrayMinSize(1)
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@Min(1, { each: true })
|
||||
@IsUnique()
|
||||
typistIds: number[];
|
||||
}
|
||||
|
||||
export class UpdateTypistGroupRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 50 })
|
||||
@MinLength(1)
|
||||
@MaxLength(50)
|
||||
typistGroupName: string;
|
||||
@ApiProperty({ minItems: 1, isArray: true, type: 'integer' })
|
||||
@ArrayMinSize(1)
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@Min(1, { each: true })
|
||||
@IsUnique()
|
||||
typistIds: number[];
|
||||
}
|
||||
export class UpdateTypistGroupRequestParam {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
typistGroupId: number;
|
||||
}
|
||||
export class CreatePartnerAccountRequest {
|
||||
@ApiProperty()
|
||||
@MaxLength(255)
|
||||
companyName: string;
|
||||
@ApiProperty({
|
||||
description: '国名(ISO 3166-1 alpha-2)',
|
||||
minLength: 2,
|
||||
maxLength: 2,
|
||||
})
|
||||
@MinLength(2)
|
||||
@MaxLength(2)
|
||||
country: string;
|
||||
@ApiProperty({ required: false })
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
dealerAccountId?: number;
|
||||
@ApiProperty()
|
||||
@MaxLength(256) // AzureAdB2Cの仕様上、256文字まで[https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/user-profile-attributes]
|
||||
adminName: string;
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
adminMail: string;
|
||||
@ApiProperty()
|
||||
@IsAdminPasswordvalid()
|
||||
adminPassword: string;
|
||||
@ApiProperty({ description: '同意済み利用規約のバージョン(EULA)' })
|
||||
acceptedEulaVersion: string;
|
||||
@ApiProperty({ description: '同意済みプライバシーポリシーのバージョン' })
|
||||
acceptedPrivacyNoticeVersion: string;
|
||||
@ApiProperty({ description: '同意済み利用規約のバージョン(DPA)' })
|
||||
acceptedDpaVersion: string;
|
||||
@ApiProperty({ description: 'reCAPTCHA Token' })
|
||||
token: string;
|
||||
@IsEmail({ blacklisted_chars: '*' })
|
||||
email: string;
|
||||
}
|
||||
|
||||
export class GetPartnerLicensesRequest {
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
@Min(0)
|
||||
limit: number;
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
@Min(0)
|
||||
offset: number;
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class GetOrderHistoriesRequest {
|
||||
@ApiProperty({ description: '取得件数' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
limit: number;
|
||||
@ApiProperty({ description: '開始位置' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
offset: number;
|
||||
@ApiProperty({ description: 'アカウントID' })
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class IssueLicenseRequest {
|
||||
@ApiProperty({ description: '注文元アカウントID' })
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
orderedAccountId: number;
|
||||
@ApiProperty({ description: 'POナンバー' })
|
||||
@Matches(/^[A-Z0-9]+$/)
|
||||
poNumber: string;
|
||||
}
|
||||
|
||||
export class CancelIssueRequest {
|
||||
@ApiProperty({ description: '注文元アカウントID' })
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
orderedAccountId: number;
|
||||
|
||||
@ApiProperty({ description: 'POナンバー' })
|
||||
@Matches(/^[A-Z0-9]+$/)
|
||||
poNumber: string;
|
||||
}
|
||||
|
||||
export class CreateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 255, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@MaxLength(255)
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class UpdateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@MaxLength(255)
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class PostWorktypeOptionItem {
|
||||
@ApiProperty({ maxLength: 16 })
|
||||
@MaxLength(16)
|
||||
@IsRecorderAllowed()
|
||||
itemLabel: string;
|
||||
@ApiProperty({
|
||||
maxLength: 20,
|
||||
description: `${Object.values(OPTION_ITEM_VALUE_TYPE).join(' / ')}`,
|
||||
})
|
||||
@MaxLength(20)
|
||||
@IsIn(Object.values(OPTION_ITEM_VALUE_TYPE))
|
||||
defaultValueType: string;
|
||||
@ApiProperty({ maxLength: 20 })
|
||||
@MaxLength(20)
|
||||
@IsRecorderAllowed()
|
||||
@IsInitialValue()
|
||||
initialValue: string;
|
||||
}
|
||||
|
||||
export class GetOptionItemsRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class UpdateOptionItemsRequest {
|
||||
@ApiProperty({
|
||||
maxItems: 10,
|
||||
minItems: 10,
|
||||
type: [PostWorktypeOptionItem],
|
||||
})
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => PostWorktypeOptionItem)
|
||||
@ArrayMinSize(10)
|
||||
@ArrayMaxSize(10)
|
||||
optionItems: PostWorktypeOptionItem[];
|
||||
}
|
||||
|
||||
export class UpdateOptionItemsRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class UpdateWorktypeRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class DeleteWorktypeRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class PostActiveWorktypeRequest {
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
description: 'Active WorkTypeIDにするWorktypeの内部ID',
|
||||
})
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
id?: number;
|
||||
}
|
||||
|
||||
export class GetPartnersRequest {
|
||||
@ApiProperty({ description: '取得件数' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
limit: number;
|
||||
@ApiProperty({ description: '開始位置' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export class UpdateAccountInfoRequest {
|
||||
@ApiProperty({ description: '親アカウントのID', required: false })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
parentAccountId?: number;
|
||||
@ApiProperty({ description: '代行操作許可' })
|
||||
@Type(() => Boolean)
|
||||
delegationPermission: boolean;
|
||||
@ApiProperty({ description: 'プライマリ管理者ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
primaryAdminUserId: number;
|
||||
@ApiProperty({ description: 'セカンダリ管理者ID', required: false })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
secondryAdminUserId?: number;
|
||||
}
|
||||
|
||||
export class DeleteAccountRequest {
|
||||
@ApiProperty({ description: 'アカウントID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class GetAccountInfoMinimalAccessRequest {
|
||||
@ApiProperty({ description: 'idトークン' })
|
||||
idToken: string;
|
||||
}
|
||||
|
||||
export class GetCompanyNameRequest {
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
// ==============================
|
||||
// RESPONSE
|
||||
// ==============================
|
||||
|
||||
export class CreateAccountResponse {}
|
||||
|
||||
export class GetLicenseSummaryRequest {
|
||||
@ApiProperty()
|
||||
accountId: number;
|
||||
}
|
||||
export class LicenseSummaryInfo {
|
||||
totalLicense: number;
|
||||
allocatedLicense: number;
|
||||
reusableLicense: number;
|
||||
freeLicense: number;
|
||||
expiringSoonLicense: number;
|
||||
issueRequesting: number;
|
||||
numberOfRequesting: number;
|
||||
allocatableLicenseWithMargin: number;
|
||||
}
|
||||
export class GetLicenseSummaryResponse {
|
||||
@ApiProperty()
|
||||
totalLicense: number;
|
||||
@ -104,6 +389,11 @@ export class GetLicenseSummaryResponse {
|
||||
isStorageAvailable: boolean;
|
||||
}
|
||||
|
||||
export class GetCompanyNameResponse {
|
||||
@ApiProperty()
|
||||
companyName: string;
|
||||
}
|
||||
|
||||
export class Account {
|
||||
@ApiProperty()
|
||||
accountId: number;
|
||||
@ -133,11 +423,6 @@ export class Account {
|
||||
parentAccountName?: string;
|
||||
}
|
||||
|
||||
export class GetMyAccountResponse {
|
||||
@ApiProperty({ type: Account })
|
||||
account: Account;
|
||||
}
|
||||
|
||||
export class Author {
|
||||
@ApiProperty({ description: 'Authorユーザーの内部ID' })
|
||||
id: number;
|
||||
@ -145,11 +430,6 @@ export class Author {
|
||||
authorId: string;
|
||||
}
|
||||
|
||||
export class GetAuthorsResponse {
|
||||
@ApiProperty({ type: [Author] })
|
||||
authors: Author[];
|
||||
}
|
||||
|
||||
export class Typist {
|
||||
@ApiProperty({
|
||||
description: 'TypistのユーザーID',
|
||||
@ -160,11 +440,6 @@ export class Typist {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class GetTypistsResponse {
|
||||
@ApiProperty({ type: [Typist] })
|
||||
typists: Typist[];
|
||||
}
|
||||
|
||||
export class TypistGroup {
|
||||
@ApiProperty({
|
||||
description: 'TypistGroupのID',
|
||||
@ -175,18 +450,26 @@ export class TypistGroup {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class GetMyAccountResponse {
|
||||
@ApiProperty({ type: Account })
|
||||
account: Account;
|
||||
}
|
||||
|
||||
export class GetAuthorsResponse {
|
||||
@ApiProperty({ type: [Author] })
|
||||
authors: Author[];
|
||||
}
|
||||
|
||||
export class GetTypistsResponse {
|
||||
@ApiProperty({ type: [Typist] })
|
||||
typists: Typist[];
|
||||
}
|
||||
|
||||
export class GetTypistGroupsResponse {
|
||||
@ApiProperty({ type: [TypistGroup] })
|
||||
typistGroups: TypistGroup[];
|
||||
}
|
||||
|
||||
export class GetTypistGroupRequest {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
typistGroupId: number;
|
||||
}
|
||||
export class GetTypistGroupResponse {
|
||||
@ApiProperty()
|
||||
typistGroupName: string;
|
||||
@ -194,72 +477,12 @@ export class GetTypistGroupResponse {
|
||||
typistIds: number[];
|
||||
}
|
||||
|
||||
export class CreateTypistGroupRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 50 })
|
||||
@MinLength(1)
|
||||
@MaxLength(50)
|
||||
typistGroupName: string;
|
||||
@ApiProperty({ minItems: 1, isArray: true, type: 'integer' })
|
||||
@ArrayMinSize(1)
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@Min(0, { each: true })
|
||||
@IsUnique()
|
||||
typistIds: number[];
|
||||
}
|
||||
|
||||
export class CreateTypistGroupResponse {}
|
||||
|
||||
export class UpdateTypistGroupRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 50 })
|
||||
@MinLength(1)
|
||||
@MaxLength(50)
|
||||
typistGroupName: string;
|
||||
@ApiProperty({ minItems: 1, isArray: true, type: 'integer' })
|
||||
@ArrayMinSize(1)
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@Min(0, { each: true })
|
||||
@IsUnique()
|
||||
typistIds: number[];
|
||||
}
|
||||
export class UpdateTypistGroupRequestParam {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
typistGroupId: number;
|
||||
}
|
||||
export class UpdateTypistGroupResponse {}
|
||||
|
||||
export class CreatePartnerAccountRequest {
|
||||
@ApiProperty()
|
||||
companyName: string;
|
||||
@ApiProperty({
|
||||
description: '国名(ISO 3166-1 alpha-2)',
|
||||
minLength: 2,
|
||||
maxLength: 2,
|
||||
})
|
||||
country: string;
|
||||
@ApiProperty()
|
||||
adminName: string;
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
}
|
||||
|
||||
export class CreatePartnerAccountResponse {}
|
||||
|
||||
export class GetPartnerLicensesRequest {
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
limit: number;
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
offset: number;
|
||||
@ApiProperty()
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class PartnerLicenseInfo {
|
||||
@ApiProperty({ description: 'アカウントID' })
|
||||
@ -291,7 +514,6 @@ export class PartnerLicenseInfo {
|
||||
})
|
||||
issueRequesting: number;
|
||||
}
|
||||
|
||||
export class GetPartnerLicensesResponse {
|
||||
@ApiProperty()
|
||||
total: number;
|
||||
@ -301,30 +523,6 @@ export class GetPartnerLicensesResponse {
|
||||
childrenPartnerLicenses: PartnerLicenseInfo[];
|
||||
}
|
||||
|
||||
// 第五階層のshortage算出用
|
||||
export class PartnerLicenseInfoForShortage {
|
||||
expiringSoonLicense?: number;
|
||||
allocatableLicenseWithMargin?: number;
|
||||
}
|
||||
|
||||
// RepositoryからPartnerLicenseInfoに関する情報を取得する際の型
|
||||
export type PartnerLicenseInfoForRepository = Omit<
|
||||
PartnerLicenseInfo & PartnerLicenseInfoForShortage,
|
||||
'shortage'
|
||||
>;
|
||||
|
||||
export class GetOrderHistoriesRequest {
|
||||
@ApiProperty({ description: '取得件数' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
limit: number;
|
||||
@ApiProperty({ description: '開始位置' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
offset: number;
|
||||
@ApiProperty({ description: 'アカウントID' })
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class LicenseOrder {
|
||||
@ApiProperty({ description: '注文日付' })
|
||||
@ -338,6 +536,7 @@ export class LicenseOrder {
|
||||
@ApiProperty({ description: '注文状態' })
|
||||
status: string;
|
||||
}
|
||||
|
||||
export class GetOrderHistoriesResponse {
|
||||
@ApiProperty({ description: '合計件数' })
|
||||
total: number;
|
||||
@ -345,16 +544,9 @@ export class GetOrderHistoriesResponse {
|
||||
orderHistories: LicenseOrder[];
|
||||
}
|
||||
|
||||
export class IssueLicenseRequest {
|
||||
@ApiProperty({ description: '注文元アカウントID' })
|
||||
orderedAccountId: number;
|
||||
@ApiProperty({ description: 'POナンバー' })
|
||||
@Matches(/^[A-Z0-9]+$/)
|
||||
poNumber: string;
|
||||
}
|
||||
|
||||
export class IssueLicenseResponse {}
|
||||
|
||||
|
||||
export class Dealer {
|
||||
@ApiProperty({ description: 'アカウントID' })
|
||||
id: number;
|
||||
@ -363,22 +555,15 @@ export class Dealer {
|
||||
@ApiProperty({ description: '国名(ISO 3166-1 alpha-2)' })
|
||||
country: string;
|
||||
}
|
||||
|
||||
export class GetDealersResponse {
|
||||
@ApiProperty({ type: [Dealer] })
|
||||
dealers: Dealer[];
|
||||
}
|
||||
|
||||
export class CancelIssueRequest {
|
||||
@ApiProperty({ description: '注文元アカウントID' })
|
||||
orderedAccountId: number;
|
||||
|
||||
@ApiProperty({ description: 'POナンバー' })
|
||||
@Matches(/^[A-Z0-9]+$/)
|
||||
poNumber: string;
|
||||
}
|
||||
|
||||
export class CancelIssueResponse {}
|
||||
|
||||
|
||||
export class Worktype {
|
||||
@ApiProperty({ description: 'WorktypeのID' })
|
||||
id: number;
|
||||
@ -399,52 +584,11 @@ export class GetWorktypesResponse {
|
||||
active?: number;
|
||||
}
|
||||
|
||||
export class CreateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 255, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@MaxLength(255)
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class CreateWorktypeResponse {}
|
||||
|
||||
export class UpdateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@MaxLength(255)
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class UpdateWorktypeResponse {}
|
||||
|
||||
export class PostWorktypeOptionItem {
|
||||
@ApiProperty({ maxLength: 16 })
|
||||
@MaxLength(16)
|
||||
@IsRecorderAllowed()
|
||||
itemLabel: string;
|
||||
@ApiProperty({
|
||||
maxLength: 20,
|
||||
description: `${Object.values(OPTION_ITEM_VALUE_TYPE).join(' / ')}`,
|
||||
})
|
||||
@MaxLength(20)
|
||||
@IsIn(Object.values(OPTION_ITEM_VALUE_TYPE))
|
||||
defaultValueType: string;
|
||||
@ApiProperty({ maxLength: 20 })
|
||||
@MaxLength(20)
|
||||
@IsRecorderAllowed()
|
||||
@IsInitialValue()
|
||||
initialValue: string;
|
||||
}
|
||||
|
||||
export class GetWorktypeOptionItem extends PostWorktypeOptionItem {
|
||||
@ApiProperty()
|
||||
id: number;
|
||||
@ -459,82 +603,12 @@ export class GetOptionItemsResponse {
|
||||
optionItems: GetWorktypeOptionItem[];
|
||||
}
|
||||
|
||||
export class GetOptionItemsRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class UpdateOptionItemsRequest {
|
||||
@ApiProperty({
|
||||
maxItems: 10,
|
||||
minItems: 10,
|
||||
type: [PostWorktypeOptionItem],
|
||||
})
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => PostWorktypeOptionItem)
|
||||
@ArrayMinSize(10)
|
||||
@ArrayMaxSize(10)
|
||||
optionItems: PostWorktypeOptionItem[];
|
||||
}
|
||||
|
||||
export class UpdateOptionItemsResponse {}
|
||||
|
||||
export class UpdateOptionItemsRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class UpdateWorktypeRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class DeleteWorktypeRequestParam {
|
||||
@ApiProperty({ description: 'Worktypeの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class DeleteWorktypeResponse {}
|
||||
|
||||
export class PostActiveWorktypeRequest {
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
description: 'Active WorkTypeIDにするWorktypeの内部ID',
|
||||
})
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
id?: number;
|
||||
}
|
||||
|
||||
export class PostActiveWorktypeResponse {}
|
||||
|
||||
export class GetPartnersRequest {
|
||||
@ApiProperty({ description: '取得件数' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
limit: number;
|
||||
@ApiProperty({ description: '開始位置' })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export class Partner {
|
||||
@ApiProperty({ description: '会社名' })
|
||||
@ -560,6 +634,42 @@ export class GetPartnersResponse {
|
||||
partners: Partner[];
|
||||
}
|
||||
|
||||
export class UpdateAccountInfoResponse {}
|
||||
|
||||
export class DeleteAccountResponse {}
|
||||
|
||||
export class GetAccountInfoMinimalAccessResponse {
|
||||
@ApiProperty({ description: '階層' })
|
||||
tier: number;
|
||||
}
|
||||
|
||||
// ==============================
|
||||
// Request/Response外の型
|
||||
// TODO: Request/Response/その他の型を別ファイルに分ける
|
||||
// ==============================
|
||||
export class LicenseSummaryInfo {
|
||||
totalLicense: number;
|
||||
allocatedLicense: number;
|
||||
reusableLicense: number;
|
||||
freeLicense: number;
|
||||
expiringSoonLicense: number;
|
||||
issueRequesting: number;
|
||||
numberOfRequesting: number;
|
||||
allocatableLicenseWithMargin: number;
|
||||
}
|
||||
|
||||
// 第五階層のshortage算出用
|
||||
export class PartnerLicenseInfoForShortage {
|
||||
expiringSoonLicense?: number;
|
||||
allocatableLicenseWithMargin?: number;
|
||||
}
|
||||
|
||||
// RepositoryからPartnerLicenseInfoに関する情報を取得する際の型
|
||||
export type PartnerLicenseInfoForRepository = Omit<
|
||||
PartnerLicenseInfo & PartnerLicenseInfoForShortage,
|
||||
'shortage'
|
||||
>;
|
||||
|
||||
// RepositoryからPartnerLicenseInfoに関する情報を取得する際の型
|
||||
export type PartnerInfoFromDb = {
|
||||
name: string;
|
||||
@ -569,45 +679,3 @@ export type PartnerInfoFromDb = {
|
||||
primaryAccountExternalId: string;
|
||||
dealerManagement: boolean;
|
||||
};
|
||||
|
||||
export class UpdateAccountInfoRequest {
|
||||
@ApiProperty({ description: '親アカウントのID', required: false })
|
||||
@IsOptional()
|
||||
parentAccountId?: number;
|
||||
@ApiProperty({ description: '代行操作許可' })
|
||||
delegationPermission: boolean;
|
||||
@ApiProperty({ description: 'プライマリ管理者ID' })
|
||||
primaryAdminUserId: number;
|
||||
@ApiProperty({ description: 'セカンダリ管理者ID', required: false })
|
||||
@IsOptional()
|
||||
secondryAdminUserId?: number;
|
||||
}
|
||||
|
||||
export class UpdateAccountInfoResponse {}
|
||||
|
||||
export class DeleteAccountRequest {
|
||||
@ApiProperty({ description: 'アカウントID' })
|
||||
accountId: number;
|
||||
}
|
||||
|
||||
export class DeleteAccountResponse {}
|
||||
|
||||
export class GetAccountInfoMinimalAccessRequest {
|
||||
@ApiProperty({ description: 'idトークン' })
|
||||
idToken: string;
|
||||
}
|
||||
|
||||
export class GetAccountInfoMinimalAccessResponse {
|
||||
@ApiProperty({ description: '階層' })
|
||||
tier: number;
|
||||
}
|
||||
export class GetCompanyNameRequest {
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
accountId: number;
|
||||
}
|
||||
export class GetCompanyNameResponse {
|
||||
@ApiProperty()
|
||||
companyName: string;
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
Controller,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
@ -25,8 +26,7 @@ import {
|
||||
DelegationAccessTokenResponse,
|
||||
} from './types/types';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { Request } from 'express';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
@ -39,6 +39,7 @@ import { RedisService } from '../../gateways/redis/redis.service';
|
||||
@ApiTags('auth')
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
private readonly logger = new Logger(AuthController.name);
|
||||
constructor(
|
||||
private readonly authService: AuthService,
|
||||
private readonly redisService: RedisService,
|
||||
@ -65,8 +66,29 @@ export class AuthController {
|
||||
'AzureADB2Cでのサインイン後に払いだされるIDトークンを元に認証用のアクセストークンとリフレッシュトークンを生成します',
|
||||
operationId: 'token',
|
||||
})
|
||||
async token(@Body() body: TokenRequest): Promise<TokenResponse> {
|
||||
const context = makeContext(uuidv4());
|
||||
async token(
|
||||
@Body() body: TokenRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<TokenResponse> {
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const idToken = await this.authService.getVerifiedIdToken(
|
||||
context,
|
||||
body.idToken,
|
||||
@ -145,7 +167,6 @@ export class AuthController {
|
||||
})
|
||||
async accessToken(@Req() req: Request): Promise<AccessTokenResponse> {
|
||||
const refreshToken = retrieveAuthorizationToken(req);
|
||||
|
||||
if (!refreshToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
@ -153,7 +174,24 @@ export class AuthController {
|
||||
);
|
||||
}
|
||||
|
||||
const context = makeContext(uuidv4());
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const accessToken = await this.authService.generateAccessToken(
|
||||
context,
|
||||
@ -202,13 +240,29 @@ export class AuthController {
|
||||
): Promise<DelegationTokenResponse> {
|
||||
const { delegatedAccountId } = body;
|
||||
const token = retrieveAuthorizationToken(req);
|
||||
|
||||
if (!token) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(token, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -218,7 +272,9 @@ export class AuthController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const refreshToken = await this.authService.generateDelegationRefreshToken(
|
||||
context,
|
||||
userId,
|
||||
@ -257,13 +313,29 @@ export class AuthController {
|
||||
@Req() req: Request,
|
||||
): Promise<DelegationAccessTokenResponse> {
|
||||
const refreshToken = retrieveAuthorizationToken(req);
|
||||
|
||||
if (!refreshToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedRefreshToken = jwt.decode(refreshToken, { json: true });
|
||||
if (!decodedRefreshToken) {
|
||||
throw new HttpException(
|
||||
@ -273,7 +345,9 @@ export class AuthController {
|
||||
}
|
||||
const { userId, delegateUserId } = decodedRefreshToken as RefreshToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const accessToken = await this.authService.updateDelegationAccessToken(
|
||||
context,
|
||||
delegateUserId,
|
||||
|
||||
@ -31,7 +31,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
expect(await service.getVerifiedIdToken(context, token)).toEqual(
|
||||
idTokenPayload,
|
||||
);
|
||||
@ -43,7 +43,7 @@ describe('AuthService', () => {
|
||||
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
|
||||
const token = 'invalid.id.token';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000101'), HttpStatus.UNAUTHORIZED),
|
||||
);
|
||||
@ -58,7 +58,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjEwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.r9x61Mf1S2qFgU_QDKB6tRFBmTQXyOEtpoacOlL_bQzFz1t3GsxMy6SJIvQQ-LtDgylQ1UCdMFiRuy4V8nyLuME0fR-9IkKsboGvwllHB_Isai3XFoja0jpDHMVby1m0B3Z9xOTb7YsaQGyEH-qs1TtnRm6Ny98h4Po80nK8HGefQZHBOlfQN_B1LiHwI3nLXV18NL-4olKXj2NloNRYtnWM0PaqDQcGvZFaSNvtrSYpo9ddD906QWDGVOQ7WvGSUgdNCoxX8Lb3r2-VSj6n84jpb-Y1Fz-GhLluNglAsBhasnJfUIvCIO3iG5pRyTYjHFAVHmzjr8xMOmhS3s41Jw';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000102'), HttpStatus.UNAUTHORIZED),
|
||||
);
|
||||
@ -73,7 +73,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6OTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.fX2Gbd7fDPNE3Lw-xbum_5CVqQYqEmMhv_v5u8A-U81pmPD2P5rsJEJx66ns1taFLVaE3j9_OzotxrqjqqQqbACkagGcN5wvA3_ZIxyqmhrKYFJc53ZcO7d0pFWiQlluNBI_pnFNDlSMB2Ut8Th5aiPy2uamBM9wC99bcjo7HkHvTKBf6ljU6rPKoD51qGDWqNxjoH-hdSJ29wprvyxyk_yX6dp-cxXUj5DIgXYQuIZF71rdiPtGlAiyTBns8rS2QlEEXapZVlvYrK4mkpUXVDA7ifD8q6gAC2BStqHeys7CGp2MbV4ZwKCVbAUbMs6Tboh8rADZvQhuTEq7qlhZ-w';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000103'), HttpStatus.UNAUTHORIZED),
|
||||
);
|
||||
@ -86,7 +86,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdXNlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.sign';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000104'), HttpStatus.UNAUTHORIZED),
|
||||
);
|
||||
@ -101,7 +101,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaW52bGlkX2lzc3VlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.0bp3e1mDG78PX3lo8zgOLXGenIqG_Vi6kw7CbwauAQM-cnUZ_aVCoJ_dAv_QmPElOQKcCkRrAvAZ91FwuHDlBGuuDqx8OwqN0VaD-4NPouoAswj-9HNvBm8gUn-pGaXkvWt_72UdCJavZJjDj_RHur8y8kFt5Qeab3mUP2x-uNcV2Q2x3M_IIfcRiIZkRZm_azKfiVIy7tzoUFLDss97y938aR8imMVxazoSQvj7RWIWylgeRr9yVt7qYl18cnEVL0IGtslFbqhfNsiEmRCMsttm5kXs7E9B0bhhUe_xbJW9VumQ6G7dgMrswevp_jRgbpWJoZsgErtqIRl9Tc9ikA';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000105'), HttpStatus.UNAUTHORIZED),
|
||||
);
|
||||
@ -115,7 +115,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -131,7 +131,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -150,7 +150,7 @@ describe('AuthService', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.getVerifiedIdToken(context, token)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -186,7 +186,7 @@ describe('checkIsAcceptedLatestVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 5,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const idToken = {
|
||||
emails: [],
|
||||
@ -210,7 +210,7 @@ describe('checkIsAcceptedLatestVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 4,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const idToken = {
|
||||
emails: [],
|
||||
@ -234,7 +234,7 @@ describe('checkIsAcceptedLatestVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 5,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const idToken = {
|
||||
emails: [],
|
||||
@ -258,7 +258,7 @@ describe('checkIsAcceptedLatestVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 4,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const idToken = {
|
||||
emails: [],
|
||||
@ -282,7 +282,7 @@ describe('checkIsAcceptedLatestVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 4,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const idToken = {
|
||||
emails: [],
|
||||
@ -306,7 +306,7 @@ describe('checkIsAcceptedLatestVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 4,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const idToken = {
|
||||
emails: [],
|
||||
@ -361,7 +361,7 @@ describe('generateDelegationRefreshToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||||
context,
|
||||
@ -399,7 +399,7 @@ describe('generateDelegationRefreshToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
try {
|
||||
await service.generateDelegationRefreshToken(
|
||||
@ -437,7 +437,7 @@ describe('generateDelegationRefreshToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
try {
|
||||
await service.generateDelegationRefreshToken(
|
||||
@ -495,7 +495,7 @@ describe('generateDelegationAccessToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||||
context,
|
||||
@ -540,7 +540,7 @@ describe('generateDelegationAccessToken', () => {
|
||||
tier: 4,
|
||||
});
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
try {
|
||||
await service.generateDelegationAccessToken(context, 'invalid token');
|
||||
@ -595,7 +595,7 @@ describe('updateDelegationAccessToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||||
context,
|
||||
@ -653,7 +653,7 @@ describe('updateDelegationAccessToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||||
context,
|
||||
@ -719,7 +719,7 @@ describe('updateDelegationAccessToken', () => {
|
||||
{ role: USER_ROLES.NONE },
|
||||
);
|
||||
|
||||
const context = makeContext(parentAdmin.external_id);
|
||||
const context = makeContext(parentAdmin.external_id, 'requestId');
|
||||
|
||||
const delegationRefreshToken = await service.generateDelegationRefreshToken(
|
||||
context,
|
||||
|
||||
@ -61,7 +61,10 @@ export class AuthService {
|
||||
);
|
||||
try {
|
||||
// IDトークンのユーザーがDBに登録されていてメール認証が完了しているユーザーか検証
|
||||
const user = await this.usersRepository.findVerifiedUser(idToken.sub);
|
||||
const user = await this.usersRepository.findVerifiedUser(
|
||||
context,
|
||||
idToken.sub,
|
||||
);
|
||||
|
||||
return user !== undefined;
|
||||
} catch (e) {
|
||||
@ -96,7 +99,10 @@ export class AuthService {
|
||||
|
||||
// ユーザー情報とユーザーが属しているアカウント情報を取得
|
||||
try {
|
||||
const user = await this.usersRepository.findUserByExternalId(idToken.sub);
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
idToken.sub,
|
||||
);
|
||||
if (!user.account) {
|
||||
throw new Error('Account information not found');
|
||||
}
|
||||
@ -236,11 +242,13 @@ export class AuthService {
|
||||
// ユーザー情報とユーザーが属しているアカウント情報を取得
|
||||
try {
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
delegateUserExternalId,
|
||||
);
|
||||
|
||||
// 代行操作対象アカウントの管理者ユーザーを取得
|
||||
const adminUser = await this.usersRepository.findDelegateUser(
|
||||
context,
|
||||
user.account_id,
|
||||
originAccountId,
|
||||
);
|
||||
@ -382,6 +390,7 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
delegateUserExternalId,
|
||||
);
|
||||
|
||||
@ -406,6 +415,7 @@ export class AuthService {
|
||||
// 代行操作対象アカウントの管理者ユーザーが存在して、アカウントに対して代行操作権限があるか確認
|
||||
const delegationPermission =
|
||||
await this.usersRepository.isAllowDelegationPermission(
|
||||
context,
|
||||
user.account_id,
|
||||
userId,
|
||||
);
|
||||
@ -694,7 +704,10 @@ export class AuthService {
|
||||
acceptedDpaVersion,
|
||||
latestDpaVersion,
|
||||
tier,
|
||||
} = await this.usersRepository.getAcceptedAndLatestVersion(idToken.sub);
|
||||
} = await this.usersRepository.getAcceptedAndLatestVersion(
|
||||
context,
|
||||
idToken.sub,
|
||||
);
|
||||
|
||||
// 第五階層はEULAとPrivacyNoticeのみ判定
|
||||
if (tier === TIERS.TIER5) {
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsInt } from 'class-validator';
|
||||
import { IsIn, IsInt, IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class TokenRequest {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
idToken: string;
|
||||
@ApiProperty({ description: 'web or mobile or desktop' })
|
||||
@IsIn(['web', 'mobile', 'desktop'], { message: 'invalid type' })
|
||||
@IsNotEmpty()
|
||||
type: string;
|
||||
}
|
||||
export class TokenResponse {
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
@ -37,12 +38,13 @@ import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES, USER_ROLES } from '../../constants';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { Request } from 'express';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
@ApiTags('files')
|
||||
@Controller('files')
|
||||
export class FilesController {
|
||||
private readonly logger = new Logger(FilesController.name);
|
||||
constructor(private readonly filesService: FilesService) {}
|
||||
|
||||
@ApiResponse({
|
||||
@ -84,6 +86,22 @@ export class FilesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -93,7 +111,8 @@ export class FilesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const {
|
||||
url,
|
||||
@ -176,6 +195,22 @@ export class FilesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -185,7 +220,8 @@ export class FilesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const url = await this.filesService.publishUploadSas(context, userId);
|
||||
return { url };
|
||||
@ -237,6 +273,22 @@ export class FilesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -246,7 +298,8 @@ export class FilesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const url = await this.filesService.publishAudioFileDownloadSas(
|
||||
context,
|
||||
@ -285,9 +338,7 @@ export class FilesController {
|
||||
})
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(
|
||||
RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST] }),
|
||||
)
|
||||
@UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.TYPIST] }))
|
||||
async downloadTemplateLocation(
|
||||
@Req() req: Request,
|
||||
@Query() body: TemplateDownloadLocationRequest,
|
||||
@ -301,6 +352,22 @@ export class FilesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -310,7 +377,8 @@ export class FilesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const url = await this.filesService.publishTemplateFileDownloadSas(
|
||||
context,
|
||||
@ -357,6 +425,22 @@ export class FilesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -366,7 +450,8 @@ export class FilesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const url = await this.filesService.publishTemplateFileUploadSas(
|
||||
context,
|
||||
@ -418,6 +503,22 @@ export class FilesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -427,7 +528,8 @@ export class FilesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.filesService.templateUploadFinished(context, userId, url, name);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -34,7 +34,12 @@ import { TasksRepositoryService } from '../../repositories/tasks/tasks.repositor
|
||||
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
|
||||
import { getCheckoutPermissions, getTask } from '../tasks/test/utility';
|
||||
import { DateWithZeroTime } from '../licenses/types/types';
|
||||
import { LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from '../../constants';
|
||||
import {
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_TYPE,
|
||||
TASK_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants';
|
||||
|
||||
describe('publishUploadSas', () => {
|
||||
let source: DataSource | null = null;
|
||||
@ -85,7 +90,7 @@ describe('publishUploadSas', () => {
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const context = makeContext(externalId);
|
||||
const context = makeContext(externalId, 'requestId');
|
||||
const baseUrl = `https://saodmsusdev.blob.core.windows.net/account-${account.id}/${userId}`;
|
||||
|
||||
//SASトークンを返却する
|
||||
@ -107,7 +112,7 @@ describe('publishUploadSas', () => {
|
||||
// 第四階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 4 });
|
||||
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//Blobコンテナ存在チェックに失敗するようにする
|
||||
overrideBlobstorageService(service, {
|
||||
@ -135,7 +140,7 @@ describe('publishUploadSas', () => {
|
||||
// 第四階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 4 });
|
||||
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//BlobのSASトークン生成に失敗するようにする
|
||||
overrideBlobstorageService(service, {
|
||||
@ -164,7 +169,7 @@ describe('publishUploadSas', () => {
|
||||
// 第五階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 5, locked: true });
|
||||
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
try {
|
||||
await service.publishUploadSas(context, admin.external_id);
|
||||
@ -209,7 +214,10 @@ describe('publishUploadSas', () => {
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishUploadSas(makeContext('trackingId'), externalId),
|
||||
service.publishUploadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010812'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -267,7 +275,10 @@ describe('publishUploadSas', () => {
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishUploadSas(makeContext('trackingId'), externalId),
|
||||
service.publishUploadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -348,7 +359,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
const result = await service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
authorExternalId,
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '',
|
||||
@ -368,7 +379,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
expect(result).toEqual({ jobNumber: '00000001' });
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId}`],
|
||||
{
|
||||
authorId: 'AUTHOR_ID',
|
||||
@ -449,7 +460,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
const result = await service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
authorExternalId,
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '',
|
||||
@ -469,7 +480,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
expect(result).toEqual({ jobNumber: '00000002' });
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId}`],
|
||||
{
|
||||
authorId: 'AUTHOR_ID',
|
||||
@ -572,7 +583,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
const result = await service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
myExternalId, // API実行者のユーザーIDを設定
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '', // 音声ファイルの情報には、録音者のAuthorIDが入る
|
||||
@ -592,7 +603,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
expect(result).toEqual({ jobNumber: '00000001' });
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId}`],
|
||||
{
|
||||
authorId: 'AUTHOR_ID',
|
||||
@ -694,7 +705,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
const result = await service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
myExternalId, // API実行者のユーザーIDを設定
|
||||
'http://blob/url/file.zip',
|
||||
'XXXXXXXXXX', // 音声ファイルの情報には、録音者のAuthorIDが入る
|
||||
@ -714,7 +725,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
expect(result).toEqual({ jobNumber: '00000001' });
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId}`],
|
||||
{
|
||||
authorId: 'XXXXXXXXXX',
|
||||
@ -763,7 +774,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
const result = await service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
authorExternalId, // API実行者のユーザーIDを設定
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '', // 音声ファイルの情報には、録音者のAuthorIDが入る
|
||||
@ -819,7 +830,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
|
||||
await expect(
|
||||
service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
authorExternalId,
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '',
|
||||
@ -866,7 +877,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
|
||||
await expect(
|
||||
service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
authorExternalId,
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '',
|
||||
@ -907,7 +918,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
|
||||
await expect(
|
||||
service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
'authorExternalId',
|
||||
'http://blob/url/file.zip',
|
||||
'authorAuthorId',
|
||||
@ -958,7 +969,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
|
||||
await expect(
|
||||
service.uploadFinished(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
authorExternalId,
|
||||
'http://blob/url/file.zip',
|
||||
authorAuthorId ?? '',
|
||||
@ -1043,7 +1054,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
expect(
|
||||
await service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1113,7 +1124,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
expect(
|
||||
await service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1160,7 +1171,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1214,7 +1225,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
makeContext('tracking', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1259,7 +1270,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1291,7 +1302,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
1,
|
||||
),
|
||||
@ -1340,7 +1351,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1395,7 +1406,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1467,7 +1478,7 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
@ -1499,15 +1510,11 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
it('ダウンロードSASトークンが乗っているURLを取得できる', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId, author_id: authorId } = await makeTestUser(
|
||||
source,
|
||||
{
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
},
|
||||
);
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
@ -1515,9 +1522,9 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
accountId,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
userId,
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
@ -1533,13 +1540,12 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
expect(
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).toEqual(`${url}?sas-token`);
|
||||
const resultUrl = await service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
);
|
||||
expect(resultUrl).toBe(`${url}?sas-token`);
|
||||
});
|
||||
it('ダウンロードSASトークンが乗っているURLを取得できる(第五階層の場合ライセンスのチェックを行う)', async () => {
|
||||
if (!source) fail();
|
||||
@ -1551,15 +1557,10 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
// 本日の日付を作成
|
||||
let yesterday = new Date();
|
||||
@ -1585,9 +1586,9 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
userId,
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
@ -1603,22 +1604,20 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
expect(
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).toEqual(`${url}?sas-token`);
|
||||
const resultUrl = await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
);
|
||||
expect(resultUrl).toBe(`${url}?sas-token`);
|
||||
});
|
||||
it('Typistの場合、タスクのステータスが[Inprogress,Pending]以外でエラー', async () => {
|
||||
it('タスクのステータスが[Inprogress,Pending]以外でエラー', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
author_id: undefined,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
|
||||
|
||||
@ -1627,7 +1626,7 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
accountId,
|
||||
url,
|
||||
'test.zip',
|
||||
'Finished',
|
||||
TASK_STATUS.FINISHED,
|
||||
userId,
|
||||
);
|
||||
|
||||
@ -1644,31 +1643,35 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010603'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('Typistの場合、自身が担当するタスクでない場合エラー', async () => {
|
||||
it('自身が担当するタスクでない場合エラー', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
author_id: undefined,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const { id: otherId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'other-typist-user-external-id',
|
||||
role: 'typist',
|
||||
author_id: undefined,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
|
||||
|
||||
@ -1677,7 +1680,7 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
accountId,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
otherId,
|
||||
);
|
||||
|
||||
@ -1694,60 +1697,21 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
|
||||
it('Authorの場合、自身が登録したタスクでない場合エラー', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
accountId,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
'OTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = true;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010603'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('Taskが存在しない場合はエラーとなる', async () => {
|
||||
@ -1755,9 +1719,8 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
@ -1771,29 +1734,31 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking', 'requestId'),
|
||||
externalId,
|
||||
1,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010603'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('blobストレージにファイルが存在しない場合はエラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId, author_id: authorId } = await makeTestUser(
|
||||
source,
|
||||
{
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
},
|
||||
);
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
@ -1801,9 +1766,9 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
accountId,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
userId,
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
@ -1819,15 +1784,21 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking'),
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('tracking', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010701'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010701'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
it('ダウンロード時にユーザーにライセンスが未割当の場合エラーとなる(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
@ -1839,15 +1810,10 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
@ -1856,9 +1822,9 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
@ -1874,15 +1840,21 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010812'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010812'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
it('ダウンロード時にユーザーに割り当てられたライセンスが有効期限切れの場合エラー(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
@ -1894,15 +1866,10 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
// 昨日の日付を作成
|
||||
let yesterday = new Date();
|
||||
@ -1928,9 +1895,9 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
@ -1946,15 +1913,21 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId'),
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010805'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1985,7 +1958,7 @@ describe('publishTemplateFileUploadSas', () => {
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
const baseUrl = `https://saodmsusdev.blob.core.windows.net/account-${account.id}/Templates`;
|
||||
|
||||
//SASトークンを返却する
|
||||
@ -2010,7 +1983,7 @@ describe('publishTemplateFileUploadSas', () => {
|
||||
// 第五階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//Blobコンテナ存在チェックに失敗するようにする
|
||||
overrideBlobstorageService(service, {
|
||||
@ -2038,7 +2011,7 @@ describe('publishTemplateFileUploadSas', () => {
|
||||
// 第五階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//BlobのSASトークン生成に失敗するようにする
|
||||
overrideBlobstorageService(service, {
|
||||
@ -2087,7 +2060,7 @@ describe('templateUploadFinished', () => {
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const fileName = 'test.docs';
|
||||
const url = `https://blob.url/account-${account.id}/Templates`;
|
||||
@ -2121,7 +2094,7 @@ describe('templateUploadFinished', () => {
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const fileName = 'test.docs';
|
||||
const url = `https://blob.url/account-${account.id}/Templates`;
|
||||
@ -2161,7 +2134,7 @@ describe('templateUploadFinished', () => {
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const fileName = 'test.docs';
|
||||
const url = `https://blob.url/account-${account.id}/Templates`;
|
||||
|
||||
@ -166,7 +166,7 @@ export class FilesService {
|
||||
let user: User;
|
||||
try {
|
||||
// ユーザー取得
|
||||
user = await this.usersRepository.findUserByExternalId(userId);
|
||||
user = await this.usersRepository.findUserByExternalId(context, userId);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
this.logger.log(
|
||||
@ -190,6 +190,7 @@ export class FilesService {
|
||||
// 文字起こしタスク追加(音声ファイルとオプションアイテムも同時に追加)
|
||||
// 追加時に末尾のJOBナンバーにインクリメントする
|
||||
task = await this.tasksRepositoryService.create(
|
||||
context,
|
||||
user.account_id,
|
||||
user.id,
|
||||
priority,
|
||||
@ -218,6 +219,7 @@ export class FilesService {
|
||||
// ルーティング設定に従い、チェックアウト権限を付与する
|
||||
const { typistGroupIds, typistIds } =
|
||||
await this.tasksRepositoryService.autoRouting(
|
||||
context,
|
||||
task.audio_file_id,
|
||||
user.account_id,
|
||||
user.author_id ?? undefined,
|
||||
@ -225,6 +227,7 @@ export class FilesService {
|
||||
|
||||
const groupMembers =
|
||||
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(
|
||||
context,
|
||||
typistGroupIds,
|
||||
);
|
||||
|
||||
@ -284,7 +287,10 @@ export class FilesService {
|
||||
|
||||
//DBから国情報とアカウントIDを取得する
|
||||
try {
|
||||
const user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
if (!user.account) {
|
||||
throw new AccountNotFoundError('account not found.');
|
||||
}
|
||||
@ -299,6 +305,7 @@ export class FilesService {
|
||||
|
||||
// ライセンスが有効でない場合、エラー
|
||||
const { state } = await this.licensesRepository.getLicenseState(
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
if (state === 'expired') {
|
||||
@ -378,7 +385,10 @@ export class FilesService {
|
||||
let authorId: string | undefined;
|
||||
let isAdmin: boolean;
|
||||
try {
|
||||
const user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
if (!user.account) {
|
||||
throw new AccountNotFoundError('account not found.');
|
||||
}
|
||||
@ -386,6 +396,7 @@ export class FilesService {
|
||||
if (user.account.tier === TIERS.TIER5) {
|
||||
// ライセンスが有効でない場合、エラー
|
||||
const { state } = await this.licensesRepository.getLicenseState(
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
if (state === 'expired') {
|
||||
@ -437,6 +448,7 @@ export class FilesService {
|
||||
: Object.values(TASK_STATUS);
|
||||
|
||||
const task = await this.tasksRepository.getTaskAndAudioFile(
|
||||
context,
|
||||
audioFileId,
|
||||
accountId,
|
||||
status,
|
||||
@ -462,7 +474,7 @@ export class FilesService {
|
||||
|
||||
// ユーザーがTypistの場合、自身が担当したタスクでない場合はエラー
|
||||
if (isTypist && task.typist_user_id !== userId) {
|
||||
throw new AuthorUserNotMatchError(
|
||||
throw new TypistUserNotFoundError(
|
||||
`task typist is not match. audio_file_id:${audioFileId}, task.typist_user_id:${task.typist_user_id}, userId:${userId}`,
|
||||
);
|
||||
}
|
||||
@ -551,10 +563,11 @@ export class FilesService {
|
||||
let accountId: number;
|
||||
let userId: number;
|
||||
let country: string;
|
||||
let isTypist: boolean;
|
||||
let authorId: string | undefined;
|
||||
try {
|
||||
const user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
if (!user.account) {
|
||||
throw new AccountNotFoundError('account not found.');
|
||||
}
|
||||
@ -562,6 +575,7 @@ export class FilesService {
|
||||
if (user.account.tier === TIERS.TIER5) {
|
||||
// ライセンスが有効でない場合、エラー
|
||||
const { state } = await this.licensesRepository.getLicenseState(
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
if (state === 'expired') {
|
||||
@ -574,8 +588,6 @@ export class FilesService {
|
||||
accountId = user.account_id;
|
||||
userId = user.id;
|
||||
country = user.account.country;
|
||||
isTypist = user.role === USER_ROLES.TYPIST;
|
||||
authorId = user.author_id ?? undefined;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
this.logger.log(
|
||||
@ -603,26 +615,13 @@ export class FilesService {
|
||||
}
|
||||
|
||||
try {
|
||||
const status = isTypist
|
||||
? [TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING]
|
||||
: Object.values(TASK_STATUS);
|
||||
|
||||
const task = await this.tasksRepository.getTaskAndAudioFile(
|
||||
context,
|
||||
audioFileId,
|
||||
accountId,
|
||||
status,
|
||||
[TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING],
|
||||
);
|
||||
const { file } = task;
|
||||
|
||||
// タスクに紐づく音声ファイルだけが消される場合がある。
|
||||
// その場合はダウンロード不可なので不在エラーとして扱う
|
||||
if (!file) {
|
||||
throw new AudioFileNotFoundError(
|
||||
`Audio file is not exists in DB. audio_file_id:${audioFileId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const template_file = task.template_file;
|
||||
const { template_file } = task;
|
||||
|
||||
// タスクに紐づくテンプレートファイルがない場合がある。
|
||||
// その場合はダウンロード不可なので不在エラーとして扱う
|
||||
@ -632,16 +631,9 @@ export class FilesService {
|
||||
);
|
||||
}
|
||||
|
||||
// ユーザーがAuthorの場合、自身が追加したタスクでない場合はエラー
|
||||
if (!isTypist && file.author_id !== authorId) {
|
||||
throw new AuthorUserNotMatchError(
|
||||
`task author is not match. audio_file_id:${audioFileId}, task.file.author_id:${file.author_id}, authorId:${authorId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// ユーザーがTypistの場合、自身が担当したタスクでない場合はエラー
|
||||
if (isTypist && task.typist_user_id !== userId) {
|
||||
throw new AuthorUserNotMatchError(
|
||||
// ユーザー自身が担当したタスクでない場合はエラー
|
||||
if (task.typist_user_id !== userId) {
|
||||
throw new TypistUserNotFoundError(
|
||||
`task typist is not match. audio_file_id:${audioFileId}, task.typist_user_id:${task.typist_user_id}, userId:${userId}`,
|
||||
);
|
||||
}
|
||||
@ -676,7 +668,6 @@ export class FilesService {
|
||||
case TasksNotFoundError:
|
||||
case AccountNotMatchError:
|
||||
case StatusNotMatchError:
|
||||
case AuthorUserNotMatchError:
|
||||
case TypistUserNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010603'),
|
||||
@ -726,6 +717,7 @@ export class FilesService {
|
||||
);
|
||||
try {
|
||||
const { account } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
if (!account) {
|
||||
@ -788,7 +780,7 @@ export class FilesService {
|
||||
try {
|
||||
// ユーザー取得
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// URLにSASトークンがついている場合は取り除く;
|
||||
const urlObj = new URL(url);
|
||||
@ -800,6 +792,7 @@ export class FilesService {
|
||||
|
||||
// テンプレートファイル情報をDBに登録
|
||||
await this.templateFilesRepository.upsertTemplateFile(
|
||||
context,
|
||||
accountId,
|
||||
fileName,
|
||||
fileUrl,
|
||||
|
||||
@ -1,4 +1,17 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
ArrayMaxSize,
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumberString,
|
||||
MaxLength,
|
||||
Min,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
|
||||
export class AudioUploadLocationRequest {}
|
||||
|
||||
@ -11,6 +24,9 @@ export class AudioUploadLocationResponse {
|
||||
|
||||
export class AudioDownloadLocationRequest {
|
||||
@ApiProperty({ description: 'ODMSCloud上で管理する音声ファイルのID' })
|
||||
@Type(() => Number)
|
||||
@Min(1)
|
||||
@IsInt()
|
||||
audioFileId: number;
|
||||
}
|
||||
|
||||
@ -23,6 +39,9 @@ export class AudioDownloadLocationResponse {
|
||||
|
||||
export class TemplateDownloadLocationRequest {
|
||||
@ApiProperty({ description: '文字起こし対象の音声ファイルID' })
|
||||
@Type(() => Number)
|
||||
@Min(1)
|
||||
@IsInt()
|
||||
audioFileId: number;
|
||||
}
|
||||
|
||||
@ -38,40 +57,58 @@ export class TemplateUploadLocationResponse {
|
||||
|
||||
export class AudioOptionItem {
|
||||
@ApiProperty({ minLength: 1, maxLength: 16 })
|
||||
@MinLength(1)
|
||||
@MaxLength(16)
|
||||
optionItemLabel: string;
|
||||
@ApiProperty({ minLength: 1, maxLength: 20 })
|
||||
@MinLength(1)
|
||||
@MaxLength(20)
|
||||
optionItemValue: string;
|
||||
}
|
||||
|
||||
export class AudioUploadFinishedRequest {
|
||||
@ApiProperty({ description: 'アップロード先Blob Storage(ファイル名含む)' })
|
||||
@IsNotEmpty()
|
||||
url: string;
|
||||
@ApiProperty({ description: '自分自身(ログイン認証)したAuthorID' })
|
||||
@IsNotEmpty()
|
||||
authorId: string;
|
||||
@ApiProperty({ description: '音声ファイル名' })
|
||||
@IsNotEmpty()
|
||||
fileName: string;
|
||||
@ApiProperty({
|
||||
description: '音声ファイルの録音時間(ミリ秒の整数値)',
|
||||
})
|
||||
@IsNumberString()
|
||||
@IsNotEmpty()
|
||||
duration: string;
|
||||
@ApiProperty({
|
||||
description:
|
||||
'音声ファイルの録音作成日時(開始日時)(yyyy-mm-ddThh:mm:ss.sss)',
|
||||
})
|
||||
@IsNotEmpty()
|
||||
createdDate: string;
|
||||
@ApiProperty({
|
||||
description: '音声ファイルの録音作成終了日時(yyyy-mm-ddThh:mm:ss.sss)',
|
||||
})
|
||||
@IsNotEmpty()
|
||||
finishedDate: string;
|
||||
@ApiProperty({
|
||||
description: '音声ファイルのアップロード日時(yyyy-mm-ddThh:mm:ss.sss)',
|
||||
})
|
||||
@IsNotEmpty()
|
||||
uploadedDate: string;
|
||||
@ApiProperty({ description: '音声ファイルのファイルサイズ(Byte)' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
fileSize: number;
|
||||
@ApiProperty({ description: '優先度 "00":Normal / "01":High' })
|
||||
@IsIn(['00', '01'], { message: 'invalid priority' })
|
||||
@IsNotEmpty()
|
||||
priority: string;
|
||||
@ApiProperty({ description: '録音形式: DSS/DS2(SP)/DS2(QP)' })
|
||||
@IsNotEmpty()
|
||||
audioFormat: string;
|
||||
@ApiProperty()
|
||||
comment: string;
|
||||
@ -83,6 +120,9 @@ export class AudioUploadFinishedRequest {
|
||||
minItems: 10,
|
||||
description: '音声ファイルに紐づくOption Itemの一覧(10個固定)',
|
||||
})
|
||||
@IsArray()
|
||||
@ArrayMinSize(10)
|
||||
@ArrayMaxSize(10)
|
||||
optionItemList: AudioOptionItem[];
|
||||
@ApiProperty()
|
||||
isEncrypted: boolean;
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
@ -34,12 +35,13 @@ import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES, TIERS } from '../../constants';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
@ApiTags('licenses')
|
||||
@Controller('licenses')
|
||||
export class LicensesController {
|
||||
private readonly logger = new Logger(LicensesController.name);
|
||||
constructor(private readonly licensesService: LicensesService) {}
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
@ -83,6 +85,22 @@ export class LicensesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -91,7 +109,9 @@ export class LicensesController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
// ライセンス注文処理
|
||||
await this.licensesService.licenseOrders(
|
||||
@ -136,6 +156,22 @@ export class LicensesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -144,7 +180,9 @@ export class LicensesController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const cardLicenseKeys = await this.licensesService.issueCardLicenseKeys(
|
||||
context,
|
||||
@ -198,6 +236,22 @@ export class LicensesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -206,7 +260,9 @@ export class LicensesController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.licensesService.activateCardLicenseKey(
|
||||
context,
|
||||
@ -257,6 +313,22 @@ export class LicensesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -266,7 +338,8 @@ export class LicensesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const allocatableLicenses =
|
||||
await this.licensesService.getAllocatableLicenses(context, userId);
|
||||
@ -319,6 +392,22 @@ export class LicensesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -328,7 +417,8 @@ export class LicensesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.licensesService.cancelOrder(context, userId, body.poNumber);
|
||||
return {};
|
||||
|
||||
@ -4,12 +4,16 @@ import { LicensesService } from './licenses.service';
|
||||
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
||||
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
||||
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
|
||||
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
|
||||
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
UsersRepositoryModule,
|
||||
AccountsRepositoryModule,
|
||||
LicensesRepositoryModule,
|
||||
AdB2cModule,
|
||||
SendGridModule,
|
||||
],
|
||||
controllers: [LicensesController],
|
||||
providers: [LicensesService],
|
||||
|
||||
@ -1,24 +1,6 @@
|
||||
import { AccessToken } from '../../common/token';
|
||||
import {
|
||||
CreateOrdersRequest,
|
||||
IssueCardLicensesRequest,
|
||||
IssueCardLicensesResponse,
|
||||
ActivateCardLicensesRequest,
|
||||
NewAllocatedLicenseExpirationDate,
|
||||
} from './types/types';
|
||||
import {
|
||||
makeDefaultAccountsRepositoryMockValue,
|
||||
makeDefaultLicensesRepositoryMockValue,
|
||||
makeDefaultUsersRepositoryMockValue,
|
||||
makeLicensesServiceMock,
|
||||
} from './test/liscense.service.mock';
|
||||
import { NewAllocatedLicenseExpirationDate } from './types/types';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import {
|
||||
PoNumberAlreadyExistError,
|
||||
LicenseKeyAlreadyActivatedError,
|
||||
LicenseNotExistError,
|
||||
} from '../../repositories/licenses/errors/types';
|
||||
import { LicensesService } from './licenses.service';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
import { DataSource } from 'typeorm';
|
||||
@ -42,273 +24,171 @@ import {
|
||||
makeTestSimpleAccount,
|
||||
makeTestUser,
|
||||
} from '../../common/test/utility';
|
||||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||||
import { overrideSendgridService } from '../../common/test/overrides';
|
||||
|
||||
describe('ライセンス注文', () => {
|
||||
let source: DataSource | null = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: ':memory:',
|
||||
logging: false,
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!source) return;
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
describe('LicensesService', () => {
|
||||
it('ライセンス注文が完了する', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId, parent_account_id: parentAccountId } =
|
||||
await makeTestSimpleAccount(source, {
|
||||
parent_account_id: 2,
|
||||
});
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
});
|
||||
|
||||
const poNumber = 'test1';
|
||||
const orderCount = 100;
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await service.licenseOrders(context, externalId, poNumber, orderCount);
|
||||
const dbSelectResult = await selectOrderLicense(
|
||||
source,
|
||||
accountId,
|
||||
poNumber,
|
||||
);
|
||||
const body = new CreateOrdersRequest();
|
||||
const userId = '0001';
|
||||
body.orderCount = 1000;
|
||||
body.poNumber = '1';
|
||||
const context = makeContext(`uuidv4`);
|
||||
expect(
|
||||
|
||||
expect(dbSelectResult.orderLicense?.po_number).toEqual(poNumber);
|
||||
expect(dbSelectResult.orderLicense?.quantity).toEqual(orderCount);
|
||||
expect(dbSelectResult.orderLicense?.from_account_id).toEqual(accountId);
|
||||
expect(dbSelectResult.orderLicense?.to_account_id).toEqual(parentAccountId);
|
||||
expect(dbSelectResult.orderLicense?.status).toEqual('Issue Requesting');
|
||||
});
|
||||
|
||||
it('POナンバー重複時、エラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source, {
|
||||
parent_account_id: 2,
|
||||
});
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
});
|
||||
|
||||
const poNumber = 'test1';
|
||||
const orderCount = 100;
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
try {
|
||||
await service.licenseOrders(context, externalId, poNumber, orderCount);
|
||||
await service.licenseOrders(context, externalId, poNumber, orderCount);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010401'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('ユーザID取得できなかった場合、エラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source, {
|
||||
parent_account_id: 2,
|
||||
});
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'useId',
|
||||
role: 'admin',
|
||||
});
|
||||
|
||||
const poNumber = 'test1';
|
||||
const orderCount = 100;
|
||||
|
||||
// 存在しないのユーザーIDを作成
|
||||
const notExistUserId = 'notExistId';
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
try {
|
||||
await service.licenseOrders(
|
||||
context,
|
||||
userId,
|
||||
body.poNumber,
|
||||
body.orderCount,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
it('ユーザID取得できなかった場合、エラーとなる', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserByExternalId = new Error(
|
||||
'User not Found Error.',
|
||||
);
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new CreateOrdersRequest();
|
||||
const userId = '';
|
||||
body.orderCount = 1000;
|
||||
body.poNumber = '1';
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
notExistUserId,
|
||||
poNumber,
|
||||
orderCount,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010204'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('親ユーザID取得できなかった場合、エラーとなる', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserByExternalId = new Error(
|
||||
'Account not Found Error.',
|
||||
);
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new CreateOrdersRequest();
|
||||
const userId = '0001';
|
||||
body.orderCount = 1000;
|
||||
body.poNumber = '1';
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('POナンバー重複時、エラーとなる', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
lisencesRepositoryMockValue.order = new PoNumberAlreadyExistError(
|
||||
`This PoNumber already used`,
|
||||
);
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new CreateOrdersRequest();
|
||||
const userId = '0001';
|
||||
body.orderCount = 1000;
|
||||
body.poNumber = '1';
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E010401'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('カードライセンス発行が完了する', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new IssueCardLicensesRequest();
|
||||
const userId = '0001';
|
||||
body.createCount = 10;
|
||||
const issueCardLicensesResponse: IssueCardLicensesResponse = {
|
||||
cardLicenseKeys: [
|
||||
'WZCETXC0Z9PQZ9GKRGGY',
|
||||
'F0JD7EZEDBH4PQRQ83YF',
|
||||
'H0HXBP5K9RW7T7JSVDJV',
|
||||
'HKIWX54EESYL4X132223',
|
||||
'363E81JR460UBHXGFXFI',
|
||||
'70IKAPV9K6YMEVLTOXBY',
|
||||
'1RJY1TRRYYTGF1LL9WLU',
|
||||
'BXM0HKFO7IULTL0A1B36',
|
||||
'XYLEWNY2LR6Q657CZE41',
|
||||
'AEJWRFFSWRQYQQJ6WVLV',
|
||||
],
|
||||
};
|
||||
const context = makeContext(`uuidv4`);
|
||||
expect(
|
||||
await service.issueCardLicenseKeys(context, userId, body.createCount),
|
||||
).toEqual(issueCardLicensesResponse);
|
||||
});
|
||||
it('カードライセンス発行に失敗した場合、エラーになる', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
lisencesRepositoryMockValue.createCardLicenses = new Error('DB failed');
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new IssueCardLicensesRequest();
|
||||
const userId = '0001';
|
||||
body.createCount = 1000;
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.issueCardLicenseKeys(context, userId, body.createCount),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('カードライセンス取り込みが完了する', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new ActivateCardLicensesRequest();
|
||||
const userId = '0001';
|
||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
||||
const context = makeContext(`uuidv4`);
|
||||
expect(
|
||||
await service.activateCardLicenseKey(
|
||||
context,
|
||||
userId,
|
||||
body.cardLicenseKey,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
it('カードライセンス取り込みに失敗した場合、エラーになる(DBエラー)', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
lisencesRepositoryMockValue.activateCardLicense = new Error('DB failed');
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new ActivateCardLicensesRequest();
|
||||
const userId = '0001';
|
||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが存在しないエラー)', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
lisencesRepositoryMockValue.activateCardLicense = new LicenseNotExistError(
|
||||
`License not exist`,
|
||||
);
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new ActivateCardLicensesRequest();
|
||||
const userId = '0001';
|
||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010801'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが既に取り込まれているエラー)', async () => {
|
||||
const lisencesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
lisencesRepositoryMockValue.activateCardLicense =
|
||||
new LicenseKeyAlreadyActivatedError(`License already activated`);
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const accountsRepositoryMockValue =
|
||||
makeDefaultAccountsRepositoryMockValue();
|
||||
const service = await makeLicensesServiceMock(
|
||||
lisencesRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
accountsRepositoryMockValue,
|
||||
);
|
||||
const body = new ActivateCardLicensesRequest();
|
||||
const userId = '0001';
|
||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
||||
const context = makeContext(`uuidv4`);
|
||||
await expect(
|
||||
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010802'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source, {
|
||||
parent_account_id: undefined,
|
||||
});
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
});
|
||||
|
||||
const poNumber = 'test1';
|
||||
const orderCount = 100;
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
try {
|
||||
await service.licenseOrders(context, externalId, poNumber, orderCount);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('DBテスト', () => {
|
||||
describe('カードライセンス発行', () => {
|
||||
let source: DataSource | null = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
@ -342,12 +222,68 @@ describe('DBテスト', () => {
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const issueCount = 500;
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await service.issueCardLicenseKeys(context, externalId, issueCount);
|
||||
const dbSelectResult = await selectCardLicensesCount(source);
|
||||
expect(dbSelectResult.count).toEqual(issueCount);
|
||||
});
|
||||
|
||||
it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
author_id: undefined,
|
||||
});
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const issueCount = 500;
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const licensesService = module.get<LicensesRepositoryService>(
|
||||
LicensesRepositoryService,
|
||||
);
|
||||
licensesService.createCardLicenses = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
|
||||
try {
|
||||
await service.issueCardLicenseKeys(context, externalId, issueCount);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('カードライセンスを取り込む', () => {
|
||||
let source: DataSource | null = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: ':memory:',
|
||||
logging: false,
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (!source) return;
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
it('カードライセンス取り込みが完了する', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
@ -382,7 +318,7 @@ describe('DBテスト', () => {
|
||||
await createCardLicenseIssue(source, issueId);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await service.activateCardLicenseKey(context, externalId, cardLicenseKey);
|
||||
const dbSelectResultFromCardLicense = await selectCardLicense(
|
||||
@ -529,7 +465,7 @@ describe('DBテスト', () => {
|
||||
null,
|
||||
);
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const context = makeContext('userId');
|
||||
const context = makeContext('userId', 'requestId');
|
||||
const response = await service.getAllocatableLicenses(context, externalId);
|
||||
// 対象外のデータは取得していないことを確認する
|
||||
expect(response.allocatableLicenses.length).toBe(5);
|
||||
@ -541,6 +477,126 @@ describe('DBテスト', () => {
|
||||
expect(response.allocatableLicenses[3].licenseId).toBe(1);
|
||||
expect(response.allocatableLicenses[4].licenseId).toBe(3);
|
||||
});
|
||||
|
||||
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが存在しないエラー)', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
author_id: undefined,
|
||||
});
|
||||
|
||||
//存在しないライセンスキーを作成
|
||||
const notExistCardLicenseKey = 'XXCETXC0Z9PQZ9GKRGGY';
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
try {
|
||||
await service.activateCardLicenseKey(
|
||||
context,
|
||||
externalId,
|
||||
notExistCardLicenseKey,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010801'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが既に取り込まれているエラー)', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
author_id: undefined,
|
||||
});
|
||||
|
||||
const cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
||||
const defaultAccountId = 150;
|
||||
const license_id = 50;
|
||||
const issueId = 100;
|
||||
|
||||
await createLicense(
|
||||
source,
|
||||
license_id,
|
||||
null,
|
||||
defaultAccountId,
|
||||
LICENSE_TYPE.CARD,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
await createCardLicense(source, license_id, issueId, cardLicenseKey);
|
||||
await createCardLicenseIssue(source, issueId);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
try {
|
||||
await service.activateCardLicenseKey(context, externalId, cardLicenseKey);
|
||||
await service.activateCardLicenseKey(context, externalId, cardLicenseKey);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010802'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { external_id: externalId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'admin',
|
||||
author_id: undefined,
|
||||
});
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
const cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const licensesService = module.get<LicensesRepositoryService>(
|
||||
LicensesRepositoryService,
|
||||
);
|
||||
licensesService.activateCardLicense = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
try {
|
||||
await service.activateCardLicenseKey(context, externalId, cardLicenseKey);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ライセンス割り当て', () => {
|
||||
@ -596,10 +652,15 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
|
||||
const expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 1);
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
1,
|
||||
);
|
||||
const resultLicense = await selectLicense(source, 1);
|
||||
expect(resultLicense.license?.allocated_user_id).toBe(userId);
|
||||
expect(resultLicense.license?.status).toBe(
|
||||
@ -663,8 +724,13 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 1);
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
1,
|
||||
);
|
||||
const result = await selectLicense(source, 1);
|
||||
expect(result.license?.allocated_user_id).toBe(userId);
|
||||
expect(result.license?.status).toBe(LICENSE_ALLOCATED_STATUS.ALLOCATED);
|
||||
@ -736,10 +802,14 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
overrideSendgridService(service, {});
|
||||
const expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 2);
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
2,
|
||||
);
|
||||
|
||||
// もともと割り当てられていたライセンスの状態確認
|
||||
const result1 = await selectLicense(source, 1);
|
||||
@ -838,7 +908,12 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 2);
|
||||
overrideSendgridService(service, {});
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
2,
|
||||
);
|
||||
|
||||
const licenseAllocationHistory = await selectLicenseAllocationHistory(
|
||||
source,
|
||||
@ -898,7 +973,12 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 2);
|
||||
overrideSendgridService(service, {});
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
2,
|
||||
);
|
||||
|
||||
const licenseAllocationHistory = await selectLicenseAllocationHistory(
|
||||
source,
|
||||
@ -958,7 +1038,12 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 2);
|
||||
overrideSendgridService(service, {});
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
2,
|
||||
);
|
||||
|
||||
const licenseAllocationHistory = await selectLicenseAllocationHistory(
|
||||
source,
|
||||
@ -998,9 +1083,14 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
|
||||
await expect(
|
||||
service.allocateLicense(makeContext('trackingId'), userId, 1),
|
||||
service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
1,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -1046,14 +1136,23 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
|
||||
await expect(
|
||||
service.allocateLicense(makeContext('trackingId'), userId, 1),
|
||||
service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
1,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
await expect(
|
||||
service.allocateLicense(makeContext('trackingId'), userId, 2),
|
||||
service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
2,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -1115,7 +1214,11 @@ describe('ライセンス割り当て解除', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.deallocateLicense(makeContext('trackingId'), userId);
|
||||
overrideSendgridService(service, {});
|
||||
await service.deallocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
);
|
||||
|
||||
// 割り当て解除したライセンスの状態確認
|
||||
const deallocatedLicense = await selectLicense(source, 1);
|
||||
@ -1202,8 +1305,9 @@ describe('ライセンス割り当て解除', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
await expect(
|
||||
service.deallocateLicense(makeContext('trackingId'), userId),
|
||||
service.deallocateLicense(makeContext('trackingId', 'requestId'), userId),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010807'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -1258,8 +1362,9 @@ describe('ライセンス注文キャンセル', () => {
|
||||
);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
overrideSendgridService(service, {});
|
||||
await service.cancelOrder(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
tier2Accounts[0].users[0].external_id,
|
||||
poNumber,
|
||||
);
|
||||
@ -1293,9 +1398,10 @@ describe('ライセンス注文キャンセル', () => {
|
||||
);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
overrideSendgridService(service, {});
|
||||
await expect(
|
||||
service.cancelOrder(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
tier2Accounts[0].users[0].external_id,
|
||||
poNumber,
|
||||
),
|
||||
@ -1324,9 +1430,10 @@ describe('ライセンス注文キャンセル', () => {
|
||||
);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
overrideSendgridService(service, {});
|
||||
await expect(
|
||||
service.cancelOrder(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
tier2Accounts[0].users[0].external_id,
|
||||
poNumber,
|
||||
),
|
||||
|
||||
@ -16,6 +16,10 @@ import {
|
||||
IssueCardLicensesResponse,
|
||||
} from './types/types';
|
||||
import { Context } from '../../common/log';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
||||
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||
import { LICENSE_ISSUE_STATUS } from '../../constants';
|
||||
|
||||
@Injectable()
|
||||
export class LicensesService {
|
||||
@ -23,6 +27,8 @@ export class LicensesService {
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly accountsRepository: AccountsRepositoryService,
|
||||
private readonly licensesRepository: LicensesRepositoryService,
|
||||
private readonly adb2cService: AdB2cService,
|
||||
private readonly sendgridService: SendGridService,
|
||||
) {}
|
||||
private readonly logger = new Logger(LicensesService.name);
|
||||
|
||||
@ -49,7 +55,7 @@ export class LicensesService {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
try {
|
||||
myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(externalId)
|
||||
await this.usersRepository.findUserByExternalId(context, externalId)
|
||||
).account_id;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -70,7 +76,7 @@ export class LicensesService {
|
||||
// 親アカウントIDを取得
|
||||
try {
|
||||
parentAccountId =
|
||||
(await this.accountsRepository.findAccountById(myAccountId))
|
||||
(await this.accountsRepository.findAccountById(context, myAccountId))
|
||||
.parent_account_id ?? undefined;
|
||||
// 親アカウントIDが取得できない場合はエラー
|
||||
if (parentAccountId === undefined) {
|
||||
@ -94,11 +100,34 @@ export class LicensesService {
|
||||
|
||||
try {
|
||||
await this.licensesRepository.order(
|
||||
context,
|
||||
poNumber,
|
||||
myAccountId,
|
||||
parentAccountId,
|
||||
orderCount,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
const customer = await this.getAccountInformation(context, myAccountId);
|
||||
const dealer = await this.getAccountInformation(
|
||||
context,
|
||||
parentAccountId,
|
||||
);
|
||||
|
||||
this.sendgridService.sendMailWithU105(
|
||||
context,
|
||||
customer.adminEmails,
|
||||
customer.companyName,
|
||||
orderCount,
|
||||
poNumber,
|
||||
dealer.adminEmails,
|
||||
dealer.companyName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
switch (e.constructor) {
|
||||
case PoNumberAlreadyExistError:
|
||||
@ -134,7 +163,7 @@ export class LicensesService {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
try {
|
||||
myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(externalId)
|
||||
await this.usersRepository.findUserByExternalId(context, externalId)
|
||||
).account_id;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -153,6 +182,7 @@ export class LicensesService {
|
||||
}
|
||||
try {
|
||||
const licenseKeys = await this.licensesRepository.createCardLicenses(
|
||||
context,
|
||||
myAccountId,
|
||||
createCount,
|
||||
);
|
||||
@ -194,7 +224,7 @@ export class LicensesService {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
try {
|
||||
myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(externalId)
|
||||
await this.usersRepository.findUserByExternalId(context, externalId)
|
||||
).account_id;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -215,6 +245,7 @@ export class LicensesService {
|
||||
// カードライセンスを取り込む
|
||||
try {
|
||||
await this.licensesRepository.activateCardLicense(
|
||||
context,
|
||||
myAccountId,
|
||||
cardLicenseKey,
|
||||
);
|
||||
@ -266,11 +297,14 @@ export class LicensesService {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
try {
|
||||
const myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(userId)
|
||||
await this.usersRepository.findUserByExternalId(context, userId)
|
||||
).account_id;
|
||||
// 割り当て可能なライセンスを取得する
|
||||
const allocatableLicenses =
|
||||
await this.licensesRepository.getAllocatableLicenses(myAccountId);
|
||||
await this.licensesRepository.getAllocatableLicenses(
|
||||
context,
|
||||
myAccountId,
|
||||
);
|
||||
|
||||
return {
|
||||
allocatableLicenses,
|
||||
@ -316,10 +350,78 @@ export class LicensesService {
|
||||
try {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
myAccountId = (
|
||||
await this.usersRepository.findUserByExternalId(externalId)
|
||||
await this.usersRepository.findUserByExternalId(context, externalId)
|
||||
).account_id;
|
||||
// 注文キャンセル処理
|
||||
await this.licensesRepository.cancelOrder(myAccountId, poNumber);
|
||||
const { canceledOrderId } = await this.licensesRepository.cancelOrder(
|
||||
context,
|
||||
myAccountId,
|
||||
poNumber,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
// 注文キャンセルを実行したカスタマー企業の管理者情報を取得する
|
||||
const customerAccountId = myAccountId;
|
||||
|
||||
// カスタマー企業の企業名と管理者情報を取得する
|
||||
const {
|
||||
companyName: customerCompanyName,
|
||||
adminEmails: customerAdminEmails,
|
||||
} = await this.getAccountInformation(context, customerAccountId);
|
||||
|
||||
// ディーラー企業を特定する
|
||||
const { parent_account_id: dealerAccountId } =
|
||||
await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
customerAccountId,
|
||||
);
|
||||
// ライセンス発行が行われているので、ディーラーは必ず存在するはず
|
||||
if (dealerAccountId == null) {
|
||||
throw new Error('dealerAccountId is null');
|
||||
}
|
||||
|
||||
// ディーラー企業の企業名と管理者情報を取得する
|
||||
const {
|
||||
companyName: dealerCompanyName,
|
||||
adminEmails: dealerAdminEmails,
|
||||
} = await this.getAccountInformation(context, dealerAccountId);
|
||||
|
||||
// キャンセル済みの注文をID指定して取得する
|
||||
const order = await this.licensesRepository.getLicenseOrder(
|
||||
context,
|
||||
customerAccountId,
|
||||
poNumber,
|
||||
canceledOrderId,
|
||||
);
|
||||
|
||||
if (order == null) {
|
||||
throw new Error(
|
||||
`cancel target order not found. fromAccountId: ${customerAccountId}, poNumber:${poNumber}`,
|
||||
);
|
||||
}
|
||||
if (order.status !== LICENSE_ISSUE_STATUS.CANCELED) {
|
||||
throw new Error(
|
||||
`target order is not canceled. fromAccountId: ${order.from_account_id}, poNumber:${order.po_number}, status:${order.status}, id:${order.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
const { quantity } = order;
|
||||
|
||||
// 注文キャンセルが成功した旨をメール送信する
|
||||
await this.sendgridService.sendMailWithU106(
|
||||
context,
|
||||
customerAdminEmails,
|
||||
customerCompanyName,
|
||||
quantity,
|
||||
poNumber,
|
||||
dealerAdminEmails,
|
||||
dealerCompanyName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
switch (e.constructor) {
|
||||
@ -346,4 +448,51 @@ export class LicensesService {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウントIDを指定して、アカウント情報と管理者情報を取得する
|
||||
* @param context
|
||||
* @param accountId 対象アカウントID
|
||||
* @returns 企業名/管理者メールアドレス
|
||||
*/
|
||||
private async getAccountInformation(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<{
|
||||
companyName: string;
|
||||
adminEmails: string[];
|
||||
}> {
|
||||
// アカウントIDから企業名を取得する
|
||||
const { company_name } = await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// 管理者一覧を取得
|
||||
const admins = await this.usersRepository.findAdminUsers(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
const adminExternalIDs = admins.map((x) => x.external_id);
|
||||
|
||||
// ADB2Cから管理者IDを元にメールアドレスを取得する
|
||||
const usersInfo = await this.adb2cService.getUsers(
|
||||
context,
|
||||
adminExternalIDs,
|
||||
);
|
||||
|
||||
// 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する
|
||||
const adminEmails = usersInfo.map((x) => {
|
||||
const { emailAddress } = getUserNameAndMailAddress(x);
|
||||
if (emailAddress == null) {
|
||||
throw new Error('dealer admin email-address is not found');
|
||||
}
|
||||
return emailAddress;
|
||||
});
|
||||
|
||||
return {
|
||||
companyName: company_name,
|
||||
adminEmails: adminEmails,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
LICENSE_EXPIRATION_TIME_WITH_TIMEZONE,
|
||||
TRIAL_LICENSE_EXPIRATION_DAYS,
|
||||
} from '../../../constants';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class CreateOrdersRequest {
|
||||
@ApiProperty()
|
||||
@ -13,6 +14,7 @@ export class CreateOrdersRequest {
|
||||
poNumber: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(9999)
|
||||
@ -23,6 +25,7 @@ export class CreateOrdersResponse {}
|
||||
|
||||
export class IssueCardLicensesRequest {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(9999)
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
Controller,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
@ -21,12 +22,13 @@ import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
@ApiTags('notification')
|
||||
@Controller('notification')
|
||||
export class NotificationController {
|
||||
private readonly logger = new Logger(NotificationController.name);
|
||||
constructor(private readonly notificationService: NotificationService) {}
|
||||
@Post('register')
|
||||
@ApiResponse({
|
||||
@ -65,6 +67,22 @@ export class NotificationController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -74,7 +92,8 @@ export class NotificationController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.notificationService.register(context, userId, pns, handler);
|
||||
return {};
|
||||
|
||||
@ -19,7 +19,7 @@ describe('NotificationService.register', () => {
|
||||
|
||||
expect(
|
||||
await service.register(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
'external_id',
|
||||
'apns',
|
||||
'handler',
|
||||
@ -38,7 +38,7 @@ describe('NotificationService.register', () => {
|
||||
|
||||
await expect(
|
||||
service.register(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
'external_id',
|
||||
'apns',
|
||||
'handler',
|
||||
@ -63,7 +63,7 @@ describe('NotificationService.register', () => {
|
||||
|
||||
await expect(
|
||||
service.register(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
'external_id',
|
||||
'apns',
|
||||
'handler',
|
||||
|
||||
@ -34,7 +34,9 @@ export class NotificationService {
|
||||
// ユーザIDからアカウントIDを取得する
|
||||
let userId: number;
|
||||
try {
|
||||
userId = (await this.usersRepository.findUserByExternalId(externalId)).id;
|
||||
userId = (
|
||||
await this.usersRepository.findUserByExternalId(context, externalId)
|
||||
).id;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
switch (e.constructor) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsIn } from 'class-validator';
|
||||
import { IsIn, IsNotEmpty, IsString } from 'class-validator';
|
||||
import { PNS } from '../../../constants';
|
||||
|
||||
export class RegisterRequest {
|
||||
@ -9,6 +9,8 @@ export class RegisterRequest {
|
||||
})
|
||||
pns: string;
|
||||
@ApiProperty({ description: 'wnsのチャネルURI or apnsのデバイストークン' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
handler: string;
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,9 @@ import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Headers,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
@ -45,12 +45,13 @@ import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES, USER_ROLES } from '../../constants';
|
||||
import { Roles } from '../../common/types/role';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
@ApiTags('tasks')
|
||||
@Controller('tasks')
|
||||
export class TasksController {
|
||||
private readonly logger = new Logger(TasksController.name);
|
||||
constructor(private readonly taskService: TasksService) {}
|
||||
|
||||
@ApiResponse({
|
||||
@ -91,6 +92,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -102,7 +120,8 @@ export class TasksController {
|
||||
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
|
||||
const roles = role.split(' ') as Roles[];
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const { limit, offset, status } = body;
|
||||
const paramName = isTaskListSortableAttribute(body.paramName ?? '')
|
||||
@ -164,13 +183,29 @@ export class TasksController {
|
||||
): Promise<AudioNextResponse> {
|
||||
const { endedFileId } = param;
|
||||
|
||||
const accessToken = retrieveAuthorizationToken(req) as string;
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
if (!accessToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -179,7 +214,8 @@ export class TasksController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const nextFileId = await this.taskService.getNextTask(
|
||||
context,
|
||||
@ -241,6 +277,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -253,7 +306,8 @@ export class TasksController {
|
||||
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
|
||||
const roles = role.split(' ') as Roles[];
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.taskService.checkout(context, param.audioFileId, roles, userId);
|
||||
return {};
|
||||
@ -311,6 +365,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -320,7 +391,8 @@ export class TasksController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.taskService.checkin(context, audioFileId, userId);
|
||||
return {};
|
||||
@ -378,6 +450,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -389,7 +478,8 @@ export class TasksController {
|
||||
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
|
||||
const roles = role.split(' ') as Roles[];
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.taskService.cancel(context, audioFileId, userId, roles);
|
||||
return {};
|
||||
@ -447,6 +537,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -456,7 +563,8 @@ export class TasksController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.taskService.suspend(context, audioFileId, userId);
|
||||
return {};
|
||||
@ -513,6 +621,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -522,7 +647,8 @@ export class TasksController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.taskService.backup(context, audioFileId, userId);
|
||||
return {};
|
||||
@ -585,6 +711,23 @@ export class TasksController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -596,7 +739,8 @@ export class TasksController {
|
||||
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
|
||||
const roles = role.split(' ') as Roles[];
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.taskService.changeCheckoutPermission(
|
||||
context,
|
||||
|
||||
@ -6,14 +6,18 @@ import { TasksRepositoryModule } from '../../repositories/tasks/tasks.repository
|
||||
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
|
||||
import { UserGroupsRepositoryModule } from '../../repositories/user_groups/user_groups.repository.module';
|
||||
import { NotificationhubModule } from '../../gateways/notificationhub/notificationhub.module';
|
||||
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
|
||||
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AccountsRepositoryModule,
|
||||
UsersRepositoryModule,
|
||||
UserGroupsRepositoryModule,
|
||||
TasksRepositoryModule,
|
||||
AdB2cModule,
|
||||
NotificationhubModule,
|
||||
SendGridModule,
|
||||
],
|
||||
providers: [TasksService],
|
||||
controllers: [TasksController],
|
||||
|
||||
@ -63,7 +63,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
expect(
|
||||
await service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
|
||||
offset,
|
||||
@ -138,7 +138,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
await expect(
|
||||
service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
|
||||
offset,
|
||||
@ -180,7 +180,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
await expect(
|
||||
service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
|
||||
offset,
|
||||
@ -266,7 +266,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
await expect(
|
||||
service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
|
||||
offset,
|
||||
@ -309,8 +309,9 @@ describe('TasksService', () => {
|
||||
const status = ['Uploaded', 'Backup'];
|
||||
const paramName = 'JOB_NUMBER';
|
||||
const direction = 'ASC';
|
||||
const context = makeContext('trackingId', 'requestId');
|
||||
const result = await service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
context,
|
||||
userId,
|
||||
[USER_ROLES.AUTHOR],
|
||||
offset,
|
||||
@ -360,7 +361,7 @@ describe('TasksService', () => {
|
||||
});
|
||||
expect(
|
||||
service.taskRepoService.getTasksFromAuthorIdAndAccountId,
|
||||
).toHaveBeenCalledWith('abcdef', 1, 0, 20, 'JOB_NUMBER', 'ASC', [
|
||||
).toHaveBeenCalledWith(context, 'abcdef', 1, 0, 20, 'JOB_NUMBER', 'ASC', [
|
||||
'Uploaded',
|
||||
'Backup',
|
||||
]);
|
||||
@ -393,7 +394,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
await expect(
|
||||
service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[USER_ROLES.AUTHOR],
|
||||
offset,
|
||||
@ -437,8 +438,9 @@ describe('TasksService', () => {
|
||||
const status = ['Uploaded', 'Backup'];
|
||||
const paramName = 'JOB_NUMBER';
|
||||
const direction = 'ASC';
|
||||
const context = makeContext('trackingId', 'requestId');
|
||||
const result = await service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
context,
|
||||
userId,
|
||||
[USER_ROLES.TYPIST],
|
||||
offset,
|
||||
@ -488,7 +490,7 @@ describe('TasksService', () => {
|
||||
});
|
||||
expect(
|
||||
service.taskRepoService.getTasksFromTypistRelations,
|
||||
).toHaveBeenCalledWith('userId', 0, 20, 'JOB_NUMBER', 'ASC', [
|
||||
).toHaveBeenCalledWith(context, 'userId', 0, 20, 'JOB_NUMBER', 'ASC', [
|
||||
'Uploaded',
|
||||
'Backup',
|
||||
]);
|
||||
@ -521,7 +523,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
await expect(
|
||||
service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[USER_ROLES.TYPIST],
|
||||
offset,
|
||||
@ -563,7 +565,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
await expect(
|
||||
service.tasksService.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
[ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
|
||||
offset,
|
||||
@ -623,7 +625,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
|
||||
const { tasks, total } = await service.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
[ADMIN_ROLES.ADMIN, USER_ROLES.NONE],
|
||||
offset,
|
||||
@ -681,7 +683,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
|
||||
const { tasks, total } = await service.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
external_id,
|
||||
[USER_ROLES.AUTHOR],
|
||||
offset,
|
||||
@ -753,7 +755,7 @@ describe('TasksService', () => {
|
||||
const direction = 'ASC';
|
||||
|
||||
const { tasks, total } = await service.getTasks(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
external_id,
|
||||
[USER_ROLES.AUTHOR],
|
||||
offset,
|
||||
@ -839,7 +841,7 @@ describe('changeCheckoutPermission', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }],
|
||||
'author-user-external-id',
|
||||
@ -856,7 +858,7 @@ describe('changeCheckoutPermission', () => {
|
||||
const resultTask = await getTask(source, taskId);
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId_2}`],
|
||||
{
|
||||
authorId: 'MY_AUTHOR_ID',
|
||||
@ -922,7 +924,7 @@ describe('changeCheckoutPermission', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'USER_GROUP_B', typistGroupId: userGroupId_2 }],
|
||||
'author-user-external-id',
|
||||
@ -940,7 +942,7 @@ describe('changeCheckoutPermission', () => {
|
||||
const resultTask = await getTask(source, taskId);
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId_2}`],
|
||||
{
|
||||
authorId: 'MY_AUTHOR_ID',
|
||||
@ -992,7 +994,7 @@ describe('changeCheckoutPermission', () => {
|
||||
await createCheckoutPermissions(source, taskId, undefined, userGroupId);
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[],
|
||||
'author-user-external-id',
|
||||
@ -1045,7 +1047,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'not-exist-user', typistUserId: 999 }],
|
||||
'author-user-external-id',
|
||||
@ -1111,7 +1113,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'not-verified-user', typistUserId: typistUserId_2 }],
|
||||
'author-user-external-id',
|
||||
@ -1171,7 +1173,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'not-exist-user-group', typistGroupId: 999 }],
|
||||
'author-user-external-id',
|
||||
@ -1213,7 +1215,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'typist-user', typistUserId: typistUserId }],
|
||||
'author-user-external-id',
|
||||
@ -1265,7 +1267,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'typist-user', typistUserId: typistUserId }],
|
||||
'author-user-external-id',
|
||||
@ -1317,7 +1319,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'typist-user', typistUserId: typistUserId }],
|
||||
'author-user-external-id',
|
||||
@ -1383,7 +1385,7 @@ describe('changeCheckoutPermission', () => {
|
||||
|
||||
try {
|
||||
await service.changeCheckoutPermission(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
[{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }],
|
||||
'author-user-external-id',
|
||||
@ -1460,7 +1462,7 @@ describe('checkout', () => {
|
||||
const initTask = await getTask(source, taskId);
|
||||
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
@ -1520,7 +1522,7 @@ describe('checkout', () => {
|
||||
const initTask = await getTask(source, taskId);
|
||||
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
@ -1573,7 +1575,7 @@ describe('checkout', () => {
|
||||
const initTask = await getTask(source, taskId);
|
||||
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
@ -1625,7 +1627,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
@ -1672,7 +1674,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
@ -1733,7 +1735,7 @@ describe('checkout', () => {
|
||||
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
audioFileId,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
@ -1798,7 +1800,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
2,
|
||||
['typist'],
|
||||
'typist-user-external-id2',
|
||||
@ -1839,7 +1841,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
expect(
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['author'],
|
||||
'author-user-external-id',
|
||||
@ -1873,7 +1875,7 @@ describe('checkout', () => {
|
||||
|
||||
expect(
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['author'],
|
||||
'author-user-external-id',
|
||||
@ -1896,7 +1898,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['author'],
|
||||
'author-user-external-id',
|
||||
@ -1937,7 +1939,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['author'],
|
||||
'author-user-external-id',
|
||||
@ -1968,7 +1970,7 @@ describe('checkout', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['none'],
|
||||
'none-user-external-id',
|
||||
@ -2043,7 +2045,7 @@ describe('checkin', () => {
|
||||
const initTask = await getTask(source, taskId);
|
||||
|
||||
await service.checkin(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
);
|
||||
@ -2089,7 +2091,11 @@ describe('checkin', () => {
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
await expect(
|
||||
service.checkin(makeContext('trackingId'), 1, 'typist-user-external-id'),
|
||||
service.checkin(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -2137,7 +2143,11 @@ describe('checkin', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.checkin(makeContext('trackingId'), 1, 'typist-user-external-id'),
|
||||
service.checkin(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -2169,7 +2179,11 @@ describe('checkin', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.checkin(makeContext('trackingId'), 1, 'typist-user-external-id'),
|
||||
service.checkin(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.NOT_FOUND),
|
||||
);
|
||||
@ -2231,7 +2245,7 @@ describe('suspend', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await service.suspend(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
);
|
||||
@ -2276,7 +2290,11 @@ describe('suspend', () => {
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
await expect(
|
||||
service.suspend(makeContext('trackingId'), 1, 'typist-user-external-id'),
|
||||
service.suspend(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -2324,7 +2342,11 @@ describe('suspend', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.checkin(makeContext('trackingId'), 1, 'typist-user-external-id'),
|
||||
service.checkin(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -2356,7 +2378,11 @@ describe('suspend', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.checkin(makeContext('trackingId'), 1, 'typist-user-external-id'),
|
||||
service.checkin(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.NOT_FOUND),
|
||||
);
|
||||
@ -2419,7 +2445,7 @@ describe('cancel', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['typist', 'standard'],
|
||||
@ -2468,7 +2494,7 @@ describe('cancel', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['typist', 'standard'],
|
||||
@ -2520,7 +2546,7 @@ describe('cancel', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['admin', 'author'],
|
||||
@ -2571,7 +2597,7 @@ describe('cancel', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['admin', 'author'],
|
||||
@ -2620,10 +2646,12 @@ describe('cancel', () => {
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
await expect(
|
||||
service.cancel(makeContext('trackingId'), 1, 'typist-user-external-id', [
|
||||
'admin',
|
||||
'author',
|
||||
]),
|
||||
service.cancel(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['admin', 'author'],
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -2671,10 +2699,12 @@ describe('cancel', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.cancel(makeContext('trackingId'), 1, 'typist-user-external-id', [
|
||||
'typist',
|
||||
'standard',
|
||||
]),
|
||||
service.cancel(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['typist', 'standard'],
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -2706,10 +2736,12 @@ describe('cancel', () => {
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.cancel(makeContext('trackingId'), 1, 'typist-user-external-id', [
|
||||
'typist',
|
||||
'standard',
|
||||
]),
|
||||
service.cancel(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['typist', 'standard'],
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010603'), HttpStatus.NOT_FOUND),
|
||||
);
|
||||
@ -2774,7 +2806,7 @@ describe('cancel', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
'typist-user-external-id',
|
||||
['typist', 'standard'],
|
||||
@ -2791,7 +2823,7 @@ describe('cancel', () => {
|
||||
expect(permisions[0].user_id).toEqual(typistUserId);
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${typistUserId}`],
|
||||
{
|
||||
authorId: 'AUTHOR_ID',
|
||||
@ -2884,7 +2916,7 @@ describe('cancel', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
external_id,
|
||||
role.split(' ') as Roles[],
|
||||
@ -2901,7 +2933,7 @@ describe('cancel', () => {
|
||||
expect(permisions[0].user_id).toEqual(autoRoutingTypistUserId);
|
||||
// 通知処理が想定通りの引数で呼ばれているか確認
|
||||
expect(NotificationHubService.notify).toHaveBeenCalledWith(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
[`user_${autoRoutingTypistUserId}`],
|
||||
{
|
||||
authorId: 'AUTHOR_ID',
|
||||
@ -2956,7 +2988,7 @@ describe('cancel', () => {
|
||||
NotificationhubService,
|
||||
);
|
||||
await service.cancel(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
external_id,
|
||||
role.split(' ') as Roles[],
|
||||
@ -3030,7 +3062,7 @@ describe('backup', () => {
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
await service.backup(
|
||||
makeContext(admin.external_id),
|
||||
makeContext(admin.external_id, 'requestId'),
|
||||
audioFileId,
|
||||
admin.external_id,
|
||||
);
|
||||
@ -3082,7 +3114,7 @@ describe('backup', () => {
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
await service.backup(
|
||||
makeContext(admin.external_id),
|
||||
makeContext(admin.external_id, 'requestId'),
|
||||
audioFileId,
|
||||
admin.external_id,
|
||||
);
|
||||
@ -3135,7 +3167,7 @@ describe('backup', () => {
|
||||
|
||||
try {
|
||||
await service.backup(
|
||||
makeContext(admin.external_id),
|
||||
makeContext(admin.external_id, 'requestId'),
|
||||
audioFileId,
|
||||
admin.external_id,
|
||||
);
|
||||
@ -3190,7 +3222,7 @@ describe('backup', () => {
|
||||
|
||||
try {
|
||||
await service.backup(
|
||||
makeContext(admin.external_id),
|
||||
makeContext(admin.external_id, 'requestId'),
|
||||
9999, // 存在しないタスクID
|
||||
admin.external_id,
|
||||
);
|
||||
@ -3251,7 +3283,7 @@ describe('backup', () => {
|
||||
|
||||
try {
|
||||
await service.backup(
|
||||
makeContext(admin.external_id),
|
||||
makeContext(admin.external_id, 'requestId'),
|
||||
audioFileId,
|
||||
admin.external_id,
|
||||
);
|
||||
@ -3344,7 +3376,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId2, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const nextAudioFileId = await service.getNextTask(
|
||||
context,
|
||||
@ -3416,7 +3448,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId2, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const nextAudioFileId = await service.getNextTask(
|
||||
context,
|
||||
@ -3488,7 +3520,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId2, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const nextAudioFileId = await service.getNextTask(
|
||||
context,
|
||||
@ -3560,7 +3592,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId2, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const nextAudioFileId = await service.getNextTask(
|
||||
context,
|
||||
@ -3632,7 +3664,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId2, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const nextAudioFileId = await service.getNextTask(
|
||||
context,
|
||||
@ -3680,7 +3712,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId1, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const nextAudioFileId = await service.getNextTask(
|
||||
context,
|
||||
@ -3727,7 +3759,7 @@ describe('getNextTask', () => {
|
||||
await createCheckoutPermissions(source, taskId1, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
// 実行結果が正しいか確認
|
||||
try {
|
||||
|
||||
@ -33,15 +33,20 @@ import { NotificationhubService } from '../../gateways/notificationhub/notificat
|
||||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||||
import { Context } from '../../common/log';
|
||||
import { User } from '../../repositories/users/entity/user.entity';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
|
||||
@Injectable()
|
||||
export class TasksService {
|
||||
private readonly logger = new Logger(TasksService.name);
|
||||
constructor(
|
||||
private readonly accountsRepository: AccountsRepositoryService,
|
||||
private readonly taskRepository: TasksRepositoryService,
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly userGroupsRepositoryService: UserGroupsRepositoryService,
|
||||
private readonly adB2cService: AdB2cService,
|
||||
private readonly sendgridService: SendGridService,
|
||||
private readonly notificationhubService: NotificationhubService,
|
||||
) {}
|
||||
|
||||
@ -74,10 +79,11 @@ export class TasksService {
|
||||
|
||||
try {
|
||||
const { account_id, author_id } =
|
||||
await this.usersRepository.findUserByExternalId(userId);
|
||||
await this.usersRepository.findUserByExternalId(context, userId);
|
||||
|
||||
if (roles.includes(ADMIN_ROLES.ADMIN)) {
|
||||
const result = await this.taskRepository.getTasksFromAccountId(
|
||||
context,
|
||||
account_id,
|
||||
offset,
|
||||
limit,
|
||||
@ -104,6 +110,7 @@ export class TasksService {
|
||||
}
|
||||
const result =
|
||||
await this.taskRepository.getTasksFromAuthorIdAndAccountId(
|
||||
context,
|
||||
author_id,
|
||||
account_id,
|
||||
offset,
|
||||
@ -126,6 +133,7 @@ export class TasksService {
|
||||
|
||||
if (roles.includes(USER_ROLES.TYPIST)) {
|
||||
const result = await this.taskRepository.getTasksFromTypistRelations(
|
||||
context,
|
||||
userId,
|
||||
offset,
|
||||
limit,
|
||||
@ -187,10 +195,11 @@ export class TasksService {
|
||||
|
||||
try {
|
||||
const { account_id: accountId, id } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// タスク一覧を取得する
|
||||
const tasks = await this.taskRepository.getSortedTasks(
|
||||
context,
|
||||
accountId,
|
||||
id,
|
||||
fileId,
|
||||
@ -268,7 +277,7 @@ export class TasksService {
|
||||
);
|
||||
|
||||
const { id, account_id, author_id } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
if (roles.includes(USER_ROLES.AUTHOR)) {
|
||||
// API実行者がAuthorで、AuthorIDが存在しないことは想定外のため、エラーとする
|
||||
@ -276,6 +285,7 @@ export class TasksService {
|
||||
throw new Error('AuthorID not found');
|
||||
}
|
||||
await this.taskRepository.getTaskFromAudioFileId(
|
||||
context,
|
||||
audioFileId,
|
||||
account_id,
|
||||
author_id,
|
||||
@ -284,11 +294,13 @@ export class TasksService {
|
||||
}
|
||||
|
||||
if (roles.includes(USER_ROLES.TYPIST)) {
|
||||
return await this.taskRepository.checkout(audioFileId, account_id, id, [
|
||||
TASK_STATUS.UPLOADED,
|
||||
TASK_STATUS.PENDING,
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
]);
|
||||
return await this.taskRepository.checkout(
|
||||
context,
|
||||
audioFileId,
|
||||
account_id,
|
||||
id,
|
||||
[TASK_STATUS.UPLOADED, TASK_STATUS.PENDING, TASK_STATUS.IN_PROGRESS],
|
||||
);
|
||||
}
|
||||
|
||||
throw new InvalidRoleError(`invalid roles: ${roles.join(',')}`);
|
||||
@ -350,15 +362,103 @@ export class TasksService {
|
||||
this.checkin.name
|
||||
} | params: { audioFileId: ${audioFileId}, externalId: ${externalId} };`,
|
||||
);
|
||||
const { id } = await this.usersRepository.findUserByExternalId(
|
||||
const user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
return await this.taskRepository.checkin(
|
||||
await this.taskRepository.checkin(
|
||||
context,
|
||||
audioFileId,
|
||||
id,
|
||||
user.id,
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
// タスク情報の取得
|
||||
const task = await this.taskRepository.getTaskAndAudioFile(
|
||||
context,
|
||||
audioFileId,
|
||||
user.account_id,
|
||||
[TASK_STATUS.FINISHED],
|
||||
);
|
||||
if (!task) {
|
||||
throw new Error(
|
||||
`task not found. audioFileId: ${audioFileId}. account_id: ${user.account_id}`,
|
||||
);
|
||||
}
|
||||
|
||||
// author情報の取得
|
||||
if (!task.file?.author_id) {
|
||||
throw new Error(
|
||||
`author_id not found. audioFileId: ${audioFileId}. account_id: ${user.account_id}`,
|
||||
);
|
||||
}
|
||||
const { external_id: authorExternalId } =
|
||||
await this.usersRepository.findUserByAuthorId(
|
||||
context,
|
||||
task.file.author_id,
|
||||
user.account_id,
|
||||
);
|
||||
|
||||
// プライマリ管理者を取得
|
||||
const { external_id: primaryAdminExternalId } =
|
||||
await this.getPrimaryAdminUser(context, user.account_id);
|
||||
|
||||
// ADB2C情報を取得する
|
||||
const usersInfo = await this.adB2cService.getUsers(context, [
|
||||
externalId,
|
||||
authorExternalId,
|
||||
primaryAdminExternalId,
|
||||
]);
|
||||
|
||||
// メール送信に必要な情報を取得
|
||||
const author = usersInfo.find((x) => x.id === authorExternalId);
|
||||
if (!author) {
|
||||
throw new Error(`author not found. id=${authorExternalId}`);
|
||||
}
|
||||
const { displayName: authorName, emailAddress: authorEmail } =
|
||||
getUserNameAndMailAddress(author);
|
||||
if (!authorEmail) {
|
||||
throw new Error(`author email not found. id=${authorExternalId}`);
|
||||
}
|
||||
|
||||
const typist = usersInfo.find((x) => x.id === externalId);
|
||||
if (!typist) {
|
||||
throw new Error(`typist not found. id=${externalId}`);
|
||||
}
|
||||
const { displayName: typistName, emailAddress: typistEmail } =
|
||||
getUserNameAndMailAddress(typist);
|
||||
if (!typistEmail) {
|
||||
throw new Error(`typist email not found. id=${externalId}`);
|
||||
}
|
||||
|
||||
const primaryAdmin = usersInfo.find(
|
||||
(x) => x.id === primaryAdminExternalId,
|
||||
);
|
||||
if (!primaryAdmin) {
|
||||
throw new Error(
|
||||
`primary admin not found. id=${primaryAdminExternalId}`,
|
||||
);
|
||||
}
|
||||
const { displayName: primaryAdminName } =
|
||||
getUserNameAndMailAddress(primaryAdmin);
|
||||
|
||||
// メール送信
|
||||
this.sendgridService.sendMailWithU117(
|
||||
context,
|
||||
authorEmail,
|
||||
typistEmail,
|
||||
authorName,
|
||||
task.file.file_name.replace('.zip', ''),
|
||||
typistName,
|
||||
primaryAdminName,
|
||||
);
|
||||
} catch (e) {
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -391,6 +491,26 @@ export class TasksService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async getPrimaryAdminUser(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<User> {
|
||||
const accountInfo = await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
if (!accountInfo || !accountInfo.primary_admin_user_id) {
|
||||
throw new Error(`account or primary admin not found. id=${accountId}`);
|
||||
}
|
||||
|
||||
const primaryAdmin = await this.usersRepository.findUserById(
|
||||
context,
|
||||
accountInfo.primary_admin_user_id,
|
||||
);
|
||||
|
||||
return primaryAdmin;
|
||||
}
|
||||
/**
|
||||
* 指定した音声ファイルに紐づくタスクをキャンセルする
|
||||
* @param audioFileId
|
||||
@ -412,7 +532,10 @@ export class TasksService {
|
||||
let user: User;
|
||||
try {
|
||||
// ユーザー取得
|
||||
user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
this.logger.log(`[OUT] [${context.getTrackingId()}] ${this.cancel.name}`);
|
||||
@ -425,6 +548,7 @@ export class TasksService {
|
||||
try {
|
||||
// roleにAdminが含まれていれば、文字起こし担当でなくてもキャンセルできるため、ユーザーIDは指定しない
|
||||
await this.taskRepository.cancel(
|
||||
context,
|
||||
audioFileId,
|
||||
[TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING],
|
||||
user.account_id,
|
||||
@ -462,6 +586,7 @@ export class TasksService {
|
||||
// キャンセルしたタスクに自動ルーティングを行う
|
||||
const { typistGroupIds, typistIds } =
|
||||
await this.taskRepository.autoRouting(
|
||||
context,
|
||||
audioFileId,
|
||||
user.account_id,
|
||||
user.author_id ?? undefined,
|
||||
@ -504,10 +629,12 @@ export class TasksService {
|
||||
} | params: { audioFileId: ${audioFileId}, externalId: ${externalId} };`,
|
||||
);
|
||||
const { id } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
return await this.taskRepository.suspend(
|
||||
context,
|
||||
audioFileId,
|
||||
id,
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
@ -564,9 +691,9 @@ export class TasksService {
|
||||
} | params: { audioFileId: ${audioFileId}, externalId: ${externalId} };`,
|
||||
);
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
await this.taskRepository.backup(accountId, audioFileId, [
|
||||
await this.taskRepository.backup(context, accountId, audioFileId, [
|
||||
TASK_STATUS.FINISHED,
|
||||
TASK_STATUS.BACKUP,
|
||||
]);
|
||||
@ -651,13 +778,14 @@ export class TasksService {
|
||||
} | params: { audioFileId: ${audioFileId}, assignees: ${assignees}, externalId: ${externalId}, role: ${role} };`,
|
||||
);
|
||||
const { author_id, account_id } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
// RoleがAuthorで、AuthorIDが存在しないことは想定外のため、エラーとする
|
||||
if (role.includes(USER_ROLES.AUTHOR) && !author_id) {
|
||||
throw new Error('AuthorID not found');
|
||||
}
|
||||
|
||||
await this.taskRepository.changeCheckoutPermission(
|
||||
context,
|
||||
audioFileId,
|
||||
author_id ?? undefined,
|
||||
account_id,
|
||||
@ -737,6 +865,7 @@ export class TasksService {
|
||||
);
|
||||
const groupMembers =
|
||||
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(
|
||||
context,
|
||||
typistGroupIds,
|
||||
);
|
||||
|
||||
@ -757,6 +886,7 @@ export class TasksService {
|
||||
|
||||
// 通知内容に含む音声ファイル情報を取得
|
||||
const { file } = await this.taskRepository.getTaskAndAudioFile(
|
||||
context,
|
||||
audioFileId,
|
||||
accountId,
|
||||
[TASK_STATUS.UPLOADED],
|
||||
|
||||
@ -15,6 +15,8 @@ import { Assignee } from '../types/types';
|
||||
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
|
||||
import { NotificationhubService } from '../../../gateways/notificationhub/notificationhub.service';
|
||||
import { UserGroupsRepositoryService } from '../../../repositories/user_groups/user_groups.repository.service';
|
||||
import { AccountsRepositoryService } from '../../../repositories/accounts/accounts.repository.service';
|
||||
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
|
||||
|
||||
export type TasksRepositoryMockValue = {
|
||||
getTasksFromAccountId:
|
||||
@ -84,6 +86,12 @@ export const makeTasksServiceMock = async (
|
||||
return makeNotificationhubServiceMock(
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
// メール送信でしか利用しておらず、テストする必要がないが、依存関係解決のため空オブジェクトを定義しておく。
|
||||
case AccountsRepositoryService:
|
||||
return {};
|
||||
// メール送信でしか利用しておらず、テストする必要がないが、依存関係解決のため空オブジェクトを定義しておく。
|
||||
case SendGridService:
|
||||
return {};
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
|
||||
@ -22,7 +22,6 @@ export class TasksRequest {
|
||||
})
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit: number;
|
||||
@ -35,7 +34,6 @@ export class TasksRequest {
|
||||
})
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Type(() => Number)
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset: number;
|
||||
@ -69,6 +67,7 @@ export class TasksRequest {
|
||||
paramName?: string;
|
||||
}
|
||||
|
||||
// TODO: RequestでもResponseでも使われているので、Requestに使用される箇所のみバリデータでチェックが行われる状態になっている
|
||||
export class Assignee {
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
@ -192,6 +191,7 @@ export class AudioNextRequest {
|
||||
@ApiProperty({ description: '文字起こし完了したタスクの音声ファイルID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
endedFileId: number;
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
@ -21,13 +22,14 @@ import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES } from '../../constants';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { Request } from 'express';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { TemplatesService } from './templates.service';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
@ApiTags('templates')
|
||||
@Controller('templates')
|
||||
export class TemplatesController {
|
||||
private readonly logger = new Logger(TemplatesController.name);
|
||||
constructor(private readonly templatesService: TemplatesService) {}
|
||||
|
||||
@ApiResponse({
|
||||
@ -63,6 +65,22 @@ export class TemplatesController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -72,7 +90,9 @@ export class TemplatesController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const templates = await this.templatesService.getTemplates(context, userId);
|
||||
|
||||
return { templates };
|
||||
|
||||
@ -35,7 +35,7 @@ describe('getTemplates', () => {
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const template1 = await createTemplateFile(
|
||||
source,
|
||||
@ -76,7 +76,7 @@ describe('getTemplates', () => {
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const templates = await service.getTemplates(context, admin.external_id);
|
||||
|
||||
@ -94,7 +94,7 @@ describe('getTemplates', () => {
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const typistGroupService = module.get<TemplateFilesRepositoryService>(
|
||||
|
||||
@ -31,10 +31,10 @@ export class TemplatesService {
|
||||
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
const templateFileRecords =
|
||||
await this.templateFilesRepository.getTemplateFiles(accountId);
|
||||
await this.templateFilesRepository.getTemplateFiles(context, accountId);
|
||||
|
||||
// DBから取得したテンプレートファイルのレコードをレスポンス用に整形する
|
||||
const resTemplates = templateFileRecords.map((templateFile) => ({
|
||||
|
||||
@ -1,14 +1,24 @@
|
||||
import { Controller, HttpStatus, Get } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
HttpStatus,
|
||||
Get,
|
||||
Logger,
|
||||
HttpException,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { TermsService } from '../terms/terms.service';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { GetTermsInfoResponse } from './types/types';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
import { Request } from 'express';
|
||||
|
||||
@ApiTags('terms')
|
||||
@Controller('terms')
|
||||
export class TermsController {
|
||||
private readonly logger = new Logger(TermsController.name);
|
||||
constructor(
|
||||
private readonly termsService: TermsService, //private readonly cryptoService: CryptoService,
|
||||
) {}
|
||||
@ -25,8 +35,24 @@ export class TermsController {
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'getTermsInfo' })
|
||||
async getTermsInfo(): Promise<GetTermsInfoResponse> {
|
||||
const context = makeContext(uuidv4());
|
||||
async getTermsInfo(@Req() req: Request): Promise<GetTermsInfoResponse> {
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const termsInfo = await this.termsService.getTermsInfo(context);
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ describe('利用規約取得', () => {
|
||||
await createTermInfo(source, 'DPA', 'v1.0');
|
||||
await createTermInfo(source, 'DPA', 'v1.2');
|
||||
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
const result = await service.getTermsInfo(context);
|
||||
|
||||
expect(result[0].documentType).toBe('EULA');
|
||||
@ -55,7 +55,7 @@ describe('利用規約取得', () => {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const service = module.get<TermsService>(TermsService);
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
await expect(service.getTermsInfo(context)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -70,7 +70,7 @@ describe('利用規約取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<TermsService>(TermsService);
|
||||
await createTermInfo(source, 'DPA', 'v1.0');
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
await expect(service.getTermsInfo(context)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -85,7 +85,7 @@ describe('利用規約取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<TermsService>(TermsService);
|
||||
await createTermInfo(source, 'PrivacyNotice', 'v1.0');
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
await expect(service.getTermsInfo(context)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -100,7 +100,7 @@ describe('利用規約取得', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<TermsService>(TermsService);
|
||||
await createTermInfo(source, 'EULA', 'v1.0');
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
await expect(service.getTermsInfo(context)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
|
||||
@ -20,7 +20,7 @@ export class TermsService {
|
||||
);
|
||||
try {
|
||||
const { eulaVersion, privacyNoticeVersion, dpaVersion } =
|
||||
await this.termsRepository.getLatestTermsInfo();
|
||||
await this.termsRepository.getLatestTermsInfo(context);
|
||||
return [
|
||||
{
|
||||
documentType: TERM_TYPE.EULA,
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
} from '../../../common/types/sort';
|
||||
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
|
||||
import { ADB2C_SIGN_IN_TYPE } from '../../../constants';
|
||||
import { AccountsRepositoryService } from '../../../repositories/accounts/accounts.repository.service';
|
||||
|
||||
export type SortCriteriaRepositoryMockValue = {
|
||||
updateSortCriteria: SortCriteria | Error;
|
||||
@ -47,15 +48,11 @@ export type AdB2cMockValue = {
|
||||
};
|
||||
|
||||
export type SendGridMockValue = {
|
||||
createMailContentFromEmailConfirm: {
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
};
|
||||
createMailContentFromEmailConfirmForNormalUser:
|
||||
| { subject: string; text: string; html: string }
|
||||
| Error;
|
||||
sendMail: undefined | Error;
|
||||
sendMailWithU113: undefined | Error;
|
||||
};
|
||||
|
||||
export type ConfigMockValue = {
|
||||
@ -80,6 +77,8 @@ export const makeUsersServiceMock = async (
|
||||
})
|
||||
.useMocker((token) => {
|
||||
switch (token) {
|
||||
case AccountsRepositoryService:
|
||||
return {};
|
||||
case UsersRepositoryService:
|
||||
return makeUsersRepositoryMock(usersRepositoryMockValue);
|
||||
case LicensesRepositoryService:
|
||||
@ -131,16 +130,8 @@ export const makeSortCriteriaRepositoryMock = (
|
||||
};
|
||||
|
||||
export const makeSendGridServiceMock = (value: SendGridMockValue) => {
|
||||
const { createMailContentFromEmailConfirm, sendMail } = value;
|
||||
const { sendMail } = value;
|
||||
return {
|
||||
createMailContentFromEmailConfirm:
|
||||
createMailContentFromEmailConfirm instanceof Error
|
||||
? jest
|
||||
.fn<Promise<void>, []>()
|
||||
.mockRejectedValue(createMailContentFromEmailConfirm)
|
||||
: jest
|
||||
.fn<Promise<{ subject: string; text: string; html: string }>, []>()
|
||||
.mockResolvedValue(createMailContentFromEmailConfirm),
|
||||
sendMail:
|
||||
sendMail instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(sendMail)
|
||||
@ -287,12 +278,12 @@ export const makeConfigMock = (value: ConfigMockValue) => {
|
||||
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
|
||||
return {
|
||||
sendMail: undefined,
|
||||
createMailContentFromEmailConfirm: { subject: '', text: '', html: '' },
|
||||
createMailContentFromEmailConfirmForNormalUser: {
|
||||
subject: 'test',
|
||||
text: 'test',
|
||||
html: 'test',
|
||||
},
|
||||
sendMailWithU113: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsIn } from 'class-validator';
|
||||
import { IsBoolean, IsEmail, IsIn, IsInt, IsOptional, MaxLength } from 'class-validator';
|
||||
import {
|
||||
TASK_LIST_SORTABLE_ATTRIBUTES,
|
||||
USER_LICENSE_STATUS,
|
||||
@ -10,6 +10,7 @@ import {
|
||||
IsPasswordvalid,
|
||||
} from '../../../common/validators/encryptionPassword.validator';
|
||||
import { IsRoleAuthorDataValid } from '../../../common/validators/roleAuthor.validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class ConfirmRequest {
|
||||
@ApiProperty()
|
||||
@ -88,15 +89,22 @@ export class SignupRequest {
|
||||
authorId?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsEmail({ blacklisted_chars: '*' })
|
||||
email: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
autoRenew: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
licenseAlert: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
notification: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@ -208,6 +216,8 @@ export class GetSortCriteriaResponse {
|
||||
|
||||
export class PostUpdateUserRequest {
|
||||
@ApiProperty()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: 'none/author/typist' })
|
||||
@ -219,12 +229,18 @@ export class PostUpdateUserRequest {
|
||||
authorId?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
autoRenew: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
licenseAlart: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Boolean)
|
||||
@IsBoolean()
|
||||
notification: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@ -244,8 +260,12 @@ export class PostUpdateUserResponse {}
|
||||
|
||||
export class AllocateLicenseRequest {
|
||||
@ApiProperty({ description: 'ユーザーID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
userId: number;
|
||||
@ApiProperty({ description: '割り当てるライセンスのID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
newLicenseId: number;
|
||||
}
|
||||
|
||||
@ -253,6 +273,8 @@ export class AllocateLicenseResponse {}
|
||||
|
||||
export class DeallocateLicenseRequest {
|
||||
@ApiProperty({ description: 'ユーザーID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
userId: number;
|
||||
}
|
||||
|
||||
@ -262,10 +284,14 @@ export class UpdateAcceptedVersionRequest {
|
||||
@ApiProperty({ description: 'IDトークン' })
|
||||
idToken: string;
|
||||
@ApiProperty({ description: '更新バージョン(EULA)' })
|
||||
@MaxLength(255)
|
||||
acceptedEULAVersion: string;
|
||||
@ApiProperty({ description: '更新バージョン(PrivacyNotice)' })
|
||||
@MaxLength(255)
|
||||
acceptedPrivacyNoticeVersion: string;
|
||||
@ApiProperty({ description: '更新バージョン(DPA)', required: false })
|
||||
@MaxLength(255)
|
||||
@IsOptional()
|
||||
acceptedDPAVersion?: string;
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Ip,
|
||||
Logger,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
@ -52,13 +53,13 @@ import {
|
||||
} from '../../common/types/sort';
|
||||
import { ADMIN_ROLES, TIERS } from '../../constants';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { UserRoles } from '../../common/types/role';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@ApiTags('users')
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
private readonly logger = new Logger(UsersController.name);
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly authService: AuthService,
|
||||
@ -81,8 +82,27 @@ export class UsersController {
|
||||
})
|
||||
@ApiOperation({ operationId: 'confirmUser' })
|
||||
@Post('confirm')
|
||||
async confirmUser(@Body() body: ConfirmRequest): Promise<ConfirmResponse> {
|
||||
const context = makeContext(uuidv4());
|
||||
async confirmUser(
|
||||
@Body() body: ConfirmRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<ConfirmResponse> {
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.usersService.confirmUser(context, body.token);
|
||||
return {};
|
||||
@ -107,8 +127,25 @@ export class UsersController {
|
||||
@Post('confirm/initpassword')
|
||||
async confirmUserAndInitPassword(
|
||||
@Body() body: ConfirmRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<ConfirmResponse> {
|
||||
const context = makeContext(uuidv4());
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.usersService.confirmUserAndInitPassword(context, body.token);
|
||||
return {};
|
||||
}
|
||||
@ -144,6 +181,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -152,7 +206,8 @@ export class UsersController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const users = await this.usersService.getUsers(context, userId);
|
||||
return { users };
|
||||
@ -209,6 +264,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -218,7 +290,8 @@ export class UsersController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
//ユーザ作成処理
|
||||
await this.usersService.createUser(
|
||||
@ -268,6 +341,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -277,7 +367,8 @@ export class UsersController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
return await this.usersService.getRelations(context, userId);
|
||||
}
|
||||
@ -322,6 +413,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -330,7 +438,8 @@ export class UsersController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
//型チェック
|
||||
if (
|
||||
@ -386,6 +495,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -394,7 +520,8 @@ export class UsersController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const { direction, paramName } = await this.usersService.getSortCriteria(
|
||||
context,
|
||||
@ -456,6 +583,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -465,7 +609,8 @@ export class UsersController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.usersService.updateUser(
|
||||
context,
|
||||
@ -528,6 +673,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -537,7 +699,8 @@ export class UsersController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.usersService.allocateLicense(
|
||||
context,
|
||||
body.userId,
|
||||
@ -591,6 +754,23 @@ export class UsersController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -600,7 +780,8 @@ export class UsersController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
await this.usersService.deallocateLicense(context, body.userId);
|
||||
return {};
|
||||
@ -628,6 +809,7 @@ export class UsersController {
|
||||
@Post('/accepted-version')
|
||||
async updateAcceptedVersion(
|
||||
@Body() body: UpdateAcceptedVersionRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<UpdateAcceptedVersionResponse> {
|
||||
const {
|
||||
idToken,
|
||||
@ -636,7 +818,23 @@ export class UsersController {
|
||||
acceptedDPAVersion,
|
||||
} = body;
|
||||
|
||||
const context = makeContext(uuidv4());
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const context = makeContext('anonymous', requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const verifiedIdToken = await this.authService.getVerifiedIdToken(
|
||||
context,
|
||||
@ -685,13 +883,30 @@ export class UsersController {
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('me')
|
||||
async getMyUser(@Req() req: Request): Promise<GetMyUserResponse> {
|
||||
const accessToken = retrieveAuthorizationToken(req) as string;
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
if (!accessToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -700,7 +915,8 @@ export class UsersController {
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
const userName = await this.usersService.getUserName(context, userId);
|
||||
return { userName };
|
||||
}
|
||||
|
||||
@ -8,9 +8,11 @@ import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.r
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AccountsRepositoryModule,
|
||||
UsersRepositoryModule,
|
||||
LicensesRepositoryModule,
|
||||
SortCriteriaRepositoryModule,
|
||||
|
||||
@ -94,10 +94,12 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
|
||||
// account id:1, user id: 2のトークン
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await service.confirmUser(context, token);
|
||||
//result
|
||||
const resultUser = await getUser(source, userId);
|
||||
@ -141,7 +143,8 @@ describe('UsersService.confirmUser', () => {
|
||||
if (!module) fail();
|
||||
const token = 'invalid.id.token';
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.confirmUser(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -175,9 +178,10 @@ describe('UsersService.confirmUser', () => {
|
||||
email_verified: false,
|
||||
});
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.confirmUser(context, token)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -187,9 +191,10 @@ describe('UsersService.confirmUser', () => {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {});
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
await expect(service.confirmUser(context, token)).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -246,7 +251,7 @@ describe('UsersService.confirmUserAndInitPassword', () => {
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
expect(
|
||||
await service.confirmUserAndInitPassword(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@ -295,7 +300,10 @@ describe('UsersService.confirmUserAndInitPassword', () => {
|
||||
);
|
||||
const token = 'invalid.id.token';
|
||||
await expect(
|
||||
service.confirmUserAndInitPassword(makeContext('trackingId'), token),
|
||||
service.confirmUserAndInitPassword(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -348,7 +356,10 @@ describe('UsersService.confirmUserAndInitPassword', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
await expect(
|
||||
service.confirmUserAndInitPassword(makeContext('trackingId'), token),
|
||||
service.confirmUserAndInitPassword(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
@ -398,7 +409,10 @@ describe('UsersService.confirmUserAndInitPassword', () => {
|
||||
const token =
|
||||
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
|
||||
await expect(
|
||||
service.confirmUserAndInitPassword(makeContext('trackingId'), token),
|
||||
service.confirmUserAndInitPassword(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
token,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -434,19 +448,12 @@ describe('UsersService.createUser', () => {
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -470,6 +477,12 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
return { sub: externalId };
|
||||
},
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -482,7 +495,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
expect(
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -518,19 +531,12 @@ describe('UsersService.createUser', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user2';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
@ -558,6 +564,12 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
return { sub: externalId };
|
||||
},
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -570,7 +582,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
expect(
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -610,19 +622,12 @@ describe('UsersService.createUser', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user2';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
@ -649,6 +654,12 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
return { sub: externalId };
|
||||
},
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -661,7 +672,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
expect(
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -701,19 +712,12 @@ describe('UsersService.createUser', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user3';
|
||||
const role = USER_ROLES.TYPIST;
|
||||
@ -737,6 +741,12 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
return { sub: externalId };
|
||||
},
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -749,7 +759,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
expect(
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -786,19 +796,7 @@ describe('UsersService.createUser', () => {
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const b2cService = module.get<AdB2cService>(AdB2cService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
await makeTestAccount(source, {}, { external_id: adminExternalId });
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -823,6 +821,12 @@ describe('UsersService.createUser', () => {
|
||||
return { sub: externalId };
|
||||
},
|
||||
deleteUser: jest.fn(),
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -842,7 +846,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -862,7 +866,7 @@ describe('UsersService.createUser', () => {
|
||||
// ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
|
||||
expect(b2cService.deleteUser).toBeCalledWith(
|
||||
externalId,
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
);
|
||||
});
|
||||
|
||||
@ -873,19 +877,12 @@ describe('UsersService.createUser', () => {
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const b2cService = module.get<AdB2cService>(AdB2cService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -910,6 +907,12 @@ describe('UsersService.createUser', () => {
|
||||
return { sub: externalId };
|
||||
},
|
||||
deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C error')),
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -929,7 +932,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -954,7 +957,7 @@ describe('UsersService.createUser', () => {
|
||||
// ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
|
||||
expect(b2cService.deleteUser).toBeCalledWith(
|
||||
externalId,
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
);
|
||||
});
|
||||
|
||||
@ -964,19 +967,7 @@ describe('UsersService.createUser', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
await makeTestAccount(source, {}, { external_id: adminExternalId });
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -1010,7 +1001,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1039,19 +1030,7 @@ describe('UsersService.createUser', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
await makeTestAccount(source, {}, { external_id: adminExternalId });
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -1089,7 +1068,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1118,19 +1097,7 @@ describe('UsersService.createUser', () => {
|
||||
if (!module) fail();
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
await makeTestAccount(source, {}, { external_id: adminExternalId });
|
||||
|
||||
const name = 'test_user2';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
@ -1158,6 +1125,12 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
return { sub: externalId_1 };
|
||||
},
|
||||
getUser: async () => {
|
||||
return {
|
||||
id: adminExternalId,
|
||||
displayName: 'admin',
|
||||
};
|
||||
},
|
||||
});
|
||||
overrideSendgridService(service, {
|
||||
sendMail: async () => {
|
||||
@ -1170,7 +1143,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
expect(
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1211,7 +1184,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1247,19 +1220,12 @@ describe('UsersService.createUser', () => {
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const b2cService = module.get<AdB2cService>(AdB2cService);
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user2';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
@ -1307,7 +1273,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1335,7 +1301,7 @@ describe('UsersService.createUser', () => {
|
||||
// ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
|
||||
expect(b2cService.deleteUser).toBeCalledWith(
|
||||
externalId,
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1347,19 +1313,12 @@ describe('UsersService.createUser', () => {
|
||||
const b2cService = module.get<AdB2cService>(AdB2cService);
|
||||
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
const { account } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { id: accountId, tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
const { id: accountId } = account;
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -1396,7 +1355,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1422,7 +1381,7 @@ describe('UsersService.createUser', () => {
|
||||
// ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
|
||||
expect(b2cService.deleteUser).toBeCalledWith(
|
||||
externalId,
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1434,19 +1393,7 @@ describe('UsersService.createUser', () => {
|
||||
const b2cService = module.get<AdB2cService>(AdB2cService);
|
||||
|
||||
const adminExternalId = 'ADMIN0001';
|
||||
const { account, admin } = await makeTestAccount(
|
||||
source,
|
||||
{},
|
||||
{ external_id: adminExternalId },
|
||||
);
|
||||
const { tier } = account;
|
||||
const { role: adminRole } = admin;
|
||||
|
||||
const token: AccessToken = {
|
||||
userId: adminExternalId,
|
||||
role: adminRole,
|
||||
tier: tier,
|
||||
};
|
||||
await makeTestAccount(source, {}, { external_id: adminExternalId });
|
||||
|
||||
const name = 'test_user1';
|
||||
const role = USER_ROLES.NONE;
|
||||
@ -1488,7 +1435,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
try {
|
||||
await service.createUser(
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
adminExternalId,
|
||||
name,
|
||||
role,
|
||||
@ -1512,7 +1459,7 @@ describe('UsersService.createUser', () => {
|
||||
// ADB2Cに作成したユーザーを削除するメソッドが呼ばれていることを確認
|
||||
expect(b2cService.deleteUser).toBeCalledWith(
|
||||
externalId,
|
||||
makeContext('trackingId'),
|
||||
makeContext('trackingId', 'requestId'),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1635,7 +1582,7 @@ describe('UsersService.getUsers', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
expect(await service.getUsers(context, externalId_author)).toEqual(
|
||||
expectedUsers,
|
||||
);
|
||||
@ -1754,7 +1701,7 @@ describe('UsersService.getUsers', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
expect(await service.getUsers(context, external_id1)).toEqual(
|
||||
expectedUsers,
|
||||
);
|
||||
@ -1778,7 +1725,7 @@ describe('UsersService.getUsers', () => {
|
||||
prompt: false,
|
||||
});
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await expect(
|
||||
service.getUsers(context, 'externalId_failed'),
|
||||
@ -1806,7 +1753,7 @@ describe('UsersService.getUsers', () => {
|
||||
prompt: false,
|
||||
});
|
||||
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await expect(service.getUsers(context, externalId_author)).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND),
|
||||
@ -1831,7 +1778,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateSortCriteria(
|
||||
@ -1862,7 +1809,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.updateSortCriteria(context, 'AUTHOR_ID', 'ASC', 'external_id'),
|
||||
@ -1894,7 +1841,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.updateSortCriteria(context, 'AUTHOR_ID', 'ASC', 'external_id'),
|
||||
@ -1924,7 +1871,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(await service.getSortCriteria(context, 'external_id')).toEqual({
|
||||
direction: 'ASC',
|
||||
@ -1953,7 +1900,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.getSortCriteria(context, 'external_id'),
|
||||
@ -1988,7 +1935,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const context = makeContext(`uuidv4`);
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.getSortCriteria(context, 'external_id'),
|
||||
@ -2048,7 +1995,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2107,7 +2055,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2166,7 +2115,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2225,7 +2175,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2284,7 +2235,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2343,7 +2295,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.updateUser(
|
||||
@ -2392,7 +2345,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2451,7 +2405,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
expect(
|
||||
await service.updateUser(
|
||||
@ -2510,7 +2465,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.updateUser(
|
||||
@ -2570,7 +2526,8 @@ describe('UsersService.updateUser', () => {
|
||||
});
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const context = makeContext(`uuidv4`);
|
||||
overrideSendgridService(service, {});
|
||||
const context = makeContext(`uuidv4`, 'requestId');
|
||||
|
||||
await expect(
|
||||
service.updateUser(
|
||||
@ -2618,7 +2575,7 @@ describe('UsersService.updateAcceptedVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 5,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.updateAcceptedVersion(
|
||||
@ -2639,7 +2596,7 @@ describe('UsersService.updateAcceptedVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 4,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.updateAcceptedVersion(
|
||||
@ -2662,7 +2619,7 @@ describe('UsersService.updateAcceptedVersion', () => {
|
||||
const { admin } = await makeTestAccount(source, {
|
||||
tier: 4,
|
||||
});
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await expect(
|
||||
@ -2705,7 +2662,7 @@ describe('UsersService.getUserName', () => {
|
||||
try {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.getUserName(context, 'external_id');
|
||||
@ -2800,7 +2757,7 @@ describe('UsersService.getRelations', () => {
|
||||
expect(workflows[3].author_id).toBe(user2);
|
||||
}
|
||||
|
||||
const context = makeContext(external_id);
|
||||
const context = makeContext(external_id, 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const relations = await service.getRelations(context, external_id);
|
||||
@ -2863,7 +2820,7 @@ describe('UsersService.getRelations', () => {
|
||||
expect(workflows[0].author_id).toBe(user2);
|
||||
}
|
||||
|
||||
const context = makeContext(external_id);
|
||||
const context = makeContext(external_id, 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
const relations = await service.getRelations(context, external_id);
|
||||
@ -2889,7 +2846,7 @@ describe('UsersService.getRelations', () => {
|
||||
try {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const context = makeContext(uuidv4());
|
||||
const context = makeContext(uuidv4(), 'requestId');
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
await service.getRelations(context, 'external_id');
|
||||
|
||||
@ -33,7 +33,6 @@ import {
|
||||
UserNotFoundError,
|
||||
} from '../../repositories/users/errors/types';
|
||||
import {
|
||||
ADB2C_SIGN_IN_TYPE,
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
MANUAL_RECOVERY_REQUIRED,
|
||||
OPTION_ITEM_VALUE_TYPE_NUMBER,
|
||||
@ -50,6 +49,9 @@ import {
|
||||
LicenseUnavailableError,
|
||||
} from '../../repositories/licenses/errors/types';
|
||||
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
import { Account } from '../../repositories/accounts/entity/account.entity';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@ -57,6 +59,7 @@ export class UsersService {
|
||||
private readonly mailFrom: string;
|
||||
private readonly appDomain: string;
|
||||
constructor(
|
||||
private readonly accountsRepository: AccountsRepositoryService,
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly licensesRepository: LicensesRepositoryService,
|
||||
private readonly sortCriteriaRepository: SortCriteriaRepositoryService,
|
||||
@ -94,8 +97,27 @@ export class UsersService {
|
||||
// トランザクションで取得と更新をまとめる
|
||||
const userId = decodedToken.userId;
|
||||
await this.usersRepository.updateUserVerifiedAndCreateTrialLicense(
|
||||
context,
|
||||
userId,
|
||||
);
|
||||
|
||||
try {
|
||||
const { company_name: companyName } =
|
||||
await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
decodedToken.accountId,
|
||||
);
|
||||
|
||||
// アカウント認証が完了した旨をメール送信する
|
||||
await this.sendgridService.sendMailWithU101(
|
||||
context,
|
||||
decodedToken.email,
|
||||
companyName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -166,8 +188,12 @@ export class UsersService {
|
||||
);
|
||||
//DBよりアクセス者の所属するアカウントIDを取得する
|
||||
let adminUser: EntityUser;
|
||||
let account: Account | null;
|
||||
try {
|
||||
adminUser = await this.usersRepository.findUserByExternalId(externalId);
|
||||
adminUser = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw new HttpException(
|
||||
@ -177,12 +203,14 @@ export class UsersService {
|
||||
}
|
||||
|
||||
const accountId = adminUser.account_id;
|
||||
account = adminUser.account;
|
||||
|
||||
//authorIdが重複していないかチェックする
|
||||
if (authorId) {
|
||||
let isAuthorIdDuplicated = false;
|
||||
try {
|
||||
isAuthorIdDuplicated = await this.usersRepository.existsAuthorId(
|
||||
context,
|
||||
accountId,
|
||||
authorId,
|
||||
);
|
||||
@ -252,7 +280,10 @@ export class UsersService {
|
||||
prompt,
|
||||
);
|
||||
// ユーザ作成
|
||||
newUser = await this.usersRepository.createNormalUser(newUserInfo);
|
||||
newUser = await this.usersRepository.createNormalUser(
|
||||
context,
|
||||
newUserInfo,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
this.logger.error(`[${context.getTrackingId()}]create user failed`);
|
||||
@ -277,23 +308,32 @@ export class UsersService {
|
||||
|
||||
//Email送信用のコンテンツを作成する
|
||||
try {
|
||||
// メールの内容を構成
|
||||
const { subject, text, html } =
|
||||
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser(
|
||||
context,
|
||||
accountId,
|
||||
newUser.id,
|
||||
email,
|
||||
if (account === null) {
|
||||
throw new Error(`account is null. account_id=${accountId}`);
|
||||
}
|
||||
const { primary_admin_user_id: primaryAdminUserId } = account;
|
||||
if (primaryAdminUserId === null) {
|
||||
throw new Error(
|
||||
`primary_admin_user_id is null. account_id=${accountId}`,
|
||||
);
|
||||
}
|
||||
const { external_id: extarnalId } =
|
||||
await this.usersRepository.findUserById(context, primaryAdminUserId);
|
||||
|
||||
//SendGridAPIを呼び出してメールを送信する
|
||||
await this.sendgridService.sendMail(
|
||||
const primaryAdmimAdb2cUser = await this.adB2cService.getUser(
|
||||
context,
|
||||
extarnalId,
|
||||
);
|
||||
const { displayName: primaryAdminUserName } = getUserNameAndMailAddress(
|
||||
primaryAdmimAdb2cUser,
|
||||
);
|
||||
|
||||
await this.sendgridService.sendMailWithU114(
|
||||
context,
|
||||
accountId,
|
||||
newUser.id,
|
||||
email,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
primaryAdminUserName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -347,7 +387,7 @@ export class UsersService {
|
||||
} | params: { userId: ${userId} }`,
|
||||
);
|
||||
try {
|
||||
await this.usersRepository.deleteNormalUser(userId);
|
||||
await this.usersRepository.deleteNormalUser(context, userId);
|
||||
this.logger.log(`[${context.getTrackingId()}] delete user: ${userId}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${error}`);
|
||||
@ -475,11 +515,11 @@ export class UsersService {
|
||||
|
||||
// ランダムなパスワードを生成する
|
||||
const ramdomPassword = makePassword();
|
||||
const { userId, email } = decodedToken;
|
||||
const { accountId, userId, email } = decodedToken;
|
||||
|
||||
try {
|
||||
// ユーザー情報からAzure AD B2CのIDを特定する
|
||||
const user = await this.usersRepository.findUserById(userId);
|
||||
const user = await this.usersRepository.findUserById(context, userId);
|
||||
const extarnalId = user.external_id;
|
||||
// パスワードを変更する
|
||||
await this.adB2cService.changePassword(
|
||||
@ -488,21 +528,31 @@ export class UsersService {
|
||||
ramdomPassword,
|
||||
);
|
||||
// ユーザを認証済みにする
|
||||
await this.usersRepository.updateUserVerified(userId);
|
||||
// TODO [Task2163] ODMS側が正式にメッセージを決めるまで仮のメール内容とする
|
||||
const subject = 'A temporary password has been issued.';
|
||||
const text = 'temporary password: ' + ramdomPassword;
|
||||
const html = `<p>OMDS TOP PAGE URL.<p><a href="${this.appDomain}">${this.appDomain}</a><br>temporary password: ${ramdomPassword}`;
|
||||
await this.usersRepository.updateUserVerified(context, userId);
|
||||
|
||||
// メールを送信
|
||||
await this.sendgridService.sendMail(
|
||||
context,
|
||||
email,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
// メール送信処理
|
||||
try {
|
||||
const { external_id: primaryAdminUserExternalId } =
|
||||
await this.getPrimaryAdminUser(context, accountId);
|
||||
|
||||
const adb2cUser = await this.adB2cService.getUser(
|
||||
context,
|
||||
primaryAdminUserExternalId,
|
||||
);
|
||||
|
||||
const { displayName: primaryAdminName } =
|
||||
getUserNameAndMailAddress(adb2cUser);
|
||||
|
||||
await this.sendgridService.sendMailWithU113(
|
||||
context,
|
||||
email,
|
||||
primaryAdminName,
|
||||
ramdomPassword,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -545,12 +595,7 @@ export class UsersService {
|
||||
|
||||
// DBから取得したユーザーの外部IDをもとにADB2Cからユーザーを取得する
|
||||
const externalIds = dbUsers.map((x) => x.external_id);
|
||||
const trackingId = new Context(context.trackingId);
|
||||
const adb2cUsers = await this.adB2cService.getUsers(
|
||||
// TODO: 外部連携以外のログ強化時に、ContollerからContextを取得するように修正する
|
||||
trackingId,
|
||||
externalIds,
|
||||
);
|
||||
const adb2cUsers = await this.adB2cService.getUsers(context, externalIds);
|
||||
|
||||
// DBから取得した各ユーザーをもとにADB2C情報をマージしライセンス情報を算出
|
||||
const users = dbUsers.map((dbUser): User => {
|
||||
@ -567,10 +612,12 @@ export class UsersService {
|
||||
(user) => user.id === dbUser.external_id,
|
||||
);
|
||||
|
||||
if (adb2cUser == null) {
|
||||
throw new Error('mail not found.'); // TODO: リファクタ時に挙動を変更しないようエラー文面をmail not foundのまま据え置き。影響がない事が確認できたらエラー文面を変更する。
|
||||
}
|
||||
|
||||
// メールアドレスを取得する
|
||||
const mail = adb2cUser?.identities?.find(
|
||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
)?.issuerAssignedId;
|
||||
const { emailAddress: mail } = getUserNameAndMailAddress(adb2cUser);
|
||||
|
||||
//メールアドレスが取得できない場合はエラー
|
||||
if (!mail) {
|
||||
@ -668,7 +715,10 @@ export class UsersService {
|
||||
let user: EntityUser;
|
||||
try {
|
||||
// ユーザー情報を取得
|
||||
user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
|
||||
@ -681,6 +731,7 @@ export class UsersService {
|
||||
try {
|
||||
// ユーザーのソート条件を更新
|
||||
await this.sortCriteriaRepository.updateSortCriteria(
|
||||
context,
|
||||
user.id,
|
||||
paramName,
|
||||
direction,
|
||||
@ -717,7 +768,10 @@ export class UsersService {
|
||||
let user: EntityUser;
|
||||
try {
|
||||
// ユーザー情報を取得
|
||||
user = await this.usersRepository.findUserByExternalId(externalId);
|
||||
user = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
|
||||
@ -730,6 +784,7 @@ export class UsersService {
|
||||
try {
|
||||
// ユーザーのソート条件を取得
|
||||
const sortCriteria = await this.sortCriteriaRepository.getSortCriteria(
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
const { direction, parameter } = sortCriteria;
|
||||
@ -769,11 +824,14 @@ export class UsersService {
|
||||
} | params: { userId: ${userId} };`,
|
||||
);
|
||||
try {
|
||||
const { id } = await this.usersRepository.findUserByExternalId(userId);
|
||||
const { id } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
userId,
|
||||
);
|
||||
|
||||
// ユーザー関連情報を取得
|
||||
const { user, authors, worktypes, activeWorktype } =
|
||||
await this.usersRepository.getUserRelations(id);
|
||||
await this.usersRepository.getUserRelations(context, id);
|
||||
|
||||
// AuthorIDのリストを作成
|
||||
const authorIds = authors.flatMap((author) =>
|
||||
@ -888,11 +946,12 @@ export class UsersService {
|
||||
|
||||
// 実行ユーザーのアカウントIDを取得
|
||||
const accountId = (
|
||||
await this.usersRepository.findUserByExternalId(extarnalId)
|
||||
await this.usersRepository.findUserByExternalId(context, extarnalId)
|
||||
).account_id;
|
||||
|
||||
// ユーザー情報を更新
|
||||
await this.usersRepository.update(
|
||||
context,
|
||||
accountId,
|
||||
id,
|
||||
role,
|
||||
@ -904,6 +963,51 @@ export class UsersService {
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
const { adminEmails } = await this.getAccountInformation(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// 変更ユーザー情報を取得
|
||||
const { external_id: userExtarnalId } =
|
||||
await this.usersRepository.findUserById(context, id);
|
||||
const adb2cUser = await this.adB2cService.getUser(
|
||||
context,
|
||||
userExtarnalId,
|
||||
);
|
||||
const { displayName: userName, emailAddress: userEmail } =
|
||||
getUserNameAndMailAddress(adb2cUser);
|
||||
|
||||
if (userEmail === undefined) {
|
||||
throw new Error(`userEmail is null. externalId=${extarnalId}`);
|
||||
}
|
||||
|
||||
// プライマリ管理者を取得
|
||||
const { external_id: adminExternalId } = await this.getPrimaryAdminUser(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
const adb2cAdminUser = await this.adB2cService.getUser(
|
||||
context,
|
||||
adminExternalId,
|
||||
);
|
||||
const { displayName: primaryAdminName } =
|
||||
getUserNameAndMailAddress(adb2cAdminUser);
|
||||
|
||||
await this.sendgridService.sendMailWithU115(
|
||||
context,
|
||||
userName,
|
||||
userEmail,
|
||||
primaryAdminName,
|
||||
adminEmails,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -966,14 +1070,53 @@ export class UsersService {
|
||||
);
|
||||
|
||||
try {
|
||||
const accountId = (await this.usersRepository.findUserById(userId))
|
||||
.account_id;
|
||||
const { external_id: externalId, account_id: accountId } =
|
||||
await this.usersRepository.findUserById(context, userId);
|
||||
|
||||
await this.licensesRepository.allocateLicense(
|
||||
context,
|
||||
userId,
|
||||
newLicenseId,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
const { parent_account_id: dealerId } =
|
||||
await this.accountsRepository.findAccountById(context, accountId);
|
||||
|
||||
if (dealerId == null) {
|
||||
throw new Error(`dealer is null. account_id=${accountId}`);
|
||||
}
|
||||
|
||||
const { company_name: dealerName } =
|
||||
await this.accountsRepository.findAccountById(context, dealerId);
|
||||
|
||||
const { companyName, adminEmails } = await this.getAccountInformation(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
const adb2cUser = await this.adB2cService.getUser(context, externalId);
|
||||
const { displayName, emailAddress } =
|
||||
getUserNameAndMailAddress(adb2cUser);
|
||||
|
||||
if (emailAddress == null) {
|
||||
throw new Error(`emailAddress is null. externalId=${externalId}`);
|
||||
}
|
||||
|
||||
await this.sendgridService.sendMailWithU108(
|
||||
context,
|
||||
displayName,
|
||||
emailAddress,
|
||||
adminEmails,
|
||||
companyName,
|
||||
dealerName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -1015,10 +1158,15 @@ export class UsersService {
|
||||
);
|
||||
|
||||
try {
|
||||
const accountId = (await this.usersRepository.findUserById(userId))
|
||||
.account_id;
|
||||
const accountId = (
|
||||
await this.usersRepository.findUserById(context, userId)
|
||||
).account_id;
|
||||
|
||||
await this.licensesRepository.deallocateLicense(userId, accountId);
|
||||
await this.licensesRepository.deallocateLicense(
|
||||
context,
|
||||
userId,
|
||||
accountId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -1069,6 +1217,7 @@ export class UsersService {
|
||||
|
||||
try {
|
||||
await this.usersRepository.updateAcceptedTermsVersion(
|
||||
context,
|
||||
externalId,
|
||||
eulaVersion,
|
||||
privacyNoticeVersion,
|
||||
@ -1120,7 +1269,7 @@ export class UsersService {
|
||||
|
||||
try {
|
||||
// extarnalIdの存在チェックを行う
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
// ADB2Cからユーザー名を取得する
|
||||
const adb2cUser = await this.adB2cService.getUser(context, externalId);
|
||||
return adb2cUser.displayName;
|
||||
@ -1150,4 +1299,76 @@ export class UsersService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウントIDを指定して、アカウント情報と管理者情報を取得する
|
||||
* @param context
|
||||
* @param accountId 対象アカウントID
|
||||
* @returns 企業名/管理者メールアドレス
|
||||
*/
|
||||
private async getAccountInformation(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<{
|
||||
companyName: string;
|
||||
adminEmails: string[];
|
||||
}> {
|
||||
// アカウントIDから企業名を取得する
|
||||
const { company_name } = await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// 管理者一覧を取得
|
||||
const admins = await this.usersRepository.findAdminUsers(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
const adminExternalIDs = admins.map((x) => x.external_id);
|
||||
|
||||
// ADB2Cから管理者IDを元にメールアドレスを取得する
|
||||
const usersInfo = await this.adB2cService.getUsers(
|
||||
context,
|
||||
adminExternalIDs,
|
||||
);
|
||||
|
||||
// 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する
|
||||
const adminEmails = usersInfo.map((x) => {
|
||||
const { emailAddress } = getUserNameAndMailAddress(x);
|
||||
if (emailAddress == null) {
|
||||
throw new Error('dealer admin email-address is not found');
|
||||
}
|
||||
return emailAddress;
|
||||
});
|
||||
|
||||
return {
|
||||
companyName: company_name,
|
||||
adminEmails: adminEmails,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* アカウントのプライマリ管理者を取得する
|
||||
* @param context
|
||||
* @param accountId
|
||||
* @returns primary admin user
|
||||
*/
|
||||
private async getPrimaryAdminUser(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<EntityUser> {
|
||||
const accountInfo = await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
if (!accountInfo || !accountInfo.primary_admin_user_id) {
|
||||
throw new Error(`account or primary admin not found. id=${accountId}`);
|
||||
}
|
||||
|
||||
const primaryAdmin = await this.usersRepository.findUserById(
|
||||
context,
|
||||
accountInfo.primary_admin_user_id,
|
||||
);
|
||||
|
||||
return primaryAdmin;
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,8 +44,14 @@ export class GetWorkflowsResponse {
|
||||
|
||||
export class WorkflowTypist {
|
||||
@ApiProperty({ description: 'タイピストユーザーの内部ID', required: false })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
typistId?: number;
|
||||
@ApiProperty({ description: 'タイピストグループの内部ID', required: false })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
typistGroupId?: number;
|
||||
}
|
||||
|
||||
@ -53,19 +59,19 @@ export class CreateWorkflowsRequest {
|
||||
@ApiProperty({ description: 'Authorの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
authorId: number;
|
||||
@ApiProperty({ description: 'Worktypeの内部ID', required: false })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
worktypeId?: number;
|
||||
@ApiProperty({ description: 'テンプレートの内部ID', required: false })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
templateId?: number;
|
||||
@ApiProperty({
|
||||
description: 'ルーティング候補のタイピストユーザー/タイピストグループ',
|
||||
@ -84,7 +90,7 @@ export class UpdateWorkflowRequestParam {
|
||||
@ApiProperty({ description: 'ワークフローの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
workflowId: number;
|
||||
}
|
||||
|
||||
@ -92,19 +98,19 @@ export class UpdateWorkflowRequest {
|
||||
@ApiProperty({ description: 'Authorの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
authorId: number;
|
||||
@ApiProperty({ description: 'Worktypeの内部ID', required: false })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
worktypeId?: number;
|
||||
@ApiProperty({ description: 'テンプレートの内部ID', required: false })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
templateId?: number;
|
||||
@ApiProperty({
|
||||
description: 'ルーティング候補のタイピストユーザー/タイピストグループ',
|
||||
@ -123,7 +129,7 @@ export class DeleteWorkflowRequestParam {
|
||||
@ApiProperty({ description: 'ワークフローの内部ID' })
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
workflowId: number;
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Param,
|
||||
Post,
|
||||
Req,
|
||||
@ -33,13 +34,14 @@ import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES } from '../../constants';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
import { Request } from 'express';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { makeContext, retrieveRequestId, retrieveIp } from '../../common/log';
|
||||
import { WorkflowsService } from './workflows.service';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
|
||||
@ApiTags('workflows')
|
||||
@Controller('workflows')
|
||||
export class WorkflowsController {
|
||||
private readonly logger = new Logger(WorkflowsController.name);
|
||||
constructor(private readonly workflowsService: WorkflowsService) {}
|
||||
|
||||
@ApiResponse({
|
||||
@ -75,6 +77,21 @@ export class WorkflowsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -84,7 +101,8 @@ export class WorkflowsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
const workflows = await this.workflowsService.getWorkflows(context, userId);
|
||||
|
||||
@ -134,6 +152,21 @@ export class WorkflowsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -143,7 +176,8 @@ export class WorkflowsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.workflowsService.createWorkflow(
|
||||
context,
|
||||
userId,
|
||||
@ -201,6 +235,21 @@ export class WorkflowsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -210,7 +259,8 @@ export class WorkflowsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.workflowsService.updateWorkflow(
|
||||
context,
|
||||
userId,
|
||||
@ -267,6 +317,21 @@ export class WorkflowsController {
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
@ -276,7 +341,8 @@ export class WorkflowsController {
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
await this.workflowsService.deleteWorkflow(context, userId, workflowId);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ describe('getWorkflows', () => {
|
||||
await createWorkflowTypist(source, workflow3.id, undefined, userGroupId);
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
@ -190,7 +190,7 @@ describe('getWorkflows', () => {
|
||||
const { admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
overrideAdB2cService(service, {
|
||||
getUsers: async () => [],
|
||||
@ -212,7 +212,7 @@ describe('getWorkflows', () => {
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const templatesService = module.get<WorkflowsRepositoryService>(
|
||||
@ -292,7 +292,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.createWorkflow(
|
||||
context,
|
||||
@ -357,7 +357,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.createWorkflow(
|
||||
context,
|
||||
@ -421,7 +421,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.createWorkflow(
|
||||
context,
|
||||
@ -479,7 +479,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.createWorkflow(
|
||||
context,
|
||||
@ -543,7 +543,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
// 同一AuthorIDのワークフローを作成
|
||||
await service.createWorkflow(
|
||||
@ -616,7 +616,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.createWorkflow(
|
||||
@ -673,7 +673,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -734,7 +734,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -794,7 +794,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -856,7 +856,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -924,7 +924,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -986,7 +986,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -1057,7 +1057,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -1124,7 +1124,7 @@ describe('createWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const templatesService = module.get<WorkflowsRepositoryService>(
|
||||
@ -1243,7 +1243,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.updateWorkflow(
|
||||
context,
|
||||
@ -1333,7 +1333,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.updateWorkflow(
|
||||
context,
|
||||
@ -1422,7 +1422,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.updateWorkflow(
|
||||
context,
|
||||
@ -1505,7 +1505,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.updateWorkflow(
|
||||
context,
|
||||
@ -1608,7 +1608,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.updateWorkflow(
|
||||
context,
|
||||
@ -1687,7 +1687,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -1730,7 +1730,7 @@ describe('updateWorkflow', () => {
|
||||
});
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -1804,7 +1804,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -1873,7 +1873,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -1941,7 +1941,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2016,7 +2016,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2097,7 +2097,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2172,7 +2172,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2241,7 +2241,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2310,7 +2310,7 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const workflowsRepositoryService = module.get<WorkflowsRepositoryService>(
|
||||
@ -2401,7 +2401,7 @@ describe('deleteWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.deleteWorkflow(context, admin.external_id, workflow.id);
|
||||
|
||||
@ -2452,7 +2452,7 @@ describe('deleteWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
await service.deleteWorkflow(context, admin.external_id, workflow1.id);
|
||||
|
||||
@ -2503,7 +2503,7 @@ describe('deleteWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2578,7 +2578,7 @@ describe('deleteWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
@ -2633,7 +2633,7 @@ describe('deleteWorkflows', () => {
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const workflowsRepositoryService = module.get<WorkflowsRepositoryService>(
|
||||
|
||||
@ -41,10 +41,11 @@ export class WorkflowsService {
|
||||
);
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
// DBからワークフロー一覧を取得
|
||||
const workflowRecords = await this.workflowsRepository.getWorkflows(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
@ -165,9 +166,10 @@ export class WorkflowsService {
|
||||
);
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
await this.workflowsRepository.createtWorkflows(
|
||||
context,
|
||||
accountId,
|
||||
authorId,
|
||||
typists,
|
||||
@ -253,9 +255,10 @@ export class WorkflowsService {
|
||||
);
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
await this.workflowsRepository.updatetWorkflow(
|
||||
context,
|
||||
accountId,
|
||||
workflowId,
|
||||
authorId,
|
||||
@ -336,6 +339,7 @@ export class WorkflowsService {
|
||||
);
|
||||
try {
|
||||
const { account } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
@ -345,7 +349,11 @@ export class WorkflowsService {
|
||||
);
|
||||
}
|
||||
|
||||
await this.workflowsRepository.deleteWorkflow(account.id, workflowId);
|
||||
await this.workflowsRepository.deleteWorkflow(
|
||||
context,
|
||||
account.id,
|
||||
workflowId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { ADB2C_SIGN_IN_TYPE } from '../../../constants';
|
||||
import { AdB2cUser } from '../types/types';
|
||||
|
||||
export const isPromiseRejectedResult = (
|
||||
data: unknown,
|
||||
): data is PromiseRejectedResult => {
|
||||
@ -8,3 +11,12 @@ export const isPromiseRejectedResult = (
|
||||
'reason' in data
|
||||
);
|
||||
};
|
||||
|
||||
// 生のAdB2cUserのレスポンスから表示名とメールアドレスを取得する
|
||||
export const getUserNameAndMailAddress = (user: AdB2cUser) => {
|
||||
const { displayName, identities } = user;
|
||||
const emailAddress = identities?.find(
|
||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||
)?.issuerAssignedId;
|
||||
return { displayName, emailAddress };
|
||||
};
|
||||
|
||||
@ -4,64 +4,205 @@ import { sign } from '../../common/jwt';
|
||||
import sendgrid from '@sendgrid/mail';
|
||||
import { getPrivateKey } from '../../common/jwt/jwt';
|
||||
import { Context } from '../../common/log';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
PRIMARY_ADMIN_NAME,
|
||||
AUTHOR_NAME,
|
||||
CUSTOMER_NAME,
|
||||
DEALER_NAME,
|
||||
FILE_NAME,
|
||||
LICENSE_QUANTITY,
|
||||
PO_NUMBER,
|
||||
TOP_URL,
|
||||
USER_EMAIL,
|
||||
USER_NAME,
|
||||
TYPIST_NAME,
|
||||
VERIFY_LINK,
|
||||
TEMPORARY_PASSWORD,
|
||||
} from '../../templates/constants';
|
||||
|
||||
@Injectable()
|
||||
export class SendGridService {
|
||||
private readonly logger = new Logger(SendGridService.name);
|
||||
private readonly emailConfirmLifetime: number;
|
||||
private readonly appDomain: string;
|
||||
private readonly mailFrom: string;
|
||||
private readonly templateEmailVerifyHtml: string;
|
||||
private readonly templateEmailVerifyText: string;
|
||||
private readonly templateU101Html: string;
|
||||
private readonly templateU101Text: string;
|
||||
private readonly templateU102Html: string;
|
||||
private readonly templateU102Text: string;
|
||||
private readonly templateU105Html: string;
|
||||
private readonly templateU105Text: string;
|
||||
private readonly templateU106Html: string;
|
||||
private readonly templateU106Text: string;
|
||||
private readonly templateU107Html: string;
|
||||
private readonly templateU107Text: string;
|
||||
private readonly templateU108Html: string;
|
||||
private readonly templateU108Text: string;
|
||||
private readonly templateU109Html: string;
|
||||
private readonly templateU109Text: string;
|
||||
private readonly templateU111Html: string;
|
||||
private readonly templateU111Text: string;
|
||||
private readonly templateU112Html: string;
|
||||
private readonly templateU112Text: string;
|
||||
// U-112のテンプレート差分(親アカウントがない場合)
|
||||
private readonly templateU112NoParentHtml: string;
|
||||
private readonly templateU112NoParentText: string;
|
||||
private readonly templateU113Html: string;
|
||||
private readonly templateU113Text: string;
|
||||
private readonly templateU114Html: string;
|
||||
private readonly templateU114Text: string;
|
||||
private readonly templateU115Html: string;
|
||||
private readonly templateU115Text: string;
|
||||
private readonly templateU117Html: string;
|
||||
private readonly templateU117Text: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
|
||||
this.mailFrom = this.configService.getOrThrow<string>('MAIL_FROM');
|
||||
this.emailConfirmLifetime = this.configService.getOrThrow<number>(
|
||||
'EMAIL_CONFIRM_LIFETIME',
|
||||
);
|
||||
const key = this.configService.getOrThrow<string>('SENDGRID_API_KEY');
|
||||
sendgrid.setApiKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Email認証用のメールコンテンツを作成する
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param email 認証対象のユーザーのメールアドレス
|
||||
* @returns メールのサブジェクトとコンテンツ
|
||||
*/
|
||||
async createMailContentFromEmailConfirm(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
email: string,
|
||||
): Promise<{ subject: string; text: string; html: string }> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${
|
||||
this.createMailContentFromEmailConfirm.name
|
||||
} | params: { ` +
|
||||
`accountId: ${accountId},` +
|
||||
`userId: ${userId} };`,
|
||||
);
|
||||
// メールテンプレートを読み込む
|
||||
{
|
||||
this.templateEmailVerifyHtml = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_email_verify.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateEmailVerifyText = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_email_verify.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const privateKey = getPrivateKey(this.configService);
|
||||
const token = sign<{ accountId: number; userId: number; email: string }>(
|
||||
{
|
||||
accountId,
|
||||
userId,
|
||||
email,
|
||||
},
|
||||
this.emailConfirmLifetime,
|
||||
privateKey,
|
||||
);
|
||||
const path = 'mail-confirm/';
|
||||
this.templateU101Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_101.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU101Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_101.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU102Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_102.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU102Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_102.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${
|
||||
this.createMailContentFromEmailConfirm.name
|
||||
}`,
|
||||
);
|
||||
return {
|
||||
subject: 'Verify your new account',
|
||||
text: `The verification URL. ${this.appDomain}${path}?verify=${token}`,
|
||||
html: `<p>The verification URL.<p><a href="${this.appDomain}${path}?verify=${token}">${this.appDomain}${path}?verify=${token}</a>`,
|
||||
};
|
||||
this.templateU105Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_105.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU105Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_105.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU106Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_106.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU106Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_106.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU107Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_107.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU107Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_107.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU108Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_108.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU108Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_108.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU109Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_109.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU109Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_109.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU111Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_111.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU111Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_111.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU112Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_112.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU112Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_112.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU112NoParentHtml = readFileSync(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
`../../templates/template_U_112_no_parent.html`,
|
||||
),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU112NoParentText = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_112_no_parent.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU113Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_113.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU113Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_113.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU114Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_114.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU114Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_114.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU115Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_115.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU115Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_115.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU117Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_117.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU117Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_117.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,10 +246,704 @@ export class SendGridService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* U-101のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param customerMail アカウント登録を行った管理者ユーザーのメールアドレス
|
||||
* @param customerAccountName アカウント登録した会社名
|
||||
* @returns mail with u101
|
||||
*/
|
||||
async sendMailWithU101(
|
||||
context: Context,
|
||||
customerMail: string,
|
||||
customerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU101.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Account Registered Notification [U-101]';
|
||||
const html = this.templateU101Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
const text = this.templateU101Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
|
||||
await this.sendMail(
|
||||
context,
|
||||
[customerMail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU101.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-102のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param adminEmail 登録したアカウントの管理者(primary)のメールアドレス
|
||||
* @param accountId 登録したアカウントのID
|
||||
* @param userId 登録したユーザーのID
|
||||
* @returns mail with u102
|
||||
*/
|
||||
async sendMailWithU102(
|
||||
context: Context,
|
||||
adminEmail: string,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU102.name}`,
|
||||
);
|
||||
try {
|
||||
const privateKey = getPrivateKey(this.configService);
|
||||
const token = sign<{ accountId: number; userId: number; email: string }>(
|
||||
{
|
||||
accountId,
|
||||
userId,
|
||||
email: adminEmail,
|
||||
},
|
||||
this.emailConfirmLifetime,
|
||||
privateKey,
|
||||
);
|
||||
const path = 'mail-confirm/';
|
||||
const verifyUrl = `${this.appDomain}${path}?verify=${token}`;
|
||||
|
||||
const subject = 'User Registration Notification [U-102]';
|
||||
const html = this.templateU102Html.replaceAll(VERIFY_LINK, verifyUrl);
|
||||
const text = this.templateU102Text.replaceAll(VERIFY_LINK, verifyUrl);
|
||||
|
||||
await this.sendMail(
|
||||
context,
|
||||
[adminEmail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU102.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-105のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param customerMails 注文を行ったアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName 送信対象の企業名
|
||||
* @param lisenceCount 注文を行った対象の注文の内容(ライセンス数)
|
||||
* @param poNumber 注文を行った対象の注文の内容(PO番号)
|
||||
* @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @returns mail with u105
|
||||
*/
|
||||
async sendMailWithU105(
|
||||
context: Context,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU105.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'License Requested Notification [U-105]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU105Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
const text = this.templateU105Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
customerMails,
|
||||
dealerEmails,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU105.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-106のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param cancelUserEmailAddress 注文キャンセルを行ったアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName 送信対象の企業名
|
||||
* @param lisenceCount 注文キャンセルを行った対象の注文の内容(ライセンス数)
|
||||
* @param poNumber 注文キャンセルを行った対象の注文の内容(PO番号)
|
||||
* @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @returns mail with u106
|
||||
*/
|
||||
async sendMailWithU106(
|
||||
context: Context,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Cancelled License Order Notification [U-106]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU106Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
const text = this.templateU106Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
customerMails,
|
||||
dealerEmails,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-107のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param cancelUserEmailAddress ライセンス発行をされたアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName 送信対象の企業名
|
||||
* @param lisenceCount ライセンス発行を行った対象の注文の内容(ライセンス数)
|
||||
* @param poNumber ライセンス発行を行った対象の注文の内容(PO番号)
|
||||
* @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @returns mail with u107
|
||||
*/
|
||||
async sendMailWithU107(
|
||||
context: Context,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU107.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'License Issued Notification [U-107]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU107Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
const text = this.templateU107Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
customerMails,
|
||||
dealerEmails,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU107.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-109のテンプレートを使用したメールを送信する
|
||||
* @param context context
|
||||
* @param dealerEmails ライセンス発行をキャンセルした上位アカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param dealerAccountName ライセンス発行をキャンセルした上位アカウントの会社名
|
||||
* @param lisenceCount ライセンス発行をキャンセルした対象の注文の内容(ライセンス数)
|
||||
* @param poNumber ライセンス発行をキャンセルした対象の注文の内容(PO番号)
|
||||
* @param customerMails ライセンス発行をキャンセルされたアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName ライセンス発行をキャンセルされたアカウントの会社名
|
||||
* @returns
|
||||
*/
|
||||
async sendMailWithU109(
|
||||
context: Context,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'License Returned Notification [U-109]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU109Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
const text = this.templateU109Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PO_NUMBER, poNumber)
|
||||
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
dealerEmails,
|
||||
customerMails,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-108のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param userName ライセンス割り当てされたユーザーの名前
|
||||
* @param userMail ライセンス割り当てされたユーザーのメールアドレス
|
||||
* @param customerAdminMails ライセンス割り当てされたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName ライセンス割り当てされたユーザーの所属するアカウントの名前
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @returns mail with u108
|
||||
*/
|
||||
async sendMailWithU108(
|
||||
context: Context,
|
||||
userName: string,
|
||||
userMail: string,
|
||||
customerAdminMails: string[],
|
||||
customerAccountName: string,
|
||||
dealerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU108.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'License Assigned Notification [U-108]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU108Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(USER_NAME, userName)
|
||||
.replaceAll(USER_EMAIL, userMail)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
const text = this.templateU108Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(USER_NAME, userName)
|
||||
.replaceAll(USER_EMAIL, userMail)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
|
||||
const ccAddress = customerAdminMails.includes(userMail) ? [] : [userMail];
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
customerAdminMails,
|
||||
ccAddress,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU108.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-111のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param primaryAdminName 削除されたアカウントの管理者(primary)の名前
|
||||
* @param primaryAdminMail 削除されたアカウントの管理者(primary)のメールアドレス
|
||||
* @param customerAccountName 削除されたアカウントの会社名
|
||||
* @returns mail with u111
|
||||
*/
|
||||
async sendMailWithU111(
|
||||
context: Context,
|
||||
primaryAdminName: string,
|
||||
primaryAdminMail: string,
|
||||
customerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU111.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Account Deleted Notification [U-111]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU111Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
|
||||
const text = this.templateU111Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[primaryAdminMail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU111.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-112のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param primaryAdminName 情報変更を行ったアカウントの管理者(primary)の名前
|
||||
* @param primaryAdminMail 情報変更を行ったアカウントの管理者(primary)のメールアドレス
|
||||
* @param customerAccountName 情報変更を行ったアカウントの名前
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @returns mail with u112
|
||||
*/
|
||||
async sendMailWithU112(
|
||||
context: Context,
|
||||
primaryAdminName: string,
|
||||
primaryAdminMail: string,
|
||||
customerAccountName: string,
|
||||
dealerAccountName: string | null,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU112.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Account Edit Notification [U-112]';
|
||||
|
||||
let html: string;
|
||||
let text: string;
|
||||
|
||||
// 親アカウントがない場合は別のテンプレートを使用する
|
||||
if (dealerAccountName === null) {
|
||||
// メールの本文を作成する
|
||||
html = this.templateU112NoParentHtml
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
text = this.templateU112NoParentText
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
} else {
|
||||
html = this.templateU112Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
text = this.templateU112Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
}
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[primaryAdminMail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU112.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-113のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param userMail 認証済みユーザーのメールアドレス
|
||||
* @param primaryAdminName 認証済みユーザーが所属するアカウントの管理者(primary)の名前
|
||||
* @param temporaryPassword 認証済みユーザーの一時パスワード
|
||||
* @returns mail with u113
|
||||
*/
|
||||
async sendMailWithU113(
|
||||
context: Context,
|
||||
userMail: string,
|
||||
primaryAdminName: string,
|
||||
temporaryPassword: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU113.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Temporary password [U-113]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU113Html
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TEMPORARY_PASSWORD, temporaryPassword);
|
||||
const text = this.templateU113Text
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(TEMPORARY_PASSWORD, temporaryPassword);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[userMail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU113.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-114のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param accountId 認証対象のユーザーが所属するアカウントのID
|
||||
* @param userId 認証対象のユーザーのID
|
||||
* @param userMail 認証対象のユーザーのメールアドレス
|
||||
* @param primaryAdminName 認証対象のユーザーが所属するアカウントの管理者(primary)の名前
|
||||
* @returns mail with u114
|
||||
*/
|
||||
async sendMailWithU114(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
userMail: string,
|
||||
primaryAdminName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU114.name}`,
|
||||
);
|
||||
try {
|
||||
// ユーザー認証用のトークンを作成する
|
||||
const privateKey = getPrivateKey(this.configService);
|
||||
const token = sign<{ accountId: number; userId: number; email: string }>(
|
||||
{
|
||||
accountId,
|
||||
userId,
|
||||
email: userMail,
|
||||
},
|
||||
this.emailConfirmLifetime,
|
||||
privateKey,
|
||||
);
|
||||
const path = 'mail-confirm/user/';
|
||||
const verifyLink = `${this.appDomain}${path}?verify=${token}`;
|
||||
|
||||
const subject = 'User Registration Notification [U-114]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU114Html
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(VERIFY_LINK, verifyLink);
|
||||
const text = this.templateU114Text
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName)
|
||||
.replaceAll(VERIFY_LINK, verifyLink);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[userMail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU114.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-115のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param userName 情報更新が行われたユーザーの名前
|
||||
* @param userMail 情報更新が行われたユーザーのメールアドレス
|
||||
* @param primaryAdminName 情報更新が行われたユーザーの所属するアカウントの管理者(primary)の名前
|
||||
* @param adminMails 情報更新が行われたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @returns mail with u115
|
||||
*/
|
||||
async sendMailWithU115(
|
||||
context: Context,
|
||||
userName: string,
|
||||
userMail: string,
|
||||
primaryAdminName: string,
|
||||
adminMails: string[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU115.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Edit User Notification [U-115]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU115Html
|
||||
.replaceAll(USER_NAME, userName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName);
|
||||
const text = this.templateU115Text
|
||||
.replaceAll(USER_NAME, userName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName);
|
||||
|
||||
// 管理者ユーザーの情報を変更した場合にはTOに管理者のメールアドレスを設定するので、CCには管理者のメールアドレスを設定しない
|
||||
const ccAdminMails = adminMails.filter((x) => x !== userMail);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[userMail],
|
||||
ccAdminMails,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU115.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-117のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param authorEmail 文字起こしファイルのAuthorのメールアドレス
|
||||
* @param typistEmail 文字起こしを行ったTypistのメールアドレス
|
||||
* @param authorName 文字起こしファイルのAuthorの名前
|
||||
* @param fileName 文字起こしファイルのファイル名
|
||||
* @param typistName 文字起こしを行ったTypistの名前
|
||||
* @param adminName アカウント管理者の名前(プライマリ)
|
||||
* @returns mail with u117
|
||||
*/
|
||||
async sendMailWithU117(
|
||||
context: Context,
|
||||
authorEmail: string,
|
||||
typistEmail: string,
|
||||
authorName: string,
|
||||
fileName: string,
|
||||
typistName: string,
|
||||
adminName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU117.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'Transcription Completion Notification [U-117]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU117Html
|
||||
.replaceAll(AUTHOR_NAME, authorName)
|
||||
.replaceAll(FILE_NAME, fileName)
|
||||
.replaceAll(TYPIST_NAME, typistName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, adminName);
|
||||
const text = this.templateU117Text
|
||||
.replaceAll(AUTHOR_NAME, authorName)
|
||||
.replaceAll(FILE_NAME, fileName)
|
||||
.replaceAll(TYPIST_NAME, typistName)
|
||||
.replaceAll(PRIMARY_ADMIN_NAME, adminName);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[authorEmail, typistEmail],
|
||||
[],
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU117.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* メールを送信する
|
||||
* @param context
|
||||
* @param to
|
||||
* @param cc
|
||||
* @param from
|
||||
* @param subject
|
||||
* @param text
|
||||
@ -117,7 +952,8 @@ export class SendGridService {
|
||||
*/
|
||||
async sendMail(
|
||||
context: Context,
|
||||
to: string,
|
||||
to: string[],
|
||||
cc: string[],
|
||||
from: string,
|
||||
subject: string,
|
||||
text: string,
|
||||
@ -130,9 +966,8 @@ export class SendGridService {
|
||||
from: {
|
||||
email: from,
|
||||
},
|
||||
to: {
|
||||
email: to,
|
||||
},
|
||||
to: to.map((v) => ({ email: v })),
|
||||
cc: cc.map((v) => ({ email: v })),
|
||||
subject: subject,
|
||||
text: text,
|
||||
html: html,
|
||||
|
||||
@ -31,11 +31,6 @@ import {
|
||||
LICENSE_ISSUE_STATUS,
|
||||
TIERS,
|
||||
} from '../../constants';
|
||||
import {
|
||||
LicenseSummaryInfo,
|
||||
PartnerLicenseInfoForRepository,
|
||||
PartnerInfoFromDb,
|
||||
} from '../../features/accounts/types/types';
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
AdminUserNotFoundError,
|
||||
@ -57,11 +52,20 @@ import { AudioOptionItem } from '../audio_option_items/entity/audio_option_item.
|
||||
import { UserGroup } from '../user_groups/entity/user_group.entity';
|
||||
import { UserGroupMember } from '../user_groups/entity/user_group_member.entity';
|
||||
import { TemplateFile } from '../template_files/entity/template_file.entity';
|
||||
import {
|
||||
insertEntity,
|
||||
insertEntities,
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
import { LicenseSummaryInfo, PartnerInfoFromDb, PartnerLicenseInfoForRepository } from '../../features/accounts/types/types';
|
||||
|
||||
@Injectable()
|
||||
export class AccountsRepositoryService {
|
||||
// クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
* 管理ユーザー無しでアカウントを作成する
|
||||
* @param companyName
|
||||
@ -71,6 +75,7 @@ export class AccountsRepositoryService {
|
||||
* @returns create
|
||||
*/
|
||||
async create(
|
||||
context: Context,
|
||||
companyName: string,
|
||||
country: string,
|
||||
dealerAccountId: number | null,
|
||||
@ -88,7 +93,13 @@ export class AccountsRepositoryService {
|
||||
async (entityManager) => {
|
||||
const repo = entityManager.getRepository(Account);
|
||||
const newAccount = repo.create(account);
|
||||
const persisted = await repo.save(newAccount);
|
||||
const persisted = await insertEntity(
|
||||
Account,
|
||||
repo,
|
||||
newAccount,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
return persisted;
|
||||
},
|
||||
);
|
||||
@ -100,10 +111,16 @@ export class AccountsRepositoryService {
|
||||
* @param account
|
||||
* @returns update
|
||||
*/
|
||||
async update(account: Account): Promise<UpdateResult> {
|
||||
async update(context: Context, account: Account): Promise<UpdateResult> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repo = entityManager.getRepository(Account);
|
||||
return await repo.update({ id: account.id }, account);
|
||||
return await updateEntity(
|
||||
repo,
|
||||
{ id: account.id },
|
||||
account,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -120,6 +137,7 @@ export class AccountsRepositoryService {
|
||||
* @returns account/admin user
|
||||
*/
|
||||
async createAccount(
|
||||
context: Context,
|
||||
companyName: string,
|
||||
country: string,
|
||||
dealerAccountId: number | undefined,
|
||||
@ -140,7 +158,13 @@ export class AccountsRepositoryService {
|
||||
}
|
||||
const accountsRepo = entityManager.getRepository(Account);
|
||||
const newAccount = accountsRepo.create(account);
|
||||
const persistedAccount = await accountsRepo.save(newAccount);
|
||||
const persistedAccount = await insertEntity(
|
||||
Account,
|
||||
accountsRepo,
|
||||
newAccount,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 作成されたAccountのIDを使用してユーザーを作成
|
||||
const user = new User();
|
||||
@ -155,14 +179,23 @@ export class AccountsRepositoryService {
|
||||
}
|
||||
const usersRepo = entityManager.getRepository(User);
|
||||
const newUser = usersRepo.create(user);
|
||||
const persistedUser = await usersRepo.save(newUser);
|
||||
const persistedUser = await insertEntity(
|
||||
User,
|
||||
usersRepo,
|
||||
newUser,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// アカウントに管理者を設定して更新
|
||||
persistedAccount.primary_admin_user_id = persistedUser.id;
|
||||
|
||||
const result = await accountsRepo.update(
|
||||
const result = await updateEntity(
|
||||
accountsRepo,
|
||||
{ id: persistedAccount.id },
|
||||
persistedAccount,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 想定外の更新が行われた場合はロールバックを行った上でエラー送出
|
||||
@ -179,7 +212,13 @@ export class AccountsRepositoryService {
|
||||
}
|
||||
const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
|
||||
const newSortCriteria = sortCriteriaRepo.create(sortCriteria);
|
||||
await sortCriteriaRepo.save(newSortCriteria);
|
||||
await insertEntity(
|
||||
SortCriteria,
|
||||
sortCriteriaRepo,
|
||||
newSortCriteria,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
return { newAccount: persistedAccount, adminUser: persistedUser };
|
||||
});
|
||||
@ -190,19 +229,31 @@ export class AccountsRepositoryService {
|
||||
* @param accountId
|
||||
* @returns delete
|
||||
*/
|
||||
async deleteAccount(accountId: number, userId: number): Promise<void> {
|
||||
async deleteAccount(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const accountsRepo = entityManager.getRepository(Account);
|
||||
const usersRepo = entityManager.getRepository(User);
|
||||
const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
|
||||
// ソート条件を削除
|
||||
await sortCriteriaRepo.delete({
|
||||
user_id: userId,
|
||||
});
|
||||
await deleteEntity(
|
||||
sortCriteriaRepo,
|
||||
{ user_id: userId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
// プライマリ管理者を削除
|
||||
await usersRepo.delete({ id: userId });
|
||||
await deleteEntity(usersRepo, { id: userId }, this.isCommentOut, context);
|
||||
// アカウントを削除
|
||||
await accountsRepo.delete({ id: accountId });
|
||||
await deleteEntity(
|
||||
accountsRepo,
|
||||
{ id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -211,11 +262,12 @@ export class AccountsRepositoryService {
|
||||
* @param id
|
||||
* @returns account
|
||||
*/
|
||||
async findAccountById(id: number): Promise<Account> {
|
||||
async findAccountById(context: Context, id: number): Promise<Account> {
|
||||
const account = await this.dataSource.getRepository(Account).findOne({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
@ -234,6 +286,7 @@ export class AccountsRepositoryService {
|
||||
* @returns expiringSoonLicense
|
||||
*/
|
||||
private async getExpiringSoonLicense(
|
||||
context: Context,
|
||||
entityManager: EntityManager,
|
||||
id: number,
|
||||
currentDate: Date,
|
||||
@ -248,6 +301,7 @@ export class AccountsRepositoryService {
|
||||
expiry_date: Between(currentDate, expiringSoonDate),
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return expiringSoonLicense;
|
||||
@ -262,6 +316,7 @@ export class AccountsRepositoryService {
|
||||
* @returns allocatableLicenseWithMargin
|
||||
*/
|
||||
private async getAllocatableLicenseWithMargin(
|
||||
context: Context,
|
||||
entityManager: EntityManager,
|
||||
id: number,
|
||||
expiringSoonDate: Date,
|
||||
@ -288,6 +343,7 @@ export class AccountsRepositoryService {
|
||||
expiry_date: IsNull(),
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return allocatableLicenseWithMargin;
|
||||
@ -301,6 +357,7 @@ export class AccountsRepositoryService {
|
||||
* @returns licenseSummary
|
||||
*/
|
||||
async getLicenseSummaryInfo(
|
||||
context: Context,
|
||||
id: number,
|
||||
currentDate: Date,
|
||||
expiringSoonDate: Date,
|
||||
@ -318,14 +375,21 @@ export class AccountsRepositoryService {
|
||||
{
|
||||
account_id: id,
|
||||
expiry_date: MoreThanOrEqual(currentDate),
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
]),
|
||||
},
|
||||
{
|
||||
account_id: id,
|
||||
expiry_date: IsNull(),
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
]),
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 有効な総ライセンス数のうち、ユーザーに割り当て済みのライセンス数を取得する
|
||||
@ -344,6 +408,7 @@ export class AccountsRepositoryService {
|
||||
status: LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 総ライセンス数のうち、ユーザーに割り当てたことがあるが、現在は割り当て解除され誰にも割り当たっていないライセンス数を取得する
|
||||
@ -360,6 +425,7 @@ export class AccountsRepositoryService {
|
||||
status: LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 総ライセンス数のうち、一度もユーザーに割り当てたことのないライセンス数を取得する
|
||||
@ -376,10 +442,12 @@ export class AccountsRepositoryService {
|
||||
status: LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 有効期限が現在日付からしきい値以内のライセンス数を取得する
|
||||
const expiringSoonLicense = await this.getExpiringSoonLicense(
|
||||
context,
|
||||
entityManager,
|
||||
id,
|
||||
currentDate,
|
||||
@ -392,6 +460,7 @@ export class AccountsRepositoryService {
|
||||
from_account_id: id,
|
||||
status: LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 未発行状態あるいは発行キャンセルされた注文の総ライセンス数を取得する
|
||||
@ -402,19 +471,22 @@ export class AccountsRepositoryService {
|
||||
.andWhere('license_orders.status = :status', {
|
||||
status: LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||
})
|
||||
.comment(`${context.getTrackingId()}_${new Date().toUTCString()}`)
|
||||
.getRawOne();
|
||||
const issueRequesting = parseInt(result.sum, 10) || 0;
|
||||
|
||||
// 有効期限がしきい値より未来または未設定で、割り当て可能なライセンス数の取得を行う
|
||||
const allocatableLicenseWithMargin =
|
||||
await this.getAllocatableLicenseWithMargin(
|
||||
context,
|
||||
entityManager,
|
||||
id,
|
||||
expiringSoonDate,
|
||||
);
|
||||
|
||||
// アカウントのロック状態を取得する
|
||||
const isStorageAvailable = (await this.findAccountById(id)).locked;
|
||||
const isStorageAvailable = (await this.findAccountById(context, id))
|
||||
.locked;
|
||||
|
||||
let licenseSummary = new LicenseSummaryInfo();
|
||||
licenseSummary = {
|
||||
@ -442,6 +514,7 @@ export class AccountsRepositoryService {
|
||||
* @returns issueRequesting
|
||||
*/
|
||||
private async getAccountLicenseOrderStatus(
|
||||
context: Context,
|
||||
id: number,
|
||||
currentDate: Date,
|
||||
entityManager: EntityManager,
|
||||
@ -459,14 +532,21 @@ export class AccountsRepositoryService {
|
||||
{
|
||||
account_id: id,
|
||||
expiry_date: MoreThanOrEqual(currentDate),
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
]),
|
||||
},
|
||||
{
|
||||
account_id: id,
|
||||
expiry_date: IsNull(),
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
|
||||
status: In([
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
]),
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 子アカウントからの、未発行状態あるいは発行キャンセルされた注文の総ライセンス数を取得する
|
||||
@ -477,6 +557,7 @@ export class AccountsRepositoryService {
|
||||
.andWhere('license_orders.status = :status', {
|
||||
status: LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||
})
|
||||
.comment(`${context.getTrackingId()}_${new Date().toUTCString()}`)
|
||||
.getRawOne();
|
||||
const issuedRequested = parseInt(issuedRequestedSqlResult.sum, 10) || 0;
|
||||
|
||||
@ -488,6 +569,7 @@ export class AccountsRepositoryService {
|
||||
.andWhere('license_orders.status = :status', {
|
||||
status: LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||
})
|
||||
.comment(`${context.getTrackingId()}_${new Date().toUTCString()}`)
|
||||
.getRawOne();
|
||||
const issuedRequesting = parseInt(issuedRequestingSqlResult.sum, 10) || 0;
|
||||
|
||||
@ -508,6 +590,7 @@ export class AccountsRepositoryService {
|
||||
* @returns childrenPartnerLicensesFromRepository: リポジトリから取得した子アカウントのライセンス情報
|
||||
*/
|
||||
async getPartnerLicense(
|
||||
context: Context,
|
||||
id: number,
|
||||
currentDate: Date,
|
||||
expiringSoonDate: Date,
|
||||
@ -526,6 +609,7 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!ownAccount) {
|
||||
throw new AccountNotFoundError(`Account is Not Found.`);
|
||||
@ -533,6 +617,7 @@ export class AccountsRepositoryService {
|
||||
|
||||
// 自アカウントのライセンス注文状況を取得する
|
||||
const ownLicenseOrderStatus = await this.getAccountLicenseOrderStatus(
|
||||
context,
|
||||
id,
|
||||
currentDate,
|
||||
entityManager,
|
||||
@ -558,6 +643,7 @@ export class AccountsRepositoryService {
|
||||
},
|
||||
take: limit,
|
||||
skip: offset,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 各子アカウントのライセンス注文状況を取得する
|
||||
@ -566,6 +652,7 @@ export class AccountsRepositoryService {
|
||||
for (const childAccount of childAccounts) {
|
||||
// ライセンス注文状況を取得する
|
||||
const childLicenseOrderStatus = await this.getAccountLicenseOrderStatus(
|
||||
context,
|
||||
childAccount.id,
|
||||
currentDate,
|
||||
entityManager,
|
||||
@ -576,6 +663,7 @@ export class AccountsRepositoryService {
|
||||
let allocatableLicenseWithMargin: number = 0;
|
||||
if (childAccount.tier === TIERS.TIER5) {
|
||||
expiringSoonLicense = await this.getExpiringSoonLicense(
|
||||
context,
|
||||
entityManager,
|
||||
childAccount.id,
|
||||
currentDate,
|
||||
@ -583,6 +671,7 @@ export class AccountsRepositoryService {
|
||||
);
|
||||
allocatableLicenseWithMargin =
|
||||
await this.getAllocatableLicenseWithMargin(
|
||||
context,
|
||||
entityManager,
|
||||
childAccount.id,
|
||||
expiringSoonDate,
|
||||
@ -612,6 +701,7 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
parent_account_id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return {
|
||||
@ -626,11 +716,12 @@ export class AccountsRepositoryService {
|
||||
* Dealer(Tier4)アカウント情報を取得する
|
||||
* @returns dealer accounts
|
||||
*/
|
||||
async findDealerAccounts(): Promise<Account[]> {
|
||||
async findDealerAccounts(context: Context): Promise<Account[]> {
|
||||
const accounts = await this.dataSource.getRepository(Account).find({
|
||||
where: {
|
||||
tier: TIERS.TIER4,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return accounts;
|
||||
@ -642,7 +733,10 @@ export class AccountsRepositoryService {
|
||||
* @param targetAccountId
|
||||
* @returns accountIds
|
||||
*/
|
||||
async getHierarchyParents(targetAccountId: number): Promise<number[]> {
|
||||
async getHierarchyParents(
|
||||
context: Context,
|
||||
targetAccountId: number,
|
||||
): Promise<number[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const accountRepository = entityManager.getRepository(Account);
|
||||
const maxTierDifference = TIERS.TIER5 - TIERS.TIER1;
|
||||
@ -655,6 +749,7 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
id: currentAccountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!account) {
|
||||
break;
|
||||
@ -675,9 +770,14 @@ export class AccountsRepositoryService {
|
||||
* 注文元アカウントIDとPOナンバーに紐づくライセンス発行をキャンセルする
|
||||
* @param orderedAccountId:キャンセルしたい発行の注文元アカウントID
|
||||
* @param poNumber:POナンバー
|
||||
* @returns { canceledIssueLicenseOrderId } : キャンセルされたライセンス発行に紐づく注文ID
|
||||
*/
|
||||
async cancelIssue(orderedAccountId: number, poNumber: string): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
async cancelIssue(
|
||||
context: Context,
|
||||
orderedAccountId: number,
|
||||
poNumber: string,
|
||||
): Promise<{ canceledIssueLicenseOrderId: number }> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const orderRepo = entityManager.getRepository(LicenseOrder);
|
||||
|
||||
// キャンセル対象の発行を取得
|
||||
@ -687,6 +787,7 @@ export class AccountsRepositoryService {
|
||||
po_number: poNumber,
|
||||
status: LICENSE_ISSUE_STATUS.ISSUED,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// キャンセル対象の発行が存在しない場合エラー
|
||||
@ -715,6 +816,7 @@ export class AccountsRepositoryService {
|
||||
order_id: targetOrder.id,
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.UNALLOCATED),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 存在した場合エラー
|
||||
@ -730,18 +832,35 @@ export class AccountsRepositoryService {
|
||||
// 注文を発行待ちに戻す
|
||||
updatedOrder.issued_at = null;
|
||||
updatedOrder.status = LICENSE_ISSUE_STATUS.ISSUE_REQUESTING;
|
||||
await orderRepo.save(updatedOrder);
|
||||
await updateEntity(
|
||||
orderRepo,
|
||||
{ id: targetOrder.id },
|
||||
updatedOrder,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 発行時に削除したライセンスを未割当に戻す
|
||||
await licenseRepo.update(
|
||||
await updateEntity(
|
||||
licenseRepo,
|
||||
{ delete_order_id: targetOrder.id },
|
||||
{
|
||||
status: LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||
deleted_at: null,
|
||||
delete_order_id: null,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
// 発行時に発行されたライセンスを削除する
|
||||
await licenseRepo.delete({ order_id: targetOrder.id });
|
||||
await deleteEntity(
|
||||
licenseRepo,
|
||||
{ order_id: targetOrder.id },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
return { canceledIssueLicenseOrderId: targetOrder.id };
|
||||
});
|
||||
}
|
||||
|
||||
@ -754,6 +873,7 @@ export class AccountsRepositoryService {
|
||||
* @returns partners: DBから取得できるパートナー一覧情報
|
||||
*/
|
||||
async getPartners(
|
||||
context: Context,
|
||||
id: number,
|
||||
limit: number,
|
||||
offset: number,
|
||||
@ -769,6 +889,7 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
parent_account_id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
const partnerAccounts = await accountRepo.find({
|
||||
@ -780,6 +901,7 @@ export class AccountsRepositoryService {
|
||||
},
|
||||
take: limit,
|
||||
skip: offset,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ADB2Cから情報を取得するための外部ユーザIDを取得する(念のためプライマリ管理者IDが存在しない場合を考慮)
|
||||
@ -797,6 +919,7 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
id: In(primaryUserIds),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// アカウント情報とプライマリ管理者の外部ユーザIDをマージ
|
||||
@ -836,6 +959,7 @@ export class AccountsRepositoryService {
|
||||
* @returns account: 一階層上のアカウント
|
||||
*/
|
||||
async getOneUpperTierAccount(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
tier: number,
|
||||
): Promise<Account | null> {
|
||||
@ -846,6 +970,7 @@ export class AccountsRepositoryService {
|
||||
id: accountId,
|
||||
tier: tier - 1,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -860,6 +985,7 @@ export class AccountsRepositoryService {
|
||||
* @param secondryAdminUserId
|
||||
*/
|
||||
async updateAccountInfo(
|
||||
context: Context,
|
||||
myAccountId: number,
|
||||
tier: number,
|
||||
delegationPermission: boolean,
|
||||
@ -871,6 +997,7 @@ export class AccountsRepositoryService {
|
||||
// ディーラーアカウントが指定されている場合、存在チェックを行う
|
||||
if (parentAccountId) {
|
||||
const dealerAccount = await this.getOneUpperTierAccount(
|
||||
context,
|
||||
parentAccountId,
|
||||
tier,
|
||||
);
|
||||
@ -891,6 +1018,7 @@ export class AccountsRepositoryService {
|
||||
account_id: myAccountId,
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!primaryAdminUser) {
|
||||
throw new AdminUserNotFoundError(
|
||||
@ -907,6 +1035,7 @@ export class AccountsRepositoryService {
|
||||
account_id: myAccountId,
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!secondryAdminUser) {
|
||||
throw new AdminUserNotFoundError(
|
||||
@ -916,7 +1045,8 @@ export class AccountsRepositoryService {
|
||||
}
|
||||
const accountRepo = entityManager.getRepository(Account);
|
||||
// アカウント情報を更新
|
||||
await accountRepo.update(
|
||||
await updateEntity(
|
||||
accountRepo,
|
||||
{ id: myAccountId },
|
||||
{
|
||||
parent_account_id: parentAccountId ?? null,
|
||||
@ -924,6 +1054,8 @@ export class AccountsRepositoryService {
|
||||
primary_admin_user_id: primaryAdminUserId,
|
||||
secondary_admin_user_id: secondryAdminUserId ?? null,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -935,6 +1067,7 @@ export class AccountsRepositoryService {
|
||||
* @returns active worktype id
|
||||
*/
|
||||
async updateActiveWorktypeId(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
id?: number | undefined,
|
||||
): Promise<void> {
|
||||
@ -946,6 +1079,7 @@ export class AccountsRepositoryService {
|
||||
// 自アカウント内に指定IDのワークタイプが存在するか確認
|
||||
const worktype = await worktypeRepo.findOne({
|
||||
where: { account_id: accountId, id: id },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ワークタイプが存在しない場合はエラー
|
||||
@ -955,9 +1089,12 @@ export class AccountsRepositoryService {
|
||||
}
|
||||
|
||||
// アカウントのActiveWorktypeIDを更新
|
||||
await accountRepo.update(
|
||||
await updateEntity(
|
||||
accountRepo,
|
||||
{ id: accountId },
|
||||
{ active_worktype_id: id ?? null },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -967,35 +1104,42 @@ export class AccountsRepositoryService {
|
||||
* @param accountId
|
||||
* @returns users 削除対象のユーザー
|
||||
*/
|
||||
async deleteAccountAndInsertArchives(accountId: number): Promise<User[]> {
|
||||
async deleteAccountAndInsertArchives(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<User[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
// 削除対象のユーザーを退避テーブルに退避
|
||||
const users = await this.dataSource.getRepository(User).find({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const userArchiveRepo = entityManager.getRepository(UserArchive);
|
||||
await userArchiveRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(UserArchive)
|
||||
.values(users)
|
||||
.execute();
|
||||
await insertEntities(
|
||||
UserArchive,
|
||||
userArchiveRepo,
|
||||
users,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 削除対象のライセンスを退避テーブルに退避
|
||||
const licenses = await this.dataSource.getRepository(License).find({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const licenseArchiveRepo = entityManager.getRepository(LicenseArchive);
|
||||
await licenseArchiveRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(LicenseArchive)
|
||||
.values(licenses)
|
||||
.execute();
|
||||
await insertEntities(
|
||||
LicenseArchive,
|
||||
licenseArchiveRepo,
|
||||
licenses,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 削除対象のライセンス割り当て履歴を退避テーブルに退避
|
||||
const licenseHistories = await this.dataSource
|
||||
@ -1004,57 +1148,85 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const licenseHistoryArchiveRepo = entityManager.getRepository(
|
||||
LicenseAllocationHistoryArchive,
|
||||
);
|
||||
await licenseHistoryArchiveRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(LicenseAllocationHistoryArchive)
|
||||
.values(licenseHistories)
|
||||
.execute();
|
||||
await insertEntities(
|
||||
LicenseAllocationHistoryArchive,
|
||||
licenseHistoryArchiveRepo,
|
||||
licenseHistories,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// アカウントを削除
|
||||
const accountRepo = entityManager.getRepository(Account);
|
||||
await accountRepo.delete({ id: accountId });
|
||||
|
||||
await deleteEntity(
|
||||
accountRepo,
|
||||
{ id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
// ライセンス系(card_license_issue以外)のテーブルのレコードを削除する
|
||||
const orderRepo = entityManager.getRepository(LicenseOrder);
|
||||
await orderRepo.delete({
|
||||
from_account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
orderRepo,
|
||||
{ from_account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
const licenseRepo = entityManager.getRepository(License);
|
||||
const targetLicenses = await licenseRepo.find({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const cardLicenseRepo = entityManager.getRepository(CardLicense);
|
||||
await cardLicenseRepo.delete({
|
||||
license_id: In(targetLicenses.map((license) => license.id)),
|
||||
});
|
||||
await licenseRepo.delete({
|
||||
account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
cardLicenseRepo,
|
||||
{ license_id: In(targetLicenses.map((license) => license.id)) },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
licenseRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
const LicenseAllocationHistoryRepo = entityManager.getRepository(
|
||||
LicenseAllocationHistory,
|
||||
);
|
||||
await LicenseAllocationHistoryRepo.delete({
|
||||
account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
LicenseAllocationHistoryRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ワークタイプ系のテーブルのレコードを削除する
|
||||
const worktypeRepo = entityManager.getRepository(Worktype);
|
||||
const taggerWorktypes = await worktypeRepo.find({
|
||||
where: { account_id: accountId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
const optionItemRepo = entityManager.getRepository(OptionItem);
|
||||
await optionItemRepo.delete({
|
||||
worktype_id: In(taggerWorktypes.map((worktype) => worktype.id)),
|
||||
});
|
||||
await worktypeRepo.delete({ account_id: accountId });
|
||||
await deleteEntity(
|
||||
optionItemRepo,
|
||||
{ worktype_id: In(taggerWorktypes.map((worktype) => worktype.id)) },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
worktypeRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// タスク系のテーブルのレコードを削除する
|
||||
const taskRepo = entityManager.getRepository(Task);
|
||||
@ -1062,15 +1234,22 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const checkoutPermissionRepo =
|
||||
entityManager.getRepository(CheckoutPermission);
|
||||
await checkoutPermissionRepo.delete({
|
||||
task_id: In(targetTasks.map((task) => task.id)),
|
||||
});
|
||||
await taskRepo.delete({
|
||||
account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
checkoutPermissionRepo,
|
||||
{ task_id: In(targetTasks.map((task) => task.id)) },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
taskRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// オーディオファイル系のテーブルのレコードを削除する
|
||||
const audioFileRepo = entityManager.getRepository(AudioFile);
|
||||
@ -1078,14 +1257,23 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const audioOptionItemsRepo = entityManager.getRepository(AudioOptionItem);
|
||||
await audioOptionItemsRepo.delete({
|
||||
audio_file_id: In(targetaudioFiles.map((audioFile) => audioFile.id)),
|
||||
});
|
||||
await audioFileRepo.delete({
|
||||
account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
audioOptionItemsRepo,
|
||||
{
|
||||
audio_file_id: In(targetaudioFiles.map((audioFile) => audioFile.id)),
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
audioFileRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ユーザーグループ系のテーブルのレコードを削除する
|
||||
const userGroupRepo = entityManager.getRepository(UserGroup);
|
||||
@ -1093,30 +1281,50 @@ export class AccountsRepositoryService {
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const userGroupMemberRepo = entityManager.getRepository(UserGroupMember);
|
||||
await userGroupMemberRepo.delete({
|
||||
user_group_id: In(targetUserGroup.map((userGroup) => userGroup.id)),
|
||||
});
|
||||
await userGroupRepo.delete({
|
||||
account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
userGroupMemberRepo,
|
||||
{
|
||||
user_group_id: In(targetUserGroup.map((userGroup) => userGroup.id)),
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
userGroupRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// テンプレートファイルテーブルのレコードを削除する
|
||||
const templateFileRepo = entityManager.getRepository(TemplateFile);
|
||||
await templateFileRepo.delete({ account_id: accountId });
|
||||
await deleteEntity(
|
||||
templateFileRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ユーザテーブルのレコードを削除する
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
await userRepo.delete({
|
||||
account_id: accountId,
|
||||
});
|
||||
await deleteEntity(
|
||||
userRepo,
|
||||
{ account_id: accountId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ソート条件のテーブルのレコードを削除する
|
||||
const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
|
||||
await sortCriteriaRepo.delete({
|
||||
user_id: In(users.map((user) => user.id)),
|
||||
});
|
||||
await deleteEntity(
|
||||
sortCriteriaRepo,
|
||||
{ user_id: In(users.map((user) => user.id)) },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
return users;
|
||||
});
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_ISSUE_STATUS,
|
||||
LICENSE_TYPE,
|
||||
NODE_ENV_TEST,
|
||||
SWITCH_FROM_TYPE,
|
||||
TIERS,
|
||||
} from '../../constants';
|
||||
@ -32,13 +33,22 @@ import {
|
||||
DateWithZeroTime,
|
||||
} from '../../features/licenses/types/types';
|
||||
import { NewAllocatedLicenseExpirationDate } from '../../features/licenses/types/types';
|
||||
import {
|
||||
insertEntity,
|
||||
insertEntities,
|
||||
updateEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class LicensesRepositoryService {
|
||||
//クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
constructor(private dataSource: DataSource) {}
|
||||
private readonly logger = new Logger(LicensesRepositoryService.name);
|
||||
|
||||
async order(
|
||||
context: Context,
|
||||
poNumber: string,
|
||||
fromAccountId: number,
|
||||
toAccountId: number,
|
||||
@ -70,6 +80,7 @@ export class LicensesRepositoryService {
|
||||
status: LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// 重複があった場合はエラーを返却する
|
||||
if (isPoNumberDuplicated) {
|
||||
@ -78,13 +89,49 @@ export class LicensesRepositoryService {
|
||||
|
||||
const repo = entityManager.getRepository(LicenseOrder);
|
||||
const newLicenseOrder = repo.create(licenseOrder);
|
||||
const persisted = await repo.save(newLicenseOrder);
|
||||
const persisted = await insertEntity(
|
||||
LicenseOrder,
|
||||
repo,
|
||||
newLicenseOrder,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
return persisted;
|
||||
},
|
||||
);
|
||||
return createdEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* オーダーIDとPO番号と依頼元アカウントIDを元にライセンス注文を取得する
|
||||
* @param context context
|
||||
* @param fromAccountId ライセンス注文を行ったアカウントのID
|
||||
* @param poNumber PO番号
|
||||
* @param orderId LicenseOrderのID
|
||||
* @returns license order
|
||||
*/
|
||||
async getLicenseOrder(
|
||||
context: Context,
|
||||
fromAccountId: number,
|
||||
poNumber: string,
|
||||
orderId: number,
|
||||
): Promise<LicenseOrder | null> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repo = entityManager.getRepository(LicenseOrder);
|
||||
|
||||
// ステータスは問わず、指定したIDのランセンス注文を取得する
|
||||
const entity = repo.findOne({
|
||||
where: {
|
||||
id: orderId,
|
||||
po_number: poNumber,
|
||||
from_account_id: fromAccountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return entity;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* カードライセンスを発行する
|
||||
* @param accountId
|
||||
@ -92,6 +139,7 @@ export class LicensesRepositoryService {
|
||||
* @returns string[] カードライセンスキーの配列
|
||||
*/
|
||||
async createCardLicenses(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
count: number,
|
||||
): Promise<string[]> {
|
||||
@ -112,19 +160,24 @@ export class LicensesRepositoryService {
|
||||
license.type = LICENSE_TYPE.CARD;
|
||||
licenses.push(license);
|
||||
}
|
||||
const savedLicenses = await licensesRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(License)
|
||||
.values(licenses)
|
||||
.execute();
|
||||
const savedLicenses = await insertEntities(
|
||||
License,
|
||||
licensesRepo,
|
||||
licenses,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// カードライセンス発行テーブルを作成する
|
||||
const cardLicenseIssue = new CardLicenseIssue();
|
||||
cardLicenseIssue.issued_at = new Date();
|
||||
const newCardLicenseIssue = cardLicenseIssueRepo.create(cardLicenseIssue);
|
||||
const savedCardLicensesIssue = await cardLicenseIssueRepo.save(
|
||||
const savedCardLicensesIssue = await insertEntity(
|
||||
CardLicenseIssue,
|
||||
cardLicenseIssueRepo,
|
||||
newCardLicenseIssue,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
let isDuplicateKeysExist = true;
|
||||
@ -139,6 +192,7 @@ export class LicensesRepositoryService {
|
||||
where: {
|
||||
card_license_key: In(generateKeys),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (existingCardLicenses.length > 0) {
|
||||
// 重複分を配列から削除
|
||||
@ -168,17 +222,20 @@ export class LicensesRepositoryService {
|
||||
// カードライセンステーブルを作成する(BULK INSERT)
|
||||
for (let i = 0; i < count; i++) {
|
||||
const cardLicense = new CardLicense();
|
||||
cardLicense.license_id = savedLicenses.generatedMaps[i].id; // Licenseテーブルの自動採番されたIDを挿入
|
||||
cardLicense.license_id = savedLicenses[i].id; // Licenseテーブルの自動採番されたIDを挿入
|
||||
cardLicense.issue_id = savedCardLicensesIssue.id; // CardLicenseIssueテーブルの自動採番されたIDを挿入
|
||||
cardLicense.card_license_key = licenseKeys[i];
|
||||
cardLicenses.push(cardLicense);
|
||||
}
|
||||
await cardLicenseRepo
|
||||
//TODO カードライセンステーブルのみPKがidではなかったためInsertEntitiesに置き換えができなかった。
|
||||
const query = cardLicenseRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(CardLicense)
|
||||
.values(cardLicenses)
|
||||
.execute();
|
||||
.into(CardLicense);
|
||||
if (this.isCommentOut) {
|
||||
query.comment(`${context.getTrackingId()}_${new Date().toUTCString()}`);
|
||||
}
|
||||
query.values(cardLicenses).execute();
|
||||
});
|
||||
return licenseKeys;
|
||||
}
|
||||
@ -222,6 +279,7 @@ export class LicensesRepositoryService {
|
||||
* @returns void
|
||||
*/
|
||||
async activateCardLicense(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
licenseKey: string,
|
||||
): Promise<void> {
|
||||
@ -233,6 +291,7 @@ export class LicensesRepositoryService {
|
||||
where: {
|
||||
card_license_key: licenseKey,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// カードライセンスが存在しなければエラー
|
||||
if (!targetCardLicense) {
|
||||
@ -256,6 +315,7 @@ export class LicensesRepositoryService {
|
||||
where: {
|
||||
id: targetCardLicense.license_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// ライセンスが存在しなければエラー
|
||||
if (!targetLicense) {
|
||||
@ -267,11 +327,23 @@ export class LicensesRepositoryService {
|
||||
|
||||
// ライセンステーブルを更新する
|
||||
targetLicense.account_id = accountId;
|
||||
await licensesRepo.save(targetLicense);
|
||||
await updateEntity(
|
||||
licensesRepo,
|
||||
{ id: targetLicense.id },
|
||||
targetLicense,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// カードライセンステーブルを更新する
|
||||
targetCardLicense.activated_at = new Date();
|
||||
await cardLicenseRepo.save(targetCardLicense);
|
||||
await updateEntity(
|
||||
cardLicenseRepo,
|
||||
{ license_id: targetCardLicense.license_id },
|
||||
targetCardLicense,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`activate success. licence_id: ${targetCardLicense.license_id}`,
|
||||
@ -289,6 +361,7 @@ export class LicensesRepositoryService {
|
||||
* @returns licenseOrders
|
||||
*/
|
||||
async getLicenseOrderHistoryInfo(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
offset: number,
|
||||
limit: number,
|
||||
@ -303,6 +376,7 @@ export class LicensesRepositoryService {
|
||||
where: {
|
||||
from_account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const licenseOrders = await licenseOrder.find({
|
||||
where: {
|
||||
@ -313,6 +387,7 @@ export class LicensesRepositoryService {
|
||||
},
|
||||
take: limit,
|
||||
skip: offset,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return {
|
||||
total: total,
|
||||
@ -330,13 +405,14 @@ export class LicensesRepositoryService {
|
||||
* @param poNumber
|
||||
*/
|
||||
async issueLicense(
|
||||
context: Context,
|
||||
orderedAccountId: number,
|
||||
myAccountId: number,
|
||||
tier: number,
|
||||
poNumber: string,
|
||||
): Promise<void> {
|
||||
): Promise<{ issuedOrderId: number }> {
|
||||
const nowDate = new Date();
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const licenseOrderRepo = entityManager.getRepository(LicenseOrder);
|
||||
const licenseRepo = entityManager.getRepository(License);
|
||||
|
||||
@ -345,9 +421,14 @@ export class LicensesRepositoryService {
|
||||
from_account_id: orderedAccountId,
|
||||
po_number: poNumber,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
// テスト環境の場合はロックを行わない(sqliteがlockに対応していないため)
|
||||
...(process.env.NODE_ENV !== NODE_ENV_TEST
|
||||
? { lock: { mode: 'pessimistic_write' } }
|
||||
: {}),
|
||||
});
|
||||
// 注文が存在しない場合、エラー
|
||||
if (!issuingOrder) {
|
||||
// 注文が存在しない場合、エラー
|
||||
throw new OrderNotFoundError(`No order found for PONumber:${poNumber}`);
|
||||
}
|
||||
// 既に発行済みの注文の場合、エラー
|
||||
@ -367,20 +448,24 @@ export class LicensesRepositoryService {
|
||||
return license;
|
||||
});
|
||||
// ライセンス注文テーブルを更新(注文元)
|
||||
await licenseOrderRepo.update(
|
||||
await updateEntity(
|
||||
licenseOrderRepo,
|
||||
{ id: issuingOrder.id },
|
||||
{
|
||||
issued_at: nowDate,
|
||||
status: LICENSE_ISSUE_STATUS.ISSUED,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
// ライセンステーブルを登録(注文元)
|
||||
await licenseRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(License)
|
||||
.values(newLicenses)
|
||||
.execute();
|
||||
await insertEntities(
|
||||
License,
|
||||
licenseRepo,
|
||||
newLicenses,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 第一階層の場合はストックライセンスの概念が存在しないため、ストックライセンス変更処理は行わない
|
||||
if (tier !== TIERS.TIER1) {
|
||||
@ -394,6 +479,7 @@ export class LicensesRepositoryService {
|
||||
id: 'ASC',
|
||||
},
|
||||
take: newLicenses.length,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 登録したライセンスに対して自身のライセンスが不足していた場合、エラー
|
||||
@ -408,8 +494,20 @@ export class LicensesRepositoryService {
|
||||
licenseToUpdate.delete_order_id = issuingOrder.id;
|
||||
}
|
||||
// 自身のライセンスを削除(論理削除)する
|
||||
await licenseRepo.save(licensesToUpdate);
|
||||
await updateEntity(
|
||||
licenseRepo,
|
||||
{ id: In(licensesToUpdate.map((l) => l.id)) },
|
||||
{
|
||||
status: LICENSE_ALLOCATED_STATUS.DELETED,
|
||||
deleted_at: nowDate,
|
||||
delete_order_id: issuingOrder.id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
return { issuedOrderId: issuingOrder.id };
|
||||
});
|
||||
}
|
||||
/**
|
||||
@ -420,6 +518,7 @@ export class LicensesRepositoryService {
|
||||
* @return AllocatableLicenseInfo[]
|
||||
*/
|
||||
async getAllocatableLicenses(
|
||||
context: Context,
|
||||
myAccountId: number,
|
||||
): Promise<AllocatableLicenseInfo[]> {
|
||||
const nowDate = new DateWithZeroTime();
|
||||
@ -438,6 +537,7 @@ export class LicensesRepositoryService {
|
||||
'(license.expiry_date >= :nowDate OR license.expiry_date IS NULL)',
|
||||
{ nowDate },
|
||||
)
|
||||
.comment(`${context.getTrackingId()}_${new Date().toUTCString()}`)
|
||||
.orderBy('license.expiry_date IS NULL', 'DESC')
|
||||
.addOrderBy('license.expiry_date', 'DESC')
|
||||
.addOrderBy('license.id', 'ASC');
|
||||
@ -453,6 +553,7 @@ export class LicensesRepositoryService {
|
||||
* @param newLicenseId
|
||||
*/
|
||||
async allocateLicense(
|
||||
context: Context,
|
||||
userId: number,
|
||||
newLicenseId: number,
|
||||
accountId: number,
|
||||
@ -467,6 +568,7 @@ export class LicensesRepositoryService {
|
||||
where: {
|
||||
id: newLicenseId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ライセンスが存在しない場合はエラー
|
||||
@ -501,6 +603,7 @@ export class LicensesRepositoryService {
|
||||
where: {
|
||||
allocated_user_id: userId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 既にライセンスが割り当てられているなら、割り当てを解除
|
||||
@ -508,7 +611,13 @@ export class LicensesRepositoryService {
|
||||
allocatedLicense.status = LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
allocatedLicense.allocated_user_id = null;
|
||||
|
||||
await licenseRepo.save(allocatedLicense);
|
||||
await updateEntity(
|
||||
licenseRepo,
|
||||
{ id: allocatedLicense.id },
|
||||
allocatedLicense,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ライセンス割り当て履歴テーブルへ登録
|
||||
const deallocationHistory = new LicenseAllocationHistory();
|
||||
@ -518,7 +627,13 @@ export class LicensesRepositoryService {
|
||||
deallocationHistory.is_allocated = false;
|
||||
deallocationHistory.executed_at = new Date();
|
||||
deallocationHistory.switch_from_type = SWITCH_FROM_TYPE.NONE;
|
||||
await licenseAllocationHistoryRepo.save(deallocationHistory);
|
||||
await insertEntity(
|
||||
LicenseAllocationHistory,
|
||||
licenseAllocationHistoryRepo,
|
||||
deallocationHistory,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
// ライセンス割り当てを実施
|
||||
@ -528,7 +643,13 @@ export class LicensesRepositoryService {
|
||||
if (!targetLicense.expiry_date) {
|
||||
targetLicense.expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
}
|
||||
await licenseRepo.save(targetLicense);
|
||||
await updateEntity(
|
||||
licenseRepo,
|
||||
{ id: targetLicense.id },
|
||||
targetLicense,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 直近割り当てたライセンス種別を取得
|
||||
const oldLicenseType = await licenseAllocationHistoryRepo.findOne({
|
||||
@ -537,6 +658,7 @@ export class LicensesRepositoryService {
|
||||
},
|
||||
where: { user_id: userId, is_allocated: true },
|
||||
order: { executed_at: 'DESC' },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
let switchFromType = '';
|
||||
@ -566,7 +688,13 @@ export class LicensesRepositoryService {
|
||||
// TODO switchFromTypeの値については「PBI1234: 第一階層として、ライセンス数推移情報をCSV出力したい」で正式対応
|
||||
allocationHistory.switch_from_type = switchFromType;
|
||||
|
||||
await licenseAllocationHistoryRepo.save(allocationHistory);
|
||||
await insertEntity(
|
||||
LicenseAllocationHistory,
|
||||
licenseAllocationHistoryRepo,
|
||||
allocationHistory,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -574,7 +702,11 @@ export class LicensesRepositoryService {
|
||||
* ユーザーに割り当てられているライセンスを解除する
|
||||
* @param userId
|
||||
*/
|
||||
async deallocateLicense(userId: number, accountId: number): Promise<void> {
|
||||
async deallocateLicense(
|
||||
context: Context,
|
||||
userId: number,
|
||||
accountId: number,
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const licenseRepo = entityManager.getRepository(License);
|
||||
const licenseAllocationHistoryRepo = entityManager.getRepository(
|
||||
@ -586,6 +718,7 @@ export class LicensesRepositoryService {
|
||||
allocated_user_id: userId,
|
||||
status: LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ライセンスが割り当てられていない場合はエラー
|
||||
@ -598,7 +731,13 @@ export class LicensesRepositoryService {
|
||||
// ライセンスの割り当てを解除
|
||||
allocatedLicense.status = LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
allocatedLicense.allocated_user_id = null;
|
||||
await licenseRepo.save(allocatedLicense);
|
||||
await updateEntity(
|
||||
licenseRepo,
|
||||
{ id: allocatedLicense.id },
|
||||
allocatedLicense,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ライセンス割り当て履歴テーブルへ登録
|
||||
const deallocationHistory = new LicenseAllocationHistory();
|
||||
@ -608,7 +747,13 @@ export class LicensesRepositoryService {
|
||||
deallocationHistory.is_allocated = false;
|
||||
deallocationHistory.executed_at = new Date();
|
||||
deallocationHistory.switch_from_type = SWITCH_FROM_TYPE.NONE;
|
||||
await licenseAllocationHistoryRepo.save(deallocationHistory);
|
||||
await insertEntity(
|
||||
LicenseAllocationHistory,
|
||||
licenseAllocationHistoryRepo,
|
||||
deallocationHistory,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -617,8 +762,12 @@ export class LicensesRepositoryService {
|
||||
* @param accountId
|
||||
* @param poNumber
|
||||
*/
|
||||
async cancelOrder(accountId: number, poNumber: string): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
async cancelOrder(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
poNumber: string,
|
||||
): Promise<{ canceledOrderId: number }> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const orderRepo = entityManager.getRepository(LicenseOrder);
|
||||
|
||||
// キャンセル対象の注文を取得
|
||||
@ -628,6 +777,7 @@ export class LicensesRepositoryService {
|
||||
po_number: poNumber,
|
||||
status: LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// キャンセル対象の注文が存在しない場合エラー
|
||||
@ -640,7 +790,15 @@ export class LicensesRepositoryService {
|
||||
// 注文キャンセル処理
|
||||
targetOrder.status = LICENSE_ISSUE_STATUS.CANCELED;
|
||||
targetOrder.canceled_at = new Date();
|
||||
await orderRepo.save(targetOrder);
|
||||
await updateEntity(
|
||||
orderRepo,
|
||||
{ id: targetOrder.id },
|
||||
targetOrder,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
return { canceledOrderId: targetOrder.id };
|
||||
});
|
||||
}
|
||||
|
||||
@ -651,6 +809,7 @@ export class LicensesRepositoryService {
|
||||
* @returns Promise<{ state: 'allocated' | 'inallocated' | 'expired' }>
|
||||
*/
|
||||
async getLicenseState(
|
||||
context: Context,
|
||||
userId: number,
|
||||
): Promise<{ state: 'allocated' | 'inallocated' | 'expired' }> {
|
||||
const allocatedLicense = await this.dataSource
|
||||
@ -660,6 +819,7 @@ export class LicensesRepositoryService {
|
||||
allocated_user_id: userId,
|
||||
status: LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ライセンスが割り当てられていない場合は未割当状態
|
||||
|
||||
@ -5,6 +5,8 @@ import {
|
||||
TaskListSortableAttribute,
|
||||
SortDirection,
|
||||
} from '../../common/types/sort';
|
||||
import { updateEntity } from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class SortCriteriaRepositoryService {
|
||||
@ -18,10 +20,11 @@ export class SortCriteriaRepositoryService {
|
||||
* @returns sort criteria
|
||||
*/
|
||||
async updateSortCriteria(
|
||||
context: Context,
|
||||
userId: number,
|
||||
parameter: TaskListSortableAttribute,
|
||||
direction: SortDirection,
|
||||
): Promise<SortCriteria> {
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
` ${this.updateSortCriteria.name}; parameter:${parameter}, direction:${direction}`,
|
||||
);
|
||||
@ -31,6 +34,7 @@ export class SortCriteriaRepositoryService {
|
||||
where: {
|
||||
user_id: userId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// 運用上はあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
if (!targetSortCriteria) {
|
||||
@ -40,8 +44,13 @@ export class SortCriteriaRepositoryService {
|
||||
targetSortCriteria.parameter = parameter;
|
||||
targetSortCriteria.direction = direction;
|
||||
|
||||
const persisted = await repo.save(targetSortCriteria);
|
||||
return persisted;
|
||||
await updateEntity(
|
||||
repo,
|
||||
{ id: targetSortCriteria.id },
|
||||
targetSortCriteria,
|
||||
false,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
/**
|
||||
@ -49,7 +58,10 @@ export class SortCriteriaRepositoryService {
|
||||
* @param userId
|
||||
* @returns sort criteria
|
||||
*/
|
||||
async getSortCriteria(userId: number): Promise<SortCriteria> {
|
||||
async getSortCriteria(
|
||||
context: Context,
|
||||
userId: number,
|
||||
): Promise<SortCriteria> {
|
||||
this.logger.log(` ${this.updateSortCriteria.name}; userId:${userId}`);
|
||||
|
||||
const repo = this.dataSource.getRepository(SortCriteria);
|
||||
@ -57,6 +69,7 @@ export class SortCriteriaRepositoryService {
|
||||
where: {
|
||||
user_id: userId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// 運用上はあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
if (!sortCriteria) {
|
||||
|
||||
@ -41,9 +41,18 @@ import { TaskStatus, isTaskStatus } from '../../common/types/taskStatus';
|
||||
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
|
||||
import { Workflow } from '../workflows/entity/workflow.entity';
|
||||
import { Worktype } from '../worktypes/entity/worktype.entity';
|
||||
import {
|
||||
insertEntities,
|
||||
insertEntity,
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class TasksRepositoryService {
|
||||
//クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
@ -54,6 +63,7 @@ export class TasksRepositoryService {
|
||||
* @returns task and audio file
|
||||
*/
|
||||
async getTaskAndAudioFile(
|
||||
context: Context,
|
||||
audioFileId: number,
|
||||
account_id: number,
|
||||
status: string[],
|
||||
@ -71,6 +81,7 @@ export class TasksRepositoryService {
|
||||
account_id: account_id,
|
||||
status: In(status),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -103,6 +114,7 @@ export class TasksRepositoryService {
|
||||
* @returns task from author id
|
||||
*/
|
||||
async getTaskFromAudioFileId(
|
||||
context: Context,
|
||||
audio_file_id: number,
|
||||
account_id: number,
|
||||
author_id: string,
|
||||
@ -117,6 +129,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
audio_file_id: audio_file_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -147,6 +160,7 @@ export class TasksRepositoryService {
|
||||
* @returns checkout
|
||||
*/
|
||||
async checkout(
|
||||
context: Context,
|
||||
audio_file_id: number,
|
||||
account_id: number,
|
||||
user_id: number,
|
||||
@ -159,6 +173,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
audio_file_id: audio_file_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -173,6 +188,7 @@ export class TasksRepositoryService {
|
||||
status: TASK_STATUS.IN_PROGRESS,
|
||||
typist_user_id: user_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (tasks.length > 0) {
|
||||
@ -210,6 +226,7 @@ export class TasksRepositoryService {
|
||||
id: user_id,
|
||||
},
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// ユーザーの所属するすべてのグループIDを列挙
|
||||
const groupIds = groups.map((member) => member.user_group_id);
|
||||
@ -231,6 +248,7 @@ export class TasksRepositoryService {
|
||||
user_group_id: In(groupIds),
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
//チェックアウト権限がなければエラー
|
||||
@ -242,7 +260,8 @@ export class TasksRepositoryService {
|
||||
|
||||
// 対象タスクの文字起こし開始日時を現在時刻に更新。割り当てユーザーを自身のユーザーIDに更新
|
||||
// タスクのステータスがUploaded以外の場合、文字起こし開始時刻は更新しない
|
||||
await taskRepo.update(
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ audio_file_id: audio_file_id },
|
||||
{
|
||||
started_at:
|
||||
@ -252,18 +271,31 @@ export class TasksRepositoryService {
|
||||
typist_user_id: user_id,
|
||||
status: TASK_STATUS.IN_PROGRESS,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
//対象のタスクに紐づくチェックアウト権限レコードを削除
|
||||
await checkoutRepo.delete({
|
||||
task_id: task.id,
|
||||
});
|
||||
await deleteEntity(
|
||||
checkoutRepo,
|
||||
{
|
||||
task_id: task.id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
//対象のタスクチェックアウト権限を自身のユーザーIDで作成
|
||||
await checkoutRepo.save({
|
||||
task_id: task.id,
|
||||
user_id: user_id,
|
||||
});
|
||||
insertEntity(
|
||||
CheckoutPermission,
|
||||
checkoutRepo,
|
||||
{
|
||||
task_id: task.id,
|
||||
user_id: user_id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -275,6 +307,7 @@ export class TasksRepositoryService {
|
||||
* @returns checkin
|
||||
*/
|
||||
async checkin(
|
||||
context: Context,
|
||||
audio_file_id: number,
|
||||
user_id: number,
|
||||
permittedSourceStatus: TaskStatus,
|
||||
@ -285,6 +318,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
audio_file_id: audio_file_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -303,12 +337,15 @@ export class TasksRepositoryService {
|
||||
}
|
||||
|
||||
// 対象タスクの文字起こし終了日時を現在時刻に更新。ステータスをFinishedに更新
|
||||
await taskRepo.update(
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ audio_file_id: audio_file_id },
|
||||
{
|
||||
finished_at: new Date().toISOString(),
|
||||
status: TASK_STATUS.FINISHED,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -322,6 +359,7 @@ export class TasksRepositoryService {
|
||||
* @returns cancel
|
||||
*/
|
||||
async cancel(
|
||||
context: Context,
|
||||
audio_file_id: number,
|
||||
permittedSourceStatus: TaskStatus[],
|
||||
account_id: number,
|
||||
@ -333,6 +371,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
audio_file_id: audio_file_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -360,12 +399,15 @@ export class TasksRepositoryService {
|
||||
}
|
||||
|
||||
// 対象タスクの文字起こし担当をnull,ステータスをUploadedに更新
|
||||
await taskRepo.update(
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ audio_file_id: audio_file_id },
|
||||
{
|
||||
typist_user_id: null,
|
||||
status: TASK_STATUS.UPLOADED,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
const checkoutPermissionRepo =
|
||||
@ -374,9 +416,14 @@ export class TasksRepositoryService {
|
||||
// 対象タスクの文字起こし候補を削除
|
||||
/* 対象タスクがInprogress,Pendingの状態の場合、文字起こし担当者個人指定のレコードのみだが、ユーザーIDの指定はしない
|
||||
(データの不整合があっても問題ないように)*/
|
||||
await checkoutPermissionRepo.delete({
|
||||
task_id: task.id,
|
||||
});
|
||||
await deleteEntity(
|
||||
checkoutPermissionRepo,
|
||||
{
|
||||
task_id: task.id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -388,6 +435,7 @@ export class TasksRepositoryService {
|
||||
* @returns suspend
|
||||
*/
|
||||
async suspend(
|
||||
context: Context,
|
||||
audio_file_id: number,
|
||||
user_id: number,
|
||||
permittedSourceStatus: TaskStatus,
|
||||
@ -398,6 +446,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
audio_file_id: audio_file_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -416,11 +465,14 @@ export class TasksRepositoryService {
|
||||
}
|
||||
|
||||
// 対象タスクの文字起こし終了日時を現在時刻に更新。ステータスをFinishedに更新
|
||||
await taskRepo.update(
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ audio_file_id: audio_file_id },
|
||||
{
|
||||
status: TASK_STATUS.PENDING,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -433,6 +485,7 @@ export class TasksRepositoryService {
|
||||
* @returns backup
|
||||
*/
|
||||
async backup(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
audio_file_id: number,
|
||||
permittedSourceStatus: TaskStatus[],
|
||||
@ -444,6 +497,7 @@ export class TasksRepositoryService {
|
||||
account_id: accountId,
|
||||
audio_file_id: audio_file_id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!task) {
|
||||
throw new TasksNotFoundError(
|
||||
@ -461,12 +515,15 @@ export class TasksRepositoryService {
|
||||
}
|
||||
|
||||
// ステータスをバックアップに更新、JobNumberを無効化
|
||||
await taskRepo.update(
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ audio_file_id: audio_file_id },
|
||||
{
|
||||
status: TASK_STATUS.BACKUP,
|
||||
is_job_number_enabled: false,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -482,6 +539,7 @@ export class TasksRepositoryService {
|
||||
* @returns tasks: タスク情報 / permissions:タスクに紐づくチェックアウト権限情報 / count: offset|limitを行わなかった場合の該当タスクの合計
|
||||
*/
|
||||
async getTasksFromAccountId(
|
||||
context: Context,
|
||||
account_id: number,
|
||||
offset: number,
|
||||
limit: number,
|
||||
@ -504,6 +562,7 @@ export class TasksRepositoryService {
|
||||
account_id: account_id,
|
||||
status: In(status),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 条件に該当するTask一覧を取得
|
||||
@ -520,6 +579,7 @@ export class TasksRepositoryService {
|
||||
order: order, // 引数によってOrderに使用するパラメータを変更
|
||||
take: limit,
|
||||
skip: offset,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// TODO [Task2249] Task内にCheckoutPermissionを含める方法が上手くいかなかった(複雑になりすぎた? 原因未調査)ため、
|
||||
@ -535,6 +595,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
task_id: In(taskIds),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return { tasks, permissions, count };
|
||||
});
|
||||
@ -552,6 +613,7 @@ export class TasksRepositoryService {
|
||||
* @returns tasks: タスク情報 / permissions:タスクに紐づくチェックアウト権限情報 / count: offset|limitを行わなかった場合の該当タスクの合計
|
||||
*/
|
||||
async getTasksFromAuthorIdAndAccountId(
|
||||
context: Context,
|
||||
author_id: string,
|
||||
account_id: number,
|
||||
offset: number,
|
||||
@ -573,6 +635,7 @@ export class TasksRepositoryService {
|
||||
status: In(status),
|
||||
file: { author_id: author_id },
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
const tasks = await taskRepo.find({
|
||||
@ -589,6 +652,7 @@ export class TasksRepositoryService {
|
||||
order: order, // 引数によってOrderに使用するパラメータを変更
|
||||
take: limit,
|
||||
skip: offset,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
const checkoutRepo = entityManager.getRepository(CheckoutPermission);
|
||||
@ -601,6 +665,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
task_id: In(taskIds),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return { tasks, permissions, count };
|
||||
});
|
||||
@ -608,6 +673,7 @@ export class TasksRepositoryService {
|
||||
}
|
||||
|
||||
async getTasksFromTypistRelations(
|
||||
context: Context,
|
||||
external_user_id: string,
|
||||
offset: number,
|
||||
limit: number,
|
||||
@ -633,6 +699,7 @@ export class TasksRepositoryService {
|
||||
external_id: external_user_id,
|
||||
},
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// ユーザーの所属するすべてのグループIDを列挙
|
||||
const groupIds = groups.map((member) => member.user_group_id);
|
||||
@ -655,6 +722,7 @@ export class TasksRepositoryService {
|
||||
user_group_id: In(groupIds),
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ユーザー本人、またはユーザーが所属するユーザーグループがチェックアウト可能なタスクIDの一覧を作成
|
||||
@ -683,6 +751,7 @@ export class TasksRepositoryService {
|
||||
status: In(status),
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 条件に該当するTask一覧を取得
|
||||
@ -709,6 +778,7 @@ export class TasksRepositoryService {
|
||||
order: order, // 引数によってOrderに使用するパラメータを変更
|
||||
take: limit,
|
||||
skip: offset,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// TODO [Task2249] Task内にCheckoutPermissionを含める方法が上手くいかなかった(複雑になりすぎた? 原因未調査)ため、
|
||||
@ -722,6 +792,7 @@ export class TasksRepositoryService {
|
||||
where: {
|
||||
task_id: In(taskIds),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return { tasks, permissions, count };
|
||||
});
|
||||
@ -732,6 +803,7 @@ export class TasksRepositoryService {
|
||||
* 文字起こしタスクと音声ファイル、オプションアイテムを追加
|
||||
*/
|
||||
async create(
|
||||
context: Context,
|
||||
account_id: number,
|
||||
owner_user_id: number,
|
||||
priority: string,
|
||||
@ -776,7 +848,13 @@ export class TasksRepositoryService {
|
||||
async (entityManager) => {
|
||||
const audioFileRepo = entityManager.getRepository(AudioFile);
|
||||
const newAudioFile = audioFileRepo.create(audioFile);
|
||||
const savedAudioFile = await audioFileRepo.save(newAudioFile);
|
||||
const savedAudioFile = await insertEntity(
|
||||
AudioFile,
|
||||
audioFileRepo,
|
||||
newAudioFile,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
task.audio_file_id = savedAudioFile.id;
|
||||
|
||||
@ -786,6 +864,7 @@ export class TasksRepositoryService {
|
||||
const lastTask = await taskRepo.findOne({
|
||||
where: { account_id: account_id, is_job_number_enabled: true },
|
||||
order: { created_at: 'DESC', job_number: 'DESC' },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
let newJobNumber = '00000001';
|
||||
@ -801,7 +880,13 @@ export class TasksRepositoryService {
|
||||
}
|
||||
task.job_number = newJobNumber;
|
||||
|
||||
const persisted = await taskRepo.save(task);
|
||||
const persisted = await insertEntity(
|
||||
Task,
|
||||
taskRepo,
|
||||
task,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
const optionItems = paramOptionItems.map((x) => {
|
||||
return {
|
||||
@ -813,7 +898,13 @@ export class TasksRepositoryService {
|
||||
|
||||
const optionItemRepo = entityManager.getRepository(AudioOptionItem);
|
||||
const newAudioOptionItems = optionItemRepo.create(optionItems);
|
||||
await optionItemRepo.save(newAudioOptionItems);
|
||||
await insertEntities(
|
||||
AudioOptionItem,
|
||||
optionItemRepo,
|
||||
newAudioOptionItems,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
return persisted;
|
||||
},
|
||||
);
|
||||
@ -828,6 +919,7 @@ export class TasksRepositoryService {
|
||||
* @returns checkout permission
|
||||
*/
|
||||
async changeCheckoutPermission(
|
||||
context: Context,
|
||||
audio_file_id: number,
|
||||
author_id: string | undefined,
|
||||
account_id: number,
|
||||
@ -848,6 +940,7 @@ export class TasksRepositoryService {
|
||||
account_id: account_id,
|
||||
deleted_at: IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// idはユニークであるため取得件数の一致でグループの存在を確認
|
||||
if (userGroupIds.length !== groupRecords.length) {
|
||||
@ -873,6 +966,7 @@ export class TasksRepositoryService {
|
||||
email_verified: true,
|
||||
deleted_at: IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// idはユニークであるため取得件数の一致でユーザーの存在を確認
|
||||
if (typistUserIds.length !== userRecords.length) {
|
||||
@ -897,6 +991,7 @@ export class TasksRepositoryService {
|
||||
: author_id,
|
||||
},
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
//タスクが存在しない or ステータスがUploadedでなければエラー
|
||||
if (!taskRecord) {
|
||||
@ -908,9 +1003,14 @@ export class TasksRepositoryService {
|
||||
// 当該タスクに紐づく既存checkoutPermissionをdelete
|
||||
const checkoutPermissionRepo =
|
||||
entityManager.getRepository(CheckoutPermission);
|
||||
await checkoutPermissionRepo.delete({
|
||||
task_id: taskRecord.id,
|
||||
});
|
||||
await deleteEntity(
|
||||
checkoutPermissionRepo,
|
||||
{
|
||||
task_id: taskRecord.id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 当該タスクに紐づく新規checkoutPermissionをinsert
|
||||
const checkoutPermissions: CheckoutPermission[] = assignees.map(
|
||||
@ -923,7 +1023,13 @@ export class TasksRepositoryService {
|
||||
},
|
||||
);
|
||||
|
||||
return await checkoutPermissionRepo.save(checkoutPermissions);
|
||||
return await insertEntities(
|
||||
CheckoutPermission,
|
||||
checkoutPermissionRepo,
|
||||
checkoutPermissions,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -935,6 +1041,7 @@ export class TasksRepositoryService {
|
||||
* @returns sorted tasks
|
||||
*/
|
||||
async getSortedTasks(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
audioFileId: number,
|
||||
@ -943,7 +1050,10 @@ export class TasksRepositoryService {
|
||||
const taskRepo = entityManager.getRepository(Task);
|
||||
const sortRepo = entityManager.getRepository(SortCriteria);
|
||||
|
||||
const sort = await sortRepo.findOne({ where: { user_id: userId } });
|
||||
const sort = await sortRepo.findOne({
|
||||
where: { user_id: userId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 運用上はあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
if (!sort) {
|
||||
@ -972,6 +1082,7 @@ export class TasksRepositoryService {
|
||||
TASK_STATUS.UPLOADED,
|
||||
]),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!targetTask) {
|
||||
@ -982,7 +1093,10 @@ export class TasksRepositoryService {
|
||||
|
||||
const groupMemberRepo = entityManager.getRepository(UserGroupMember);
|
||||
// ユーザーの所属するすべてのグループを列挙
|
||||
const groups = await groupMemberRepo.find({ where: { user_id: userId } });
|
||||
const groups = await groupMemberRepo.find({
|
||||
where: { user_id: userId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
// ユーザーの所属するすべてのグループIDを列挙
|
||||
const groupIds = groups.map((member) => member.user_group_id);
|
||||
|
||||
@ -995,6 +1109,7 @@ export class TasksRepositoryService {
|
||||
// ユーザーの所属するユーザーグループがチェックアウト可能である
|
||||
{ user_group_id: In(groupIds) },
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ユーザー本人、またはユーザーが所属するユーザーグループがチェックアウト可能なタスクIDの一覧を作成
|
||||
@ -1017,6 +1132,7 @@ export class TasksRepositoryService {
|
||||
},
|
||||
],
|
||||
order: order,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return tasks;
|
||||
@ -1031,6 +1147,7 @@ export class TasksRepositoryService {
|
||||
* @returns typistIds: タイピストIDの一覧 / typistGroupIds: タイピストグループIDの一覧
|
||||
*/
|
||||
async autoRouting(
|
||||
context: Context,
|
||||
audioFileId: number,
|
||||
accountId: number,
|
||||
myAuthorId?: string, // API実行者のAuthorId
|
||||
@ -1046,6 +1163,7 @@ export class TasksRepositoryService {
|
||||
id: audioFileId,
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!audioFile) {
|
||||
throw new Error(
|
||||
@ -1067,6 +1185,7 @@ export class TasksRepositoryService {
|
||||
author_id: audioFile.author_id,
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 音声ファイル上のworktypeIdをもとにworktypeを取得
|
||||
@ -1076,6 +1195,7 @@ export class TasksRepositoryService {
|
||||
custom_worktype_id: audioFile.work_type_id,
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 音声ファイル上のworktypeIdが設定されているが、一致するworktypeが存在しない場合はエラーを出して終了
|
||||
@ -1096,11 +1216,13 @@ export class TasksRepositoryService {
|
||||
author_id: authorUser?.id ?? IsNull(), // authorUserが存在しない場合は、必ずヒットしないようにNULLを設定する
|
||||
worktype_id: worktypeRecord?.id ?? IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// Workflow(ルーティングルール)があればタスクのチェックアウト権限を設定する
|
||||
if (workflow) {
|
||||
return await this.setCheckoutPermissionAndTemplate(
|
||||
context,
|
||||
workflow,
|
||||
task,
|
||||
accountId,
|
||||
@ -1121,6 +1243,7 @@ export class TasksRepositoryService {
|
||||
author_id: myAuthorId,
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!myAuthorUser) {
|
||||
throw new Error(
|
||||
@ -1136,6 +1259,7 @@ export class TasksRepositoryService {
|
||||
author_id: myAuthorUser.id,
|
||||
worktype_id: worktypeRecord?.id ?? IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// API実行者のAuthorIdと音声ファイルのWorktypeをもとにルーティングルールを取得できない場合はエラーを出して終了
|
||||
@ -1147,6 +1271,7 @@ export class TasksRepositoryService {
|
||||
|
||||
// Workflow(ルーティングルール)があればタスクのチェックアウト権限を設定する
|
||||
return await this.setCheckoutPermissionAndTemplate(
|
||||
context,
|
||||
defaultWorkflow,
|
||||
task,
|
||||
accountId,
|
||||
@ -1168,6 +1293,7 @@ export class TasksRepositoryService {
|
||||
* @returns checkout permission
|
||||
*/
|
||||
private async setCheckoutPermissionAndTemplate(
|
||||
context: Context,
|
||||
workflow: Workflow,
|
||||
task: Task,
|
||||
accountId: number,
|
||||
@ -1181,11 +1307,14 @@ export class TasksRepositoryService {
|
||||
|
||||
// タスクのテンプレートIDを更新
|
||||
const taskRepo = entityManager.getRepository(Task);
|
||||
await taskRepo.update(
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ id: task.id },
|
||||
{
|
||||
template_file_id: template_id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 取得したルーティングルールのタイピストまたはタイピストグループをチェックアウト権限に設定する
|
||||
@ -1196,6 +1325,7 @@ export class TasksRepositoryService {
|
||||
);
|
||||
const typistUsers = await userRepo.find({
|
||||
where: { account_id: accountId, id: In(typistIds) },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (typistUsers.length !== typistIds.length) {
|
||||
throw new Error(`typist not found. ids: ${typistIds}`);
|
||||
@ -1208,6 +1338,7 @@ export class TasksRepositoryService {
|
||||
const userGroupRepo = entityManager.getRepository(UserGroup);
|
||||
const typistGroups = await userGroupRepo.find({
|
||||
where: { account_id: accountId, id: In(groupIds) },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (typistGroups.length !== groupIds.length) {
|
||||
throw new Error(`typist group not found. ids: ${groupIds}`);
|
||||
@ -1217,9 +1348,14 @@ export class TasksRepositoryService {
|
||||
entityManager.getRepository(CheckoutPermission);
|
||||
|
||||
// 当該タスクに紐づく既存checkoutPermissionをdelete
|
||||
await checkoutPermissionRepo.delete({
|
||||
task_id: task.id,
|
||||
});
|
||||
await deleteEntity(
|
||||
checkoutPermissionRepo,
|
||||
{
|
||||
task_id: task.id,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ルーティング候補ユーザーのチェックアウト権限を作成
|
||||
const typistPermissions = typistUsers.map((typistUser) => {
|
||||
@ -1236,7 +1372,13 @@ export class TasksRepositoryService {
|
||||
return permission;
|
||||
});
|
||||
const permissions = [...typistPermissions, ...typistGroupPermissions];
|
||||
await checkoutPermissionRepo.save(permissions);
|
||||
await insertEntities(
|
||||
CheckoutPermission,
|
||||
checkoutPermissionRepo,
|
||||
permissions,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
// user_idsとuser_group_idsを返却する
|
||||
return {
|
||||
typistIds: typistIds,
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { TemplateFile } from './entity/template_file.entity';
|
||||
import { insertEntity, updateEntity } from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class TemplateFilesRepositoryService {
|
||||
//クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
@ -11,12 +16,16 @@ export class TemplateFilesRepositoryService {
|
||||
* @param accountId
|
||||
* @returns template files
|
||||
*/
|
||||
async getTemplateFiles(accountId: number): Promise<TemplateFile[]> {
|
||||
async getTemplateFiles(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<TemplateFile[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const templateFilesRepo = entityManager.getRepository(TemplateFile);
|
||||
|
||||
const templates = await templateFilesRepo.find({
|
||||
where: { account_id: accountId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return templates;
|
||||
@ -31,6 +40,7 @@ export class TemplateFilesRepositoryService {
|
||||
* @returns template file
|
||||
*/
|
||||
async upsertTemplateFile(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
fileName: string,
|
||||
url: string,
|
||||
@ -41,20 +51,30 @@ export class TemplateFilesRepositoryService {
|
||||
// アカウント内に同名ファイルがあるか確認
|
||||
const template = await templateFilesRepo.findOne({
|
||||
where: { account_id: accountId, file_name: fileName },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 同名ファイルは同じものとして扱うため、すでにファイルがあれば更新(更新日時の履歴を残しておきたい)
|
||||
if (template) {
|
||||
await templateFilesRepo.update(
|
||||
await updateEntity(
|
||||
templateFilesRepo,
|
||||
{ id: template.id },
|
||||
{ file_name: fileName, url: url },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
} else {
|
||||
const newTemplate = new TemplateFile();
|
||||
newTemplate.account_id = accountId;
|
||||
newTemplate.file_name = fileName;
|
||||
newTemplate.url = url;
|
||||
await templateFilesRepo.save(newTemplate);
|
||||
await insertEntity(
|
||||
TemplateFile,
|
||||
templateFilesRepo,
|
||||
newTemplate,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { TermsVersion } from '../../features/terms/types/types';
|
||||
import { Term } from './entity/term.entity';
|
||||
import { TERM_TYPE } from '../../constants';
|
||||
import { TermInfoNotFoundError } from '../users/errors/types';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class TermsRepositoryService {
|
||||
@ -13,7 +14,7 @@ export class TermsRepositoryService {
|
||||
* 利用規約の最新バージョンを取得する
|
||||
* @returns Term[]
|
||||
*/
|
||||
async getLatestTermsInfo(): Promise<TermsVersion> {
|
||||
async getLatestTermsInfo(context: Context): Promise<TermsVersion> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const termRepo = entityManager.getRepository(Term);
|
||||
const latestEulaInfo = await termRepo.findOne({
|
||||
@ -23,6 +24,7 @@ export class TermsRepositoryService {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const latestPrivacyNoticeInfo = await termRepo.findOne({
|
||||
where: {
|
||||
@ -31,6 +33,7 @@ export class TermsRepositoryService {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const latestDpaInfo = await termRepo.findOne({
|
||||
where: {
|
||||
@ -39,6 +42,7 @@ export class TermsRepositoryService {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!latestEulaInfo || !latestPrivacyNoticeInfo || !latestDpaInfo) {
|
||||
|
||||
@ -5,18 +5,31 @@ import { UserGroupMember } from './entity/user_group_member.entity';
|
||||
import { User } from '../users/entity/user.entity';
|
||||
import { TypistGroupNotExistError, TypistIdInvalidError } from './errors/types';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
import {
|
||||
insertEntities,
|
||||
insertEntity,
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class UserGroupsRepositoryService {
|
||||
//クエリログにコメントを付与するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
async getUserGroups(account_id: number): Promise<UserGroup[]> {
|
||||
async getUserGroups(
|
||||
context: Context,
|
||||
account_id: number,
|
||||
): Promise<UserGroup[]> {
|
||||
const value = await this.dataSource.transaction(async (entityManager) => {
|
||||
const userGroupRepo = entityManager.getRepository(UserGroup);
|
||||
|
||||
const userGroups = await userGroupRepo.find({
|
||||
// 論理削除されていないレコードを取得
|
||||
where: { account_id: account_id, deleted_at: IsNull() },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return userGroups;
|
||||
@ -29,6 +42,7 @@ export class UserGroupsRepositoryService {
|
||||
* @returns users from groups
|
||||
*/
|
||||
async getGroupMembersFromGroupIds(
|
||||
context: Context,
|
||||
groupIds: number[],
|
||||
): Promise<UserGroupMember[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
@ -41,6 +55,7 @@ export class UserGroupsRepositoryService {
|
||||
where: {
|
||||
user_group_id: In(groupIds),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return groupMembers;
|
||||
@ -54,6 +69,7 @@ export class UserGroupsRepositoryService {
|
||||
* @returns typist group
|
||||
*/
|
||||
async getTypistGroup(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
typistGroupId: number,
|
||||
): Promise<UserGroup> {
|
||||
@ -69,6 +85,7 @@ export class UserGroupsRepositoryService {
|
||||
relations: {
|
||||
userGroupMembers: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!userGroup) {
|
||||
@ -88,6 +105,7 @@ export class UserGroupsRepositoryService {
|
||||
* @returns createdTypistGroup
|
||||
*/
|
||||
async createTypistGroup(
|
||||
context: Context,
|
||||
name: string,
|
||||
typistIds: number[],
|
||||
accountId: number,
|
||||
@ -104,6 +122,7 @@ export class UserGroupsRepositoryService {
|
||||
role: USER_ROLES.TYPIST,
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (userRecords.length !== typistIds.length) {
|
||||
throw new TypistIdInvalidError(
|
||||
@ -113,10 +132,16 @@ export class UserGroupsRepositoryService {
|
||||
);
|
||||
}
|
||||
// userGroupをDBに保存する
|
||||
const userGroup = await userGroupRepo.save({
|
||||
account_id: accountId,
|
||||
name,
|
||||
});
|
||||
const userGroup = await insertEntity(
|
||||
UserGroup,
|
||||
userGroupRepo,
|
||||
{
|
||||
name,
|
||||
account_id: accountId,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
const userGroupMembers = userRecords.map((user) => {
|
||||
return {
|
||||
@ -125,7 +150,13 @@ export class UserGroupsRepositoryService {
|
||||
};
|
||||
});
|
||||
// userGroupMembersをDBに保存する
|
||||
await userGroupMemberRepo.save(userGroupMembers);
|
||||
await insertEntities(
|
||||
UserGroupMember,
|
||||
userGroupMemberRepo,
|
||||
userGroupMembers,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
return userGroup;
|
||||
});
|
||||
@ -139,6 +170,7 @@ export class UserGroupsRepositoryService {
|
||||
* @returns createdTypistGroup
|
||||
*/
|
||||
async updateTypistGroup(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
typistGroupId: number,
|
||||
typistGroupName: string,
|
||||
@ -156,6 +188,7 @@ export class UserGroupsRepositoryService {
|
||||
role: USER_ROLES.TYPIST,
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (userRecords.length !== typistIds.length) {
|
||||
throw new TypistIdInvalidError(
|
||||
@ -171,6 +204,7 @@ export class UserGroupsRepositoryService {
|
||||
id: typistGroupId,
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!typistGroup) {
|
||||
throw new TypistGroupNotExistError(
|
||||
@ -181,12 +215,23 @@ export class UserGroupsRepositoryService {
|
||||
// 対象のタイピストグループを更新する
|
||||
// ユーザーグループ名を更新する
|
||||
typistGroup.name = typistGroupName;
|
||||
await userGroupRepo.save(typistGroup);
|
||||
await updateEntity(
|
||||
userGroupRepo,
|
||||
{ id: typistGroupId },
|
||||
typistGroup,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// user_group_membersテーブルから対象のタイピストグループのユーザーを削除する
|
||||
await userGroupMemberRepo.delete({
|
||||
user_group_id: typistGroupId,
|
||||
});
|
||||
await deleteEntity(
|
||||
userGroupMemberRepo,
|
||||
{
|
||||
user_group_id: typistGroupId,
|
||||
},
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
const typistGroupMembers = userRecords.map((typist) => {
|
||||
return {
|
||||
@ -194,7 +239,13 @@ export class UserGroupsRepositoryService {
|
||||
user_id: typist.id,
|
||||
};
|
||||
});
|
||||
await userGroupMemberRepo.save(typistGroupMembers);
|
||||
await insertEntities(
|
||||
UserGroupMember,
|
||||
userGroupMemberRepo,
|
||||
typistGroupMembers,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
return typistGroup;
|
||||
});
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { User, newUser } from './entity/user.entity';
|
||||
import { DataSource, IsNull, Not, UpdateResult } from 'typeorm';
|
||||
import {
|
||||
DataSource,
|
||||
FindOptionsWhere,
|
||||
IsNull,
|
||||
Not,
|
||||
UpdateResult,
|
||||
} from 'typeorm';
|
||||
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
|
||||
import {
|
||||
getDirection,
|
||||
@ -36,9 +42,18 @@ import { Account } from '../accounts/entity/account.entity';
|
||||
import { Workflow } from '../workflows/entity/workflow.entity';
|
||||
import { Worktype } from '../worktypes/entity/worktype.entity';
|
||||
import { Context } from '../../common/log';
|
||||
import {
|
||||
insertEntity,
|
||||
insertEntities,
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
} from '../../common/repository';
|
||||
|
||||
@Injectable()
|
||||
export class UsersRepositoryService {
|
||||
// クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
@ -46,7 +61,7 @@ export class UsersRepositoryService {
|
||||
* @param user
|
||||
* @returns User
|
||||
*/
|
||||
async createNormalUser(user: newUser): Promise<User> {
|
||||
async createNormalUser(context: Context, user: newUser): Promise<User> {
|
||||
const {
|
||||
account_id: accountId,
|
||||
external_id: externalUserId,
|
||||
@ -80,7 +95,13 @@ export class UsersRepositoryService {
|
||||
async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
const newUser = repo.create(userEntity);
|
||||
const persisted = await repo.save(newUser);
|
||||
const persisted = await insertEntity(
|
||||
User,
|
||||
repo,
|
||||
newUser,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ユーザーのタスクソート条件を作成
|
||||
const sortCriteria = new SortCriteria();
|
||||
@ -91,7 +112,13 @@ export class UsersRepositoryService {
|
||||
}
|
||||
const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
|
||||
const newSortCriteria = sortCriteriaRepo.create(sortCriteria);
|
||||
await sortCriteriaRepo.save(newSortCriteria);
|
||||
await insertEntity(
|
||||
SortCriteria,
|
||||
sortCriteriaRepo,
|
||||
newSortCriteria,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
return persisted;
|
||||
},
|
||||
@ -99,12 +126,16 @@ export class UsersRepositoryService {
|
||||
return createdEntity;
|
||||
}
|
||||
|
||||
async findVerifiedUser(sub: string): Promise<User | undefined> {
|
||||
async findVerifiedUser(
|
||||
context: Context,
|
||||
sub: string,
|
||||
): Promise<User | undefined> {
|
||||
const user = await this.dataSource.getRepository(User).findOne({
|
||||
where: {
|
||||
external_id: sub,
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -113,7 +144,7 @@ export class UsersRepositoryService {
|
||||
return user;
|
||||
}
|
||||
|
||||
async findUserByExternalId(sub: string): Promise<User> {
|
||||
async findUserByExternalId(context: Context, sub: string): Promise<User> {
|
||||
const user = await this.dataSource.getRepository(User).findOne({
|
||||
where: {
|
||||
external_id: sub,
|
||||
@ -121,6 +152,7 @@ export class UsersRepositoryService {
|
||||
relations: {
|
||||
account: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -129,11 +161,12 @@ export class UsersRepositoryService {
|
||||
return user;
|
||||
}
|
||||
|
||||
async findUserById(id: number): Promise<User> {
|
||||
async findUserById(context: Context, id: number): Promise<User> {
|
||||
const user = await this.dataSource.getRepository(User).findOne({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -142,12 +175,81 @@ export class UsersRepositoryService {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthorIDをもとにユーザーを取得します。
|
||||
* AuthorIDがセットされていない場合や、ユーザーが存在しない場合はエラーを返します。
|
||||
* @param context
|
||||
* @param authorId 検索対象のAuthorID
|
||||
* @param accountId 検索対象のアカウントID
|
||||
* @returns user by author id
|
||||
*/
|
||||
async findUserByAuthorId(
|
||||
context: Context,
|
||||
authorId: string,
|
||||
accountId: number,
|
||||
): Promise<User> {
|
||||
if (!authorId) {
|
||||
throw new Error('authorId is not set.');
|
||||
}
|
||||
|
||||
const user = await this.dataSource.getRepository(User).findOne({
|
||||
where: {
|
||||
author_id: authorId,
|
||||
account_id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new UserNotFoundError(`User not Found.`);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定したアカウントIDを持つアカウントのプライマリ管理者とセカンダリ管理者を取得する
|
||||
* @param context context
|
||||
* @param accountId アカウントID
|
||||
* @throws AccountNotFoundError
|
||||
* @returns admin users
|
||||
*/
|
||||
async findAdminUsers(context: Context, accountId: number): Promise<User[]> {
|
||||
return this.dataSource.transaction(async (entityManager) => {
|
||||
const account = await entityManager.getRepository(Account).findOne({
|
||||
where: {
|
||||
id: accountId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (account == null) {
|
||||
throw new AccountNotFoundError('account not found');
|
||||
}
|
||||
const primaryAdminId = account.primary_admin_user_id;
|
||||
const secondaryAdminId = account.secondary_admin_user_id;
|
||||
|
||||
// IDが有効なユーザーだけを検索対象とする
|
||||
const targets = [primaryAdminId, secondaryAdminId]
|
||||
.flatMap((x) => (x == null ? [] : [x]))
|
||||
.map((x): FindOptionsWhere<User> => ({ id: x }));
|
||||
|
||||
const users = await entityManager.getRepository(User).find({
|
||||
where: targets,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return users;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthorIdが既に存在するか確認する
|
||||
* @param user
|
||||
* @returns 存在する:true 存在しない:false
|
||||
*/
|
||||
async existsAuthorId(accountId: number, authorId: string): Promise<boolean> {
|
||||
async existsAuthorId(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
authorId: string,
|
||||
): Promise<boolean> {
|
||||
const user = await this.dataSource.getRepository(User).findOne({
|
||||
where: [
|
||||
{
|
||||
@ -155,6 +257,7 @@ export class UsersRepositoryService {
|
||||
author_id: authorId,
|
||||
},
|
||||
],
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (user) {
|
||||
@ -169,6 +272,7 @@ export class UsersRepositoryService {
|
||||
* @returns update
|
||||
*/
|
||||
async update(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
id: number,
|
||||
role: string,
|
||||
@ -186,6 +290,7 @@ export class UsersRepositoryService {
|
||||
// 変更対象のユーザーを取得
|
||||
const targetUser = await repo.findOne({
|
||||
where: { id: id, account_id: accountId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
@ -202,6 +307,7 @@ export class UsersRepositoryService {
|
||||
// ユーザーのロールがAuthorの場合はAuthorIDの重複チェックを行う
|
||||
const authorIdDuplicatedUser = await repo.findOne({
|
||||
where: { account_id: accountId, id: Not(id), author_id: authorId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 重複したAuthorIDがあった場合はエラー
|
||||
@ -242,7 +348,13 @@ export class UsersRepositoryService {
|
||||
targetUser.license_alert = licenseAlart;
|
||||
targetUser.notification = notification;
|
||||
|
||||
const result = await repo.update({ id: id }, targetUser);
|
||||
const result = await updateEntity(
|
||||
repo,
|
||||
{ id: id },
|
||||
targetUser,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 想定外の更新が行われた場合はロールバックを行った上でエラー送出
|
||||
if (result.affected !== 1) {
|
||||
@ -257,13 +369,17 @@ export class UsersRepositoryService {
|
||||
* @param user
|
||||
* @returns update
|
||||
*/
|
||||
async updateUserVerified(id: number): Promise<UpdateResult> {
|
||||
async updateUserVerified(
|
||||
context: Context,
|
||||
id: number,
|
||||
): Promise<UpdateResult> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
const targetUser = await repo.findOne({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
@ -277,7 +393,13 @@ export class UsersRepositoryService {
|
||||
|
||||
targetUser.email_verified = true;
|
||||
|
||||
return await repo.update({ id: targetUser.id }, targetUser);
|
||||
return await updateEntity(
|
||||
repo,
|
||||
{ id: targetUser.id },
|
||||
targetUser,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -286,7 +408,10 @@ export class UsersRepositoryService {
|
||||
* @param id
|
||||
* @returns user verified and create trial license
|
||||
*/
|
||||
async updateUserVerifiedAndCreateTrialLicense(id: number): Promise<void> {
|
||||
async updateUserVerifiedAndCreateTrialLicense(
|
||||
context: Context,
|
||||
id: number,
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
const targetUser = await userRepo.findOne({
|
||||
@ -296,6 +421,7 @@ export class UsersRepositoryService {
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
@ -309,7 +435,13 @@ export class UsersRepositoryService {
|
||||
|
||||
targetUser.email_verified = true;
|
||||
|
||||
await userRepo.update({ id: targetUser.id }, targetUser);
|
||||
await updateEntity(
|
||||
userRepo,
|
||||
{ id: targetUser.id },
|
||||
targetUser,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// トライアルライセンス100件を作成する
|
||||
const licenseRepo = entityManager.getRepository(License);
|
||||
@ -327,12 +459,13 @@ export class UsersRepositoryService {
|
||||
licenses.push(license);
|
||||
}
|
||||
|
||||
await licenseRepo
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(License)
|
||||
.values(licenses)
|
||||
.execute();
|
||||
await insertEntities(
|
||||
License,
|
||||
licenseRepo,
|
||||
licenses,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -348,8 +481,12 @@ export class UsersRepositoryService {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
|
||||
const accountId = (await repo.findOne({ where: { external_id } }))
|
||||
?.account_id;
|
||||
const accountId = (
|
||||
await repo.findOne({
|
||||
where: { external_id },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
})
|
||||
)?.account_id;
|
||||
|
||||
if (!accountId) {
|
||||
throw new AccountNotFoundError('Account is Not Found.');
|
||||
@ -363,7 +500,7 @@ export class UsersRepositoryService {
|
||||
license: true,
|
||||
},
|
||||
where: { account_id: accountId },
|
||||
comment: `${context.getTrackingId()}`,
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return dbUsers;
|
||||
@ -375,7 +512,7 @@ export class UsersRepositoryService {
|
||||
* @param sub
|
||||
* @returns typist users
|
||||
*/
|
||||
async findTypistUsers(sub: string): Promise<User[]> {
|
||||
async findTypistUsers(context: Context, sub: string): Promise<User[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
|
||||
@ -383,6 +520,7 @@ export class UsersRepositoryService {
|
||||
where: {
|
||||
external_id: sub,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
@ -397,6 +535,7 @@ export class UsersRepositoryService {
|
||||
email_verified: true,
|
||||
deleted_at: IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return typists;
|
||||
@ -408,7 +547,7 @@ export class UsersRepositoryService {
|
||||
* @param accountId
|
||||
* @returns author users
|
||||
*/
|
||||
async findAuthorUsers(accountId: number): Promise<User[]> {
|
||||
async findAuthorUsers(context: Context, accountId: number): Promise<User[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
const authors = await repo.find({
|
||||
@ -418,6 +557,7 @@ export class UsersRepositoryService {
|
||||
email_verified: true,
|
||||
deleted_at: IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
return authors;
|
||||
});
|
||||
@ -428,16 +568,19 @@ export class UsersRepositoryService {
|
||||
* @param userId
|
||||
* @returns delete
|
||||
*/
|
||||
async deleteNormalUser(userId: number): Promise<void> {
|
||||
async deleteNormalUser(context: Context, userId: number): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const usersRepo = entityManager.getRepository(User);
|
||||
const sortCriteriaRepo = entityManager.getRepository(SortCriteria);
|
||||
// ソート条件を削除
|
||||
await sortCriteriaRepo.delete({
|
||||
user_id: userId,
|
||||
});
|
||||
await deleteEntity(
|
||||
sortCriteriaRepo,
|
||||
{ user_id: userId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
// プライマリ管理者を削除
|
||||
await usersRepo.delete({ id: userId });
|
||||
await deleteEntity(usersRepo, { id: userId }, this.isCommentOut, context);
|
||||
});
|
||||
}
|
||||
|
||||
@ -447,6 +590,7 @@ export class UsersRepositoryService {
|
||||
* @returns TermsCheckInfo
|
||||
*/
|
||||
async getAcceptedAndLatestVersion(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
): Promise<TermsCheckInfo> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
@ -458,6 +602,7 @@ export class UsersRepositoryService {
|
||||
relations: {
|
||||
account: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -475,6 +620,7 @@ export class UsersRepositoryService {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const latestPrivacyNoticeInfo = await termRepo.findOne({
|
||||
where: {
|
||||
@ -483,6 +629,7 @@ export class UsersRepositoryService {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
const latestDpaInfo = await termRepo.findOne({
|
||||
where: {
|
||||
@ -491,6 +638,7 @@ export class UsersRepositoryService {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!latestEulaInfo || !latestPrivacyNoticeInfo || !latestDpaInfo) {
|
||||
throw new TermInfoNotFoundError(`Terms info is not found.`);
|
||||
@ -518,6 +666,7 @@ export class UsersRepositoryService {
|
||||
* @returns update
|
||||
*/
|
||||
async updateAcceptedTermsVersion(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
eulaVersion: string,
|
||||
privacyNoticeVersion: string,
|
||||
@ -532,6 +681,7 @@ export class UsersRepositoryService {
|
||||
relations: {
|
||||
account: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -563,7 +713,13 @@ export class UsersRepositoryService {
|
||||
user.accepted_privacy_notice_version =
|
||||
privacyNoticeVersion ?? user.accepted_privacy_notice_version;
|
||||
user.accepted_dpa_version = dpaVersion ?? user.accepted_dpa_version;
|
||||
await userRepo.update({ id: user.id }, user);
|
||||
await updateEntity(
|
||||
userRepo,
|
||||
{ id: user.id },
|
||||
user,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -574,6 +730,7 @@ export class UsersRepositoryService {
|
||||
* @returns delegate accounts
|
||||
*/
|
||||
async findDelegateUser(
|
||||
context: Context,
|
||||
delegateAccountId: number,
|
||||
originAccountId: number,
|
||||
): Promise<User> {
|
||||
@ -587,6 +744,7 @@ export class UsersRepositoryService {
|
||||
parent_account_id: delegateAccountId,
|
||||
tier: TIERS.TIER5,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
@ -619,6 +777,7 @@ export class UsersRepositoryService {
|
||||
relations: {
|
||||
account: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 運用上、代行操作対象アカウントの管理者ユーザーがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
@ -637,6 +796,7 @@ export class UsersRepositoryService {
|
||||
* @returns delegate accounts
|
||||
*/
|
||||
async isAllowDelegationPermission(
|
||||
context: Context,
|
||||
delegateAccountId: number,
|
||||
originUserExternalId: string,
|
||||
): Promise<boolean> {
|
||||
@ -653,6 +813,7 @@ export class UsersRepositoryService {
|
||||
relations: {
|
||||
account: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!primaryUser) {
|
||||
@ -677,7 +838,10 @@ export class UsersRepositoryService {
|
||||
* @param userId
|
||||
* @returns user relations
|
||||
*/
|
||||
async getUserRelations(userId: number): Promise<{
|
||||
async getUserRelations(
|
||||
context: Context,
|
||||
userId: number,
|
||||
): Promise<{
|
||||
user: User;
|
||||
authors: User[];
|
||||
worktypes: Worktype[];
|
||||
@ -689,6 +853,7 @@ export class UsersRepositoryService {
|
||||
const user = await userRepo.findOne({
|
||||
where: { id: userId },
|
||||
relations: { account: true },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@ -709,6 +874,7 @@ export class UsersRepositoryService {
|
||||
role: USER_ROLES.AUTHOR,
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ユーザーの所属するアカウント内のアクティブワークタイプを取得する
|
||||
@ -722,6 +888,7 @@ export class UsersRepositoryService {
|
||||
account_id: user.account_id,
|
||||
id: activeWorktypeId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
})) ?? undefined;
|
||||
}
|
||||
|
||||
@ -740,6 +907,7 @@ export class UsersRepositoryService {
|
||||
option_items: true,
|
||||
},
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
worktypes = workflows.flatMap((workflow) =>
|
||||
|
||||
@ -15,9 +15,18 @@ import {
|
||||
AuthorIdAndWorktypeIdPairAlreadyExistsError,
|
||||
WorkflowNotFoundError,
|
||||
} from './errors/types';
|
||||
import {
|
||||
insertEntities,
|
||||
insertEntity,
|
||||
deleteEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowsRepositoryService {
|
||||
// クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== 'local';
|
||||
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
@ -25,7 +34,7 @@ export class WorkflowsRepositoryService {
|
||||
* @param externalId
|
||||
* @returns worktypes and active worktype id
|
||||
*/
|
||||
async getWorkflows(accountId: number): Promise<Workflow[]> {
|
||||
async getWorkflows(context: Context, accountId: number): Promise<Workflow[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const workflowRepo = entityManager.getRepository(Workflow);
|
||||
|
||||
@ -43,6 +52,7 @@ export class WorkflowsRepositoryService {
|
||||
order: {
|
||||
id: 'ASC',
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
return workflows;
|
||||
@ -59,6 +69,7 @@ export class WorkflowsRepositoryService {
|
||||
* @returns workflows
|
||||
*/
|
||||
async createtWorkflows(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
authorId: number,
|
||||
typists: WorkflowTypist[],
|
||||
@ -70,6 +81,7 @@ export class WorkflowsRepositoryService {
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
const author = await userRepo.findOne({
|
||||
where: { account_id: accountId, id: authorId, email_verified: true },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!author) {
|
||||
throw new UserNotFoundError(
|
||||
@ -82,6 +94,7 @@ export class WorkflowsRepositoryService {
|
||||
const worktypeRepo = entityManager.getRepository(Worktype);
|
||||
const worktypes = await worktypeRepo.find({
|
||||
where: { account_id: accountId, id: worktypeId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (worktypes.length === 0) {
|
||||
throw new WorktypeIdNotFoundError(
|
||||
@ -95,6 +108,7 @@ export class WorkflowsRepositoryService {
|
||||
const templateRepo = entityManager.getRepository(TemplateFile);
|
||||
const template = await templateRepo.findOne({
|
||||
where: { account_id: accountId, id: templateId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!template) {
|
||||
throw new TemplateFileNotExistError('template not found.');
|
||||
@ -111,6 +125,7 @@ export class WorkflowsRepositoryService {
|
||||
id: In(typistIds),
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (typistUsers.length !== typistIds.length) {
|
||||
throw new UserNotFoundError(
|
||||
@ -125,6 +140,7 @@ export class WorkflowsRepositoryService {
|
||||
const userGroupRepo = entityManager.getRepository(UserGroup);
|
||||
const typistGroups = await userGroupRepo.find({
|
||||
where: { account_id: accountId, id: In(groupIds) },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (typistGroups.length !== groupIds.length) {
|
||||
throw new TypistGroupNotExistError(
|
||||
@ -141,6 +157,7 @@ export class WorkflowsRepositoryService {
|
||||
author_id: authorId,
|
||||
worktype_id: worktypeId !== undefined ? worktypeId : IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (workflow.length !== 0) {
|
||||
throw new AuthorIdAndWorktypeIdPairAlreadyExistsError(
|
||||
@ -156,7 +173,13 @@ export class WorkflowsRepositoryService {
|
||||
templateId,
|
||||
);
|
||||
|
||||
await workflowRepo.save(newWorkflow);
|
||||
await insertEntity(
|
||||
Workflow,
|
||||
workflowRepo,
|
||||
newWorkflow,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ルーティング候補のデータ作成
|
||||
const workflowTypists = typists.map((typist) =>
|
||||
@ -168,7 +191,13 @@ export class WorkflowsRepositoryService {
|
||||
);
|
||||
|
||||
const workflowTypistsRepo = entityManager.getRepository(DbWorkflowTypist);
|
||||
await workflowTypistsRepo.save(workflowTypists);
|
||||
await insertEntities(
|
||||
DbWorkflowTypist,
|
||||
workflowTypistsRepo,
|
||||
workflowTypists,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -183,6 +212,7 @@ export class WorkflowsRepositoryService {
|
||||
* @returns workflow
|
||||
*/
|
||||
async updatetWorkflow(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
workflowId: number,
|
||||
authorId: number,
|
||||
@ -196,6 +226,7 @@ export class WorkflowsRepositoryService {
|
||||
// ワークフローの存在確認
|
||||
const targetWorkflow = await workflowRepo.findOne({
|
||||
where: { account_id: accountId, id: workflowId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!targetWorkflow) {
|
||||
throw new WorkflowNotFoundError(
|
||||
@ -207,6 +238,7 @@ export class WorkflowsRepositoryService {
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
const author = await userRepo.findOne({
|
||||
where: { account_id: accountId, id: authorId, email_verified: true },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!author) {
|
||||
throw new UserNotFoundError(
|
||||
@ -219,6 +251,7 @@ export class WorkflowsRepositoryService {
|
||||
const worktypeRepo = entityManager.getRepository(Worktype);
|
||||
const worktypes = await worktypeRepo.find({
|
||||
where: { account_id: accountId, id: worktypeId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (worktypes.length === 0) {
|
||||
throw new WorktypeIdNotFoundError(
|
||||
@ -232,6 +265,7 @@ export class WorkflowsRepositoryService {
|
||||
const templateRepo = entityManager.getRepository(TemplateFile);
|
||||
const template = await templateRepo.findOne({
|
||||
where: { account_id: accountId, id: templateId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!template) {
|
||||
throw new TemplateFileNotExistError(
|
||||
@ -250,6 +284,7 @@ export class WorkflowsRepositoryService {
|
||||
id: In(typistIds),
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (typistUsers.length !== typistIds.length) {
|
||||
throw new UserNotFoundError(
|
||||
@ -264,6 +299,7 @@ export class WorkflowsRepositoryService {
|
||||
const userGroupRepo = entityManager.getRepository(UserGroup);
|
||||
const typistGroups = await userGroupRepo.find({
|
||||
where: { account_id: accountId, id: In(groupIds) },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (typistGroups.length !== groupIds.length) {
|
||||
throw new TypistGroupNotExistError(
|
||||
@ -274,8 +310,18 @@ export class WorkflowsRepositoryService {
|
||||
const workflowTypistsRepo = entityManager.getRepository(DbWorkflowTypist);
|
||||
|
||||
// 既存データの削除
|
||||
await workflowTypistsRepo.delete({ workflow_id: workflowId });
|
||||
await workflowRepo.delete(workflowId);
|
||||
await deleteEntity(
|
||||
workflowTypistsRepo,
|
||||
{ workflow_id: workflowId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
workflowRepo,
|
||||
{ id: workflowId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
{
|
||||
// ワークフローの重複確認
|
||||
@ -285,6 +331,7 @@ export class WorkflowsRepositoryService {
|
||||
author_id: authorId,
|
||||
worktype_id: worktypeId !== undefined ? worktypeId : IsNull(),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (duplicateWorkflow.length !== 0) {
|
||||
throw new AuthorIdAndWorktypeIdPairAlreadyExistsError(
|
||||
@ -301,7 +348,13 @@ export class WorkflowsRepositoryService {
|
||||
templateId,
|
||||
);
|
||||
|
||||
await workflowRepo.save(newWorkflow);
|
||||
await insertEntity(
|
||||
Workflow,
|
||||
workflowRepo,
|
||||
newWorkflow,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// ルーティング候補のデータ作成
|
||||
const workflowTypists = typists.map((typist) =>
|
||||
@ -312,7 +365,13 @@ export class WorkflowsRepositoryService {
|
||||
),
|
||||
);
|
||||
|
||||
await workflowTypistsRepo.save(workflowTypists);
|
||||
await insertEntities(
|
||||
DbWorkflowTypist,
|
||||
workflowTypistsRepo,
|
||||
workflowTypists,
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -322,7 +381,11 @@ export class WorkflowsRepositoryService {
|
||||
* @param workflowId
|
||||
* @returns workflow
|
||||
*/
|
||||
async deleteWorkflow(accountId: number, workflowId: number): Promise<void> {
|
||||
async deleteWorkflow(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
workflowId: number,
|
||||
): Promise<void> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const workflowRepo = entityManager.getRepository(Workflow);
|
||||
const workflowTypistsRepo = entityManager.getRepository(DbWorkflowTypist);
|
||||
@ -330,15 +393,25 @@ export class WorkflowsRepositoryService {
|
||||
// ワークフローの存在確認
|
||||
const workflow = await workflowRepo.findOne({
|
||||
where: { account_id: accountId, id: workflowId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
if (!workflow) {
|
||||
throw new WorkflowNotFoundError(
|
||||
`workflow not found. id: ${workflowId}`,
|
||||
);
|
||||
}
|
||||
|
||||
await workflowTypistsRepo.delete({ workflow_id: workflowId });
|
||||
await workflowRepo.delete(workflowId);
|
||||
await deleteEntity(
|
||||
workflowTypistsRepo,
|
||||
{ workflow_id: workflowId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
await deleteEntity(
|
||||
workflowRepo,
|
||||
{ id: workflowId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user