Merge branch 'develop' into ccb
This commit is contained in:
commit
e44cb3b955
2
data_migration_tools/client/codegen.sh
Normal file
2
data_migration_tools/client/codegen.sh
Normal file
@ -0,0 +1,2 @@
|
||||
npx openapi-generator-cli version-manager set 7.1.0
|
||||
npx openapi-generator-cli generate -g typescript-axios -i /app/data_migration_tools/server/src/api/odms/openapi.json -o /app/data_migration_tools/client/src/api/
|
||||
7
data_migration_tools/client/openapitools.json
Normal file
7
data_migration_tools/client/openapitools.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||
"spaces": 2,
|
||||
"generator-cli": {
|
||||
"version": "7.1.0"
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import TopPage from "./pages/topPage";
|
||||
import DeletePage from "./pages/deletePage";
|
||||
|
||||
const AppRouter: React.FC = () => (
|
||||
<Routes>
|
||||
<Route path="/" element={<div />} />
|
||||
<Route path="/" element={<TopPage />} />
|
||||
<Route path="/delete" element={<DeletePage />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
|
||||
4
data_migration_tools/client/src/api/.gitignore
vendored
Normal file
4
data_migration_tools/client/src/api/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
wwwroot/*.js
|
||||
node_modules
|
||||
typings
|
||||
dist
|
||||
1
data_migration_tools/client/src/api/.npmignore
Normal file
1
data_migration_tools/client/src/api/.npmignore
Normal file
@ -0,0 +1 @@
|
||||
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
|
||||
@ -0,0 +1,23 @@
|
||||
# OpenAPI Generator Ignore
|
||||
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||
|
||||
# Use this file to prevent files from being overwritten by the generator.
|
||||
# The patterns follow closely to .gitignore or .dockerignore.
|
||||
|
||||
# As an example, the C# client generator defines ApiClient.cs.
|
||||
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||
#ApiClient.cs
|
||||
|
||||
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||
#foo/*/qux
|
||||
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||
|
||||
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||
#foo/**/qux
|
||||
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||
|
||||
# You can also negate patterns with an exclamation (!).
|
||||
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||
#docs/*.md
|
||||
# Then explicitly reverse the ignore rule for a single file:
|
||||
#!docs/README.md
|
||||
@ -0,0 +1,9 @@
|
||||
.gitignore
|
||||
.npmignore
|
||||
.openapi-generator-ignore
|
||||
api.ts
|
||||
base.ts
|
||||
common.ts
|
||||
configuration.ts
|
||||
git_push.sh
|
||||
index.ts
|
||||
@ -0,0 +1 @@
|
||||
7.1.0
|
||||
146
data_migration_tools/client/src/api/api.ts
Normal file
146
data_migration_tools/client/src/api/api.ts
Normal file
@ -0,0 +1,146 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* ODMSOpenAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from './configuration';
|
||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common';
|
||||
import type { RequestArgs } from './base';
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, BaseAPI, RequiredError, operationServerMap } from './base';
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ErrorResponse
|
||||
*/
|
||||
export interface ErrorResponse {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ErrorResponse
|
||||
*/
|
||||
'message': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ErrorResponse
|
||||
*/
|
||||
'code': string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DeleteApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
export const DeleteApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* すべてのデータを削除します
|
||||
* @summary
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
deleteData: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/delete`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DeleteApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
export const DeleteApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = DeleteApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* すべてのデータを削除します
|
||||
* @summary
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async deleteData(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteData(options);
|
||||
const index = configuration?.serverIndex ?? 0;
|
||||
const operationBasePath = operationServerMap['DeleteApi.deleteData']?.[index]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DeleteApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
export const DeleteApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = DeleteApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* すべてのデータを削除します
|
||||
* @summary
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
deleteData(options?: any): AxiosPromise<object> {
|
||||
return localVarFp.deleteData(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* DeleteApi - object-oriented interface
|
||||
* @export
|
||||
* @class DeleteApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class DeleteApi extends BaseAPI {
|
||||
/**
|
||||
* すべてのデータを削除します
|
||||
* @summary
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof DeleteApi
|
||||
*/
|
||||
public deleteData(options?: AxiosRequestConfig) {
|
||||
return DeleteApiFp(this.configuration).deleteData(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
86
data_migration_tools/client/src/api/base.ts
Normal file
86
data_migration_tools/client/src/api/base.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* ODMSOpenAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from './configuration';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
|
||||
export const BASE_PATH = "http://localhost".replace(/\/+$/, "");
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const COLLECTION_FORMATS = {
|
||||
csv: ",",
|
||||
ssv: " ",
|
||||
tsv: "\t",
|
||||
pipes: "|",
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface RequestArgs
|
||||
*/
|
||||
export interface RequestArgs {
|
||||
url: string;
|
||||
options: AxiosRequestConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class BaseAPI
|
||||
*/
|
||||
export class BaseAPI {
|
||||
protected configuration: Configuration | undefined;
|
||||
|
||||
constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
|
||||
if (configuration) {
|
||||
this.configuration = configuration;
|
||||
this.basePath = configuration.basePath ?? basePath;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @class RequiredError
|
||||
* @extends {Error}
|
||||
*/
|
||||
export class RequiredError extends Error {
|
||||
constructor(public field: string, msg?: string) {
|
||||
super(msg);
|
||||
this.name = "RequiredError"
|
||||
}
|
||||
}
|
||||
|
||||
interface ServerMap {
|
||||
[key: string]: {
|
||||
url: string,
|
||||
description: string,
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const operationServerMap: ServerMap = {
|
||||
}
|
||||
150
data_migration_tools/client/src/api/common.ts
Normal file
150
data_migration_tools/client/src/api/common.ts
Normal file
@ -0,0 +1,150 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* ODMSOpenAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from "./configuration";
|
||||
import type { RequestArgs } from "./base";
|
||||
import type { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import { RequiredError } from "./base";
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const DUMMY_BASE_URL = 'https://example.com'
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws {RequiredError}
|
||||
* @export
|
||||
*/
|
||||
export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) {
|
||||
if (paramValue === null || paramValue === undefined) {
|
||||
throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) {
|
||||
if (configuration && configuration.apiKey) {
|
||||
const localVarApiKeyValue = typeof configuration.apiKey === 'function'
|
||||
? await configuration.apiKey(keyParamName)
|
||||
: await configuration.apiKey;
|
||||
object[keyParamName] = localVarApiKeyValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setBasicAuthToObject = function (object: any, configuration?: Configuration) {
|
||||
if (configuration && (configuration.username || configuration.password)) {
|
||||
object["auth"] = { username: configuration.username, password: configuration.password };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) {
|
||||
if (configuration && configuration.accessToken) {
|
||||
const accessToken = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken()
|
||||
: await configuration.accessToken;
|
||||
object["Authorization"] = "Bearer " + accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) {
|
||||
if (configuration && configuration.accessToken) {
|
||||
const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
|
||||
? await configuration.accessToken(name, scopes)
|
||||
: await configuration.accessToken;
|
||||
object["Authorization"] = "Bearer " + localVarAccessTokenValue;
|
||||
}
|
||||
}
|
||||
|
||||
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
|
||||
if (parameter == null) return;
|
||||
if (typeof parameter === "object") {
|
||||
if (Array.isArray(parameter)) {
|
||||
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
|
||||
}
|
||||
else {
|
||||
Object.keys(parameter).forEach(currentKey =>
|
||||
setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (urlSearchParams.has(key)) {
|
||||
urlSearchParams.append(key, parameter);
|
||||
}
|
||||
else {
|
||||
urlSearchParams.set(key, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const setSearchParams = function (url: URL, ...objects: any[]) {
|
||||
const searchParams = new URLSearchParams(url.search);
|
||||
setFlattenedQueryParams(searchParams, objects);
|
||||
url.search = searchParams.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
|
||||
const nonString = typeof value !== 'string';
|
||||
const needsSerialization = nonString && configuration && configuration.isJsonMime
|
||||
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
|
||||
: nonString;
|
||||
return needsSerialization
|
||||
? JSON.stringify(value !== undefined ? value : {})
|
||||
: (value || "");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const toPathString = function (url: URL) {
|
||||
return url.pathname + url.search + url.hash
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
*/
|
||||
export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) {
|
||||
return <T = unknown, R = AxiosResponse<T>>(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
|
||||
const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || axios.defaults.baseURL || basePath) + axiosArgs.url};
|
||||
return axios.request<T, R>(axiosRequestArgs);
|
||||
};
|
||||
}
|
||||
110
data_migration_tools/client/src/api/configuration.ts
Normal file
110
data_migration_tools/client/src/api/configuration.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* ODMSOpenAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export interface ConfigurationParameters {
|
||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||
username?: string;
|
||||
password?: string;
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||
basePath?: string;
|
||||
serverIndex?: number;
|
||||
baseOptions?: any;
|
||||
formDataCtor?: new () => any;
|
||||
}
|
||||
|
||||
export class Configuration {
|
||||
/**
|
||||
* parameter for apiKey security
|
||||
* @param name security name
|
||||
* @memberof Configuration
|
||||
*/
|
||||
apiKey?: string | Promise<string> | ((name: string) => string) | ((name: string) => Promise<string>);
|
||||
/**
|
||||
* parameter for basic security
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
username?: string;
|
||||
/**
|
||||
* parameter for basic security
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
password?: string;
|
||||
/**
|
||||
* parameter for oauth2 security
|
||||
* @param name security name
|
||||
* @param scopes oauth2 scope
|
||||
* @memberof Configuration
|
||||
*/
|
||||
accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise<string>);
|
||||
/**
|
||||
* override base path
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
basePath?: string;
|
||||
/**
|
||||
* override server index
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
serverIndex?: number;
|
||||
/**
|
||||
* base options for axios calls
|
||||
*
|
||||
* @type {any}
|
||||
* @memberof Configuration
|
||||
*/
|
||||
baseOptions?: any;
|
||||
/**
|
||||
* The FormData constructor that will be used to create multipart form data
|
||||
* requests. You can inject this here so that execution environments that
|
||||
* do not support the FormData class can still run the generated client.
|
||||
*
|
||||
* @type {new () => FormData}
|
||||
*/
|
||||
formDataCtor?: new () => any;
|
||||
|
||||
constructor(param: ConfigurationParameters = {}) {
|
||||
this.apiKey = param.apiKey;
|
||||
this.username = param.username;
|
||||
this.password = param.password;
|
||||
this.accessToken = param.accessToken;
|
||||
this.basePath = param.basePath;
|
||||
this.serverIndex = param.serverIndex;
|
||||
this.baseOptions = param.baseOptions;
|
||||
this.formDataCtor = param.formDataCtor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given MIME is a JSON MIME.
|
||||
* JSON MIME examples:
|
||||
* application/json
|
||||
* application/json; charset=UTF8
|
||||
* APPLICATION/JSON
|
||||
* application/vnd.company+json
|
||||
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
||||
* @return True if the given MIME is JSON, false otherwise.
|
||||
*/
|
||||
public isJsonMime(mime: string): boolean {
|
||||
const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
|
||||
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
|
||||
}
|
||||
}
|
||||
57
data_migration_tools/client/src/api/git_push.sh
Normal file
57
data_migration_tools/client/src/api/git_push.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/
|
||||
#
|
||||
# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com"
|
||||
|
||||
git_user_id=$1
|
||||
git_repo_id=$2
|
||||
release_note=$3
|
||||
git_host=$4
|
||||
|
||||
if [ "$git_host" = "" ]; then
|
||||
git_host="github.com"
|
||||
echo "[INFO] No command line input provided. Set \$git_host to $git_host"
|
||||
fi
|
||||
|
||||
if [ "$git_user_id" = "" ]; then
|
||||
git_user_id="GIT_USER_ID"
|
||||
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
|
||||
fi
|
||||
|
||||
if [ "$git_repo_id" = "" ]; then
|
||||
git_repo_id="GIT_REPO_ID"
|
||||
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
|
||||
fi
|
||||
|
||||
if [ "$release_note" = "" ]; then
|
||||
release_note="Minor update"
|
||||
echo "[INFO] No command line input provided. Set \$release_note to $release_note"
|
||||
fi
|
||||
|
||||
# Initialize the local directory as a Git repository
|
||||
git init
|
||||
|
||||
# Adds the files in the local repository and stages them for commit.
|
||||
git add .
|
||||
|
||||
# Commits the tracked changes and prepares them to be pushed to a remote repository.
|
||||
git commit -m "$release_note"
|
||||
|
||||
# Sets the new remote
|
||||
git_remote=$(git remote)
|
||||
if [ "$git_remote" = "" ]; then # git remote not defined
|
||||
|
||||
if [ "$GIT_TOKEN" = "" ]; then
|
||||
echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment."
|
||||
git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git
|
||||
else
|
||||
git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
git pull origin master
|
||||
|
||||
# Pushes (Forces) the changes in the local repository up to the remote repository
|
||||
echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git"
|
||||
git push origin master 2>&1 | grep -v 'To https'
|
||||
18
data_migration_tools/client/src/api/index.ts
Normal file
18
data_migration_tools/client/src/api/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* ODMSOpenAPI
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
export * from "./api";
|
||||
export * from "./configuration";
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
|
||||
import auth from "features/auth/authSlice";
|
||||
import ui from "features/ui/uiSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
auth,
|
||||
ui,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
17
data_migration_tools/client/src/common/errors/code.ts
Normal file
17
data_migration_tools/client/src/common/errors/code.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
エラーコード作成方針
|
||||
E+6桁(数字)で構成する。
|
||||
- 1~2桁目の値は種類(業務エラー、システムエラー...)
|
||||
- 3~4桁目の値は原因箇所(トークン、DB、...)
|
||||
- 5~6桁目の値は任意の重複しない値
|
||||
ex)
|
||||
E00XXXX : システムエラー(通信エラーやDB接続失敗など)
|
||||
E01XXXX : 業務エラー
|
||||
EXX00XX : 内部エラー(内部プログラムのエラー)
|
||||
EXX01XX : トークンエラー(トークン認証関連)
|
||||
EXX02XX : DBエラー(DB関連)
|
||||
EXX03XX : ADB2Cエラー(DB関連)
|
||||
*/
|
||||
export const errorCodes = [
|
||||
"E009999", // 汎用エラー
|
||||
] as const;
|
||||
3
data_migration_tools/client/src/common/errors/index.ts
Normal file
3
data_migration_tools/client/src/common/errors/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./code";
|
||||
export * from "./types";
|
||||
export * from "./utils";
|
||||
9
data_migration_tools/client/src/common/errors/types.ts
Normal file
9
data_migration_tools/client/src/common/errors/types.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { errorCodes } from "./code";
|
||||
|
||||
export type ErrorObject = {
|
||||
message: string;
|
||||
code: ErrorCodeType;
|
||||
statusCode?: number;
|
||||
};
|
||||
|
||||
export type ErrorCodeType = (typeof errorCodes)[number];
|
||||
101
data_migration_tools/client/src/common/errors/utils.ts
Normal file
101
data_migration_tools/client/src/common/errors/utils.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { AxiosError } from "axios";
|
||||
import { isError } from "lodash";
|
||||
import { ErrorResponse } from "../../api";
|
||||
import { errorCodes } from "./code";
|
||||
import { ErrorCodeType, ErrorObject } from "./types";
|
||||
|
||||
export const createErrorObject = (error: unknown): ErrorObject => {
|
||||
// 最低限通常のエラーかを判定
|
||||
// Error以外のものがthrowされた場合
|
||||
// 基本的にないはずだがプログラム上あるので拾う
|
||||
if (!isError(error)) {
|
||||
return {
|
||||
message: "not error type.",
|
||||
code: "E009999",
|
||||
};
|
||||
}
|
||||
|
||||
// Axiosエラー 通信してのエラーであるかを判定
|
||||
if (!isAxiosError(error)) {
|
||||
return {
|
||||
message: "not axios error.",
|
||||
code: "E009999",
|
||||
};
|
||||
}
|
||||
|
||||
const errorResponse = error.response;
|
||||
if (!errorResponse) {
|
||||
return {
|
||||
message: error.message,
|
||||
code: "E009999",
|
||||
statusCode: errorResponse,
|
||||
};
|
||||
}
|
||||
|
||||
const { data } = errorResponse;
|
||||
|
||||
// 想定しているエラーレスポンスの型か判定
|
||||
if (!isErrorResponse(data)) {
|
||||
return {
|
||||
message: error.message,
|
||||
code: "E009999",
|
||||
statusCode: errorResponse.status,
|
||||
};
|
||||
}
|
||||
|
||||
const { message, code } = data;
|
||||
|
||||
// 想定しているエラーコードかを判定
|
||||
if (!isErrorCode(code)) {
|
||||
return {
|
||||
message,
|
||||
code: "E009999",
|
||||
statusCode: errorResponse.status,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
message,
|
||||
code,
|
||||
statusCode: errorResponse.status,
|
||||
};
|
||||
};
|
||||
|
||||
const isAxiosError = (e: unknown): e is AxiosError => {
|
||||
const error = e as AxiosError;
|
||||
return error?.isAxiosError ?? false;
|
||||
};
|
||||
|
||||
const isErrorResponse = (error: unknown): error is ErrorResponse => {
|
||||
const errorResponse = error as ErrorResponse;
|
||||
if (
|
||||
errorResponse === undefined ||
|
||||
errorResponse.message === undefined ||
|
||||
errorResponse.code === undefined
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const isErrorCode = (errorCode: string): errorCode is ErrorCodeType =>
|
||||
errorCodes.includes(errorCode as ErrorCodeType);
|
||||
|
||||
export const isErrorObject = (
|
||||
data: unknown
|
||||
): data is { error: ErrorObject } => {
|
||||
if (
|
||||
data &&
|
||||
typeof data === "object" &&
|
||||
"error" in data &&
|
||||
typeof (data as { error: ErrorObject }).error === "object" &&
|
||||
typeof (data as { error: ErrorObject }).error.message === "string" &&
|
||||
typeof (data as { error: ErrorObject }).error.code === "string" &&
|
||||
(typeof (data as { error: ErrorObject }).error.statusCode === "number" ||
|
||||
(data as { error: ErrorObject }).error.statusCode === undefined)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
export const getBasePath = () => {
|
||||
if (import.meta.env.VITE_STAGE === "local") {
|
||||
return "http://localhost:8180";
|
||||
return "http://localhost:8280";
|
||||
}
|
||||
return window.location.origin;
|
||||
};
|
||||
|
||||
15
data_migration_tools/client/src/features/auth/authSlice.ts
Normal file
15
data_migration_tools/client/src/features/auth/authSlice.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import type { AuthState } from "./state";
|
||||
import { initialConfig } from "./utils";
|
||||
|
||||
const initialState: AuthState = {
|
||||
configuration: initialConfig(),
|
||||
};
|
||||
|
||||
export const authSlice = createSlice({
|
||||
name: "auth",
|
||||
initialState,
|
||||
reducers: {},
|
||||
});
|
||||
|
||||
export default authSlice.reducer;
|
||||
3
data_migration_tools/client/src/features/auth/index.ts
Normal file
3
data_migration_tools/client/src/features/auth/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./authSlice";
|
||||
export * from "./state";
|
||||
export * from "./utils";
|
||||
5
data_migration_tools/client/src/features/auth/state.ts
Normal file
5
data_migration_tools/client/src/features/auth/state.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { ConfigurationParameters } from "../../api";
|
||||
|
||||
export interface AuthState {
|
||||
configuration: ConfigurationParameters;
|
||||
}
|
||||
13
data_migration_tools/client/src/features/auth/utils.ts
Normal file
13
data_migration_tools/client/src/features/auth/utils.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ConfigurationParameters } from "../../api";
|
||||
|
||||
// 初期状態のAPI Config
|
||||
export const initialConfig = (): ConfigurationParameters => {
|
||||
const config: ConfigurationParameters = {};
|
||||
if (import.meta.env.VITE_STAGE === "local") {
|
||||
config.basePath = "http://localhost:8280";
|
||||
} else {
|
||||
config.basePath = `${window.location.origin}/dictation/api`;
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
@ -0,0 +1,25 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { DeleteState } from "./state";
|
||||
import { deleteDataAsync } from "./operations";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
||||
const initialState: DeleteState = {};
|
||||
|
||||
export const deleteSlice = createSlice({
|
||||
name: "detete",
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(deleteDataAsync.pending, () => {
|
||||
/* Empty Object */
|
||||
});
|
||||
builder.addCase(deleteDataAsync.fulfilled, () => {
|
||||
/* Empty Object */
|
||||
});
|
||||
builder.addCase(deleteDataAsync.rejected, () => {
|
||||
/* Empty Object */
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default deleteSlice.reducer;
|
||||
3
data_migration_tools/client/src/features/delete/index.ts
Normal file
3
data_migration_tools/client/src/features/delete/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./state";
|
||||
export * from "./deleteSlice";
|
||||
export * from "./operations";
|
||||
@ -0,0 +1,47 @@
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import { openSnackbar } from "../ui/uiSlice";
|
||||
import type { RootState } from "../../app/store";
|
||||
import { ErrorObject, createErrorObject } from "../../common/errors";
|
||||
import { DeleteApi } from "../../api/api";
|
||||
import { Configuration } from "../../api/configuration";
|
||||
|
||||
export const deleteDataAsync = createAsyncThunk<
|
||||
{
|
||||
/* Empty Object */
|
||||
},
|
||||
void,
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("delete/deleteDataAsync", async (args, thunkApi) => {
|
||||
// apiのConfigurationを取得する
|
||||
const { getState } = thunkApi;
|
||||
const state = getState() as RootState;
|
||||
const { configuration } = state.auth;
|
||||
const config = new Configuration(configuration);
|
||||
const deleteApi = new DeleteApi(config);
|
||||
|
||||
try {
|
||||
await deleteApi.deleteData();
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: "削除しました。",
|
||||
})
|
||||
);
|
||||
return {};
|
||||
} catch (e) {
|
||||
const error = createErrorObject(e);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: "削除に失敗しました。",
|
||||
})
|
||||
);
|
||||
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
2
data_migration_tools/client/src/features/delete/state.ts
Normal file
2
data_migration_tools/client/src/features/delete/state.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface DeleteState {}
|
||||
2
data_migration_tools/client/src/features/ui/constants.ts
Normal file
2
data_migration_tools/client/src/features/ui/constants.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// 標準のスナックバー表示時間(ミリ秒)
|
||||
export const DEFAULT_SNACKBAR_DURATION = 3000;
|
||||
5
data_migration_tools/client/src/features/ui/index.ts
Normal file
5
data_migration_tools/client/src/features/ui/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from "./constants";
|
||||
export * from "./selectors";
|
||||
export * from "./state";
|
||||
export * from "./uiSlice";
|
||||
export * from "./types";
|
||||
15
data_migration_tools/client/src/features/ui/selectors.ts
Normal file
15
data_migration_tools/client/src/features/ui/selectors.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { RootState } from "app/store";
|
||||
import { SnackbarLevel } from "./types";
|
||||
|
||||
export const selectSnackber = (
|
||||
state: RootState
|
||||
): {
|
||||
isOpen: boolean;
|
||||
level: SnackbarLevel;
|
||||
message: string;
|
||||
duration?: number;
|
||||
} => {
|
||||
const { isOpen, level, message, duration } = state.ui;
|
||||
|
||||
return { isOpen, level, message, duration };
|
||||
};
|
||||
8
data_migration_tools/client/src/features/ui/state.ts
Normal file
8
data_migration_tools/client/src/features/ui/state.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { SnackbarLevel } from "./types";
|
||||
|
||||
export interface UIState {
|
||||
isOpen: boolean;
|
||||
level: SnackbarLevel;
|
||||
message: string;
|
||||
duration?: number;
|
||||
}
|
||||
1
data_migration_tools/client/src/features/ui/types.ts
Normal file
1
data_migration_tools/client/src/features/ui/types.ts
Normal file
@ -0,0 +1 @@
|
||||
export type SnackbarLevel = "info" | "error";
|
||||
38
data_migration_tools/client/src/features/ui/uiSlice.ts
Normal file
38
data_migration_tools/client/src/features/ui/uiSlice.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { SnackbarLevel } from "./types";
|
||||
import { UIState } from "./state";
|
||||
import { DEFAULT_SNACKBAR_DURATION } from "./constants";
|
||||
|
||||
const initialState: UIState = {
|
||||
isOpen: false,
|
||||
level: "error",
|
||||
message: "",
|
||||
};
|
||||
|
||||
export const uiSlice = createSlice({
|
||||
name: "ui",
|
||||
initialState,
|
||||
reducers: {
|
||||
openSnackbar: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
level: SnackbarLevel;
|
||||
message: string;
|
||||
duration?: number;
|
||||
}>
|
||||
) => {
|
||||
const { level, message, duration } = action.payload;
|
||||
state.isOpen = true;
|
||||
state.level = level;
|
||||
state.message = message;
|
||||
state.duration =
|
||||
level === "error" ? undefined : duration ?? DEFAULT_SNACKBAR_DURATION;
|
||||
},
|
||||
closeSnackbar: (state) => {
|
||||
state.isOpen = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
export const { openSnackbar, closeSnackbar } = uiSlice.actions;
|
||||
|
||||
export default uiSlice.reducer;
|
||||
32
data_migration_tools/client/src/pages/deletePage/index.tsx
Normal file
32
data_migration_tools/client/src/pages/deletePage/index.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { AppDispatch } from "app/store";
|
||||
import { deleteDataAsync } from "features/delete";
|
||||
import React, { useCallback } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const DeletePage = (): JSX.Element => {
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
|
||||
const onDelete = useCallback(() => {
|
||||
if (
|
||||
/* eslint-disable-next-line no-alert */
|
||||
!window.confirm("本当に削除しますか?")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
dispatch(deleteDataAsync());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>データ削除ツール</p>
|
||||
<button type="button" onClick={onDelete}>
|
||||
削除
|
||||
</button>
|
||||
<br />
|
||||
<Link to="/">return to TopPage</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletePage;
|
||||
15
data_migration_tools/client/src/pages/topPage/index.tsx
Normal file
15
data_migration_tools/client/src/pages/topPage/index.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const TopPage = (): JSX.Element => {
|
||||
console.log("DeletePage");
|
||||
return (
|
||||
<div>
|
||||
<Link to="/delete">データ削除ツール</Link>
|
||||
<br />
|
||||
<Link to="/">return to TopPage</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopPage;
|
||||
@ -14,5 +14,11 @@ services:
|
||||
- "8280"
|
||||
environment:
|
||||
- CHOKIDAR_USEPOLLING=true
|
||||
networks:
|
||||
- external
|
||||
networks:
|
||||
external:
|
||||
name: omds_network
|
||||
external: true
|
||||
volumes:
|
||||
data_migration_tools_server_node_modules:
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
DB_HOST=omds-mysql
|
||||
DB_PORT=3306
|
||||
DB_NAME=omds
|
||||
DB_USERNAME=omdsdbuser
|
||||
DB_PASSWORD=omdsdbpass
|
||||
2366
data_migration_tools/server/package-lock.json
generated
2366
data_migration_tools/server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,10 +19,13 @@
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"apigen": "ts-node src/api/generate.ts && prettier --write \"src/api/odms/*.json\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/identity": "^4.0.1",
|
||||
"@azure/storage-blob": "^12.14.0",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
"@nestjs/common": "^9.3.9",
|
||||
"@nestjs/config": "^2.3.1",
|
||||
"@nestjs/core": "^9.3.9",
|
||||
@ -30,6 +33,7 @@
|
||||
"@nestjs/serve-static": "^3.0.1",
|
||||
"@nestjs/swagger": "^6.2.1",
|
||||
"@nestjs/testing": "^9.3.9",
|
||||
"@nestjs/typeorm": "^10.0.2",
|
||||
"@openapitools/openapi-generator-cli": "^2.5.2",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"axios": "^1.3.4",
|
||||
@ -37,6 +41,7 @@
|
||||
"class-validator": "^0.14.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.9.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.0",
|
||||
"swagger-cli": "^4.0.4"
|
||||
|
||||
25
data_migration_tools/server/src/api/generate.ts
Normal file
25
data_migration_tools/server/src/api/generate.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
||||
import { AppModule } from "../app.module";
|
||||
import { promises as fs } from "fs";
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
async function bootstrap(): Promise<void> {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
preview: true,
|
||||
});
|
||||
|
||||
const options = new DocumentBuilder()
|
||||
.setTitle("ODMSOpenAPI")
|
||||
.setVersion("1.0.0")
|
||||
.addBearerAuth({
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
bearerFormat: "JWT",
|
||||
})
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, options);
|
||||
await fs.writeFile(
|
||||
"src/api/odms/openapi.json",
|
||||
JSON.stringify(document, null, 0)
|
||||
);
|
||||
}
|
||||
bootstrap();
|
||||
56
data_migration_tools/server/src/api/odms/openapi.json
Normal file
56
data_migration_tools/server/src/api/odms/openapi.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"paths": {
|
||||
"/delete": {
|
||||
"post": {
|
||||
"operationId": "deleteData",
|
||||
"summary": "",
|
||||
"description": "すべてのデータを削除します",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/DeleteResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["delete"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"title": "ODMSOpenAPI",
|
||||
"description": "",
|
||||
"version": "1.0.0",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [],
|
||||
"servers": [],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"bearer": { "scheme": "bearer", "bearerFormat": "JWT", "type": "http" }
|
||||
},
|
||||
"schemas": {
|
||||
"DeleteResponse": { "type": "object", "properties": {} },
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": { "type": "string" },
|
||||
"code": { "type": "string" }
|
||||
},
|
||||
"required": ["message", "code"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,15 @@
|
||||
import { MiddlewareConsumer, Module } from "@nestjs/common";
|
||||
import { ServeStaticModule } from "@nestjs/serve-static";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { join } from "path";
|
||||
import { LoggerMiddleware } from "./common/loggerMiddleware";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { DeleteModule } from "./features/delete/delete.module";
|
||||
import { AdB2cModule } from "./gateways/adb2c/adb2c.module";
|
||||
import { DeleteRepositoryModule } from "./repositories/delete/delete.repository.module";
|
||||
import { DeleteController } from "./features/delete/delete.controller";
|
||||
import { DeleteService } from "./features/delete/delete.service";
|
||||
import { BlobstorageModule } from "./gateways/blobstorage/blobstorage.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -13,9 +20,27 @@ import { LoggerMiddleware } from "./common/loggerMiddleware";
|
||||
envFilePath: [".env.local", ".env"],
|
||||
isGlobal: true,
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
type: "mysql",
|
||||
host: configService.get("DB_HOST"),
|
||||
port: configService.get("DB_PORT"),
|
||||
username: configService.get("DB_USERNAME"),
|
||||
password: configService.get("DB_PASSWORD"),
|
||||
database: configService.get("DB_NAME"),
|
||||
autoLoadEntities: true, // forFeature()で登録されたEntityを自動的にロード
|
||||
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
DeleteModule,
|
||||
AdB2cModule,
|
||||
BlobstorageModule,
|
||||
DeleteRepositoryModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
controllers: [DeleteController],
|
||||
providers: [DeleteService],
|
||||
})
|
||||
export class AppModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
import { bigintTransformer } from '.';
|
||||
|
||||
describe('bigintTransformer', () => {
|
||||
describe('to', () => {
|
||||
it('number型を整数を表す文字列に変換できる', () => {
|
||||
expect(bigintTransformer.to(0)).toBe('0');
|
||||
expect(bigintTransformer.to(1)).toBe('1');
|
||||
expect(bigintTransformer.to(1234567890)).toBe('1234567890');
|
||||
expect(bigintTransformer.to(9007199254740991)).toBe('9007199254740991');
|
||||
expect(bigintTransformer.to(-1)).toBe('-1');
|
||||
});
|
||||
it('少数点以下がある場合はエラーとなる', () => {
|
||||
expect(() => bigintTransformer.to(1.1)).toThrowError(
|
||||
'1.1 is not integer.',
|
||||
);
|
||||
});
|
||||
it('Number.MAX_SAFE_INTEGERを超える値を変換しようとするとエラーになる', () => {
|
||||
expect(() => bigintTransformer.to(9007199254740992)).toThrowError(
|
||||
'value is greater than 9007199254740991.',
|
||||
);
|
||||
expect(() => bigintTransformer.to(9223372036854775807)).toThrowError(
|
||||
'value is greater than 9007199254740991.',
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('from', () => {
|
||||
it('bigint型の文字列をnumber型に変換できる', () => {
|
||||
expect(bigintTransformer.from('0')).toBe(0);
|
||||
expect(bigintTransformer.from('1')).toBe(1);
|
||||
expect(bigintTransformer.from('1234567890')).toBe(1234567890);
|
||||
expect(bigintTransformer.from('-1')).toBe(-1);
|
||||
});
|
||||
it('Number.MAX_SAFE_INTEGERを超える値を変換しようとするとエラーになる', () => {
|
||||
expect(() => bigintTransformer.from('9007199254740992')).toThrowError(
|
||||
'9007199254740992 is greater than 9007199254740991.',
|
||||
);
|
||||
expect(() => bigintTransformer.from('9223372036854775807')).toThrowError(
|
||||
'9223372036854775807 is greater than 9007199254740991.',
|
||||
);
|
||||
});
|
||||
it('number型の場合はそのまま返す', () => {
|
||||
expect(bigintTransformer.from(0)).toBe(0);
|
||||
expect(bigintTransformer.from(1)).toBe(1);
|
||||
expect(bigintTransformer.from(1234567890)).toBe(1234567890);
|
||||
expect(bigintTransformer.from(-1)).toBe(-1);
|
||||
});
|
||||
it('nullの場合はそのまま返す', () => {
|
||||
expect(bigintTransformer.from(null)).toBe(null);
|
||||
});
|
||||
it('number型に変換できない場合はエラーとなる', () => {
|
||||
expect(() => bigintTransformer.from('a')).toThrowError('a is not int.');
|
||||
expect(() => bigintTransformer.from('')).toThrowError(' is not int.');
|
||||
expect(() => bigintTransformer.from(undefined)).toThrowError(
|
||||
'undefined is not string.',
|
||||
);
|
||||
expect(() => bigintTransformer.from({})).toThrowError(
|
||||
'[object Object] is not string.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
57
data_migration_tools/server/src/common/entity/index.ts
Normal file
57
data_migration_tools/server/src/common/entity/index.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { ValueTransformer } from 'typeorm';
|
||||
|
||||
// DBのbigint型をnumber型に変換するためのtransformer
|
||||
// DBのBigInt型をそのまま扱うと、JSのNumber型の最大値を超えると誤差が発生するため、本来はNumber型に変換すべきではないが、
|
||||
// 影響範囲を最小限に抑えるため、Number型に変換する。使用するのはAutoIncrementされるIDのみの想定のため、
|
||||
// Number.MAX_SAFE_INTEGERより大きい値は現実的には発生しない想定で変換する。
|
||||
export const bigintTransformer: ValueTransformer = {
|
||||
from: (value: any): number | null => {
|
||||
// valueがnullであればそのまま返す
|
||||
if (value === null) {
|
||||
return value;
|
||||
}
|
||||
// valueがnumber型かどうかを判定
|
||||
// 利用DBによってはbigint型であってもnumber型で返ってくる場合があるため、number型の場合はそのまま返す(sqliteの場合)
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
// valueが文字列かどうかを判定
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(`${value} is not string.`);
|
||||
}
|
||||
// 数値に変換可能な文字列かどうかを判定
|
||||
if (Number.isNaN(parseInt(value))) {
|
||||
throw new Error(`${value} is not int.`);
|
||||
}
|
||||
|
||||
// 文字列ならbigintに変換
|
||||
// valueが整数でない場合は値が丸められてしまうが、TypeORMのEntityの定義上、整数を表す文字列以外はありえないため、少数点は考慮しない
|
||||
const bigIntValue = BigInt(value);
|
||||
// bigIntValueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
|
||||
if (bigIntValue > Number.MAX_SAFE_INTEGER) {
|
||||
throw new Error(`${value} is greater than ${Number.MAX_SAFE_INTEGER}.`);
|
||||
}
|
||||
// number型で表現できる整数であればnumber型に変換して返す
|
||||
return Number(bigIntValue);
|
||||
},
|
||||
to: (value: any): string | null | undefined => {
|
||||
// valueがnullまたはundefinedであればそのまま返す
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
// valueがnumber型かどうかを判定
|
||||
if (typeof value !== 'number') {
|
||||
throw new Error(`${value} is not number.`);
|
||||
}
|
||||
|
||||
// valueがNumber.MAX_SAFE_INTEGERより大きいかどうかを判定
|
||||
if (value > Number.MAX_SAFE_INTEGER) {
|
||||
throw new Error(`value is greater than ${Number.MAX_SAFE_INTEGER}.`);
|
||||
}
|
||||
// valueが整数かどうかを判定
|
||||
if (!Number.isInteger(value)) {
|
||||
throw new Error(`${value} is not integer.`);
|
||||
}
|
||||
return value.toString();
|
||||
},
|
||||
};
|
||||
17
data_migration_tools/server/src/common/errors/code.ts
Normal file
17
data_migration_tools/server/src/common/errors/code.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
エラーコード作成方針
|
||||
E+6桁(数字)で構成する。
|
||||
- 1~2桁目の値は種類(業務エラー、システムエラー...)
|
||||
- 3~4桁目の値は原因箇所(トークン、DB、...)
|
||||
- 5~6桁目の値は任意の重複しない値
|
||||
ex)
|
||||
E00XXXX : システムエラー(通信エラーやDB接続失敗など)
|
||||
E01XXXX : 業務エラー
|
||||
EXX00XX : 内部エラー(内部プログラムのエラー)
|
||||
EXX01XX : トークンエラー(トークン認証関連)
|
||||
EXX02XX : DBエラー(DB関連)
|
||||
EXX03XX : ADB2Cエラー(DB関連)
|
||||
*/
|
||||
export const ErrorCodes = [
|
||||
"E009999", // 汎用エラー
|
||||
] as const;
|
||||
@ -0,0 +1,10 @@
|
||||
import { errors } from "./message";
|
||||
import { ErrorCodeType, ErrorResponse } from "./types/types";
|
||||
|
||||
export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => {
|
||||
const msg = errors[errorcode];
|
||||
return {
|
||||
code: errorcode,
|
||||
message: msg,
|
||||
};
|
||||
};
|
||||
6
data_migration_tools/server/src/common/errors/message.ts
Normal file
6
data_migration_tools/server/src/common/errors/message.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { Errors } from "./types/types";
|
||||
|
||||
// エラーコードとメッセージ対応表
|
||||
export const errors: Errors = {
|
||||
E009999: "Internal Server Error.",
|
||||
};
|
||||
15
data_migration_tools/server/src/common/errors/types/types.ts
Normal file
15
data_migration_tools/server/src/common/errors/types/types.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { ErrorCodes } from '../code';
|
||||
|
||||
export class ErrorResponse {
|
||||
@ApiProperty()
|
||||
message: string;
|
||||
@ApiProperty()
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type ErrorCodeType = (typeof ErrorCodes)[number];
|
||||
|
||||
export type Errors = {
|
||||
[P in ErrorCodeType]: string;
|
||||
};
|
||||
67
data_migration_tools/server/src/constants/index.ts
Normal file
67
data_migration_tools/server/src/constants/index.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 音声ファイルをEast USに保存する国リスト
|
||||
* @const {number}
|
||||
*/
|
||||
export const BLOB_STORAGE_REGION_US = ["CA", "KY", "US"];
|
||||
|
||||
/**
|
||||
* 音声ファイルをAustralia Eastに保存する国リスト
|
||||
* @const {number}
|
||||
*/
|
||||
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",
|
||||
];
|
||||
|
||||
/**
|
||||
* ADB2Cユーザのidentity.signInType
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const ADB2C_SIGN_IN_TYPE = {
|
||||
EMAILADDRESS: "emailAddress",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* AutoIncrementの初期値
|
||||
* @const {number}
|
||||
*/
|
||||
export const AUTO_INCREMENT_START = 853211;
|
||||
@ -0,0 +1,30 @@
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { DeleteService } from "./delete.service";
|
||||
import { DeleteController } from "./delete.controller";
|
||||
|
||||
describe("DeleteController", () => {
|
||||
let controller: DeleteController;
|
||||
const mockTemplatesService = {};
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
envFilePath: [".env.test", ".env"],
|
||||
isGlobal: true,
|
||||
}),
|
||||
],
|
||||
controllers: [DeleteController],
|
||||
providers: [DeleteService],
|
||||
})
|
||||
.overrideProvider(DeleteService)
|
||||
.useValue(mockTemplatesService)
|
||||
.compile();
|
||||
|
||||
controller = module.get<DeleteController>(DeleteController);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,39 @@
|
||||
import {
|
||||
Controller,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
Post,
|
||||
Req,
|
||||
} from "@nestjs/common";
|
||||
import { ErrorResponse } from "../../common/errors/types/types";
|
||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||
import { Request } from "express";
|
||||
import { DeleteService } from "./delete.service";
|
||||
import { DeleteResponse } from "./types/types";
|
||||
|
||||
@ApiTags("delete")
|
||||
@Controller("delete")
|
||||
export class DeleteController {
|
||||
constructor(private readonly deleteService: DeleteService) {}
|
||||
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
type: DeleteResponse,
|
||||
description: "成功時のレスポンス",
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
description: "想定外のサーバーエラー",
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({
|
||||
operationId: "deleteData",
|
||||
description: "すべてのデータを削除します",
|
||||
})
|
||||
@Post()
|
||||
async deleteData(): Promise<{}> {
|
||||
await this.deleteService.deleteData();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { DeleteRepositoryModule } from "../../repositories/delete/delete.repository.module";
|
||||
import { DeleteController } from "./delete.controller";
|
||||
import { DeleteService } from "./delete.service";
|
||||
import { AdB2cModule } from "../../gateways/adb2c/adb2c.module";
|
||||
import { BlobstorageModule } from "../../gateways/blobstorage/blobstorage.module";
|
||||
|
||||
@Module({
|
||||
imports: [DeleteRepositoryModule, AdB2cModule, BlobstorageModule],
|
||||
providers: [DeleteService],
|
||||
controllers: [DeleteController],
|
||||
})
|
||||
export class DeleteModule {}
|
||||
@ -0,0 +1,29 @@
|
||||
import { DataSource } from "typeorm";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { DeleteService } from "./delete.service";
|
||||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
|
||||
describe("DeleteController", () => {
|
||||
let service: DeleteService;
|
||||
const mockTemplatesService = {};
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
envFilePath: [".env.test", ".env"],
|
||||
isGlobal: true,
|
||||
}),
|
||||
],
|
||||
providers: [DeleteService],
|
||||
})
|
||||
.overrideProvider(DeleteService)
|
||||
.useValue(mockTemplatesService)
|
||||
.compile();
|
||||
|
||||
service = module.get<DeleteService>(DeleteService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,54 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
|
||||
import { DeleteRepositoryService } from "../../repositories/delete/delete.repository.service";
|
||||
import { makeErrorResponse } from "../../common/errors/makeErrorResponse";
|
||||
import { AdB2cService } from "../../gateways/adb2c/adb2c.service";
|
||||
import { BlobstorageService } from "../../gateways/blobstorage/blobstorage.service";
|
||||
|
||||
@Injectable()
|
||||
export class DeleteService {
|
||||
private readonly logger = new Logger(DeleteService.name);
|
||||
constructor(
|
||||
private readonly deleteRepositoryService: DeleteRepositoryService,
|
||||
private readonly blobstorageService: BlobstorageService,
|
||||
private readonly adB2cService: AdB2cService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* データを削除する
|
||||
* @returns data
|
||||
*/
|
||||
async deleteData(): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.deleteData.name}`);
|
||||
try {
|
||||
// BlobStorageからデータを削除する
|
||||
await this.blobstorageService.deleteContainers();
|
||||
|
||||
// ADB2Cからユーザ情報を取得する
|
||||
const users = await this.adB2cService.getUsers();
|
||||
const externalIds = users.map((user) => user.id);
|
||||
await this.adB2cService.deleteUsers(externalIds);
|
||||
|
||||
// データベースからデータを削除する
|
||||
await this.deleteRepositoryService.deleteData();
|
||||
// AutoIncrementの値をリセットする
|
||||
await this.deleteRepositoryService.resetAutoIncrement();
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(`[OUT] ${this.deleteData.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
import { DataSource } from "typeorm";
|
||||
@ -0,0 +1 @@
|
||||
export class DeleteResponse {}
|
||||
@ -0,0 +1,10 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { AdB2cService } from "./adb2c.service";
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
exports: [AdB2cService],
|
||||
providers: [AdB2cService],
|
||||
})
|
||||
export class AdB2cModule {}
|
||||
116
data_migration_tools/server/src/gateways/adb2c/adb2c.service.ts
Normal file
116
data_migration_tools/server/src/gateways/adb2c/adb2c.service.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import { ClientSecretCredential } from "@azure/identity";
|
||||
import { Client } from "@microsoft/microsoft-graph-client";
|
||||
import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials";
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { AdB2cResponse, AdB2cUser } from "./types/types";
|
||||
import { isPromiseRejectedResult } from "./utils/utils";
|
||||
|
||||
export type ConflictError = {
|
||||
reason: "email";
|
||||
message: string;
|
||||
};
|
||||
|
||||
export class Adb2cTooManyRequestsError extends Error {}
|
||||
|
||||
export const isConflictError = (arg: unknown): arg is ConflictError => {
|
||||
const value = arg as ConflictError;
|
||||
if (value.message === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (value.reason === "email") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AdB2cService {
|
||||
private readonly logger = new Logger(AdB2cService.name);
|
||||
private graphClient: Client;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
// ADB2Cへの認証情報
|
||||
const credential = new ClientSecretCredential(
|
||||
this.configService.getOrThrow<string>("ADB2C_TENANT_ID"),
|
||||
this.configService.getOrThrow<string>("ADB2C_CLIENT_ID"),
|
||||
this.configService.getOrThrow<string>("ADB2C_CLIENT_SECRET")
|
||||
);
|
||||
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
|
||||
scopes: ["https://graph.microsoft.com/.default"],
|
||||
});
|
||||
|
||||
this.graphClient = Client.initWithMiddleware({ authProvider });
|
||||
}
|
||||
/**
|
||||
* Gets users
|
||||
* @param externalIds
|
||||
* @returns users
|
||||
*/
|
||||
async getUsers(): Promise<AdB2cUser[]> {
|
||||
this.logger.log(`[IN] ${this.getUsers.name}`);
|
||||
|
||||
try {
|
||||
const res: AdB2cResponse = await this.graphClient
|
||||
.api(`users/`)
|
||||
.select(["id", "displayName", "identities"])
|
||||
.filter(`creationType eq 'LocalAccount'`)
|
||||
.get();
|
||||
|
||||
return res.value;
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
const { statusCode } = e;
|
||||
if (statusCode === 429) {
|
||||
throw new Adb2cTooManyRequestsError();
|
||||
}
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
this.logger.log(`[OUT] ${this.getUsers.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Azure AD B2Cからユーザ情報を削除する(複数)
|
||||
* @param externalIds 外部ユーザーID
|
||||
*/
|
||||
async deleteUsers(externalIds: string[]): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN]${this.deleteUsers.name} | params: { externalIds: ${externalIds} };`
|
||||
);
|
||||
|
||||
try {
|
||||
// 複数ユーザーを一括削除する方法がないため、1人ずつで削除を行う
|
||||
const results = await Promise.allSettled(
|
||||
externalIds.map(async (externalId) => {
|
||||
await this.graphClient.api(`users/${externalId}`).delete();
|
||||
await new Promise((resolve) => setTimeout(resolve, 15)); // 15ms待つ
|
||||
this.logger.log(`[[ADB2C DELETE] externalId: ${externalId}`);
|
||||
})
|
||||
);
|
||||
|
||||
// 失敗したプロミスのエラーをログに記録
|
||||
results.forEach((result, index) => {
|
||||
// statusがrejectedでない場合は、エラーが発生していないためログに記録しない
|
||||
if (result.status !== "rejected") {
|
||||
return;
|
||||
}
|
||||
|
||||
const failedId = externalIds[index];
|
||||
if (isPromiseRejectedResult(result)) {
|
||||
const error = result.reason.toString();
|
||||
|
||||
this.logger.error(`Failed to delete user ${failedId}: ${error}`);
|
||||
} else {
|
||||
this.logger.error(`Failed to delete user ${failedId}`);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
throw e;
|
||||
} finally {
|
||||
this.logger.log(`[OUT] ${this.deleteUsers.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
export type AdB2cResponse = {
|
||||
'@odata.context': string;
|
||||
value: AdB2cUser[];
|
||||
};
|
||||
export type AdB2cUser = {
|
||||
id: string;
|
||||
displayName: string;
|
||||
identities?: UserIdentity[];
|
||||
};
|
||||
|
||||
export type UserIdentity = {
|
||||
signInType: string;
|
||||
issuer: string;
|
||||
issuerAssignedId: string;
|
||||
};
|
||||
@ -0,0 +1,22 @@
|
||||
import { ADB2C_SIGN_IN_TYPE } from '../../../constants';
|
||||
import { AdB2cUser } from '../types/types';
|
||||
|
||||
export const isPromiseRejectedResult = (
|
||||
data: unknown,
|
||||
): data is PromiseRejectedResult => {
|
||||
return (
|
||||
data !== null &&
|
||||
typeof data === 'object' &&
|
||||
'status' in data &&
|
||||
'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 };
|
||||
};
|
||||
@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BlobstorageService } from './blobstorage.service';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
@Module({
|
||||
exports: [BlobstorageService],
|
||||
imports: [ConfigModule],
|
||||
providers: [BlobstorageService],
|
||||
})
|
||||
export class BlobstorageModule {}
|
||||
@ -0,0 +1,83 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import {
|
||||
BlobServiceClient,
|
||||
StorageSharedKeyCredential,
|
||||
} from "@azure/storage-blob";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
|
||||
@Injectable()
|
||||
export class BlobstorageService {
|
||||
private readonly logger = new Logger(BlobstorageService.name);
|
||||
private readonly blobServiceClientUS: BlobServiceClient;
|
||||
private readonly blobServiceClientEU: BlobServiceClient;
|
||||
private readonly blobServiceClientAU: BlobServiceClient;
|
||||
private readonly sharedKeyCredentialUS: StorageSharedKeyCredential;
|
||||
private readonly sharedKeyCredentialAU: StorageSharedKeyCredential;
|
||||
private readonly sharedKeyCredentialEU: StorageSharedKeyCredential;
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.sharedKeyCredentialUS = new StorageSharedKeyCredential(
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_NAME_US"),
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_KEY_US")
|
||||
);
|
||||
this.sharedKeyCredentialAU = new StorageSharedKeyCredential(
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_NAME_AU"),
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_KEY_AU")
|
||||
);
|
||||
this.sharedKeyCredentialEU = new StorageSharedKeyCredential(
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_NAME_EU"),
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_KEY_EU")
|
||||
);
|
||||
this.blobServiceClientUS = new BlobServiceClient(
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_ENDPOINT_US"),
|
||||
this.sharedKeyCredentialUS
|
||||
);
|
||||
this.blobServiceClientAU = new BlobServiceClient(
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_ENDPOINT_AU"),
|
||||
this.sharedKeyCredentialAU
|
||||
);
|
||||
this.blobServiceClientEU = new BlobServiceClient(
|
||||
this.configService.getOrThrow<string>("STORAGE_ACCOUNT_ENDPOINT_EU"),
|
||||
this.sharedKeyCredentialEU
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* すべてのコンテナを削除します。
|
||||
* @returns containers
|
||||
*/
|
||||
async deleteContainers(): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.deleteContainers.name}`);
|
||||
|
||||
try {
|
||||
for await (const container of this.blobServiceClientAU.listContainers({
|
||||
prefix: "account-",
|
||||
})) {
|
||||
const client = this.blobServiceClientAU.getContainerClient(
|
||||
container.name
|
||||
);
|
||||
await client.deleteIfExists();
|
||||
}
|
||||
for await (const container of this.blobServiceClientEU.listContainers({
|
||||
prefix: "account-",
|
||||
})) {
|
||||
const client = this.blobServiceClientEU.getContainerClient(
|
||||
container.name
|
||||
);
|
||||
await client.deleteIfExists();
|
||||
}
|
||||
for await (const container of this.blobServiceClientUS.listContainers({
|
||||
prefix: "account-",
|
||||
})) {
|
||||
const client = this.blobServiceClientUS.getContainerClient(
|
||||
container.name
|
||||
);
|
||||
await client.deleteIfExists();
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
throw e;
|
||||
} finally {
|
||||
this.logger.log(`[OUT] ${this.deleteContainers.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,35 +1,35 @@
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
||||
import { AppModule } from "./app.module";
|
||||
import { ValidationPipe } from "@nestjs/common";
|
||||
import { LoggerMiddleware } from "./common/loggerMiddleware";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { LoggerMiddleware } from './common/loggerMiddleware';
|
||||
import cookieParser from 'cookie-parser';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
cors: process.env.CORS === "TRUE",
|
||||
cors: process.env.CORS === 'TRUE',
|
||||
});
|
||||
app.use(new LoggerMiddleware(), cookieParser());
|
||||
|
||||
// バリデーター(+型の自動変換機能)を適用
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({ transform: true, forbidUnknownValues: false })
|
||||
new ValidationPipe({ transform: true, forbidUnknownValues: false }),
|
||||
);
|
||||
|
||||
if (process.env.STAGE === "local") {
|
||||
if (process.env.STAGE === 'local') {
|
||||
const options = new DocumentBuilder()
|
||||
.setTitle("data_migration_toolsOpenAPI")
|
||||
.setVersion("1.0.0")
|
||||
.setTitle('data_migration_toolsOpenAPI')
|
||||
.setVersion('1.0.0')
|
||||
.addBearerAuth({
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
bearerFormat: "JWT",
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
})
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, options);
|
||||
SwaggerModule.setup("api", app, document);
|
||||
SwaggerModule.setup('api', app, document);
|
||||
}
|
||||
|
||||
await app.listen(process.env.PORT || 8180);
|
||||
await app.listen(process.env.PORT || 8280);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { DeleteRepositoryService } from "./delete.repository.service";
|
||||
import { Account } from "./entity/account.entity";
|
||||
import { AudioFile } from "./entity/audio_file.entity";
|
||||
import { AudioOptionItem } from "./entity/audio_option_item.entity";
|
||||
import { CheckoutPermission } from "./entity/checkout_permission.entity";
|
||||
import {
|
||||
CardLicense,
|
||||
CardLicenseIssue,
|
||||
License,
|
||||
LicenseAllocationHistory,
|
||||
LicenseAllocationHistoryArchive,
|
||||
LicenseArchive,
|
||||
LicenseOrder,
|
||||
} from "./entity/license.entity";
|
||||
import { OptionItem } from "./entity/option_item.entity";
|
||||
import { SortCriteria } from "./entity/sort_criteria.entity";
|
||||
import { Task } from "./entity/task.entity";
|
||||
import { TemplateFile } from "./entity/template_file.entity";
|
||||
import { Term } from "./entity/term.entity";
|
||||
import { UserGroupMember } from "./entity/user_group_member.entity";
|
||||
import { UserGroup } from "./entity/user_group.entity";
|
||||
import { User, UserArchive } from "./entity/user.entity";
|
||||
import { WorkflowTypist } from "./entity/workflow_typists.entity";
|
||||
import { Workflow } from "./entity/workflow.entity";
|
||||
import { Worktype } from "./entity/worktype.entity";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
Account,
|
||||
AudioFile,
|
||||
AudioOptionItem,
|
||||
CheckoutPermission,
|
||||
License,
|
||||
LicenseOrder,
|
||||
CardLicense,
|
||||
CardLicenseIssue,
|
||||
LicenseArchive,
|
||||
LicenseAllocationHistory,
|
||||
LicenseAllocationHistoryArchive,
|
||||
OptionItem,
|
||||
SortCriteria,
|
||||
Task,
|
||||
TemplateFile,
|
||||
Term,
|
||||
UserGroupMember,
|
||||
UserGroup,
|
||||
User,
|
||||
UserArchive,
|
||||
WorkflowTypist,
|
||||
Workflow,
|
||||
Worktype,
|
||||
]),
|
||||
],
|
||||
providers: [DeleteRepositoryService],
|
||||
exports: [DeleteRepositoryService],
|
||||
})
|
||||
export class DeleteRepositoryModule {}
|
||||
@ -0,0 +1,57 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { DataSource } from "typeorm";
|
||||
import { logger } from "@azure/identity";
|
||||
import { Account } from "./entity/account.entity";
|
||||
import { AUTO_INCREMENT_START } from "../../constants";
|
||||
|
||||
@Injectable()
|
||||
export class DeleteRepositoryService {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
* 全テーブルをTrancateする
|
||||
* @returns data
|
||||
*/
|
||||
async deleteData(): Promise<void> {
|
||||
const entities = this.dataSource.entityMetadatas;
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
try {
|
||||
await queryRunner.startTransaction();
|
||||
await queryRunner.query("SET FOREIGN_KEY_CHECKS=0");
|
||||
for (const entity of entities) {
|
||||
await queryRunner.query(`TRUNCATE TABLE \`${entity.tableName}\``);
|
||||
}
|
||||
await queryRunner.query("SET FOREIGN_KEY_CHECKS=1");
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
logger.error(err);
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AutoIncrementの値をリセットする
|
||||
* @returns data
|
||||
*/
|
||||
async resetAutoIncrement(): Promise<void> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
try {
|
||||
await queryRunner.startTransaction();
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE accounts AUTO_INCREMENT = ${AUTO_INCREMENT_START}`
|
||||
);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
logger.error(err);
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
import { bigintTransformer } from "../../../common/entity";
|
||||
import { User } from "./user.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity({ name: "accounts" })
|
||||
export class Account {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
parent_account_id: number | null;
|
||||
|
||||
@Column()
|
||||
tier: number;
|
||||
|
||||
@Column()
|
||||
country: string;
|
||||
|
||||
@Column({ default: false })
|
||||
delegation_permission: boolean;
|
||||
|
||||
@Column({ default: false })
|
||||
locked: boolean;
|
||||
|
||||
@Column()
|
||||
company_name: string;
|
||||
|
||||
@Column({ default: false })
|
||||
verified: boolean;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
primary_admin_user_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
secondary_admin_user_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
active_worktype_id: number | null;
|
||||
|
||||
@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",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@OneToMany(() => User, (user) => user.id)
|
||||
user: User[] | null;
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import { Task } from "./task.entity";
|
||||
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm";
|
||||
|
||||
@Entity({ name: "audio_files" })
|
||||
export class AudioFile {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
@Column()
|
||||
owner_user_id: number;
|
||||
@Column()
|
||||
url: string;
|
||||
@Column()
|
||||
file_name: string;
|
||||
@Column()
|
||||
author_id: string;
|
||||
@Column()
|
||||
work_type_id: string;
|
||||
@Column()
|
||||
started_at: Date;
|
||||
@Column({ type: "time" })
|
||||
duration: string;
|
||||
@Column()
|
||||
finished_at: Date;
|
||||
@Column()
|
||||
uploaded_at: Date;
|
||||
@Column()
|
||||
file_size: number;
|
||||
@Column()
|
||||
priority: string;
|
||||
@Column()
|
||||
audio_format: string;
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
comment: string | null;
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
deleted_at: Date | null;
|
||||
@Column()
|
||||
is_encrypted: boolean;
|
||||
@OneToOne(() => Task, (task) => task.file)
|
||||
task: Task | null;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import { Task } from "./task.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity({ name: "audio_option_items" })
|
||||
export class AudioOptionItem {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column()
|
||||
audio_file_id: number;
|
||||
@Column()
|
||||
label: string;
|
||||
@Column()
|
||||
value: string;
|
||||
@ManyToOne(() => Task, (task) => task.audio_file_id)
|
||||
@JoinColumn({ name: "audio_file_id", referencedColumnName: "audio_file_id" })
|
||||
task: Task | null;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import { bigintTransformer } from "../../../common/entity";
|
||||
import { Task } from "./task.entity";
|
||||
import { UserGroup } from "./user_group.entity";
|
||||
import { User } from "./user.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity({ name: "checkout_permission" })
|
||||
export class CheckoutPermission {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({})
|
||||
task_id: number;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
user_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
user_group_id: number | null;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "user_id" })
|
||||
user: User | null;
|
||||
|
||||
@ManyToOne(() => UserGroup, (group) => group.id)
|
||||
@JoinColumn({ name: "user_group_id" })
|
||||
user_group: UserGroup | null;
|
||||
|
||||
@ManyToOne(() => Task, (task) => task.id)
|
||||
@JoinColumn({ name: "task_id" })
|
||||
task: Task | null;
|
||||
}
|
||||
@ -0,0 +1,322 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
} from "typeorm";
|
||||
import { User } from "./user.entity";
|
||||
import { bigintTransformer } from "../../../common/entity";
|
||||
|
||||
@Entity({ name: "license_orders" })
|
||||
export class LicenseOrder {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
po_number: string;
|
||||
|
||||
@Column()
|
||||
from_account_id: number;
|
||||
|
||||
@Column()
|
||||
to_account_id: number;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
ordered_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
issued_at: Date | null;
|
||||
|
||||
@Column()
|
||||
quantity: number;
|
||||
|
||||
@Column()
|
||||
status: string;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
canceled_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;
|
||||
}
|
||||
|
||||
@Entity({ name: "licenses" })
|
||||
export class License {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
expiry_date: Date | null;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
type: string;
|
||||
|
||||
@Column()
|
||||
status: string;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
allocated_user_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
order_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
deleted_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
delete_order_id: number | 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;
|
||||
|
||||
@OneToOne(() => User, (user) => user.license, {
|
||||
createForeignKeyConstraints: false,
|
||||
}) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
|
||||
@JoinColumn({ name: "allocated_user_id" })
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
@Entity({ name: "card_license_issue" })
|
||||
export class CardLicenseIssue {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
issued_at: Date;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Entity({ name: "card_licenses" })
|
||||
export class CardLicense {
|
||||
@PrimaryGeneratedColumn()
|
||||
license_id: number;
|
||||
|
||||
@Column()
|
||||
issue_id: number;
|
||||
|
||||
@Column()
|
||||
card_license_key: string;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
activated_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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Entity({ name: "licenses_archive" })
|
||||
export class LicenseArchive {
|
||||
@PrimaryColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
expiry_date: Date | null;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
type: string;
|
||||
|
||||
@Column()
|
||||
status: string;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
allocated_user_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
order_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
deleted_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
delete_order_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@Column()
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@Column()
|
||||
updated_at: Date;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
archived_at: Date;
|
||||
}
|
||||
|
||||
@Entity({ name: "license_allocation_history_archive" })
|
||||
export class LicenseAllocationHistoryArchive {
|
||||
@PrimaryColumn()
|
||||
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;
|
||||
|
||||
@Column()
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@Column()
|
||||
updated_at: Date;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
archived_at: Date;
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Worktype } from './worktype.entity';
|
||||
|
||||
@Entity({ name: 'option_items' })
|
||||
export class OptionItem {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column()
|
||||
worktype_id: number;
|
||||
@Column()
|
||||
item_label: string;
|
||||
@Column()
|
||||
default_value_type: string;
|
||||
@Column()
|
||||
initial_value: string;
|
||||
@Column({ nullable: true, type: 'datetime' })
|
||||
created_by: string | null;
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: 'datetime',
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date | null;
|
||||
@Column({ nullable: true, type: 'datetime' })
|
||||
updated_by: string | null;
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: 'datetime',
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date | null;
|
||||
|
||||
@ManyToOne(() => Worktype, (worktype) => worktype.id)
|
||||
@JoinColumn({ name: 'worktype_id' })
|
||||
worktype: Worktype;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity({ name: 'sort_criteria' })
|
||||
export class SortCriteria {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
user_id: number;
|
||||
|
||||
@Column()
|
||||
parameter: string;
|
||||
|
||||
@Column()
|
||||
direction: string;
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
import { AudioOptionItem } from "./audio_option_item.entity";
|
||||
import { AudioFile } from "./audio_file.entity";
|
||||
import { User } from "./user.entity";
|
||||
import { TemplateFile } from "./template_file.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm";
|
||||
import { bigintTransformer } from "../../../common/entity";
|
||||
|
||||
@Entity({ name: "tasks" })
|
||||
export class Task {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column()
|
||||
job_number: string;
|
||||
@Column()
|
||||
account_id: number;
|
||||
@Column({ nullable: true, type: "boolean" })
|
||||
is_job_number_enabled: boolean | null;
|
||||
@Column()
|
||||
audio_file_id: number;
|
||||
@Column()
|
||||
status: string;
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
typist_user_id: number | null;
|
||||
@Column()
|
||||
priority: string;
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
template_file_id: number | null;
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
started_at: Date | null;
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
finished_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
@OneToOne(() => AudioFile, (audiofile) => audiofile.task)
|
||||
@JoinColumn({ name: "audio_file_id" })
|
||||
file: AudioFile | null;
|
||||
@OneToMany(() => AudioOptionItem, (option) => option.task)
|
||||
option_items: AudioOptionItem[] | null;
|
||||
@OneToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "typist_user_id" })
|
||||
typist_user: User | null;
|
||||
@ManyToOne(() => TemplateFile, (templateFile) => templateFile.id)
|
||||
@JoinColumn({ name: "template_file_id" })
|
||||
template_file: TemplateFile | null;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
} from "typeorm";
|
||||
import { Task } from "./task.entity";
|
||||
|
||||
@Entity({ name: "template_files" })
|
||||
export class TemplateFile {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@Column()
|
||||
account_id: number;
|
||||
@Column()
|
||||
url: string;
|
||||
@Column()
|
||||
file_name: string;
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
@CreateDateColumn()
|
||||
created_at: Date;
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date;
|
||||
@OneToMany(() => Task, (task) => task.template_file)
|
||||
tasks: Task[] | null;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity({ name: 'terms' })
|
||||
export class Term {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
document_type: string;
|
||||
|
||||
@Column()
|
||||
version: string;
|
||||
|
||||
@Column({ nullable: true, type: 'datetime' })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: 'datetime',
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: 'varchar' })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: 'datetime',
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
import { Account } from "./account.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
OneToMany,
|
||||
PrimaryColumn,
|
||||
} from "typeorm";
|
||||
import { License } from "./license.entity";
|
||||
import { UserGroupMember } from "./user_group_member.entity";
|
||||
|
||||
@Entity({ name: "users" })
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
external_id: string;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
role: string;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
author_id: string | null;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
accepted_eula_version: string | null;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
accepted_privacy_notice_version: string | null;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
accepted_dpa_version: string | null;
|
||||
|
||||
@Column({ default: false })
|
||||
email_verified: boolean;
|
||||
|
||||
@Column({ default: true })
|
||||
auto_renew: boolean;
|
||||
|
||||
@Column({ default: true })
|
||||
notification: boolean;
|
||||
|
||||
@Column({ default: false })
|
||||
encryption: boolean;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
encryption_password: string | null;
|
||||
|
||||
@Column({ default: false })
|
||||
prompt: boolean;
|
||||
|
||||
@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",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // 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;
|
||||
|
||||
@OneToMany(() => UserGroupMember, (userGroupMember) => userGroupMember.user)
|
||||
userGroupMembers: UserGroupMember[] | null;
|
||||
}
|
||||
|
||||
@Entity({ name: "users_archive" })
|
||||
export class UserArchive {
|
||||
@PrimaryColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
external_id: string;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
role: string;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
author_id: string | null;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
accepted_eula_version: string | null;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
accepted_privacy_notice_version: string | null;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
accepted_dpa_version: string | null;
|
||||
|
||||
@Column()
|
||||
email_verified: boolean;
|
||||
|
||||
@Column()
|
||||
auto_renew: boolean;
|
||||
|
||||
@Column()
|
||||
notification: boolean;
|
||||
|
||||
@Column()
|
||||
encryption: boolean;
|
||||
|
||||
@Column()
|
||||
prompt: boolean;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
deleted_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@Column()
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@Column()
|
||||
updated_at: Date;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
archived_at: Date;
|
||||
}
|
||||
|
||||
export type newUser = Omit<
|
||||
User,
|
||||
| "id"
|
||||
| "deleted_at"
|
||||
| "created_at"
|
||||
| "updated_at"
|
||||
| "updated_by"
|
||||
| "created_by"
|
||||
| "account"
|
||||
| "license"
|
||||
| "userGroupMembers"
|
||||
| "email_verified"
|
||||
>;
|
||||
@ -0,0 +1,48 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
OneToMany,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { UserGroupMember } from './user_group_member.entity';
|
||||
|
||||
@Entity({ name: 'user_group' })
|
||||
export class UserGroup {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
name: 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',
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: 'datetime' })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: 'datetime',
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date | null;
|
||||
|
||||
@OneToMany(
|
||||
() => UserGroupMember,
|
||||
(userGroupMember) => userGroupMember.userGroup,
|
||||
)
|
||||
userGroupMembers: UserGroupMember[] | null;
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
import { User } from "./user.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm";
|
||||
import { UserGroup } from "./user_group.entity";
|
||||
|
||||
@Entity({ name: "user_group_member" })
|
||||
export class UserGroupMember {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
user_group_id: number;
|
||||
|
||||
@Column()
|
||||
user_id: number;
|
||||
|
||||
@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",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date | null;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "user_id" })
|
||||
user: User | null;
|
||||
|
||||
@ManyToOne(() => UserGroup, (userGroup) => userGroup.id)
|
||||
@JoinColumn({ name: "user_group_id" })
|
||||
userGroup: UserGroup | null;
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
} from "typeorm";
|
||||
import { WorkflowTypist } from "./workflow_typists.entity";
|
||||
import { Worktype } from "./worktype.entity";
|
||||
import { TemplateFile } from "./template_file.entity";
|
||||
import { User } from "./user.entity";
|
||||
import { bigintTransformer } from "../../../common/entity";
|
||||
|
||||
@Entity({ name: "workflows" })
|
||||
export class Workflow {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
author_id: number;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
worktype_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
template_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "author_id" })
|
||||
author: User | null;
|
||||
|
||||
@ManyToOne(() => Worktype, (worktype) => worktype.id)
|
||||
@JoinColumn({ name: "worktype_id" })
|
||||
worktype: Worktype | null;
|
||||
|
||||
@ManyToOne(() => TemplateFile, (templateFile) => templateFile.id)
|
||||
@JoinColumn({ name: "template_id" })
|
||||
template: TemplateFile | null;
|
||||
|
||||
@OneToMany(() => WorkflowTypist, (workflowTypist) => workflowTypist.workflow)
|
||||
workflowTypists: WorkflowTypist[] | null;
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from "typeorm";
|
||||
import { Workflow } from "./workflow.entity";
|
||||
import { User } from "./user.entity";
|
||||
import { UserGroup } from "./user_group.entity";
|
||||
import { bigintTransformer } from "../../../common/entity";
|
||||
|
||||
@Entity({ name: "workflow_typists" })
|
||||
export class WorkflowTypist {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
workflow_id: number;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
typist_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
|
||||
typist_group_id: number | null;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@ManyToOne(() => Workflow, (workflow) => workflow.id)
|
||||
@JoinColumn({ name: "workflow_id" })
|
||||
workflow: Workflow | null;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.id)
|
||||
@JoinColumn({ name: "typist_id" })
|
||||
typist: User | null;
|
||||
|
||||
@ManyToOne(() => UserGroup, (userGroup) => userGroup.id)
|
||||
@JoinColumn({ name: "typist_group_id" })
|
||||
typistGroup: UserGroup | null;
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import { Account } from "./account.entity";
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
} from "typeorm";
|
||||
import { OptionItem } from "./option_item.entity";
|
||||
|
||||
@Entity({ name: "worktypes" })
|
||||
export class Worktype {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
account_id: number;
|
||||
|
||||
@Column()
|
||||
custom_worktype_id: string;
|
||||
|
||||
@Column({ nullable: true, type: "varchar" })
|
||||
description: string | null;
|
||||
|
||||
@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",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at: Date;
|
||||
|
||||
@OneToMany(() => OptionItem, (optionItem) => optionItem.worktype)
|
||||
option_items: OptionItem[];
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user