Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions packages/main/src/config/SchemaConfig.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { DataTypes, LiteralDataTypes } from "@src/core/DDL/abstracts/BaseFieldBuilder";
import Schema from "@src/core/DDL/implements/Schema"

export type MissingSchemaStrategy = 'create' | 'ignore' | 'error'

// 이 인터페이스는 이제 Configs.ts에서 직접 정의되므로 주석 처리하거나 삭제할 수 있습니다.
export interface SchemaConfigOptions<T extends Schema[]> {
onMissingSchema?: MissingSchemaStrategy;
recordDetailType?:boolean,
parseDetailType?:boolean,
schemas?: T;
}

Expand All @@ -13,11 +16,38 @@ export type SchemaMap<T extends Schema[]> = {
}

class SchemaConfig<T extends Schema[]>{
missingSchemaStartegy: MissingSchemaStrategy
readonly DEFAULT_MISSING_STRATEGY: MissingSchemaStrategy = 'create'

missingSchemaStartegy: MissingSchemaStrategy
recordDetailType: boolean
parseDetailType: boolean

readonly schemaList: T;
readonly schemaMap: SchemaMap<T>;
schemaSetted:boolean

// type parser for spreadsheet values
typeParsers:Record<LiteralDataTypes, (value:string) => DataTypes> = {
boolean:(value) => value === "true",
date: (value) => new Date(value),
number: (value) => Number(value),
string: (value) => value,
}

constructor({
onMissingSchema = this.DEFAULT_MISSING_STRATEGY,
recordDetailType = true,
parseDetailType = true,
schemas = [] as unknown as T,
}: SchemaConfigOptions<T>) {
this.missingSchemaStartegy = onMissingSchema;
this.recordDetailType = recordDetailType;
this.parseDetailType = parseDetailType;

this.schemaList = schemas;
this.schemaSetted = this.schemaList.length > 0;
this.schemaMap = this.makeSchemaMap(this.schemaList);
}

private makeSchemaMap(schemas:T){
const schemaMap = schemas.reduce((schemaDefinition, schema) => {
Expand All @@ -27,12 +57,6 @@ class SchemaConfig<T extends Schema[]>{
}, {} as SchemaMap<T>)
return schemaMap
}

constructor(options: SchemaConfigOptions<T>) {
this.missingSchemaStartegy = options.onMissingSchema ?? this.DEFAULT_MISSING_STRATEGY
this.schemaList = (options.schemas ?? []) as T
this.schemaMap = this.makeSchemaMap(this.schemaList)
}
}
export default SchemaConfig

Expand Down
22 changes: 18 additions & 4 deletions packages/main/src/config/SpreadConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ class SpreadConfig{
return false
}

// Google Sheets 시리얼 번호로 변환하는 헬퍼 함수
static jsDateToSheetsSerial(jsDate:Date) {
const sheetsEpoch = new Date(1899, 11, 30, 0, 0, 0);
const diffMillis = jsDate.getTime() - sheetsEpoch.getTime();
const millisPerDay = 24 * 60 * 60 * 1000;
let sheetsSerial = diffMillis / millisPerDay;
if (jsDate.getFullYear() > 1900 || (jsDate.getFullYear() === 1900 && jsDate.getMonth() > 1)) {
sheetsSerial += 1;
}

return sheetsSerial;
}

/**
* instance properties
Expand Down Expand Up @@ -72,14 +84,16 @@ class SpreadConfig{
}

async batchUpdateQuery(requests: sheets_v4.Schema$Request[], spreadsheetId?: string,) {
const response = await this.API.spreadsheets.batchUpdate({
spreadsheetId:spreadsheetId ?? this.ID,
requestBody: { requests }
});
const request = {
spreadsheetId:spreadsheetId ?? this.ID,
requestBody: { requests }
}
const response = await this.API.spreadsheets.batchUpdate(request);
if (response.status !== 200) throw new Error("Batch failed");
return response.data.replies;
}


private checkFormat(options:SpreadConfigOptions){
if (!this.isValidEmail(options.email)){
throw Error("Invalid email format")
Expand Down
9 changes: 5 additions & 4 deletions packages/main/src/core/DDL/SchemaManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,17 @@ class SchemaManager<T extends Schema[]> {

if (dataType !== "number" && dataType !== "date") continue

const startColumnIndex = this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN) + idx
const request = SheetQueries.repeatTypedCell(sheetId, dataType, {
startRowIndex: this.config.sheet.DATA_STARTING_ROW,
const startColumnIndex = this.config.sheet.columnToNumber(this.config.sheet.DEFAULT_RECORDING_START_COLUMN) - 1 + idx
const request = SheetQueries.setNumberTypedCell(dataType, {
sheetId,
startRowIndex: this.config.sheet.DATA_STARTING_ROW - 1,
startColumnIndex,
endColumnIndex: startColumnIndex + 1
})
setTypedColumnRequests.push(request)
}
}
this.config.spread.batchUpdateQuery(setTypedColumnRequests)
await this.config.spread.batchUpdateQuery(setTypedColumnRequests)
console.log("set type to column successfully")

return result
Expand Down
24 changes: 4 additions & 20 deletions packages/main/src/core/DML/implements/InsertBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Schema from "@src/core/DDL/implements/Schema";
import AndAbleQueryStore from "../abstracts/mixins/AndAbleQueryStore";
import QueryStore, { BasicQueryQueueType } from "../abstracts/QueryStore";
import { SchemaMap } from "@src/config/SchemaConfig";
import SpreadConfig from "@src/config/SpreadConfig";

interface InsertQueryQueueType extends BasicQueryQueueType{
insertValues:DataTypes[]
Expand Down Expand Up @@ -75,17 +76,16 @@ class SettedInsertBuilder<T extends Schema[], SheetName extends T[number]['sheet
newGroupedBySheet.set(sheetName, values); // 스키마 없으면 원본 그대로
continue;
}

console.log(values)
const newValues: DataTypes[][] = values.map(row => {
const newRow = [...row]; // 원본 행 복사

for (const fieldName in schema.fields) {
const field = schema.fields[fieldName];
if (field.dataType === 'date' && field.timestampAtCreated) {
const columnIndex = field.columnOrder - 1; // columnOrder는 1부터 시작, 배열 인덱스는 0부터 시작

const now = new Date();
newRow[columnIndex] = this.dateToGoogleSheetsSerial(now);
newRow[columnIndex] = SpreadConfig.jsDateToSheetsSerial(now); // not working now
}
}
return newRow;
Expand All @@ -95,22 +95,6 @@ class SettedInsertBuilder<T extends Schema[], SheetName extends T[number]['sheet
return newGroupedBySheet;
}

// Google Sheets 시리얼 번호로 변환하는 헬퍼 함수
private dateToGoogleSheetsSerial(date: Date): number {
// Google Sheets epoch: December 30, 1899
// JavaScript epoch: January 1, 1970
// 1900년 2월 29일 버그 (Lotus 1-2-3에서 유래, Excel/Sheets가 계승) 때문에 25569를 더함
// (1900년은 윤년이 아니지만, Excel/Sheets는 윤년으로 간주)
const excelEpoch = new Date(Date.UTC(1899, 11, 30)); // December 30, 1899
const msPerDay = 24 * 60 * 60 * 1000;
const serial = (date.getTime() - excelEpoch.getTime()) / msPerDay;

// 1900년 2월 29일 버그 보정 (1900년 3월 1일 이후 날짜에만 해당)
// 1900년 3월 1일은 시리얼 61
if (serial >= 61) {
return serial + 1;
}
return serial;
}


}
17 changes: 2 additions & 15 deletions packages/main/src/core/DML/implements/SelectBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ extends WhereableAndQueryStore<T, SelectBuilder<T>, SelectQueryQueueType>{
const composedRange = this.config.sheet.composeRange(query.sheetName, this.config.sheet.DATA_STARTING_ROW, specifiedColumn)
return composedRange
})
console.log("composedRanges",composedRanges)
const requestBody = this.makeRequestBody(composedRanges)
console.log("requestBody",requestBody)

const response = await this.config.spread.API.spreadsheets.values.batchGetByDataFilter({
spreadsheetId:this.config.spread.ID,
Expand Down Expand Up @@ -85,19 +83,8 @@ extends WhereableAndQueryStore<T, SelectBuilder<T>, SelectQueryQueueType>{
if (!(queriedFrom in this.config.schema.schemaMap)) return sheetValue

const currentSchema = this.config.schema.schemaMap[queriedFrom as keyof SchemaMap<T>]
const typeConverters:((value:string)=>DataTypes)[] = currentSchema.orderedColumns.map((column) => {
const type = currentSchema.fields[column].dataType
switch (type){
case "boolean":
return (value:string) => value.toLowerCase() === "true"
case "date":
return (value:string) => new Date(value)
case "number":
return (value:string) => Number(value)
default:
return (value:string) => value
}
})
// orderColumns 를 기준으로 typeParser메서드들이 indexing된 배열을
const typeConverters:((value:string)=>DataTypes)[] = currentSchema.orderedColumns.map((column) => this.config.schema.typeParsers[currentSchema.fields[column].dataType])

const result = sheetValue.map((row) => row.map((value,idx) => typeConverters[idx](value)))
return result
Expand Down
16 changes: 7 additions & 9 deletions packages/main/src/generators/SheetQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,28 @@ export class SheetQueries {
}
}

static repeatTypedCell(
sheetId:number,
static setNumberTypedCell(
type:"number" | "date",
range:Omit<sheets_v4.Schema$GridRange, "sheetId"> = {endRowIndex:1000000})
range:sheets_v4.Schema$GridRange)
:sheets_v4.Schema$Request{
const numberFormat = {
"number":{
type:"NUMBER"
type:"NUMBER",
},
"date":{
type:"DATE_TIME",
pattern: "yyyy-mm-dd hh:mm:ss"
}
}

return {
repeatCell:{
range:{
sheetId,
...range,
startRowIndex:2,
endRowIndex:1000000,
...range
},
cell:{
userEnteredFormat: {
numberFormat:numberFormat[type]
numberFormat:numberFormat[type],
}
},
fields:"userEnteredFormat.numberFormat"
Expand Down
9 changes: 4 additions & 5 deletions packages/test/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import createSpreadsheetClient, { Credentials, defineTable, fieldBuilder } from "spreadsheet-orm"
import credentials from "./security/credentials.json"

const connectionParameters:Credentials = credentials

const userSchemaFields = {
Expand Down Expand Up @@ -36,14 +35,14 @@ const spreadsheetClient = createSpreadsheetClient({
})
// spreadsheetClient.configs.schema.schemaMap.cars
await spreadsheetClient.schemaManager.sync({mode:"force"})
await spreadsheetClient.queryBuilder.insert(["volvo", 1960]).into("cars").execute()
// await spreadsheetClient.queryBuilder.insert(["volvo",1960]).into("cars").execute()

await spreadsheetClient.queryBuilder.insert(["volvo",1960]).into("cars").execute()
// await spreadsheetClient.queryBuilder.delete().from("cars").where((data) => data[2] === "1960").execute() // index0 = index
// await spreadsheetClient.queryBuilder.update(["hyundai", 2000]).from("cars").execute()
// await spreadsheetClient.queryBuilder.insert(["volvo",1960]).into("cars").and(["hyundai", 2020]).into("cars").execute()
// // await spreadsheetClient.queryBuilder.insert(["volve", 1960]).into("cars").and()
const result = await spreadsheetClient.queryBuilder.select().from("cars").and().from("user").execute({detail:false})
console.log(result)
// const result = await spreadsheetClient.queryBuilder.select().from("cars").and().from("user").execute({detail:false})
// console.log(result)


// await spreadsheetClient.queryBuilder.update()
Expand Down
Loading