- Before submitting, ensure that your answers are accurate and complete, and that necessary attachments (including
- the
- signature form) have been uploaded.
+ Before submitting, ensure that your answers are accurate and complete, and that necessary attachments have been uploaded. If NYC Planning does not receive enough accurate information to provide guidance, the Lead Planner will notify you and request that this form be resubmitted with necessary materials, corrections, or
+ clarifications.
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js
index 5ab960b9..54f40237 100644
--- a/client/app/components/projects/new.js
+++ b/client/app/components/projects/new.js
@@ -4,6 +4,7 @@ import { inject as service } from '@ember/service';
import SubmittableProjectsNewForm from '../../validations/submittable-projects-new-form';
import { optionset } from '../../helpers/optionset';
import config from '../../config/environment';
+import validateFileUpload from '../../validators/validate-file-presence';
export default class ProjectsNewFormComponent extends Component {
validations = {
@@ -42,6 +43,17 @@ export default class ProjectsNewFormComponent extends Component {
const contactInputs = [primaryContactInput, applicantInput];
+ const validationResult = validateFileUpload(
+ {
+ message: 'Please upload at least one file before submitting.',
+ },
+ )('documents', this.args.package.documents);
+
+ if (validationResult !== true) {
+ this.errorMessage = validationResult;
+ return;
+ }
+
try {
const contactPromises = contactInputs.map((contact) => this.store.queryRecord('contact', {
email: contact.email,
@@ -96,13 +108,16 @@ export default class ProjectsNewFormComponent extends Component {
dcpApplicanttype: this.args.package.applicantType.code,
dcpProjectbrief: this.args.package.projectBrief,
_dcpApplicantadministratorCustomerValue:
- verifiedPrimaryContact.id,
+ verifiedPrimaryContact.id,
_dcpApplicantCustomerValue: verifiedApplicant.id,
},
},
}),
});
const { data: project } = await response.json();
+
+ this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']);
+
this.router.transitionTo('project', project.id);
} catch {
/* eslint-disable-next-line no-console */
diff --git a/client/app/components/projects/projects-new-attached-documents.hbs b/client/app/components/projects/projects-new-attached-documents.hbs
new file mode 100644
index 00000000..60642d84
--- /dev/null
+++ b/client/app/components/projects/projects-new-attached-documents.hbs
@@ -0,0 +1,23 @@
+{{#let @form as |form|}}
+<@form.Section @title='Attached Documents'>
+
+ Please attach the required items listed on the
+
+ Informational Interest Meeting Checklist
+ in at least one PDF document. The maximum
+ size for a document is 50MB.
+
\ No newline at end of file
diff --git a/client/app/components/projects/projects-new-attachments.js b/client/app/components/projects/projects-new-attachments.js
new file mode 100644
index 00000000..5e81ff89
--- /dev/null
+++ b/client/app/components/projects/projects-new-attachments.js
@@ -0,0 +1,63 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+import validateFileUpload from '../../validators/validate-file-presence';
+
+/**
+ * This component wires a fileManager to the attachments UI.
+ * @param {Artifact Model} artifact
+ */
+export default class ProjectsNewAttachmentsComponent extends Component {
+ get fileManager() {
+ // should be an instance of FileManager
+ return this.args.fileManager;
+ }
+
+ @action
+ markFileForDeletion(file) {
+ this.fileManager.markFileForDeletion(file);
+
+ this.args.package.documents = this.fileManager.existingFiles;
+ }
+
+ @action
+ unmarkFileForDeletion(file) {
+ this.fileManager.unMarkFileForDeletion(file);
+ }
+
+ // This action doesn't perform any file selection.
+ // That part is automatically handled by the
+ // ember-file-upload addon.
+ // Here we manually increment the number of files to
+ // upload to update the fileManager isDirty state.
+ @action
+ trackFileForUpload() {
+ this.fileManager.trackFileForUpload();
+ this.args.package.documents = [
+ ...this.args.package.documents,
+ ...this.fileManager.filesToUpload.files,
+ ];
+ }
+
+ @action
+ deselectFileForUpload(file) {
+ this.fileManager.deselectFileForUpload(file);
+
+ this.args.package.documents = this.args.package.documents.filter(
+ (document) => document !== file,
+ );
+ }
+
+ @action
+ validateFilePresence() {
+ const validationResult = validateFileUpload({ message: 'One or more document uploads is required.' })(
+ 'documents',
+ this.fileManager.filesToUpload.files,
+ );
+
+ if (validationResult !== true) {
+ this.errorMessage = validationResult;
+ } else {
+ this.errorMessage = null;
+ }
+ }
+}
diff --git a/client/app/components/projects/projects-new-project-description.hbs b/client/app/components/projects/projects-new-project-description.hbs
index ecd8a503..f2a33b31 100644
--- a/client/app/components/projects/projects-new-project-description.hbs
+++ b/client/app/components/projects/projects-new-project-description.hbs
@@ -1,16 +1,16 @@
{{#let @form as |form|}}
-
-
-
- Please replace information in the brackets to the best of your ability.
-
+
+
+
+ Please replace information in the brackets to the best of your ability.
+
-
-
+
+
{{/let}}
diff --git a/client/app/models/artifact.js b/client/app/models/artifact.js
index f6baabaf..56667272 100644
--- a/client/app/models/artifact.js
+++ b/client/app/models/artifact.js
@@ -33,6 +33,9 @@ export default class ArtifactModel extends Model {
@belongsTo('project', { async: false })
project;
+ @belongsTo('project-new', { async: false })
+ projectNew
+
@attr()
dcpName;
diff --git a/client/app/models/project-new.js b/client/app/models/project-new.js
new file mode 100644
index 00000000..d49936a4
--- /dev/null
+++ b/client/app/models/project-new.js
@@ -0,0 +1,124 @@
+import Model, { attr, belongsTo } from '@ember-data/model';
+import { inject as service } from '@ember/service';
+import { tracked } from '@glimmer/tracking';
+import FileManager from '../services/file-manager';
+
+export default class ProjectNew extends Model {
+ createFileQueue() {
+ if (this.fileManager) {
+ this.fileManager.existingFiles = this.documents;
+ } else {
+ const fileQueue = this.fileQueue.create(`artifact${this.id}`);
+
+ this.fileManager = new FileManager(
+ this.id,
+ 'artifact',
+ this.documents,
+ [],
+ fileQueue,
+ this.session,
+ );
+ }
+ }
+
+ // Since file upload doesn't perform requests through
+ // an Ember Model save() process, it doesn't automatically
+ // hydrate the package.adapterError property. When an error occurs
+ // during upload we have to manually hydrate a custom error property
+ // to trigger the error box displayed to the user.
+ @tracked
+ fileUploadErrors = null;
+
+ @service
+ session;
+
+ @service
+ fileQueue;
+
+ @belongsTo('projects', { async: false })
+ projects;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ projectName;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ borough;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ applicantType;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ primaryContactFirstName;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ primaryContactLastName;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ primaryContactEmail;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ primaryContactPhone;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ applicantFirstName;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ applicantLastName;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ applicantEmail;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ applicantPhone;
+
+ @attr('string', {
+ defaultValue: '',
+ })
+ projectBrief;
+
+ @attr('string', {
+ defaultValue: () => [],
+ })
+ documents;
+
+ async saveAttachedFiles(instanceId) {
+ try {
+ await this.fileManager.save(instanceId);
+ } catch (e) {
+ console.log('Error saving files: ', e); // eslint-disable-line no-console
+
+ // See comment on the tracked fileUploadError property
+ // definition above.
+ this.fileUploadErrors = [
+ {
+ code: 'UPLOAD_DOC_FAILED',
+ title: 'Failed to upload documents',
+ detail:
+ 'An error occured while uploading your documents. Please refresh and retry.',
+ },
+ ];
+ }
+ }
+}
diff --git a/client/app/routes/projects/new.js b/client/app/routes/projects/new.js
index 731aa2d8..f49105d6 100644
--- a/client/app/routes/projects/new.js
+++ b/client/app/routes/projects/new.js
@@ -1,25 +1,21 @@
import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
+import { inject as service } from '@ember/service';
export default class ProjectsNewRoute extends Route.extend(
AuthenticatedRouteMixin,
) {
authenticationRoute = '/';
+ @service
+ store
+
async model() {
- return {
- projectName: '',
- borough: '',
- applicantType: '',
- primaryContactFirstName: '',
- primaryContactLastName: '',
- primaryContactEmail: '',
- primaryContactPhone: '',
- applicantFirstName: '',
- applicantLastName: '',
- applicantEmail: '',
- applicantPhone: '',
- projectBrief: '',
- };
+ const projectsNewForm = this.store.createRecord('project-new');
+
+ projectsNewForm.id = `new${self.crypto.randomUUID()}`;
+ projectsNewForm.createFileQueue();
+
+ return projectsNewForm;
}
}
diff --git a/client/app/services/file-manager.js b/client/app/services/file-manager.js
index c516b586..e27c748a 100644
--- a/client/app/services/file-manager.js
+++ b/client/app/services/file-manager.js
@@ -12,7 +12,10 @@ export default class FileManager {
filesToUpload, // EmberFileUpload Queue Object
session,
) {
- console.assert(entityType === 'package' || entityType === 'artifact', "entityType must be 'package' or 'artifact'");
+ console.assert(
+ entityType === 'package' || entityType === 'artifact',
+ "entityType must be 'package' or 'artifact'",
+ );
this.recordId = recordId;
this.entityType = entityType;
@@ -61,18 +64,24 @@ export default class FileManager {
this.numFilesToUpload -= 1;
}
- async uploadFiles() {
+ async uploadFiles(instanceId = this.recordId) {
for (let i = 0; i < this.filesToUpload.files.length; i += 1) {
- await this.filesToUpload.files[i].upload(`${ENV.host}/documents/${this.entityType}`, { // eslint-disable-line
- fileKey: 'file',
- headers: {
- Authorization: `Bearer ${this.session.data.authenticated.access_token}`,
+ // eslint-disable-next-line no-await-in-loop
+ await this.filesToUpload.files[i].upload(
+ `${ENV.host}/documents/${this.entityType}`,
+ {
+ // eslint-disable-line
+ fileKey: 'file',
+ headers: {
+ Authorization: `Bearer ${this.session.data.authenticated.access_token}`,
+ },
+ data: {
+ instanceId,
+ entityName:
+ this.entityType === 'artifact' ? 'dcp_artifacts' : 'dcp_package',
+ },
},
- data: {
- instanceId: this.recordId,
- entityName: this.entityType === 'artifact' ? 'dcp_artifacts' : 'dcp_package',
- },
- });
+ );
}
}
@@ -82,21 +91,24 @@ export default class FileManager {
// TODO: If this is not possible, rework this to be a
// POST request to a differently named endpoint, like
// deleteDocument
- return Promise.all(this.filesToDelete.map((file) => fetch(
- `${ENV.host}/documents?serverRelativeUrl=${file.serverRelativeUrl}`, {
- method: 'DELETE',
- credentials: 'include',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${this.session.data.authenticated.access_token}`,
+ return Promise.all(
+ this.filesToDelete.map((file) => fetch(
+ `${ENV.host}/documents?serverRelativeUrl=${file.serverRelativeUrl}`,
+ {
+ method: 'DELETE',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${this.session.data.authenticated.access_token}`,
+ },
},
- },
- )));
+ )),
+ );
}
- async save() {
+ async save(instanceId) {
// See TODO at top of this file.
- await this.uploadFiles();
+ await this.uploadFiles(instanceId);
await this.deleteFiles();
diff --git a/client/app/validations/submittable-projects-new-form.js b/client/app/validations/submittable-projects-new-form.js
index 14603a18..498af0ad 100644
--- a/client/app/validations/submittable-projects-new-form.js
+++ b/client/app/validations/submittable-projects-new-form.js
@@ -3,6 +3,7 @@ import {
validateLength,
validatePresence,
} from 'ember-changeset-validations/validators';
+import validateFileUpload from '../validators/validate-file-presence';
export default {
primaryContactFirstName: [
@@ -137,4 +138,9 @@ export default {
message: 'This field is required',
}),
],
+ documents: [
+ validateFileUpload({
+ message: 'One or more document uploads is required.',
+ }),
+ ],
};
diff --git a/client/app/validators/validate-file-presence.js b/client/app/validators/validate-file-presence.js
new file mode 100644
index 00000000..293e41a0
--- /dev/null
+++ b/client/app/validators/validate-file-presence.js
@@ -0,0 +1,9 @@
+export default function validateFileUpload(options = {}) {
+ return (key, newValue) => {
+ if (!newValue || newValue.length === 0) {
+ return options.message || `${key} one or more document uploads is required.`;
+ }
+
+ return true;
+ };
+}
diff --git a/server/src/artifacts/artifacts.service.ts b/server/src/artifacts/artifacts.service.ts
index 5d732ff1..d5d2fda9 100644
--- a/server/src/artifacts/artifacts.service.ts
+++ b/server/src/artifacts/artifacts.service.ts
@@ -7,6 +7,7 @@ import { ConfigService } from '../config/config.service';
@Injectable()
export class ArtifactService {
rerFiletypeUuid = '';
+ letterFiletypeUuid= '';
constructor(
private readonly crmService: CrmService,
@@ -14,6 +15,7 @@ export class ArtifactService {
private readonly config: ConfigService,
) {
this.rerFiletypeUuid = this.config.get('RER_FILETYPE_UUID');
+ this.letterFiletypeUuid = this.config.get('LETTER_FILETYPE_UUID');
}
public async createEquityReport(projectId: string) {
@@ -45,6 +47,35 @@ export class ArtifactService {
return newArtifact;
}
+ public async createProjectInitiationArtifacts(projectId: string) {
+ let newArtifact = null;
+
+ try {
+ newArtifact = this.crmService.create('dcp_artifactses', {
+ dcp_name: `Project Initiation`,
+ dcp_isdcpinternal: false,
+ dcp_filecreator: 717170000, // Applicant
+ dcp_filecategory: 717170006, // Other
+ dcp_visibility: 717170002, // Applicant Only
+ 'dcp_applicantfiletype@odata.bind': `/dcp_filetypes(${this.letterFiletypeUuid})`,
+ ...(projectId
+ ? { 'dcp_project@odata.bind': `/dcp_projects(${projectId})` }
+ : {}),
+ });
+ } catch (e) {
+ throw new HttpException(
+ {
+ code: 'CREATE_PROJECT_ARTIFACT_ERROR',
+ title: `Unable to create Project Initiation Artifact dcp_artifactses entity for project with UUID ${projectId}`,
+ detail: e,
+ },
+ HttpStatus.INTERNAL_SERVER_ERROR,
+ );
+ }
+
+ return newArtifact;
+ }
+
async getArtifactSharepointDocuments(relativeUrl: string, dcp_name: string) {
if (relativeUrl) {
try {
diff --git a/server/src/projects/projects.controller.ts b/server/src/projects/projects.controller.ts
index 40e2d0ae..9c6370c7 100644
--- a/server/src/projects/projects.controller.ts
+++ b/server/src/projects/projects.controller.ts
@@ -38,6 +38,7 @@ import { INVOICE_ATTRS } from '../invoices/invoices.attrs';
'team-members',
'contacts',
'milestones',
+ 'dcp_artifactsid',
],
packages: {
ref: 'dcp_packageid',
diff --git a/server/src/projects/projects.module.ts b/server/src/projects/projects.module.ts
index 97e4eb17..48b3b693 100644
--- a/server/src/projects/projects.module.ts
+++ b/server/src/projects/projects.module.ts
@@ -6,10 +6,12 @@ import { ConfigModule } from '../config/config.module';
import { AuthModule } from '../auth/auth.module';
import { ProjectsController } from './projects.controller';
import { ProjectApplicantController } from './project-applicants/project-applicant.controller';
+import { ArtifactService } from '../artifacts/artifacts.service';
+import { SharepointModule } from '../sharepoint/sharepoint.module';
@Module({
- imports: [CrmModule, ConfigModule, ContactModule, AuthModule],
- providers: [ProjectsService],
+ imports: [CrmModule, SharepointModule, ConfigModule, ContactModule, AuthModule],
+ providers: [ProjectsService, ArtifactService],
exports: [ProjectsService],
controllers: [ProjectsController, ProjectApplicantController],
})
diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts
index d1364fdd..4d9b2ce5 100644
--- a/server/src/projects/projects.service.ts
+++ b/server/src/projects/projects.service.ts
@@ -4,6 +4,7 @@ import { NycidService } from '../contact/nycid/nycid.service';
import { CrmService } from '../crm/crm.service';
import { overwriteCodesWithLabels } from '../_utils/overwrite-codes-with-labels';
import { MILESTONE_ATTRS, MILESTONE_NON_DATE_ATTRS } from './projects.attrs';
+import { ArtifactService } from '../artifacts/artifacts.service';
const APPLICANT_ACTIVE_STATUS_CODE = 1;
const PROJECT_ACTIVE_STATE_CODE = 0;
@@ -52,6 +53,7 @@ export class ProjectsService {
constructor(
private readonly crmService: CrmService,
private readonly nycidService: NycidService,
+ private readonly artifactService: ArtifactService,
) {}
public async findManyByContactId(contactId: string) {
@@ -122,8 +124,14 @@ export class ProjectsService {
'dcp_projects',
data,
);
+ const { dcp_artifactsid } =
+ await this.artifactService.createProjectInitiationArtifacts(
+ dcp_projectid,
+ );
+
return {
dcp_projectid,
+ dcp_artifactsid,
};
} catch (e) {
console.debug('error creating project', e);
From 4006bf8211a1fe2e92b33fc21dc9e78bb257bbee Mon Sep 17 00:00:00 2001
From: horatio
Date: Fri, 13 Dec 2024 15:01:09 -0500
Subject: [PATCH 02/12] some logging, will probs add much more
---
client/app/components/projects/new.js | 8 +++++++
server/src/projects/projects.controller.ts | 5 ++++
server/src/projects/projects.service.ts | 28 +++++++++++++++++++---
3 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js
index 54f40237..2ab6bc79 100644
--- a/client/app/components/projects/new.js
+++ b/client/app/components/projects/new.js
@@ -11,6 +11,8 @@ export default class ProjectsNewFormComponent extends Component {
SubmittableProjectsNewForm,
};
+ requestCounter = 0;
+
@service
router;
@@ -27,6 +29,9 @@ export default class ProjectsNewFormComponent extends Component {
@action
async submitProject() {
+ const requestStartTime = Date.now();
+ console.log(`[Total Requests Made in the service] ${this.requestCounter}`);
+
const primaryContactInput = {
first: this.args.package.primaryContactFirstName,
last: this.args.package.primaryContactLastName,
@@ -115,6 +120,9 @@ export default class ProjectsNewFormComponent extends Component {
}),
});
const { data: project } = await response.json();
+ const requestEndTime = Date.now();
+ console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`);
+ console.debug('response in client controller: ', response, 'response status: ', response.status);
this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']);
diff --git a/server/src/projects/projects.controller.ts b/server/src/projects/projects.controller.ts
index 9c6370c7..972314ef 100644
--- a/server/src/projects/projects.controller.ts
+++ b/server/src/projects/projects.controller.ts
@@ -83,6 +83,7 @@ import { INVOICE_ATTRS } from '../invoices/invoices.attrs';
@Controller('projects')
export class ProjectsController {
CRM_IMPOSTER_ID = '';
+ requestCounter = 0;
constructor(
private readonly projectsService: ProjectsService,
@@ -132,6 +133,8 @@ export class ProjectsController {
@Post('/')
async createProject(@Body() body) {
+ const requestStartTime = Date.now();
+ console.log(`[Total Requests Made in the controller] ${this.requestCounter}`)
const allowedAttrs = pick(body, PROJECT_ATTRS) as {
dcp_projectname: string;
dcp_borough: string;
@@ -149,6 +152,8 @@ export class ProjectsController {
HttpStatus.NOT_FOUND,
);
}
+ const requestEndTime = Date.now();
+ console.debug(`POST request in the controller to took ${requestEndTime - requestStartTime} ms`);
return await this.projectsService.create(allowedAttrs);
}
diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts
index 4d9b2ce5..55ee048f 100644
--- a/server/src/projects/projects.service.ts
+++ b/server/src/projects/projects.service.ts
@@ -10,6 +10,7 @@ const APPLICANT_ACTIVE_STATUS_CODE = 1;
const PROJECT_ACTIVE_STATE_CODE = 0;
const PROJECT_VISIBILITY_APPLICANT_ONLY = 717170002;
const PROJECT_VISIBILITY_GENERAL_PUBLIC = 717170003;
+let requestCounter = 0;
const PACKAGE_VISIBILITY = {
APPLICANT_ONLY: 717170002,
@@ -112,6 +113,11 @@ export class ProjectsService {
_dcp_applicantadministrator_customer_value: string;
}) {
try {
+ const requestStartTime = Date.now();
+ console.log(`[Total Requests Made in the service] ${requestCounter}`);
+
+ requestCounter++;
+
const data = {
dcp_projectname: attributes.dcp_projectname,
dcp_borough: attributes.dcp_borough,
@@ -120,15 +126,31 @@ export class ProjectsService {
'dcp_applicant_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicant_customer_value})`,
'dcp_applicantadministrator_customer_contact@odata.bind': `/contacts(${attributes._dcp_applicantadministrator_customer_value})`,
};
- const { dcp_projectid } = await this.crmService.create(
+ // const { dcp_projectid } = await this.crmService.create(
+ // 'dcp_projects',
+ // data,
+ // );
+ const project = await this.crmService.create(
'dcp_projects',
data,
);
- const { dcp_artifactsid } =
+ console.debug("project", project);
+ console.debug("project response", project.response);
+ const { dcp_projectid } = project;
+
+ // const { dcp_artifactsid } =
+ // await this.artifactService.createProjectInitiationArtifacts(
+ // dcp_projectid,
+ // );
+
+ const artifact =
await this.artifactService.createProjectInitiationArtifacts(
dcp_projectid,
);
-
+ console.debug('artifact', artifact);
+ const { dcp_artifactsid } = artifact;
+ const requestEndTime = Date.now();
+ console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`);
return {
dcp_projectid,
dcp_artifactsid,
From 7f10a9c0c5ec2ab3643e2dbc07eb42617b77f4e7 Mon Sep 17 00:00:00 2001
From: tangoyankee
Date: Fri, 13 Dec 2024 16:04:43 -0500
Subject: [PATCH 03/12] manually install heroku cli
---
.github/workflows/qa.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml
index bb147417..59c6860d 100644
--- a/.github/workflows/qa.yml
+++ b/.github/workflows/qa.yml
@@ -36,6 +36,8 @@ jobs:
- uses: actions/checkout@v4
with:
sparse-checkout: server
+ - name: 'Install Heroku CLI'
+ run: curl https://cli-assets.heroku.com/install.sh | sh
- uses: akhileshns/heroku-deploy@v3.13.15
name: Deploy server to Heroku
with:
From ec1b5b38ff7c8a8cb5fce9b2fcbadccc42a5308e Mon Sep 17 00:00:00 2001
From: horatio
Date: Wed, 18 Dec 2024 13:51:10 -0500
Subject: [PATCH 04/12] transition to projects list after submit
---
client/app/components/projects/new.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js
index 2ab6bc79..80f9035b 100644
--- a/client/app/components/projects/new.js
+++ b/client/app/components/projects/new.js
@@ -126,7 +126,7 @@ export default class ProjectsNewFormComponent extends Component {
this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']);
- this.router.transitionTo('project', project.id);
+ this.router.transitionTo('projects');
} catch {
/* eslint-disable-next-line no-console */
console.error('Error while creating project');
From d7070da5165bddd3685c5b5f08e7ee379794687d Mon Sep 17 00:00:00 2001
From: horatio
Date: Fri, 27 Dec 2024 12:48:00 -0500
Subject: [PATCH 05/12] some tweaks to the logging
---
client/app/components/projects/new.js | 5 +++--
server/src/projects/projects.controller.ts | 5 +++--
server/src/projects/projects.service.ts | 13 ++++++-------
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js
index 80f9035b..b4753802 100644
--- a/client/app/components/projects/new.js
+++ b/client/app/components/projects/new.js
@@ -30,7 +30,8 @@ export default class ProjectsNewFormComponent extends Component {
@action
async submitProject() {
const requestStartTime = Date.now();
- console.log(`[Total Requests Made in the service] ${this.requestCounter}`);
+ this.requestCounter++;
+ console.log(`LOGGER: [Total Requests Made in the client controller] ${this.requestCounter}`);
const primaryContactInput = {
first: this.args.package.primaryContactFirstName,
@@ -121,7 +122,7 @@ export default class ProjectsNewFormComponent extends Component {
});
const { data: project } = await response.json();
const requestEndTime = Date.now();
- console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`);
+ console.debug(`LOGGER: POST request in the client controller to took ${requestEndTime - requestStartTime} ms`);
console.debug('response in client controller: ', response, 'response status: ', response.status);
this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']);
diff --git a/server/src/projects/projects.controller.ts b/server/src/projects/projects.controller.ts
index 972314ef..ffcc65a3 100644
--- a/server/src/projects/projects.controller.ts
+++ b/server/src/projects/projects.controller.ts
@@ -134,7 +134,6 @@ export class ProjectsController {
@Post('/')
async createProject(@Body() body) {
const requestStartTime = Date.now();
- console.log(`[Total Requests Made in the controller] ${this.requestCounter}`)
const allowedAttrs = pick(body, PROJECT_ATTRS) as {
dcp_projectname: string;
dcp_borough: string;
@@ -153,7 +152,9 @@ export class ProjectsController {
);
}
const requestEndTime = Date.now();
- console.debug(`POST request in the controller to took ${requestEndTime - requestStartTime} ms`);
+ this.requestCounter++;
+ console.log(`LOGGER: [Total Requests Made in the controller] ${this.requestCounter}`)
+ console.debug(`LOGGER: POST request in the controller to took ${requestEndTime - requestStartTime} ms`);
return await this.projectsService.create(allowedAttrs);
}
diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts
index 55ee048f..ceff4675 100644
--- a/server/src/projects/projects.service.ts
+++ b/server/src/projects/projects.service.ts
@@ -114,9 +114,7 @@ export class ProjectsService {
}) {
try {
const requestStartTime = Date.now();
- console.log(`[Total Requests Made in the service] ${requestCounter}`);
- requestCounter++;
const data = {
dcp_projectname: attributes.dcp_projectname,
@@ -134,8 +132,7 @@ export class ProjectsService {
'dcp_projects',
data,
);
- console.debug("project", project);
- console.debug("project response", project.response);
+ console.debug("LOGGER: (service) project", project);
const { dcp_projectid } = project;
// const { dcp_artifactsid } =
@@ -147,16 +144,18 @@ export class ProjectsService {
await this.artifactService.createProjectInitiationArtifacts(
dcp_projectid,
);
- console.debug('artifact', artifact);
+ console.debug('LOGGER: (service) artifact', artifact);
const { dcp_artifactsid } = artifact;
const requestEndTime = Date.now();
- console.debug(`POST request in the service to took ${requestEndTime - requestStartTime} ms`);
+ console.debug(`LOGGER: POST (service) request in the service to took ${requestEndTime - requestStartTime} ms`);
+ requestCounter++;
+ console.log(`LOGGER: [Total Requests Made in the service] ${requestCounter}`);
return {
dcp_projectid,
dcp_artifactsid,
};
} catch (e) {
- console.debug('error creating project', e);
+ console.debug('(service) error creating project', e);
throw new HttpException(
'Unable to create project',
HttpStatus.INTERNAL_SERVER_ERROR,
From f49b954183baffa3802df0615038d8d97bc0670c Mon Sep 17 00:00:00 2001
From: horatio
Date: Tue, 31 Dec 2024 10:28:39 -0500
Subject: [PATCH 06/12] added logging to cms service
---
server/src/crm/crm.service.ts | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/server/src/crm/crm.service.ts b/server/src/crm/crm.service.ts
index 5f0f0d8e..8ef9aba5 100644
--- a/server/src/crm/crm.service.ts
+++ b/server/src/crm/crm.service.ts
@@ -19,6 +19,7 @@ export class CrmService {
crmUrlPath = '';
crmHost = '';
host = '';
+ requestCounter = 0;
constructor(private readonly config: ConfigService) {
ADAL.ADAL_CONFIG = {
@@ -62,7 +63,21 @@ export class CrmService {
}
async create(query, data, headers = {}) {
- return this._create(query, data, headers);
+ try {
+ console.debug(
+ "LOGGER CRM create query", query,
+ "LOGGER CRM create data", data,
+ "LOGGER CRM create headers", headers,
+ );
+ const requestStartTime = Date.now();
+ const response = this._create(query, data, headers);
+ const requestEndTime = Date.now();
+ console.debug(`LOGGER: LOGGER CRM create request took ${requestEndTime - requestStartTime} ms`);
+ return response;
+
+ } catch (e) {
+ console.debug(`LOGGER error in CRM create: ${e}`);
+ }
}
async update(entity, guid, data, headers = {}) {
From a34d21851b58d829b41d6b34861fe2a937048687 Mon Sep 17 00:00:00 2001
From: horatio
Date: Tue, 31 Dec 2024 11:10:00 -0500
Subject: [PATCH 07/12] removed logging of peoject and artifact to reduce noise
in papertrail
---
server/src/projects/projects.service.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts
index ceff4675..c5f67d24 100644
--- a/server/src/projects/projects.service.ts
+++ b/server/src/projects/projects.service.ts
@@ -132,7 +132,7 @@ export class ProjectsService {
'dcp_projects',
data,
);
- console.debug("LOGGER: (service) project", project);
+ // console.debug("LOGGER: (service) project", project);
const { dcp_projectid } = project;
// const { dcp_artifactsid } =
@@ -144,7 +144,7 @@ export class ProjectsService {
await this.artifactService.createProjectInitiationArtifacts(
dcp_projectid,
);
- console.debug('LOGGER: (service) artifact', artifact);
+ // console.debug('LOGGER: (service) artifact', artifact);
const { dcp_artifactsid } = artifact;
const requestEndTime = Date.now();
console.debug(`LOGGER: POST (service) request in the service to took ${requestEndTime - requestStartTime} ms`);
From 580bb36c16468e98dc322c4f8509adcb500b713f Mon Sep 17 00:00:00 2001
From: horatio
Date: Fri, 3 Jan 2025 14:05:58 -0500
Subject: [PATCH 08/12] some guard rails - projects.new and projects.service,
first pass
---
client/app/components/projects/new.js | 7 ++++++-
server/src/projects/projects.service.ts | 13 +++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js
index b4753802..9486e44c 100644
--- a/client/app/components/projects/new.js
+++ b/client/app/components/projects/new.js
@@ -125,7 +125,12 @@ export default class ProjectsNewFormComponent extends Component {
console.debug(`LOGGER: POST request in the client controller to took ${requestEndTime - requestStartTime} ms`);
console.debug('response in client controller: ', response, 'response status: ', response.status);
- this.args.package.saveAttachedFiles(project.attributes['dcp-artifactsid']);
+ const artifactsId = project.attributes['dcp-artifactsid'];
+ if (artifactsId === undefined) {
+ throw new Error('failed to create project with artifact');
+ }
+
+ await this.args.package.saveAttachedFiles(artifactsId);
this.router.transitionTo('projects');
} catch {
diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts
index c5f67d24..e3e90805 100644
--- a/server/src/projects/projects.service.ts
+++ b/server/src/projects/projects.service.ts
@@ -128,6 +128,19 @@ export class ProjectsService {
// 'dcp_projects',
// data,
// );
+ const crmResponse = await this.crmService.get(
+ 'dcp_projects',
+ // 3e5 = created within 5 minutes
+
+ `
+ $filter=
+ dcp_projectname eq '${data.dcp_projectname}'
+ and createdon ge '${new Date(Date.now() - 3e5).toISOString()}'
+ `
+ )
+
+ console.debug('HELLO??: crm response', crmResponse);
+
const project = await this.crmService.create(
'dcp_projects',
data,
From 247514fdcb1d147a44be44e7089f2833ad4f3a09 Mon Sep 17 00:00:00 2001
From: horatio
Date: Mon, 6 Jan 2025 12:12:54 -0500
Subject: [PATCH 09/12] needs error handling and/or redirect
---
server/src/projects/projects.service.ts | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/server/src/projects/projects.service.ts b/server/src/projects/projects.service.ts
index e3e90805..fee58699 100644
--- a/server/src/projects/projects.service.ts
+++ b/server/src/projects/projects.service.ts
@@ -138,8 +138,17 @@ export class ProjectsService {
and createdon ge '${new Date(Date.now() - 3e5).toISOString()}'
`
)
+ const { records } = crmResponse;
+ console.debug('LOGGER: crm response', crmResponse);
+ try {
+ if (records.length > 0) {
+ console.log('Project already exists');
+ throw new Error('Project already exists');
+ }
+ } catch (e) {
+
+ }
- console.debug('HELLO??: crm response', crmResponse);
const project = await this.crmService.create(
'dcp_projects',
From 33bc5aa161e09234cfc13d03248a3183a75dc6fd Mon Sep 17 00:00:00 2001
From: horatio
Date: Wed, 8 Jan 2025 10:43:30 -0500
Subject: [PATCH 10/12] cherrypick changes from ty/1254/upload-documents
---
.github/workflows/production.yml | 2 +
.github/workflows/staging.yml | 2 +
client/app/components/projects/new.hbs | 24 +++++----
client/app/components/projects/new.js | 5 ++
.../projects/projects-new-attachments.hbs | 2 +-
server/src/projects/projects.module.ts | 8 ++-
server/src/projects/projects.service.ts | 50 ++++++-------------
7 files changed, 48 insertions(+), 45 deletions(-)
diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml
index 06c9f178..f47bed15 100644
--- a/.github/workflows/production.yml
+++ b/.github/workflows/production.yml
@@ -36,6 +36,8 @@ jobs:
- uses: actions/checkout@v4
with:
sparse-checkout: server
+ - name: 'Install Heroku CLI'
+ run: curl https://cli-assets.heroku.com/install.sh | sh
- uses: akhileshns/heroku-deploy@v3.13.15
name: Deploy server to Heroku
with:
diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml
index a36f397c..7a2c58ba 100644
--- a/.github/workflows/staging.yml
+++ b/.github/workflows/staging.yml
@@ -40,6 +40,8 @@ jobs:
with:
ref: 'main'
sparse-checkout: server
+ - name: 'Install Heroku CLI'
+ run: curl https://cli-assets.heroku.com/install.sh | sh
- uses: akhileshns/heroku-deploy@v3.13.15
name: Deploy server to Heroku
with:
diff --git a/client/app/components/projects/new.hbs b/client/app/components/projects/new.hbs
index a4c4416e..d9f8bf87 100644
--- a/client/app/components/projects/new.hbs
+++ b/client/app/components/projects/new.hbs
@@ -47,17 +47,23 @@
-
Confirm New Project Submission
-
- Are you sure?
-
-
- Before submitting, ensure that your answers are accurate and complete, and that necessary attachments have been uploaded. If NYC Planning does not receive enough accurate information to provide guidance, the Lead Planner will notify you and request that this form be resubmitted with necessary materials, corrections, or
- clarifications.
-
+ {{#if this.submissionError}}
+
Error while creating project. Lorem Ipsum.....
+ {{else}}
+
+ Are you sure?
+
+
+ Before submitting, ensure that your answers are accurate and complete, and that necessary attachments have been
+ uploaded. If NYC Planning does not receive enough accurate information to provide guidance, the Lead Planner will
+ notify you and request that this form be resubmitted with necessary materials, corrections, or
+ clarifications.
+
+ {{/if}}
diff --git a/client/app/components/projects/new.js b/client/app/components/projects/new.js
index 9486e44c..ef264f66 100644
--- a/client/app/components/projects/new.js
+++ b/client/app/components/projects/new.js
@@ -1,5 +1,6 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
+import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import SubmittableProjectsNewForm from '../../validations/submittable-projects-new-form';
import { optionset } from '../../helpers/optionset';
@@ -11,6 +12,8 @@ export default class ProjectsNewFormComponent extends Component {
SubmittableProjectsNewForm,
};
+ @tracked submissionError = false;
+
requestCounter = 0;
@service
@@ -29,6 +32,7 @@ export default class ProjectsNewFormComponent extends Component {
@action
async submitProject() {
+ this.submissionError = false;
const requestStartTime = Date.now();
this.requestCounter++;
console.log(`LOGGER: [Total Requests Made in the client controller] ${this.requestCounter}`);
@@ -134,6 +138,7 @@ export default class ProjectsNewFormComponent extends Component {
this.router.transitionTo('projects');
} catch {
+ this.submissionError = true;
/* eslint-disable-next-line no-console */
console.error('Error while creating project');
}
diff --git a/client/app/components/projects/projects-new-attachments.hbs b/client/app/components/projects/projects-new-attachments.hbs
index b0ed9444..f771f631 100644
--- a/client/app/components/projects/projects-new-attachments.hbs
+++ b/client/app/components/projects/projects-new-attachments.hbs
@@ -130,4 +130,4 @@
The size limit for each file is 50 MB. You can upload up to 1 GB of files.