Skip to content

Commit

Permalink
Merge pull request #1876 from swarajsaaj/feature/autofill_projectdetails
Browse files Browse the repository at this point in the history
Feature/autofill projectdetails
  • Loading branch information
pascalgrimaud authored Jun 10, 2022
2 parents 69d919d + 727b697 commit d635329
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 27 deletions.
5 changes: 4 additions & 1 deletion src/main/webapp/app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ProjectHistoryRepository from '@/common/secondary/ProjectHistoryRepositor
import ConsoleLogger from '@/common/secondary/ConsoleLogger';
import { FileDownloader } from '@/common/primary/FileDownloader';
import { useHistoryStore } from '@/common/primary/HistoryStore';
import { useProjectStore } from '@/springboot/primary/ProjectStore';
import { createPinia } from 'pinia';
import piniaPersist from 'pinia-plugin-persist';
import { MittAlertBus } from '@/common/secondary/alert/MittAlertBus';
Expand All @@ -36,8 +37,9 @@ const axiosHttp = new AxiosHttp(axios.create({ baseURL: '' }));
const fileDownloader = new FileDownloader(window);
const consoleLogger = new ConsoleLogger(console);
const historyStore = useHistoryStore();
const projectStore = useProjectStore();
const projectHistoryRepository = new ProjectHistoryRepository(axiosHttp, historyStore);
const projectRepository = new ProjectRepository(axiosHttp, projectHistoryRepository);
const projectRepository = new ProjectRepository(axiosHttp, projectHistoryRepository, projectStore);
const angularRepository = new AngularRepository(axiosHttp, projectHistoryRepository);
const reactRepository = new ReactRepository(axiosHttp, projectHistoryRepository);
const springBootRepository = new SpringBootRepository(axiosHttp, projectHistoryRepository);
Expand All @@ -50,6 +52,7 @@ app.provide('angularService', angularRepository);
app.provide('fileDownloader', fileDownloader);
app.provide('globalWindow', window);
app.provide('historyStore', historyStore);
app.provide('projectStore', projectStore);
app.provide('logger', consoleLogger);
app.provide('projectService', projectRepository);
app.provide('projectHistoryService', projectHistoryRepository);
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/app/springboot/domain/ProjectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export interface ProjectService {
download(project: Project): Promise<DocumentFile>;
addCodespacesSetup(project: Project): Promise<void>;
addGitpodSetup(project: Project): Promise<void>;
getProjectDetails(folder: string): Promise<void>;
}
23 changes: 19 additions & 4 deletions src/main/webapp/app/springboot/primary/Generator.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { IconVue } from '@/common/primary/icon';
import { ProjectGeneratorVue } from '@/springboot/primary/generator/project-generator';
import { ProjectHistoryService } from '@/common/domain/ProjectHistoryService';
import { History } from '@/common/domain/History';
import { ProjectService } from '../domain/ProjectService';
import { StoreGeneric } from 'pinia';

export default defineComponent({
name: 'GeneratorComponent',
Expand All @@ -27,8 +29,9 @@ export default defineComponent({
},
setup() {
const projectHistoryService = inject('projectHistoryService') as ProjectHistoryService;
const projectService = inject('projectService') as ProjectService;
const globalWindow = inject('globalWindow') as Window;

const projectStore = inject('projectStore') as StoreGeneric;
const selectorPrefix = 'generator';

const project = ref<ProjectToUpdate>({
Expand All @@ -40,11 +43,22 @@ export default defineComponent({
const server = ref<string>();
const client = ref<string>();

projectStore.$subscribe((_mutation, state) => {
project.value.baseName = state.project.baseName ?? project.value.baseName;
project.value.packageName = state.project.packageName ?? project.value.packageName;
project.value.projectName = state.project.projectName ?? project.value.projectName;
project.value.serverPort = state.project.serverPort ?? project.value.serverPort;
});

let timeoutId: number | undefined = undefined;
const getCurrentProjectHistory = (): Promise<History> => projectHistoryService.get(project.value.folder);
const debounceGetProjectHistory = (): void => {
const getProjectDetails = (): Promise<void> => projectService.getProjectDetails(project.value.folder);
const debounceGetProjectDetails = (): void => {
if (timeoutId) globalWindow.clearTimeout(timeoutId);
timeoutId = globalWindow.setTimeout(() => getCurrentProjectHistory(), 400);
timeoutId = globalWindow.setTimeout(() => {
getCurrentProjectHistory();
getProjectDetails();
}, 400);
};

return {
Expand All @@ -56,7 +70,8 @@ export default defineComponent({
client,
selectorPrefix,
getCurrentProjectHistory,
debounceGetProjectHistory,
getProjectDetails,
debounceGetProjectDetails,
};
},
});
2 changes: 1 addition & 1 deletion src/main/webapp/app/springboot/primary/Generator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class="form-control"
required
autofocus
@input="debounceGetProjectHistory"
@input="debounceGetProjectDetails"
/>
</div>
<div class="col-12">
Expand Down
24 changes: 24 additions & 0 deletions src/main/webapp/app/springboot/primary/ProjectStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineStore } from 'pinia';
import { Project } from '@/springboot/domain/Project';

const emptyProject = (): Project => ({
folder: '',
});

export const useProjectStore = defineStore('ProjectStore', {
state: () => {
return {
project: emptyProject(),
};
},

getters: {
getProject: state => state.project,
},

actions: {
async setProject(project: Project) {
this.project = project;
},
},
});
11 changes: 9 additions & 2 deletions src/main/webapp/app/springboot/secondary/ProjectRepository.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Project } from '@/springboot/domain/Project';
import { ProjectService } from '@/springboot/domain/ProjectService';
import { AxiosHttp } from '@/http/AxiosHttp';
import { RestProject, toRestProject } from '@/springboot/secondary/RestProject';
import { RestProject, toProject, toRestProject } from '@/springboot/secondary/RestProject';
import { DocumentFile } from '@/common/domain/DocumentFile';
import { toDocumentFile } from '@/common/secondary/ResponseDocumentFile';
import { AxiosRequestConfig } from 'axios';
import { ProjectHistoryService } from '@/common/domain/ProjectHistoryService';

export default class ProjectRepository implements ProjectService {
constructor(private axiosHttp: AxiosHttp, private projectHistoryService: ProjectHistoryService) {}
constructor(private axiosHttp: AxiosHttp, private projectHistoryService: ProjectHistoryService, private projectStore: any) {}

private async postAndGetHistory(url: string, restProject: RestProject): Promise<void> {
await this.axiosHttp.post(url, restProject).then(() => this.projectHistoryService.get(restProject.folder));
Expand Down Expand Up @@ -63,4 +63,11 @@ export default class ProjectRepository implements ProjectService {
return toDocumentFile('export.xls')(response);
});
}

async getProjectDetails(folder: string): Promise<void> {
return this.axiosHttp
.get<RestProject>('api/projects/details', { params: { folder } })
.then(response => this.projectStore.setProject(toProject(response.data)))
.catch(() => this.projectStore.setProject({ folder }));
}
}
8 changes: 8 additions & 0 deletions src/main/webapp/app/springboot/secondary/RestProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ export const toRestProject = (project: Project): RestProject => ({
folder: project.folder,
'generator-jhipster': toRestGeneratorJHipster(project),
});

export const toProject = (restProject: RestProject): Project => ({
folder: restProject.folder,
baseName: restProject['generator-jhipster']?.baseName,
packageName: restProject['generator-jhipster']?.packageName,
projectName: restProject['generator-jhipster']?.projectName,
serverPort: restProject['generator-jhipster']?.serverPort,
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface ProjectServiceFixture extends ProjectService {
download: SinonStub;
addCodespacesSetup: SinonStub;
addGitpodSetup: SinonStub;
getProjectDetails: SinonStub;
}

export const stubProjectService = (): ProjectServiceFixture => ({
Expand All @@ -25,4 +26,5 @@ export const stubProjectService = (): ProjectServiceFixture => ({
download: sinon.stub(),
addCodespacesSetup: sinon.stub(),
addGitpodSetup: sinon.stub(),
getProjectDetails: sinon.stub(),
});
71 changes: 64 additions & 7 deletions src/test/javascript/spec/springboot/primary/Generator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { GeneratorVue } from '@/springboot/primary';
import { shallowMount, VueWrapper } from '@vue/test-utils';
import { ProjectHistoryService } from '@/common/domain/ProjectHistoryService';
import { ProjectService } from '@/springboot/domain/ProjectService';
import { Project } from '@/springboot/domain/Project';
import { stubProjectHistoryService } from '../../common/domain/ProjectHistoryService.fixture';
import sinon, { SinonStub } from 'sinon';
import { stubProjectService } from '../domain/ProjectService.fixture';
import { ProjectStoreFixture, stubProjectStore } from './ProjectStore.fixture';

let wrapper: VueWrapper;
let component: any;

interface WrapperOptions {
projectHistoryService: ProjectHistoryService;
projectService: ProjectService;
globalWindow: WindowStub;
projectStore: ProjectStoreFixture;
}

interface WindowStub {
Expand All @@ -24,16 +30,20 @@ const stubGlobalWindow = (): WindowStub =>
} as WindowStub);

const wrap = (wrapperOptions?: Partial<WrapperOptions>) => {
const { projectHistoryService, globalWindow }: WrapperOptions = {
const { projectHistoryService, projectService, globalWindow, projectStore }: WrapperOptions = {
projectHistoryService: stubProjectHistoryService(),
globalWindow: stubGlobalWindow(),
projectService: stubProjectService(),
projectStore: stubProjectStore(),
...wrapperOptions,
};
wrapper = shallowMount(GeneratorVue, {
global: {
provide: {
projectHistoryService,
projectService,
globalWindow,
projectStore,
},
},
});
Expand All @@ -47,27 +57,32 @@ describe('Generator', () => {
expect(wrapper.exists()).toBe(true);
});

it('should get project history with current project folder', async () => {
it('should get project history and details with current project folder', async () => {
const projectHistoryService = stubProjectHistoryService();
wrap({ projectHistoryService });
const projectService = stubProjectService();
wrap({ projectHistoryService, projectService });

const projectPathInput = wrapper.find('#path');
await projectPathInput.setValue('path');
component.getCurrentProjectHistory();
component.getProjectDetails();

const path = projectHistoryService.get.getCall(0).args[0];
expect(path).toEqual('path');
const detailsPath = projectService.getProjectDetails.getCall(0).args[0];
expect(detailsPath).toEqual('path');
});

it('should delay get project history', () => {
it('should delay get project history and details', () => {
const projectHistoryService = stubProjectHistoryService();
const globalWindow = stubGlobalWindow();
wrap({ projectHistoryService, globalWindow });

component.debounceGetProjectHistory();
component.debounceGetProjectDetails();

const [delayedMethod, timeout] = globalWindow.setTimeout.getCall(0).args;
expect(delayedMethod()).toEqual(component.getCurrentProjectHistory());
expect(delayedMethod()).toEqual(component.getProjectDetails());
expect(timeout).toBe(400);
expect(globalWindow.clearTimeout.called).toBe(false);
});
Expand All @@ -79,10 +94,52 @@ describe('Generator', () => {
globalWindow.setTimeout.returns(TIMEOUT_ID);
wrap({ projectHistoryService, globalWindow });

component.debounceGetProjectHistory();
component.debounceGetProjectHistory();
component.debounceGetProjectDetails();
component.debounceGetProjectDetails();

const [timeoutId] = globalWindow.clearTimeout.getCall(0).args;
expect(timeoutId).toBe(TIMEOUT_ID);
});

it('should autofill project details when store is updated', () => {
const projectStore = stubProjectStore();
wrap({ projectStore });
const subscription = projectStore.$subscribe.getCall(0).args[0];
const newProject: Project = {
folder: component.project.folder,
baseName: 'projName',
projectName: 'Project Name',
packageName: 'com.project',
serverPort: 8090,
};
subscription(undefined, {
project: newProject,
});
expect(component.project).toEqual(newProject);
});

it('should not autofill project details when store is updated with empty values', () => {
const projectStore = stubProjectStore();
wrap({ projectStore });
const BASE_NAME = 'oldBaseName';
const PROJECT_NAME = 'Project name';
const PACKAGE_NAME = 'com.project.old';
const SERVER_PORT = 9090;
component.project.baseName = BASE_NAME;
component.project.projectName = PROJECT_NAME;
component.project.packageName = PACKAGE_NAME;
component.project.serverPort = SERVER_PORT;

const subscription = projectStore.$subscribe.getCall(0).args[0];
const newProject: Project = {
folder: 'tmp/beer',
};
subscription(undefined, {
project: newProject,
});
expect(component.project.baseName).toBe(BASE_NAME);
expect(component.project.packageName).toBe(PACKAGE_NAME);
expect(component.project.projectName).toBe(PROJECT_NAME);
expect(component.project.serverPort).toBe(SERVER_PORT);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import sinon, { SinonStub } from 'sinon';

export interface ProjectStoreFixture {
getProject: SinonStub;
setProject: SinonStub;
$subscribe: SinonStub;
}

export const stubProjectStore = (): ProjectStoreFixture =>
({
getProject: sinon.stub(),
setProject: sinon.stub(),
$subscribe: sinon.stub(),
} as ProjectStoreFixture);
35 changes: 35 additions & 0 deletions src/test/javascript/spec/springboot/primary/ProjectStore.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createPinia, setActivePinia } from 'pinia';
import { useProjectStore } from '@/springboot/primary/ProjectStore';
import { Project } from '@/springboot/domain/Project';
import { createProject } from '../domain/Project.fixture';

describe('ProjectStore', () => {
beforeEach(() => {
setActivePinia(createPinia());
});

it('should have an empty proejct by default', () => {
const projectStore = useProjectStore();

expect(projectStore.project).toEqual<Project>({
folder: '',
});
});

it('should get project', () => {
const projectStore = useProjectStore();

expect(projectStore.getProject).toEqual<Project>({
folder: '',
});
});

it('should set project', () => {
const projectStore = useProjectStore();
const project = createProject();

projectStore.setProject(project);

expect(projectStore.getProject).toEqual<Project>(project);
});
});
Loading

0 comments on commit d635329

Please sign in to comment.