From bce9589e4377f5f3f4f2547cadfa576f3b2dd295 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 2 Aug 2017 16:34:18 +0000 Subject: [PATCH 001/686] Spec 30686: Extensible columns. Remove codedprocs module and schema. Moved to SND module and schema. Extensible column setup. --- api-src/org/labkey/api/snd/SNDDomainKind.java | 137 ++++++++ module.properties | 8 + resources/domain-templates/test.template.xml | 24 ++ .../dbscripts/sqlserver/snd-17.20-17.21.sql | 301 ++++++++++++++++++ resources/schemas/snd.xml | 251 +++++++++++++++ src/org/labkey/snd/SNDContainerListener.java | 55 ++++ src/org/labkey/snd/SNDController.java | 51 +++ src/org/labkey/snd/SNDManager.java | 32 ++ src/org/labkey/snd/SNDModule.java | 99 ++++++ src/org/labkey/snd/SNDSchema.java | 49 +++ src/org/labkey/snd/SNDUserSchema.java | 33 ++ src/org/labkey/snd/view/hello.jsp | 25 ++ .../test/components/snd/SNDWebPart.java | 66 ++++ .../org/labkey/test/pages/snd/BeginPage.java | 53 +++ .../org/labkey/test/tests/snd/SNDTest.java | 203 ++++++++++++ 15 files changed, 1387 insertions(+) create mode 100644 api-src/org/labkey/api/snd/SNDDomainKind.java create mode 100644 module.properties create mode 100644 resources/domain-templates/test.template.xml create mode 100644 resources/schemas/dbscripts/sqlserver/snd-17.20-17.21.sql create mode 100644 resources/schemas/snd.xml create mode 100644 src/org/labkey/snd/SNDContainerListener.java create mode 100644 src/org/labkey/snd/SNDController.java create mode 100644 src/org/labkey/snd/SNDManager.java create mode 100644 src/org/labkey/snd/SNDModule.java create mode 100644 src/org/labkey/snd/SNDSchema.java create mode 100644 src/org/labkey/snd/SNDUserSchema.java create mode 100644 src/org/labkey/snd/view/hello.jsp create mode 100644 test/src/org/labkey/test/components/snd/SNDWebPart.java create mode 100644 test/src/org/labkey/test/pages/snd/BeginPage.java create mode 100644 test/src/org/labkey/test/tests/snd/SNDTest.java diff --git a/api-src/org/labkey/api/snd/SNDDomainKind.java b/api-src/org/labkey/api/snd/SNDDomainKind.java new file mode 100644 index 000000000..a4bab059c --- /dev/null +++ b/api-src/org/labkey/api/snd/SNDDomainKind.java @@ -0,0 +1,137 @@ +package org.labkey.api.snd; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.exp.ChangePropertyDescriptorException; +import org.labkey.api.exp.Handler; +import org.labkey.api.exp.Lsid; +import org.labkey.api.exp.TemplateInfo; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.exp.property.DomainUtil; +import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.gwt.client.model.GWTDomain; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; +import org.labkey.api.query.SimpleTableDomainKind; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.view.ActionURL; +import org.labkey.api.writer.ContainerUser; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by marty on 7/11/2017. + */ +public class SNDDomainKind extends SimpleTableDomainKind +{ + private final String NAMESPACE_PREFIX = "snd"; + private final String SCHEMA_NAME = "snd"; + private final String KIND_NAME = "SND"; + + + @Override + public boolean canCreateDefinition(User user, Container container) + { + return container.hasPermission("SNDDomainKind.canCreateDefinition", user, AdminPermission.class); + } + + @Override + public Domain createDomain(GWTDomain gwtDomain, Map arguments, Container container, User user, TemplateInfo templateInfo) + { + if (gwtDomain.getName() == null) + throw new IllegalArgumentException("table name is required"); + + String domainURI = generateDomainURI(SCHEMA_NAME, gwtDomain.getName(), container, user); + Domain domain = PropertyService.get().getDomain(container, domainURI); + + if( null != domain ) + { + GWTDomain existingDomain = DomainUtil.getDomainDescriptor(user, domainURI, container); + GWTDomain updatedDomain = new GWTDomain(existingDomain); + updatedDomain.setFields(gwtDomain.getFields()); + + updateDomain(existingDomain, updatedDomain, container, user); + } + else + { + List properties = gwtDomain.getFields(); + domain = PropertyService.get().createDomain(container, domainURI, gwtDomain.getName(), templateInfo); + + Set propertyUris = new HashSet<>(); + Map defaultValues = new HashMap<>(); + try + { + for (GWTPropertyDescriptor pd : properties) + { + DomainUtil.addProperty(domain, pd, defaultValues, propertyUris, null); + } + domain.save(user); + } + catch (ChangePropertyDescriptorException e) + { + throw new RuntimeException(e); + } + } + return domain; + } + + @Override + public String getKindName() + { + return KIND_NAME; + } + + @Override + public String getTypeLabel(Domain domain) + { + return domain.getName(); + } + + @Override + public SQLFragment sqlObjectIdsInDomain(Domain domain) + { + return null; + } + + @Override + public ActionURL urlShowData(Domain domain, ContainerUser containerUser) + { + return null; + } + + @Override + public + @Nullable ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) + { + return null; + } + + @Override + public Set getReservedPropertyNames(Domain domain) + { + Set result = new HashSet<>(); + result.add("Description"); + result.add("Active"); + result.add("ObjectId"); + result.add("QcState"); + result.add("Container"); + result.add("CreatedBy"); + result.add("Created"); + result.add("ModifiedBy"); + result.add("Modified"); + return result; + } + + @Override + public Handler.Priority getPriority(String domainURI) + { + Lsid lsid = new Lsid(domainURI); + return lsid.getNamespacePrefix() != null && lsid.getNamespacePrefix().startsWith(NAMESPACE_PREFIX) ? Handler.Priority.MEDIUM : null; + } +} diff --git a/module.properties b/module.properties new file mode 100644 index 000000000..a04850d6a --- /dev/null +++ b/module.properties @@ -0,0 +1,8 @@ +ModuleClass: org.labkey.snd.SNDModule +Label: FIXME! Capitalized Short Module Summary +Description: FIXME! Write a paragraph describing the functionality and the \ + purpose of this module. The description should contain multiple scentences. +License: Apache 2.0 +LicenseURL: http://www.apache.org/licenses/LICENSE-2.0 +SupportedDatabases: mssql +ModuleDependencies: Experiment diff --git a/resources/domain-templates/test.template.xml b/resources/domain-templates/test.template.xml new file mode 100644 index 000000000..f4cb0ccb8 --- /dev/null +++ b/resources/domain-templates/test.template.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/resources/schemas/dbscripts/sqlserver/snd-17.20-17.21.sql b/resources/schemas/dbscripts/sqlserver/snd-17.20-17.21.sql new file mode 100644 index 000000000..97a977d38 --- /dev/null +++ b/resources/schemas/dbscripts/sqlserver/snd-17.20-17.21.sql @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- Create schema, tables, indexes, and constraints used for SND module here +-- All SQL VIEW definitions should be created in snd-create.sql and dropped in snd-drop.sql + +EXEC core.fn_dropifexists 'CodedEvents','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'EventNotes','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'EventsCache','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'PkgCategoryJunction','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'ProjectItems','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'SuperPkgs','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'PkgCategories','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'Pkgs','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'Events','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists 'Projects','codedprocs','TABLE'; +GO + +EXEC core.fn_dropifexists NULL,'codedprocs','SCHEMA'; +GO + +CREATE SCHEMA snd; +GO + +/*==============================================================*/ +/* Table: Pkgs */ +/*==============================================================*/ +CREATE TABLE snd.Pkgs ( + PkgId INTEGER NOT NULL, + Description NVARCHAR(4000) NOT NULL, + Active BIT, + Repeatable BIT, + QcState INTEGER, + ObjectId UNIQUEIDENTIFIER NOT NULL DEFAULT newid(), + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_PKGS PRIMARY KEY (PkgId), + CONSTRAINT FK_SND_PKGS_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_PKGS_QCSTATE FOREIGN Key (QcState) REFERENCES core.QCState (RowId) + +) +GO + +CREATE INDEX IDX_SND_PKGS_CONTAINER ON snd.Pkgs(Container); +CREATE INDEX IDX_SND_PKGS_QCSTATE ON snd.Pkgs(QcState); +GO + +/*==============================================================*/ +/* Table: SuperPkgs */ +/*==============================================================*/ +CREATE TABLE snd.SuperPkgs ( + SuperPkgId INTEGER NOT NULL, + ParentSuperPkgId INTEGER, + PkgId INTEGER NOT NULL, + SuperPkgPath VARCHAR(900) NOT NULL, + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_SUPERPKGS PRIMARY KEY (SuperPkgId), + CONSTRAINT FK_SND_SUPERPKGS_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_SUPERPKGS_PKGID FOREIGN KEY (PkgId) REFERENCES snd.Pkgs (PkgId) + +) +GO + +CREATE INDEX IDX_SND_SUPERPKGS_CONTAINER ON snd.SuperPkgs(Container); +CREATE INDEX IDX_SND_SUPERPKGS_PKGID ON snd.SuperPkgs(PkgId); +GO + +/*==============================================================*/ +/* Table: PkgCategories */ +/*==============================================================*/ +CREATE TABLE snd.PkgCategories ( + CategoryId INTEGER NOT NULL, + Description NVARCHAR(4000) NOT NULL, + Comment NVARCHAR(4000), + Active BIT NOT NULL, + SortOrder INTEGER, + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_PKGCATEGORIES PRIMARY KEY (CategoryId), + CONSTRAINT FK_SND_PKGCATEGORIES_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId) + +) +GO + +CREATE INDEX IDX_SND_PKGCATEGORIES_CONTAINER ON snd.PkgCategories(Container); +GO + +/*==============================================================*/ +/* Table: PkgCategoryJunction */ +/*==============================================================*/ +CREATE TABLE snd.PkgCategoryJunction ( + PkgId INTEGER NOT NULL, + CategoryId INTEGER NOT NULL, + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_PKGCATEGORYJUNCTION PRIMARY KEY (PkgId, CategoryId), + CONSTRAINT FK_SND_PKGCATEGORYJUNCTION_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_PKGCATEGORYJUNCTION_PKGID FOREIGN KEY (PkgId) REFERENCES snd.Pkgs (PkgId), + CONSTRAINT FK_SND_PKGCATEGORYJUNCTION_CATEGORYID FOREIGN KEY (CategoryId) REFERENCES snd.PkgCategories (CategoryId) + +) +GO + +CREATE INDEX IDX_SND_PKGCATEGORYJUNCTION_CONTAINER ON snd.PkgCategoryJunction(Container); +CREATE INDEX IDX_SND_PKGCATEGORYJUNCTION_PKGID ON snd.PkgCategoryJunction(PkgId); +CREATE INDEX IDX_SND_PKGCATEGORYJUNCTION_CATEGORYID ON snd.PkgCategoryJunction(CategoryId); +GO + +/*==============================================================*/ +/* Table: Projects */ +/*==============================================================*/ +CREATE TABLE snd.Projects ( + ProjectId INTEGER NOT NULL, + RevisionNum INTEGER NOT NULL, + ReferenceId INTEGER NOT NULL, + StartDate date NOT NULL, + EndDate date, + Description NVARCHAR(4000) NOT NULL, + ObjectId UNIQUEIDENTIFIER NOT NULL DEFAULT newid(), + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_PROJECTS PRIMARY KEY (ProjectId, RevisionNum), + CONSTRAINT FK_SND_PROJECTS_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId) + +) +GO + +CREATE INDEX IDX_SND_PROJECTS_CONTAINER ON snd.Projects(Container); +CREATE UNIQUE INDEX IDX_SND_PROJECTS_OBJECTID ON snd.Projects(ObjectId); +GO + +/*==============================================================*/ +/* Table: ProjectItems */ +/*==============================================================*/ +CREATE TABLE snd.ProjectItems ( + ProjectItemId INTEGER IDENTITY, + ParentObjectId UNIQUEIDENTIFIER, + SuperPkgId INTEGER NOT NULL, + Active BIT NOT NULL DEFAULT 1, + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_PROJECTITEMS PRIMARY KEY (ProjectItemId), + CONSTRAINT FK_SND_PROJECTITEMS_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_PROJECTITEMS_SUPERPKGID FOREIGN KEY (SuperPkgId) REFERENCES snd.SuperPkgs (SuperPkgId), + CONSTRAINT FK_SND_PROJECTITEMS_PARENTOBJECTID FOREIGN KEY (ParentObjectId) REFERENCES snd.Projects (ObjectId) + +) +GO + +CREATE INDEX IDX_SND_PROJECTITEMS_CONTAINER ON snd.ProjectItems(Container); +CREATE INDEX IDX_SND_PROJECTITEMS_SUPERPKGID ON snd.ProjectItems(SuperPkgId); +CREATE INDEX IDX_SND_PROJECTITEMS_PARENTOBJECTID ON snd.ProjectItems(ParentObjectId); +GO + +/*==============================================================*/ +/* Table: Events */ +/*==============================================================*/ +CREATE TABLE snd.Events ( + EventId INTEGER NOT NULL, + Id NVARCHAR(32) NOT NULL, + ParentObjectId UNIQUEIDENTIFIER, + Date DATETIME NOT NULL, + QcState int, + ObjectId UNIQUEIDENTIFIER NOT NULL DEFAULT newid(), + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_EVENTS PRIMARY KEY (EventId), + CONSTRAINT FK_SND_EVENTS_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_EVENTS_QCSTATE FOREIGN Key (QcState) REFERENCES core.QCState (RowId), + CONSTRAINT FK_SND_EVENTS_PARENTOBJECTID FOREIGN KEY (ParentObjectId) REFERENCES snd.Projects (ObjectId) + +) +GO + +CREATE INDEX IDX_SND_EVENTS_CONTAINER ON snd.Events(Container); +CREATE INDEX IDX_SND_EVENTS_QCSTATE ON snd.Events(QcState); +CREATE INDEX IDX_SND_EVENTS_PARENTOBJECTID ON snd.Events(ParentObjectId); +GO + +/*==============================================================*/ +/* Table: CodedEvents */ +/*==============================================================*/ +CREATE TABLE snd.CodedEvents ( + CodedEventId INTEGER IDENTITY, + EventId INTEGER NOT NULL, + SuperPkgId INTEGER NOT NULL, + ObjectId UNIQUEIDENTIFIER NULL DEFAULT newid(), + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_CODEDEVENTS PRIMARY KEY (CodedEventId), + CONSTRAINT FK_SND_CODEDEVENTS_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_CODEDEVENTS_EVENTID FOREIGN KEY (EventId) REFERENCES snd.Events (EventId), + CONSTRAINT FK_SND_CODEDEVENTS_SUPERPKGID FOREIGN KEY (SuperPkgId) REFERENCES snd.SuperPkgs(SuperPkgId) + +) +GO + +CREATE INDEX IDX_SND_CODEDEVENTS_CONTAINER ON snd.CodedEvents(Container); +CREATE INDEX IDX_SND_CODEDEVENTS_EVENTID ON snd.CodedEvents(EventId); +CREATE INDEX IDX_SND_CODEDEVENTS_SUPERPKGID ON snd.CodedEvents(SuperPkgId); +GO + +/*==============================================================*/ +/* Table: EventNotes */ +/*==============================================================*/ +CREATE TABLE snd.EventNotes ( + EventNoteId INTEGER IDENTITY, + EventId INTEGER, + Note NVARCHAR(MAX) NOT NULL, + Container ENTITYID NOT NULL, + CreatedBy USERID, + Created DATETIME, + ModifiedBy USERID, + Modified DATETIME, + Lsid LSIDType, + + CONSTRAINT PK_SND_EVENTNOTES PRIMARY KEY (EventNoteId), + CONSTRAINT FK_SND_EVENTNOTES_CONTAINER FOREIGN KEY (Container) REFERENCES core.Containers (EntityId), + CONSTRAINT FK_SND_EVENTNOTES_EVENTNOTEID FOREIGN KEY (EventNoteId) REFERENCES snd.Events (EventId) + +) +GO + +CREATE INDEX IDX_SND_EVENTNOTES_CONTAINER ON snd.EventNotes(Container); +CREATE INDEX IDX_SND_EVENTNOTES_EVENTNOTEID ON snd.EventNotes(EventNoteId); +GO + + + diff --git a/resources/schemas/snd.xml b/resources/schemas/snd.xml new file mode 100644 index 000000000..f739003d5 --- /dev/null +++ b/resources/schemas/snd.xml @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+ + + + + + + + + + + + lsidtype + true + true + false + true + + ObjectUri + Object + exp + + + +
+
\ No newline at end of file diff --git a/src/org/labkey/snd/SNDContainerListener.java b/src/org/labkey/snd/SNDContainerListener.java new file mode 100644 index 000000000..1ca1008d8 --- /dev/null +++ b/src/org/labkey/snd/SNDContainerListener.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.snd; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager.ContainerListener; +import org.labkey.api.security.User; +import java.util.Collections; +import java.util.Collection; + +import java.beans.PropertyChangeEvent; + +public class SNDContainerListener implements ContainerListener +{ + @Override + public void containerCreated(Container c, User user) + { + } + + @Override + public void containerDeleted(Container c, User user) + { + } + + @Override + public void propertyChange(PropertyChangeEvent evt) + { + } + + @Override + public void containerMoved(Container c, Container oldParent, User user) + { + } + + @NotNull @Override + public Collection canMove(Container c, Container newParent, User user) + { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java new file mode 100644 index 000000000..aa47b9bca --- /dev/null +++ b/src/org/labkey/snd/SNDController.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.snd; + +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.action.SpringActionController; +import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.view.JspView; +import org.labkey.api.view.NavTree; +import org.springframework.validation.BindException; +import org.springframework.web.servlet.ModelAndView; + +public class SNDController extends SpringActionController +{ + private static final DefaultActionResolver _actionResolver = new DefaultActionResolver(SNDController.class); + public static final String NAME = "snd"; + + public SNDController() + { + setActionResolver(_actionResolver); + } + + @RequiresPermission(ReadPermission.class) + public class BeginAction extends SimpleViewAction + { + public ModelAndView getView(Object o, BindException errors) throws Exception + { + return new JspView("/org/labkey/snd/view/hello.jsp"); + } + + public NavTree appendNavTrail(NavTree root) + { + return root; + } + } +} \ No newline at end of file diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java new file mode 100644 index 000000000..361bd1f91 --- /dev/null +++ b/src/org/labkey/snd/SNDManager.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.snd; + +public class SNDManager +{ + private static final SNDManager _instance = new SNDManager(); + + private SNDManager() + { + // prevent external construction with a private default constructor + } + + public static SNDManager get() + { + return _instance; + } +} \ No newline at end of file diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java new file mode 100644 index 000000000..0b89203a4 --- /dev/null +++ b/src/org/labkey/snd/SNDModule.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.snd; + +import org.jetbrains.annotations.NotNull; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.module.DefaultModule; +import org.labkey.api.module.ModuleContext; +import org.labkey.api.view.WebPartFactory; +import org.labkey.api.snd.SNDDomainKind; +import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.query.DefaultSchema; +import org.labkey.api.module.Module; +import org.labkey.api.query.QuerySchema; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +public class SNDModule extends DefaultModule +{ + public static final String NAME = "SND"; + + @Override + public String getName() + { + return NAME; + } + + @Override + public double getVersion() + { + return 17.21; + } + + @Override + public boolean hasScripts() + { + return true; + } + + @Override + @NotNull + protected Collection createWebPartFactories() + { + return Collections.emptyList(); + } + + @Override + protected void init() + { + addController(SNDController.NAME, SNDController.class); + PropertyService.get().registerDomainKind(new SNDDomainKind()); + } + + @Override + public void doStartup(ModuleContext moduleContext) + { + // add a container listener so we'll know when our container is deleted: + ContainerManager.addContainerListener(new SNDContainerListener()); + + DefaultSchema.registerProvider(SNDSchema.NAME, new DefaultSchema.SchemaProvider(this) + { + public QuerySchema createSchema(final DefaultSchema schema, Module module) + { + return new SNDUserSchema(SNDSchema.NAME, null, schema.getUser(), schema.getContainer(), SNDSchema.getInstance().getSchema()); + } + }); + } + + @Override + @NotNull + public Collection getSummary(Container c) + { + return Collections.emptyList(); + } + + @Override + @NotNull + public Set getSchemaNames() + { + return Collections.singleton(SNDSchema.NAME); + } +} \ No newline at end of file diff --git a/src/org/labkey/snd/SNDSchema.java b/src/org/labkey/snd/SNDSchema.java new file mode 100644 index 000000000..d7fa9481a --- /dev/null +++ b/src/org/labkey/snd/SNDSchema.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.snd; + +import org.labkey.api.data.DbSchema; +import org.labkey.api.data.DbSchemaType; +import org.labkey.api.data.dialect.SqlDialect; + +public class SNDSchema +{ + private static final SNDSchema _instance = new SNDSchema(); + public static final String NAME = "snd"; + + public static SNDSchema getInstance() + { + return _instance; + } + + private SNDSchema() + { + // private constructor to prevent instantiation from + // outside this class: this singleton should only be + // accessed via org.labkey.snd.SNDSchema.getInstance() + } + + public DbSchema getSchema() + { + return DbSchema.get(NAME, DbSchemaType.Module); + } + + public SqlDialect getSqlDialect() + { + return getSchema().getSqlDialect(); + } +} diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java new file mode 100644 index 000000000..307853890 --- /dev/null +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.snd; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbSchema; +import org.labkey.api.query.SimpleUserSchema; +import org.labkey.api.security.User; + + +public class SNDUserSchema extends SimpleUserSchema +{ + public SNDUserSchema(String name, @Nullable String description, User user, Container container, DbSchema dbschema) + { + super(name, description, user, container, dbschema); + } + + +} \ No newline at end of file diff --git a/src/org/labkey/snd/view/hello.jsp b/src/org/labkey/snd/view/hello.jsp new file mode 100644 index 000000000..fbc53d256 --- /dev/null +++ b/src/org/labkey/snd/view/hello.jsp @@ -0,0 +1,25 @@ +<% +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +%> +<%@ page import="org.labkey.api.data.Container" %> +<%@ page import="org.labkey.api.security.User" %> +<%@ page extends="org.labkey.api.jsp.JspBase" %> +<% + Container c = getContainer(); + User user = getUser(); +%> +
Hello, and welcome to the SND module.
\ No newline at end of file diff --git a/test/src/org/labkey/test/components/snd/SNDWebPart.java b/test/src/org/labkey/test/components/snd/SNDWebPart.java new file mode 100644 index 000000000..89995ffc1 --- /dev/null +++ b/test/src/org/labkey/test/components/snd/SNDWebPart.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.components.snd; + +import org.labkey.test.Locator; +import org.labkey.test.components.BodyWebPart; +import org.labkey.test.components.html.Input; +import org.labkey.test.pages.LabKeyPage; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import static org.labkey.test.components.html.Input.Input; + +/** + * Component for a hypothetical webpart containing an input and a save button + */ +public class SNDWebPart extends BodyWebPart +{ + public SNDWebPart(WebDriver driver) + { + this(driver, 0); + } + + public SNDWebPart(WebDriver driver, int index) + { + super(driver, "SND", index); + } + + public SNDWebPart setInput(String value) + { + elementCache().input.set(value); + return this; + } + + public LabKeyPage clickSave() + { + getWrapper().clickAndWait(elementCache().button); + return new LabKeyPage(getDriver()); + } + + @Override + protected ElementCache newElementCache() + { + return new ElementCache(); + } + + protected class ElementCache extends BodyWebPart.ElementCache + { + protected WebElement button = Locator.tag("button").withText("Save").findWhenNeeded(this); + protected Input input = Input(Locator.tag("input"), getDriver()).findWhenNeeded(this); + } +} \ No newline at end of file diff --git a/test/src/org/labkey/test/pages/snd/BeginPage.java b/test/src/org/labkey/test/pages/snd/BeginPage.java new file mode 100644 index 000000000..300d9beb9 --- /dev/null +++ b/test/src/org/labkey/test/pages/snd/BeginPage.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.pages.snd; + +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.Locator; +import org.labkey.test.WebTestHelper; +import org.labkey.test.pages.LabKeyPage; +import org.openqa.selenium.WebElement; + +public class BeginPage extends LabKeyPage +{ + public BeginPage(BaseWebDriverTest test) + { + super(test); + } + + public static BeginPage beginAt(BaseWebDriverTest test, String containerPath) + { + test.beginAt(WebTestHelper.buildURL("snd", containerPath, "begin")); + return new BeginPage(test); + } + + public String getHelloMessage() + { + return elementCache().helloMessage.getText(); + } + + @Override + protected ElementCache newElementCache() + { + return new ElementCache(); + } + + protected class ElementCache extends LabKeyPage.ElementCache + { + WebElement helloMessage = Locator.tagWithName("div", "helloMessage").findWhenNeeded(this); + } +} diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java new file mode 100644 index 000000000..e4c527383 --- /dev/null +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.labkey.test.tests.snd; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.labkey.remoteapi.CommandException; +import org.labkey.remoteapi.Connection; +import org.labkey.remoteapi.query.DeleteRowsCommand; +import org.labkey.remoteapi.query.Filter; +import org.labkey.remoteapi.query.InsertRowsCommand; +import org.labkey.remoteapi.query.SaveRowsResponse; +import org.labkey.remoteapi.query.SelectRowsCommand; +import org.labkey.remoteapi.query.SelectRowsResponse; +import org.labkey.test.BaseWebDriverTest; +import org.labkey.test.Locator; +import org.labkey.test.TestTimeoutException; +import org.labkey.test.categories.CustomModules; +import org.labkey.test.pages.snd.BeginPage; +import org.labkey.test.util.LogMethod; +import org.labkey.test.util.Maps; +import org.labkey.test.util.PasswordUtil; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +@Category ({CustomModules.class}) +public class SNDTest extends BaseWebDriverTest +{ + private static final String PROJECTNAME = "SNDTest Project"; + private static final String TEST1SUBFOLDER = "Test1"; + private static final String TEST1PATH = PROJECTNAME + "/" + TEST1SUBFOLDER; + private static final String PKGSTESTCOL = "testPkgs"; + private static final String EXTCOLTESTDATA1 = "testString 1"; + private static final String EXTCOLTESTDATA2 = "testString 2"; + private static final String EXTCOLTESTDATA3 = "testString 3"; + + private static final String CREATEDOMAINSAPI = "LABKEY.Domain.create({\n" + + " success: this.success,\n" + + " failure: this.failure,\n" + + " domainGroup: \"test\",\n" + + " domainKind: \"SND\",\n" + + " module: \"snd\",\n" + + " importData: false\n" + + "});\n"; + + private final Map TEST1ROW1MAP = Maps.of("PkgId", 1001, "Description", "Description 1", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223451b901", "testPkgs", EXTCOLTESTDATA1); + private final Map TEST1ROW2MAP = Maps.of("PkgId", 1002, "Description", "Description 2", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223751b901", "testPkgs", EXTCOLTESTDATA2); + private final Map TEST1ROW3MAP = Maps.of("PkgId", 1003, "Description", "Description 3", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223481b901", "testPkgs", EXTCOLTESTDATA3); + + + @Override + protected void doCleanup(boolean afterTest) throws TestTimeoutException + { + if(isTextPresent(PROJECTNAME)) + { + try + { + deleteTest1Data(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + _containerHelper.deleteProject(getProjectName(), afterTest); + } + + // If values exist this will delete them. Path of project folder must be valid. + protected void deleteIfNeeded(String path, String schemaName, String queryName, Map map, String pkName) throws IOException, CommandException + { + Connection cn = createDefaultConnection(false); + + SelectRowsCommand selectCmd = new SelectRowsCommand(schemaName, queryName); + selectCmd.addFilter(new Filter(pkName, map.get(pkName))); + SelectRowsResponse srr = selectCmd.execute(cn, path); + + if (srr.getRowCount().intValue() > 0) + { + DeleteRowsCommand deleteCmd = new DeleteRowsCommand(schemaName, queryName); + deleteCmd.addRow(map); + deleteCmd.execute(cn, path); + } + } + + @LogMethod + private void deleteTest1Data() throws CommandException, IOException + { + if (_containerHelper.doesFolderExist(PROJECTNAME, PROJECTNAME, TEST1SUBFOLDER)) + { + log("Deleting test 1 data"); + + deleteIfNeeded(TEST1PATH, "snd", "Pkgs", TEST1ROW1MAP, "PkgId"); + deleteIfNeeded(TEST1PATH, "snd", "Pkgs", TEST1ROW2MAP, "PkgId"); + deleteIfNeeded(TEST1PATH, "snd", "Pkgs", TEST1ROW3MAP, "PkgId"); + } + } + + @BeforeClass + public static void setupProject() + { + SNDTest init = (SNDTest) getCurrentTest(); + + init.doSetup(); + } + + private void doSetup() + { + _containerHelper.createProject(getProjectName(), "Collaboration"); + _containerHelper.enableModules(Arrays.asList("SND")); + _containerHelper.createSubfolder(getProjectName(), getProjectName(), TEST1SUBFOLDER, "Collaboration", new String[]{"SND"}); + setupTest1Project(); + clickFolder(getProjectName()); + } + + private void setupTest1Project() + { + clickFolder(TEST1SUBFOLDER); + addTestColumns(); + } + + private void addTestColumns() + { + executeScript(CREATEDOMAINSAPI); + } + + @Before + public void preTest() + { + goToProjectHome(); + } + + @Test + public void testSNDModule() + { + _containerHelper.enableModule("SND"); + BeginPage beginPage = BeginPage.beginAt(this, getProjectName()); + assertEquals(200, getResponseCode()); + final String expectedHello = "Hello, and welcome to the SND module."; + assertEquals("Wrong hello message", expectedHello, beginPage.getHelloMessage()); + } + + @Override + protected BrowserType bestBrowser() + { + return BrowserType.CHROME; + } + + @Override + protected String getProjectName() + { + return "SNDTest Project"; + } + + @Override + public List getAssociatedModules() + { + return Collections.singletonList("SND"); + } + + @Test + public void testExtensibleColumns() throws IOException, CommandException + { + clickFolder(TEST1SUBFOLDER); + + Connection cn = new Connection(getBaseURL(), PasswordUtil.getUsername(), PasswordUtil.getPassword()); + + InsertRowsCommand insertRowsCommand = new InsertRowsCommand("snd", "Pkgs"); + insertRowsCommand.addRow(TEST1ROW1MAP); + insertRowsCommand.addRow(TEST1ROW2MAP); + insertRowsCommand.addRow(TEST1ROW3MAP); + SaveRowsResponse resp = insertRowsCommand.execute(cn, getProjectName() + "/" + TEST1SUBFOLDER); + assert resp.getRowsAffected().intValue() == 3; + + goToSchemaBrowser(); + selectQuery("snd", "Pkgs"); + assertTextPresent(PKGSTESTCOL); + waitAndClickAndWait(Locator.linkWithText("view data")); + + assertTextPresent(EXTCOLTESTDATA1, EXTCOLTESTDATA2, EXTCOLTESTDATA3); + } +} \ No newline at end of file From abaa4261825e407725fa5e664a7cec39cf63fcc8 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 2 Aug 2017 18:46:34 +0000 Subject: [PATCH 002/686] Spec 30686: Factor common extensible domain kind code into parent class. --- api-src/org/labkey/api/snd/SNDDomainKind.java | 92 ++----------------- 1 file changed, 8 insertions(+), 84 deletions(-) diff --git a/api-src/org/labkey/api/snd/SNDDomainKind.java b/api-src/org/labkey/api/snd/SNDDomainKind.java index a4bab059c..b85d42ed4 100644 --- a/api-src/org/labkey/api/snd/SNDDomainKind.java +++ b/api-src/org/labkey/api/snd/SNDDomainKind.java @@ -1,34 +1,18 @@ package org.labkey.api.snd; -import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; -import org.labkey.api.data.SQLFragment; -import org.labkey.api.exp.ChangePropertyDescriptorException; -import org.labkey.api.exp.Handler; -import org.labkey.api.exp.Lsid; -import org.labkey.api.exp.TemplateInfo; import org.labkey.api.exp.property.Domain; -import org.labkey.api.exp.property.DomainProperty; -import org.labkey.api.exp.property.DomainUtil; -import org.labkey.api.exp.property.PropertyService; -import org.labkey.api.gwt.client.model.GWTDomain; -import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; -import org.labkey.api.query.SimpleTableDomainKind; +import org.labkey.api.query.ExtendedTableDomainKind; import org.labkey.api.security.User; import org.labkey.api.security.permissions.AdminPermission; -import org.labkey.api.view.ActionURL; -import org.labkey.api.writer.ContainerUser; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; /** * Created by marty on 7/11/2017. */ -public class SNDDomainKind extends SimpleTableDomainKind +public class SNDDomainKind extends ExtendedTableDomainKind { private final String NAMESPACE_PREFIX = "snd"; private final String SCHEMA_NAME = "snd"; @@ -42,74 +26,21 @@ public boolean canCreateDefinition(User user, Container container) } @Override - public Domain createDomain(GWTDomain gwtDomain, Map arguments, Container container, User user, TemplateInfo templateInfo) + protected String getSchemaName() { - if (gwtDomain.getName() == null) - throw new IllegalArgumentException("table name is required"); - - String domainURI = generateDomainURI(SCHEMA_NAME, gwtDomain.getName(), container, user); - Domain domain = PropertyService.get().getDomain(container, domainURI); - - if( null != domain ) - { - GWTDomain existingDomain = DomainUtil.getDomainDescriptor(user, domainURI, container); - GWTDomain updatedDomain = new GWTDomain(existingDomain); - updatedDomain.setFields(gwtDomain.getFields()); - - updateDomain(existingDomain, updatedDomain, container, user); - } - else - { - List properties = gwtDomain.getFields(); - domain = PropertyService.get().createDomain(container, domainURI, gwtDomain.getName(), templateInfo); - - Set propertyUris = new HashSet<>(); - Map defaultValues = new HashMap<>(); - try - { - for (GWTPropertyDescriptor pd : properties) - { - DomainUtil.addProperty(domain, pd, defaultValues, propertyUris, null); - } - domain.save(user); - } - catch (ChangePropertyDescriptorException e) - { - throw new RuntimeException(e); - } - } - return domain; - } - - @Override - public String getKindName() - { - return KIND_NAME; - } - - @Override - public String getTypeLabel(Domain domain) - { - return domain.getName(); - } - - @Override - public SQLFragment sqlObjectIdsInDomain(Domain domain) - { - return null; + return SCHEMA_NAME; } @Override - public ActionURL urlShowData(Domain domain, ContainerUser containerUser) + protected String getNamespacePrefix() { - return null; + return NAMESPACE_PREFIX; } @Override - public - @Nullable ActionURL urlEditDefinition(Domain domain, ContainerUser containerUser) + public String getKindName() { - return null; + return KIND_NAME; } @Override @@ -127,11 +58,4 @@ public Set getReservedPropertyNames(Domain domain) result.add("Modified"); return result; } - - @Override - public Handler.Priority getPriority(String domainURI) - { - Lsid lsid = new Lsid(domainURI); - return lsid.getNamespacePrefix() != null && lsid.getNamespacePrefix().startsWith(NAMESPACE_PREFIX) ? Handler.Priority.MEDIUM : null; - } } From a89fa5cedc71c4e34a48816fa9671bd6036e5b84 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Thu, 3 Aug 2017 01:03:36 +0000 Subject: [PATCH 003/686] Spec 30686: Extensible tables. Automated tests. --- .../org/labkey/test/tests/snd/SNDTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index e4c527383..9e8ccb9a8 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -28,6 +28,7 @@ import org.labkey.remoteapi.query.SaveRowsResponse; import org.labkey.remoteapi.query.SelectRowsCommand; import org.labkey.remoteapi.query.SelectRowsResponse; +import org.labkey.remoteapi.query.UpdateRowsCommand; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; @@ -55,6 +56,7 @@ public class SNDTest extends BaseWebDriverTest private static final String EXTCOLTESTDATA1 = "testString 1"; private static final String EXTCOLTESTDATA2 = "testString 2"; private static final String EXTCOLTESTDATA3 = "testString 3"; + private static final String EXTCOLTESTDATA3A = "updated testString 3"; private static final String CREATEDOMAINSAPI = "LABKEY.Domain.create({\n" + " success: this.success,\n" + @@ -68,6 +70,7 @@ public class SNDTest extends BaseWebDriverTest private final Map TEST1ROW1MAP = Maps.of("PkgId", 1001, "Description", "Description 1", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223451b901", "testPkgs", EXTCOLTESTDATA1); private final Map TEST1ROW2MAP = Maps.of("PkgId", 1002, "Description", "Description 2", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223751b901", "testPkgs", EXTCOLTESTDATA2); private final Map TEST1ROW3MAP = Maps.of("PkgId", 1003, "Description", "Description 3", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223481b901", "testPkgs", EXTCOLTESTDATA3); + private final Map TEST1ROW3AMAP = Maps.of("PkgId", 1003, "Description", "Updated Description 3", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223481b901", "testPkgs", EXTCOLTESTDATA3A); @Override @@ -199,5 +202,29 @@ public void testExtensibleColumns() throws IOException, CommandException waitAndClickAndWait(Locator.linkWithText("view data")); assertTextPresent(EXTCOLTESTDATA1, EXTCOLTESTDATA2, EXTCOLTESTDATA3); + + clickFolder(TEST1SUBFOLDER); + + UpdateRowsCommand updateRowsCommand = new UpdateRowsCommand("snd", "Pkgs"); + updateRowsCommand.addRow(TEST1ROW3AMAP); + resp = updateRowsCommand.execute(cn, getProjectName() + "/" + TEST1SUBFOLDER); + assert resp.getRowsAffected().intValue() == 1; + + goToSchemaBrowser(); + selectQuery("snd", "Pkgs"); + waitAndClickAndWait(Locator.linkWithText("view data")); + assertTextPresent(EXTCOLTESTDATA1, EXTCOLTESTDATA2, EXTCOLTESTDATA3A, "Updated Description 3"); + + clickFolder(TEST1SUBFOLDER); + + DeleteRowsCommand deleteRowsCommand = new DeleteRowsCommand("snd", "Pkgs"); + deleteRowsCommand.addRow(TEST1ROW2MAP); + resp = deleteRowsCommand.execute(cn, getProjectName() + "/" + TEST1SUBFOLDER); + assert resp.getRowsAffected().intValue() == 1; + + goToSchemaBrowser(); + selectQuery("snd", "Pkgs"); + waitAndClickAndWait(Locator.linkWithText("view data")); + assertTextNotPresent(EXTCOLTESTDATA2, "Description 2"); } } \ No newline at end of file From 134bb447d272d87b1462e3d2294464e7841a5138 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Thu, 3 Aug 2017 01:10:08 +0000 Subject: [PATCH 004/686] Spec 30686: Update test --- test/src/org/labkey/test/tests/snd/SNDTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 9e8ccb9a8..03a387516 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -59,8 +59,9 @@ public class SNDTest extends BaseWebDriverTest private static final String EXTCOLTESTDATA3A = "updated testString 3"; private static final String CREATEDOMAINSAPI = "LABKEY.Domain.create({\n" + - " success: this.success,\n" + - " failure: this.failure,\n" + + " failure: function (e) {\n" + + " LABKEY.Utils.alert(\"Error\", e.exception);\n" + + " },\n" + " domainGroup: \"test\",\n" + " domainKind: \"SND\",\n" + " module: \"snd\",\n" + From a551dc1812f0582f6a0684acb3a353eb5b1ea43e Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 4 Aug 2017 15:39:53 +0000 Subject: [PATCH 005/686] SND module is mssql only --- test/src/org/labkey/test/tests/snd/SNDTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 03a387516..1d4944440 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -37,6 +37,7 @@ import org.labkey.test.util.LogMethod; import org.labkey.test.util.Maps; import org.labkey.test.util.PasswordUtil; +import org.labkey.test.util.SqlserverOnlyTest; import java.io.IOException; import java.util.Arrays; @@ -47,7 +48,7 @@ import static org.junit.Assert.assertEquals; @Category ({CustomModules.class}) -public class SNDTest extends BaseWebDriverTest +public class SNDTest extends BaseWebDriverTest implements SqlserverOnlyTest { private static final String PROJECTNAME = "SNDTest Project"; private static final String TEST1SUBFOLDER = "Test1"; From 88edabb3faf5d559b92ac2a518c78a3f187daf24 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Fri, 4 Aug 2017 21:00:26 +0000 Subject: [PATCH 006/686] Spec 30931: Infrastructure for package management. --- api-src/org/labkey/api/snd/SNDPackage.java | 100 +++++++++++++++++++++ api-src/org/labkey/api/snd/SNDService.java | 20 +++++ src/org/labkey/snd/SNDModule.java | 11 ++- src/org/labkey/snd/SNDServiceImpl.java | 24 +++++ 4 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 api-src/org/labkey/api/snd/SNDPackage.java create mode 100644 api-src/org/labkey/api/snd/SNDService.java create mode 100644 src/org/labkey/snd/SNDServiceImpl.java diff --git a/api-src/org/labkey/api/snd/SNDPackage.java b/api-src/org/labkey/api/snd/SNDPackage.java new file mode 100644 index 000000000..61bc678d5 --- /dev/null +++ b/api-src/org/labkey/api/snd/SNDPackage.java @@ -0,0 +1,100 @@ +package org.labkey.api.snd; + +import org.labkey.api.exp.PropertyDescriptor; + +import java.util.Collection; + +/** + * Created by marty on 8/4/2017. + */ +public class SNDPackage +{ + private int pkgId; + private String description; + private boolean repeatable; + private boolean draft; + private Collection categories; + private Collection attributes; + private Collection subpackages; + private Collection extraFields; + + public int getPkgId() + { + return pkgId; + } + + public void setPkgId(int pkgId) + { + this.pkgId = pkgId; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public boolean isRepeatable() + { + return repeatable; + } + + public void setRepeatable(boolean repeatable) + { + this.repeatable = repeatable; + } + + public boolean isDraft() + { + return draft; + } + + public void setDraft(boolean draft) + { + this.draft = draft; + } + + public Collection getCategories() + { + return categories; + } + + public void setCategories(Collection categories) + { + this.categories = categories; + } + + public Collection getAttributes() + { + return attributes; + } + + public void setAttributes(Collection attributes) + { + this.attributes = attributes; + } + + public Collection getSubpackages() + { + return subpackages; + } + + public void setSubpackages(Collection subpackages) + { + this.subpackages = subpackages; + } + + public Collection getExtraFields() + { + return extraFields; + } + + public void setExtraFields(Collection extraFields) + { + this.extraFields = extraFields; + } +} diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java new file mode 100644 index 000000000..c7d6c2bb4 --- /dev/null +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -0,0 +1,20 @@ +package org.labkey.api.snd; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.services.ServiceRegistry; + +import java.util.List; + +/** + * Created by marty on 8/4/2017. + */ +public interface SNDService +{ + @Nullable + static SNDService get() + { + return ServiceRegistry.get(SNDService.class); + } + + List savePackage(SNDPackage pkg); +} diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java index 0b89203a4..1b33b96b3 100644 --- a/src/org/labkey/snd/SNDModule.java +++ b/src/org/labkey/snd/SNDModule.java @@ -19,14 +19,16 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; +import org.labkey.api.exp.property.PropertyService; import org.labkey.api.module.DefaultModule; +import org.labkey.api.module.Module; import org.labkey.api.module.ModuleContext; -import org.labkey.api.view.WebPartFactory; -import org.labkey.api.snd.SNDDomainKind; -import org.labkey.api.exp.property.PropertyService; import org.labkey.api.query.DefaultSchema; -import org.labkey.api.module.Module; import org.labkey.api.query.QuerySchema; +import org.labkey.api.services.ServiceRegistry; +import org.labkey.api.snd.SNDDomainKind; +import org.labkey.api.snd.SNDService; +import org.labkey.api.view.WebPartFactory; import java.util.Collection; import java.util.Collections; @@ -66,6 +68,7 @@ protected void init() { addController(SNDController.NAME, SNDController.class); PropertyService.get().registerDomainKind(new SNDDomainKind()); + ServiceRegistry.get().registerService(SNDService.class, SNDServiceImpl.INSTANCE); } @Override diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java new file mode 100644 index 000000000..c71050e22 --- /dev/null +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -0,0 +1,24 @@ +package org.labkey.snd; + +import org.labkey.api.snd.SNDPackage; +import org.labkey.api.snd.SNDService; + +import java.util.Collections; +import java.util.List; + +/** + * Created by marty on 8/4/2017. + */ +public class SNDServiceImpl implements SNDService +{ + public static final SNDServiceImpl INSTANCE = new SNDServiceImpl(); + + private SNDServiceImpl() {} + + public List savePackage(SNDPackage pkg) + { + List errors = Collections.emptyList(); + + return errors; + } +} From 8d796edb112fc034dafaf4254ed8bec44aa9342a Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 7 Aug 2017 15:43:20 +0000 Subject: [PATCH 007/686] Fix test --- test/src/org/labkey/test/tests/snd/SNDTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 1d4944440..eebd9df64 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -78,6 +78,7 @@ public class SNDTest extends BaseWebDriverTest implements SqlserverOnlyTest @Override protected void doCleanup(boolean afterTest) throws TestTimeoutException { + openProjectMenu(); if(isTextPresent(PROJECTNAME)) { try From 1b31af14c7b39727059fd4bba085e0883131cf76 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 7 Aug 2017 23:53:22 +0000 Subject: [PATCH 008/686] Fix test --- test/src/org/labkey/test/tests/snd/SNDTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index eebd9df64..2fc1e5ece 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -134,6 +134,7 @@ public static void setupProject() private void doSetup() { _containerHelper.createProject(getProjectName(), "Collaboration"); + goToProjectHome(); _containerHelper.enableModules(Arrays.asList("SND")); _containerHelper.createSubfolder(getProjectName(), getProjectName(), TEST1SUBFOLDER, "Collaboration", new String[]{"SND"}); setupTest1Project(); From 38a6faaf3716f2b342168b8e4315273dff6e1dee Mon Sep 17 00:00:00 2001 From: Josh Date Date: Tue, 8 Aug 2017 18:01:59 +0000 Subject: [PATCH 009/686] SND - react.js set up for snd module --- .babelrc | 11 + package.json | 132 ++++++ resources/views/app.html | 1 + resources/views/app.view.xml | 9 + resources/views/appDev.html | 2 + resources/views/appDev.view.xml | 6 + src/client/app.tsx | 72 +++ src/client/containers/App/App.tsx | 21 + src/client/reducers/index.ts | 31 ++ src/client/routing/Routes.tsx | 0 src/client/typings/globals.d.ts | 17 + src/client/typings/main.d.ts | 18 + src/client/typings/moment.d.ts | 729 ++++++++++++++++++++++++++++++ tsconfig.json | 15 + webpack/dev-server.js | 33 ++ webpack/dev.config.js | 50 ++ webpack/hot.config.js | 44 ++ webpack/prod.config.js | 44 ++ 18 files changed, 1235 insertions(+) create mode 100644 .babelrc create mode 100644 package.json create mode 100644 resources/views/app.html create mode 100644 resources/views/app.view.xml create mode 100644 resources/views/appDev.html create mode 100644 resources/views/appDev.view.xml create mode 100644 src/client/app.tsx create mode 100644 src/client/containers/App/App.tsx create mode 100644 src/client/reducers/index.ts create mode 100644 src/client/routing/Routes.tsx create mode 100644 src/client/typings/globals.d.ts create mode 100644 src/client/typings/main.d.ts create mode 100644 src/client/typings/moment.d.ts create mode 100644 tsconfig.json create mode 100644 webpack/dev-server.js create mode 100644 webpack/dev.config.js create mode 100644 webpack/hot.config.js create mode 100644 webpack/prod.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..590e79205 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": ["es2015", "react"], + "env": { + "development": { + "presets": ["react-hmre"] + }, + "test": { + "plugins": ["transform-es2015-modules-commonjs"] + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..f1b3df148 --- /dev/null +++ b/package.json @@ -0,0 +1,132 @@ +{ + "name": "SND", + "version": "1.0.0", + "description": "Structured Narrative Datasets", + "scripts": { + "build": "better-npm-run build:prod", + "build-dev": "better-npm-run build:dev", + "build-prod": "npm run build", + "clean": "better-npm-run clean", + "setup": "npm install", + "start": "better-npm-run build:watch", + "test": "better-npm-run build:jest-test", + "teamcity": "better-npm-run build:jest-teamcity" + }, + "betterScripts": { + "build:prod": { + "command": "webpack --config webpack/prod.config.js", + "env": { + "NODE_ENV": "production" + } + }, + "build:dev": { + "command": "webpack --config webpack/dev.config.js" + }, + "build:watch": { + "command": "node webpack/dev-server.js", + "env": { + "NODE_ENV": "development" + } + }, + "build:jest-test": { + "command": "jest", + "env": { + "NODE_ENV": "test" + } + }, + "build:jest-teamcity": { + "command": "jest --testResultsProcessor=jest-teamcity-reporter", + "env": { + "NODE_ENV": "test" + } + }, + "clean": { + "command": "rimraf resources/web/snd/" + } + }, + "jest": { + "globals": { + "LABKEY": {} + }, + "transform": { + ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" + }, + "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js" + ], + "moduleDirectories": [ + "node_modules" + ] + }, + "repository": { + "type": "svn", + "url": "https://github.com/LabKey" + }, + "author": "joshd@labkey.com", + "license": "ISC", + "bugs": { + + }, + "homepage": "https://github.com/LabKey", + "dependencies": { + "@types/react": "15.6.0", + "@types/react-bootstrap": "0.0.52", + "@types/react-dom": "15.5.1", + "@types/react-redux": "4.4.47", + "@types/react-router": "4.0.14", + "@types/react-router-dom": "4.0.7", + "@types/react-router-redux": "5.0.4", + "@types/redux-actions": "1.2.7", + "@types/redux-form": "7.0.1", + "history": "4.6.3", + "moment": "2.18.1", + "prop-types": "15.5.10", + "react": "15.6.1", + "react-bootstrap": "0.31.1", + "react-dom": "15.6.1", + "react-redux": "5.0.5", + "react-router": "4.1.2", + "react-router-dom": "4.1.2", + "react-router-redux": "5.0.0-alpha.6", + "redux": "3.7.2", + "redux-actions": "2.2.1", + "redux-form": "7.0.3", + "redux-thunk": "2.2.0" + }, + "devDependencies": { + "@types/enzyme": "2.8.4", + "@types/jest": "20.0.5", + "@types/react-test-renderer": "15.5.2", + "@types/redux-mock-store": "0.0.9", + "babel-core": "6.25.0", + "babel-jest": "20.0.3", + "babel-loader": "7.1.1", + "babel-plugin-react-transform": "2.0.2", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", + "babel-preset-es2015": "6.24.1", + "babel-preset-react": "6.24.1", + "babel-preset-react-hmre": "1.1.1", + "better-npm-run": "0.0.15", + "cors": "2.8.4", + "enzyme": "2.9.1", + "express": "4.15.3", + "express-http-proxy": "1.0.6", + "extract-text-webpack-plugin": "3.0.0", + "jest": "20.0.4", + "jest-teamcity-reporter": "0.6.2", + "react-test-renderer": "15.6.1", + "react-transform-hmr": "1.0.4", + "redux-devtools-extension": "2.13.2", + "redux-mock-store": "1.2.3", + "rimraf": "2.6.1", + "ts-jest": "20.0.7", + "ts-loader": "2.3.2", + "typescript": "2.4.2", + "webpack": "3.4.1", + "webpack-dev-middleware": "1.11.0", + "webpack-hot-middleware": "2.18.2" + } +} diff --git a/resources/views/app.html b/resources/views/app.html new file mode 100644 index 000000000..865e670f4 --- /dev/null +++ b/resources/views/app.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/resources/views/app.view.xml b/resources/views/app.view.xml new file mode 100644 index 000000000..db06e33f9 --- /dev/null +++ b/resources/views/app.view.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/views/appDev.html b/resources/views/appDev.html new file mode 100644 index 000000000..dca3e6d85 --- /dev/null +++ b/resources/views/appDev.html @@ -0,0 +1,2 @@ +
+ \ No newline at end of file diff --git a/resources/views/appDev.view.xml b/resources/views/appDev.view.xml new file mode 100644 index 000000000..6ce75ddfa --- /dev/null +++ b/resources/views/appDev.view.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/client/app.tsx b/src/client/app.tsx new file mode 100644 index 000000000..8baf75acd --- /dev/null +++ b/src/client/app.tsx @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// React +import * as React from 'react'; +import * as ReactDom from 'react-dom'; + +// Redux +import { applyMiddleware, compose, createStore } from 'redux'; +import { Provider } from 'react-redux'; +/* eslint-disable no-unused-vars*/ +import { composeWithDevTools } from 'redux-devtools-extension'; + +// Routing +import { createBrowserHistory, createHashHistory } from 'history'; +/* eslint-enable no-unused-vars*/ +import { Route } from 'react-router-dom'; +import { ConnectedRouter, routerMiddleware } from 'react-router-redux'; + +// Middleware +import thunk from 'redux-thunk'; + +// Components +import { App } from './containers/App/App'; + +// Misc +declare const jQuery: Function; + +// Reducers +import { reducers } from './reducers/index'; + +// switch history to createBrowser history after servlet work +const history = createHashHistory(); +const middleware = applyMiddleware(thunk, routerMiddleware(history)); + +const storeCreator = compose( + middleware +)(createStore); +const store = storeCreator( + reducers + // uncomment following line to view redux dev tools in chrome + // ,composeWithDevTools() +); + +jQuery(() => { + ReactDom.render( + +
+ { /* ConnectedRouter will use the store from Provider automatically */ } + +
+ +
+
+
+
, + document.getElementById('app') + ); +}); \ No newline at end of file diff --git a/src/client/containers/App/App.tsx b/src/client/containers/App/App.tsx new file mode 100644 index 000000000..5352809af --- /dev/null +++ b/src/client/containers/App/App.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Route, RouteComponentProps, RouteProps, Switch } from 'react-router-dom'; + + +function mapStateToProps(state: any) { + return { + + } +} +export class AppImpl extends React.Component { + render() { + return ( +
+ Hi, I'm your home page....eventually +
+ ) + } +} + +export const App = connect(mapStateToProps)(AppImpl); \ No newline at end of file diff --git a/src/client/reducers/index.ts b/src/client/reducers/index.ts new file mode 100644 index 000000000..01ac9ff41 --- /dev/null +++ b/src/client/reducers/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { routerReducer } from 'react-router-redux'; +import { combineReducers } from 'redux'; +import { reducer as formReducer } from 'redux-form'; + + +export interface APP_STATE_PROPS { + +} + +export const reducers = combineReducers({ + form: formReducer, + router: routerReducer, +}); + + + diff --git a/src/client/routing/Routes.tsx b/src/client/routing/Routes.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/typings/globals.d.ts b/src/client/typings/globals.d.ts new file mode 100644 index 000000000..9a5fd9d4a --- /dev/null +++ b/src/client/typings/globals.d.ts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +declare var LABKEY: any; +declare var window: Window; diff --git a/src/client/typings/main.d.ts b/src/client/typings/main.d.ts new file mode 100644 index 000000000..fa4dca14d --- /dev/null +++ b/src/client/typings/main.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* App globals */ +/// +/// \ No newline at end of file diff --git a/src/client/typings/moment.d.ts b/src/client/typings/moment.d.ts new file mode 100644 index 000000000..aebd57bb2 --- /dev/null +++ b/src/client/typings/moment.d.ts @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +declare function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, strict?: boolean): moment.Moment; +declare function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, language?: string, strict?: boolean): moment.Moment; + +declare namespace moment { + type RelativeTimeKey = 's' | 'm' | 'mm' | 'h' | 'hh' | 'd' | 'dd' | 'M' | 'MM' | 'y' | 'yy'; + type CalendarKey = 'sameDay' | 'nextDay' | 'lastDay' | 'nextWeek' | 'lastWeek' | 'sameElse' | string; + type LongDateFormatKey = 'LTS' | 'LT' | 'L' | 'LL' | 'LLL' | 'LLLL' | 'lts' | 'lt' | 'l' | 'll' | 'lll' | 'llll'; + + interface Locale { + calendar(key?: CalendarKey, m?: Moment, now?: Moment): string; + + longDateFormat(key: LongDateFormatKey): string; + invalidDate(): string; + ordinal(n: number): string; + + preparse(inp: string): string; + postformat(inp: string): string; + relativeTime(n: number, withoutSuffix: boolean, + key: RelativeTimeKey, isFuture: boolean): string; + pastFuture(diff: number, absRelTime: string): string; + set(config: Object): void; + + months(): string[]; + months(m: Moment, format?: string): string; + monthsShort(): string[]; + monthsShort(m: Moment, format?: string): string; + monthsParse(monthName: string, format: string, strict: boolean): number; + monthsRegex(strict: boolean): RegExp; + monthsShortRegex(strict: boolean): RegExp; + + week(m: Moment): number; + firstDayOfYear(): number; + firstDayOfWeek(): number; + + weekdays(): string[]; + weekdays(m: Moment, format?: string): string; + weekdaysMin(): string[]; + weekdaysMin(m: Moment): string; + weekdaysShort(): string[]; + weekdaysShort(m: Moment): string; + weekdaysParse(weekdayName: string, format: string, strict: boolean): number; + weekdaysRegex(strict: boolean): RegExp; + weekdaysShortRegex(strict: boolean): RegExp; + weekdaysMinRegex(strict: boolean): RegExp; + + isPM(input: string): boolean; + meridiem(hour: number, minute: number, isLower: boolean): string; + } + + interface StandaloneFormatSpec { + format: string[]; + standalone: string[]; + isFormat?: RegExp; + } + + interface WeekSpec { + dow: number; + doy: number; + } + + type CalendarSpecVal = string | ((m?: MomentInput, now?: Moment) => string); + interface CalendarSpec { + sameDay?: CalendarSpecVal; + nextDay?: CalendarSpecVal; + lastDay?: CalendarSpecVal; + nextWeek?: CalendarSpecVal; + lastWeek?: CalendarSpecVal; + sameElse?: CalendarSpecVal; + + // any additional properties might be used with moment.calendarFormat + [x: string]: CalendarSpecVal | void; // undefined + } + + type RelativeTimeSpecVal = ( + string | + ((n: number, withoutSuffix: boolean, + key: RelativeTimeKey, isFuture: boolean) => string) + ); + type RelativeTimeFuturePastVal = string | ((relTime: string) => string); + + interface RelativeTimeSpec { + future: RelativeTimeFuturePastVal; + past: RelativeTimeFuturePastVal; + s: RelativeTimeSpecVal; + m: RelativeTimeSpecVal; + mm: RelativeTimeSpecVal; + h: RelativeTimeSpecVal; + hh: RelativeTimeSpecVal; + d: RelativeTimeSpecVal; + dd: RelativeTimeSpecVal; + M: RelativeTimeSpecVal; + MM: RelativeTimeSpecVal; + y: RelativeTimeSpecVal; + yy: RelativeTimeSpecVal; + } + + interface LongDateFormatSpec { + LTS: string; + LT: string; + L: string; + LL: string; + LLL: string; + LLLL: string; + + // lets forget for a sec that any upper/lower permutation will also work + lts?: string; + lt?: string; + l?: string; + ll?: string; + lll?: string; + llll?: string; + } + + type MonthWeekdayFn = (momentToFormat: Moment, format?: string) => string; + type WeekdaySimpleFn = (momentToFormat: Moment) => string; + + interface LocaleSpecification { + months?: string[] | StandaloneFormatSpec | MonthWeekdayFn; + monthsShort?: string[] | StandaloneFormatSpec | MonthWeekdayFn; + + weekdays?: string[] | StandaloneFormatSpec | MonthWeekdayFn; + weekdaysShort?: string[] | StandaloneFormatSpec | WeekdaySimpleFn; + weekdaysMin?: string[] | StandaloneFormatSpec | WeekdaySimpleFn; + + meridiemParse?: RegExp; + meridiem?: (hour: number, minute:number, isLower: boolean) => string; + + isPM?: (input: string) => boolean; + + longDateFormat?: LongDateFormatSpec; + calendar?: CalendarSpec; + relativeTime?: RelativeTimeSpec; + invalidDate?: string; + ordinal?: (n: number) => string; + ordinalParse?: RegExp; + + week?: WeekSpec; + + // Allow anything: in general any property that is passed as locale spec is + // put in the locale object so it can be used by locale functions + [x: string]: any; + } + + interface MomentObjectOutput { + years: number; + /* One digit */ + months: number; + /* Day of the month */ + date: number; + hours: number; + minutes: number; + seconds: number; + milliseconds: number; + } + + interface Duration { + humanize(withSuffix?: boolean): string; + + abs(): Duration; + + as(units: unitOfTime.Base): number; + get(units: unitOfTime.Base): number; + + milliseconds(): number; + asMilliseconds(): number; + + seconds(): number; + asSeconds(): number; + + minutes(): number; + asMinutes(): number; + + hours(): number; + asHours(): number; + + days(): number; + asDays(): number; + + weeks(): number; + asWeeks(): number; + + months(): number; + asMonths(): number; + + years(): number; + asYears(): number; + + add(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration; + subtract(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration; + + locale(): string; + locale(locale: LocaleSpecifier): Duration; + localeData(): Locale; + + toISOString(): string; + toJSON(): string; + + /** + * @deprecated since version 2.8.0 + */ + lang(locale: LocaleSpecifier): Moment; + /** + * @deprecated since version 2.8.0 + */ + lang(): Locale; + /** + * @deprecated + */ + toIsoString(): string; + } + + interface MomentRelativeTime { + future: any; + past: any; + s: any; + m: any; + mm: any; + h: any; + hh: any; + d: any; + dd: any; + M: any; + MM: any; + y: any; + yy: any; + } + + interface MomentLongDateFormat { + L: string; + LL: string; + LLL: string; + LLLL: string; + LT: string; + LTS: string; + + l?: string; + ll?: string; + lll?: string; + llll?: string; + lt?: string; + lts?: string; + } + + interface MomentParsingFlags { + empty: boolean; + unusedTokens: string[]; + unusedInput: string[]; + overflow: number; + charsLeftOver: number; + nullInput: boolean; + invalidMonth: string | void; // null + invalidFormat: boolean; + userInvalidated: boolean; + iso: boolean; + parsedDateParts: any[]; + meridiem: string | void; // null + } + + interface MomentParsingFlagsOpt { + empty?: boolean; + unusedTokens?: string[]; + unusedInput?: string[]; + overflow?: number; + charsLeftOver?: number; + nullInput?: boolean; + invalidMonth?: string; + invalidFormat?: boolean; + userInvalidated?: boolean; + iso?: boolean; + parsedDateParts?: any[]; + meridiem?: string; + } + + interface MomentBuiltinFormat { + __momentBuiltinFormatBrand: any; + } + + type MomentFormatSpecification = string | MomentBuiltinFormat | (string | MomentBuiltinFormat)[]; + + namespace unitOfTime { + type Base = ( + "year" | "years" | "y" | + "month" | "months" | "M" | + "week" | "weeks" | "w" | + "day" | "days" | "d" | + "hour" | "hours" | "h" | + "minute" | "minutes" | "m" | + "second" | "seconds" | "s" | + "millisecond" | "milliseconds" | "ms" + ); + + type _quarter = "quarter" | "quarters" | "Q"; + type _isoWeek = "isoWeek" | "isoWeeks" | "W"; + type _date = "date" | "dates" | "D"; + type DurationConstructor = Base | _quarter; + + type DurationAs = Base; + + type StartOf = Base | _quarter | _isoWeek | _date; + + type Diff = Base | _quarter; + + type MomentConstructor = Base | _date; + + type All = Base | _quarter | _isoWeek | _date | + "weekYear" | "weekYears" | "gg" | + "isoWeekYear" | "isoWeekYears" | "GG" | + "dayOfYear" | "dayOfYears" | "DDD" | + "weekday" | "weekdays" | "e" | + "isoWeekday" | "isoWeekdays" | "E"; + } + + interface MomentInputObject { + years?: number; + year?: number; + y?: number; + + months?: number; + month?: number; + M?: number; + + days?: number; + day?: number; + d?: number; + + dates?: number; + date?: number; + D?: number; + + hours?: number; + hour?: number; + h?: number; + + minutes?: number; + minute?: number; + m?: number; + + seconds?: number; + second?: number; + s?: number; + + milliseconds?: number; + millisecond?: number; + ms?: number; + } + + interface DurationInputObject extends MomentInputObject { + quarters?: number; + quarter?: number; + Q?: number; + + weeks?: number; + week?: number; + w?: number; + } + + interface MomentSetObject extends MomentInputObject { + weekYears?: number; + weekYear?: number; + gg?: number; + + isoWeekYears?: number; + isoWeekYear?: number; + GG?: number; + + quarters?: number; + quarter?: number; + Q?: number; + + weeks?: number; + week?: number; + w?: number; + + isoWeeks?: number; + isoWeek?: number; + W?: number; + + dayOfYears?: number; + dayOfYear?: number; + DDD?: number; + + weekdays?: number; + weekday?: number; + e?: number; + + isoWeekdays?: number; + isoWeekday?: number; + E?: number; + } + + interface FromTo { + from: MomentInput; + to: MomentInput; + } + + type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | void; // null | undefined + type DurationInputArg1 = Duration | number | string | FromTo | DurationInputObject | void; // null | undefined + type DurationInputArg2 = unitOfTime.DurationConstructor; + type LocaleSpecifier = string | Moment | Duration | string[] | boolean; + + interface MomentCreationData { + input: MomentInput; + format?: MomentFormatSpecification; + locale: Locale; + isUTC: boolean; + strict?: boolean; + } + + interface Moment extends Object{ + format(format?: string): string; + + startOf(unitOfTime: unitOfTime.StartOf): Moment; + endOf(unitOfTime: unitOfTime.StartOf): Moment; + + add(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment; + /** + * @deprecated reverse syntax + */ + add(unit: unitOfTime.DurationConstructor, amount: number|string): Moment; + + subtract(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment; + /** + * @deprecated reverse syntax + */ + subtract(unit: unitOfTime.DurationConstructor, amount: number|string): Moment; + + calendar(time?: MomentInput, formats?: CalendarSpec): string; + + clone(): Moment; + + /** + * @return Unix timestamp in milliseconds + */ + valueOf(): number; + + // current date/time in local mode + local(keepLocalTime?: boolean): Moment; + isLocal(): boolean; + + // current date/time in UTC mode + utc(keepLocalTime?: boolean): Moment; + isUTC(): boolean; + /** + * @deprecated use isUTC + */ + isUtc(): boolean; + + parseZone(): Moment; + isValid(): boolean; + invalidAt(): number; + + hasAlignedHourOffset(other?: MomentInput): boolean; + + creationData(): MomentCreationData; + parsingFlags(): MomentParsingFlags; + + year(y: number): Moment; + year(): number; + /** + * @deprecated use year(y) + */ + years(y: number): Moment; + /** + * @deprecated use year() + */ + years(): number; + quarter(): number; + quarter(q: number): Moment; + quarters(): number; + quarters(q: number): Moment; + month(M: number|string): Moment; + month(): number; + /** + * @deprecated use month(M) + */ + months(M: number|string): Moment; + /** + * @deprecated use month() + */ + months(): number; + day(d: number|string): Moment; + day(): number; + days(d: number|string): Moment; + days(): number; + date(d: number): Moment; + date(): number; + /** + * @deprecated use date(d) + */ + dates(d: number): Moment; + /** + * @deprecated use date() + */ + dates(): number; + hour(h: number): Moment; + hour(): number; + hours(h: number): Moment; + hours(): number; + minute(m: number): Moment; + minute(): number; + minutes(m: number): Moment; + minutes(): number; + second(s: number): Moment; + second(): number; + seconds(s: number): Moment; + seconds(): number; + millisecond(ms: number): Moment; + millisecond(): number; + milliseconds(ms: number): Moment; + milliseconds(): number; + weekday(): number; + weekday(d: number): Moment; + isoWeekday(): number; + isoWeekday(d: number|string): Moment; + weekYear(): number; + weekYear(d: number): Moment; + isoWeekYear(): number; + isoWeekYear(d: number): Moment; + week(): number; + week(d: number): Moment; + weeks(): number; + weeks(d: number): Moment; + isoWeek(): number; + isoWeek(d: number): Moment; + isoWeeks(): number; + isoWeeks(d: number): Moment; + weeksInYear(): number; + isoWeeksInYear(): number; + dayOfYear(): number; + dayOfYear(d: number): Moment; + + from(inp: MomentInput, suffix?: boolean): string; + to(inp: MomentInput, suffix?: boolean): string; + fromNow(withoutSuffix?: boolean): string; + toNow(withoutPrefix?: boolean): string; + + diff(b: MomentInput, unitOfTime?: unitOfTime.Diff, precise?: boolean): number; + + toArray(): number[]; + toDate(): Date; + toISOString(): string; + inspect(): string; + toJSON(): string; + unix(): number; + + isLeapYear(): boolean; + /** + * @deprecated in favor of utcOffset + */ + zone(): number; + zone(b: number|string): Moment; + utcOffset(): number; + utcOffset(b: number|string, keepLocalTime?: boolean): Moment; + isUtcOffset(): boolean; + daysInMonth(): number; + isDST(): boolean; + + zoneAbbr(): string; + zoneName(): string; + + isBefore(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isAfter(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isSame(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isSameOrAfter(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isSameOrBefore(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean; + isBetween(a: MomentInput, b: MomentInput, granularity?: unitOfTime.StartOf, inclusivity?: "()" | "[)" | "(]" | "[]"): boolean; + + /** + * @deprecated as of 2.8.0, use locale + */ + lang(language: LocaleSpecifier): Moment; + /** + * @deprecated as of 2.8.0, use locale + */ + lang(): Locale; + + locale(): string; + locale(locale: LocaleSpecifier): Moment; + + localeData(): Locale; + + /** + * @deprecated no reliable implementation + */ + isDSTShifted(): boolean; + + // NOTE(constructor): Same as moment constructor + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + max(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + max(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + // NOTE(constructor): Same as moment constructor + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + min(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + /** + * @deprecated as of 2.7.0, use moment.min/max + */ + min(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + get(unit: unitOfTime.All): number; + set(unit: unitOfTime.All, value: number): Moment; + set(objectLiteral: MomentSetObject): Moment; + + toObject(): MomentObjectOutput; + } + + export var version: string; + export var fn: Moment; + + // NOTE(constructor): Same as moment constructor + export function utc(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + export function utc(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + export function unix(timestamp: number): Moment; + + export function invalid(flags?: MomentParsingFlagsOpt): Moment; + export function isMoment(m: any): m is Moment; + export function isDate(m: any): m is Date; + export function isDuration(d: any): d is Duration; + + /** + * @deprecated in 2.8.0 + */ + export function lang(language?: string): string; + /** + * @deprecated in 2.8.0 + */ + export function lang(language?: string, definition?: Locale): string; + + export function locale(language?: string): string; + export function locale(language?: string[]): string; + export function locale(language?: string, definition?: LocaleSpecification | void): string; // null | undefined + + export function localeData(key?: string | string[]): Locale; + + export function duration(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration; + + // NOTE(constructor): Same as moment constructor + export function parseZone(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment; + export function parseZone(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment; + + export function months(): string[]; + export function months(index: number): string; + export function months(format: string): string[]; + export function months(format: string, index: number): string; + export function monthsShort(): string[]; + export function monthsShort(index: number): string; + export function monthsShort(format: string): string[]; + export function monthsShort(format: string, index: number): string; + + export function weekdays(): string[]; + export function weekdays(index: number): string; + export function weekdays(format: string): string[]; + export function weekdays(format: string, index: number): string; + export function weekdays(localeSorted: boolean): string[]; + export function weekdays(localeSorted: boolean, index: number): string; + export function weekdays(localeSorted: boolean, format: string): string[]; + export function weekdays(localeSorted: boolean, format: string, index: number): string; + export function weekdaysShort(): string[]; + export function weekdaysShort(index: number): string; + export function weekdaysShort(format: string): string[]; + export function weekdaysShort(format: string, index: number): string; + export function weekdaysShort(localeSorted: boolean): string[]; + export function weekdaysShort(localeSorted: boolean, index: number): string; + export function weekdaysShort(localeSorted: boolean, format: string): string[]; + export function weekdaysShort(localeSorted: boolean, format: string, index: number): string; + export function weekdaysMin(): string[]; + export function weekdaysMin(index: number): string; + export function weekdaysMin(format: string): string[]; + export function weekdaysMin(format: string, index: number): string; + export function weekdaysMin(localeSorted: boolean): string[]; + export function weekdaysMin(localeSorted: boolean, index: number): string; + export function weekdaysMin(localeSorted: boolean, format: string): string[]; + export function weekdaysMin(localeSorted: boolean, format: string, index: number): string; + + export function min(...moments: MomentInput[]): Moment; + export function max(...moments: MomentInput[]): Moment; + + /** + * Returns unix time in milliseconds. Overwrite for profit. + */ + export function now(): number; + + export function defineLocale(language: string, localeSpec: LocaleSpecification | void): Locale; // null + export function updateLocale(language: string, localeSpec: LocaleSpecification | void): Locale; // null + + export function locales(): string[]; + + export function normalizeUnits(unit: unitOfTime.All): string; + export function relativeTimeThreshold(threshold: string): number | boolean; + export function relativeTimeThreshold(threshold: string, limit: number): boolean; + export function relativeTimeRounding(fn: (num: number) => number): boolean; + export function relativeTimeRounding(): (num: number) => number; + export function calendarFormat(m: Moment, now: Moment): string; + + /** + * Constant used to enable explicit ISO_8601 format parsing. + */ + export var ISO_8601: MomentBuiltinFormat; + + export var defaultFormat: string; + export var defaultFormatUtc: string; +} + +declare module "moment" { + export default moment; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..17bd7b902 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es6", + "jsx": "react", + "sourceMap": false, + "experimentalDecorators": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true + }, + "exclude": [ + "../snd/node_modules", + "resources" + ] +} + diff --git a/webpack/dev-server.js b/webpack/dev-server.js new file mode 100644 index 000000000..1b918d3e5 --- /dev/null +++ b/webpack/dev-server.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +var express = require('express'); +var webpack = require('webpack'); +var cors = require('cors'); +var config = require('./dev.config'); + +var app = express(); +var compiler = webpack(config); + +app.use(cors()); + +app.use(require('webpack-dev-middleware')(compiler, { + noInfo: true, + stats: { + chunks: false + }, + publicPath: config.output.publicPath +})); + +app.use(require('webpack-hot-middleware')(compiler)); + +app.listen(3000, 'localhost', function (err) { + if (err) { + console.log(err); + return; + } + + console.log('Listening at http://localhost:3000'); +}); diff --git a/webpack/dev.config.js b/webpack/dev.config.js new file mode 100644 index 000000000..63aa251d7 --- /dev/null +++ b/webpack/dev.config.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +const path = require("path"); +const webpack = require("webpack"); + +module.exports = { + context: path.resolve(__dirname, '..'), + + devtool: 'eval', + + entry: { + 'app': [ + 'webpack-hot-middleware/client?path=http://localhost:3000/__webpack_hmr', + './src/client/app.tsx' + ] + }, + + externals: { + 'react/addons': true, + 'react/lib/ExecutionEnvironment': true, + 'react/lib/ReactContext': true + }, + + module: { + rules: [ + { + test: /\.tsx?$/, + loaders: ['babel-loader', 'ts-loader'] + } + ] + }, + + output: { + path: path.resolve(__dirname, '../resources/web/snd/'), + publicPath: 'http://localhost:3000/', + filename: '[name].js' + }, + + plugins: [ + new webpack.NoEmitOnErrorsPlugin(), + new webpack.HotModuleReplacementPlugin() + ], + + resolve: { + extensions: [ '.jsx', '.js', '.tsx', '.ts' ] + } +}; diff --git a/webpack/hot.config.js b/webpack/hot.config.js new file mode 100644 index 000000000..54eef0112 --- /dev/null +++ b/webpack/hot.config.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +const path = require("path"); +const webpack = require("webpack"); + +module.exports = { + context: path.resolve(__dirname, '..'), + + devtool: 'eval', + + entry: { + 'app': [ + 'webpack-hot-middleware/client?path=http://localhost:3000/__webpack_hmr', + './src/client/app.jsx' + ] + }, + + output: { + path: path.resolve(__dirname, '../resources/web/snd'), + publicPath: 'http://localhost:3000/', + filename: "[name].js" + }, + + module: { + rules: [ + { + test: /\.jsx?$/, + loaders: ['babel-loader'] + } + ] + }, + + resolve: { + extensions: [ '.jsx', '.js', '.tsx', '.ts' ] + }, + + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ] +}; diff --git a/webpack/prod.config.js b/webpack/prod.config.js new file mode 100644 index 000000000..d273e52c8 --- /dev/null +++ b/webpack/prod.config.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 + */ +const webpack = require("webpack"); +const path = require("path"); + +module.exports = { + context: path.resolve(__dirname, '..'), + + devtool: 'source-map', + + entry: { + 'app': [ + './src/client/app.tsx' + ] + }, + + output: { + path: path.resolve(__dirname, '../resources/web/snd/'), + publicPath: './', // allows context path to resolve in both js/css + filename: "[name].js" + }, + + module: { + rules: [ + { + test: /\.tsx?$/, + loaders: ['babel-loader', 'ts-loader'] + } + ] + }, + + resolve: { + extensions: [ '.jsx', '.js', '.tsx', '.ts' ] + }, + + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }) + ] +}; \ No newline at end of file From bc4748606909feadbc080a8d1424ecbc35f87a58 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 8 Aug 2017 21:18:01 +0000 Subject: [PATCH 010/686] Save Package API start --- api-src/org/labkey/api/snd/SNDPackage.java | 54 ++++++++-------- api-src/org/labkey/api/snd/SNDService.java | 6 +- module.properties | 5 +- src/org/labkey/snd/SNDController.java | 74 ++++++++++++++++++++++ src/org/labkey/snd/SNDManager.java | 13 ++++ src/org/labkey/snd/SNDSchema.java | 14 ++++ src/org/labkey/snd/SNDServiceImpl.java | 70 ++++++++++++++++++-- 7 files changed, 199 insertions(+), 37 deletions(-) diff --git a/api-src/org/labkey/api/snd/SNDPackage.java b/api-src/org/labkey/api/snd/SNDPackage.java index 61bc678d5..56c3535f1 100644 --- a/api-src/org/labkey/api/snd/SNDPackage.java +++ b/api-src/org/labkey/api/snd/SNDPackage.java @@ -9,92 +9,92 @@ */ public class SNDPackage { - private int pkgId; - private String description; - private boolean repeatable; - private boolean draft; - private Collection categories; - private Collection attributes; - private Collection subpackages; - private Collection extraFields; + private Integer _pkgId; + private String _description; + private boolean _repeatable; + private boolean _active; + private Collection _categories; + private Collection _attributes; + private Collection _subpackages; + private Collection _extraFields; - public int getPkgId() + public Integer getPkgId() { - return pkgId; + return _pkgId; } public void setPkgId(int pkgId) { - this.pkgId = pkgId; + this._pkgId = pkgId; } public String getDescription() { - return description; + return _description; } public void setDescription(String description) { - this.description = description; + this._description = description; } public boolean isRepeatable() { - return repeatable; + return _repeatable; } public void setRepeatable(boolean repeatable) { - this.repeatable = repeatable; + this._repeatable = repeatable; } - public boolean isDraft() + public boolean isActive() { - return draft; + return _active; } - public void setDraft(boolean draft) + public void setActive(boolean active) { - this.draft = draft; + this._active = active; } public Collection getCategories() { - return categories; + return _categories; } public void setCategories(Collection categories) { - this.categories = categories; + this._categories = categories; } public Collection getAttributes() { - return attributes; + return _attributes; } public void setAttributes(Collection attributes) { - this.attributes = attributes; + this._attributes = attributes; } public Collection getSubpackages() { - return subpackages; + return _subpackages; } public void setSubpackages(Collection subpackages) { - this.subpackages = subpackages; + this._subpackages = subpackages; } public Collection getExtraFields() { - return extraFields; + return _extraFields; } public void setExtraFields(Collection extraFields) { - this.extraFields = extraFields; + this._extraFields = extraFields; } } diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java index c7d6c2bb4..7c7c79929 100644 --- a/api-src/org/labkey/api/snd/SNDService.java +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -1,10 +1,10 @@ package org.labkey.api.snd; import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.security.User; import org.labkey.api.services.ServiceRegistry; -import java.util.List; - /** * Created by marty on 8/4/2017. */ @@ -16,5 +16,5 @@ static SNDService get() return ServiceRegistry.get(SNDService.class); } - List savePackage(SNDPackage pkg); + void savePackage(Container c, User u, SNDPackage pkg); } diff --git a/module.properties b/module.properties index a04850d6a..5aa3750d6 100644 --- a/module.properties +++ b/module.properties @@ -1,7 +1,6 @@ ModuleClass: org.labkey.snd.SNDModule -Label: FIXME! Capitalized Short Module Summary -Description: FIXME! Write a paragraph describing the functionality and the \ - purpose of this module. The description should contain multiple scentences. +Label: Structured Narrative Datasets +Description: Dynamically defined hierarchical datasets. License: Apache 2.0 LicenseURL: http://www.apache.org/licenses/LICENSE-2.0 SupportedDatabases: mssql diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index aa47b9bca..8a6ec9f2d 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -16,10 +16,16 @@ package org.labkey.snd; +import org.labkey.api.action.ApiAction; +import org.labkey.api.action.ApiResponse; +import org.labkey.api.action.ApiSimpleResponse; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.action.SpringActionController; import org.labkey.api.security.RequiresPermission; +import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; +import org.labkey.api.snd.SNDPackage; +import org.labkey.api.snd.SNDService; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.springframework.validation.BindException; @@ -48,4 +54,72 @@ public NavTree appendNavTrail(NavTree root) return root; } } + + public static class SavePackageForm + { + private int pkgId; + private String description; + private boolean active; + private boolean repeatable; + + public int getPkgId() + { + return pkgId; + } + + public void setPkgId(int pkgId) + { + this.pkgId = pkgId; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public boolean isActive() + { + return active; + } + + public void setActive(boolean active) + { + this.active = active; + } + + public boolean isRepeatable() + { + return repeatable; + } + + public void setRepeatable(boolean repeatable) + { + this.repeatable = repeatable; + } + } + + @RequiresPermission(AdminPermission.class) + public class SavePackageAction extends ApiAction + { + @Override + public ApiResponse execute(SavePackageForm form, BindException errors) throws Exception + { + SNDPackage pkg = new SNDPackage(); + pkg.setPkgId(form.getPkgId()); + pkg.setDescription(form.getDescription()); + pkg.setActive(form.isActive()); + pkg.setRepeatable(form.isRepeatable()); + SNDService.get().savePackage(getViewContext().getContainer(), getUser(), pkg); + +// for (String msg : errMsgs) +// errors.reject(ERROR_MSG, msg); + + return new ApiSimpleResponse(); + } + } } \ No newline at end of file diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 361bd1f91..022b310a9 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -16,9 +16,15 @@ package org.labkey.snd; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbSequence; +import org.labkey.api.data.DbSequenceManager; + public class SNDManager { private static final SNDManager _instance = new SNDManager(); + private static final int _minPkgId = 10000; + private static final String SND_DBSEQUENCE_NAME = "org.labkey.snd.api.SNDPackage"; private SNDManager() { @@ -29,4 +35,11 @@ public static SNDManager get() { return _instance; } + + public Integer generatePackageId(Container c) + { + DbSequence sequence = DbSequenceManager.get(c, SND_DBSEQUENCE_NAME); + sequence.ensureMinimum(_minPkgId); + return sequence.next(); + } } \ No newline at end of file diff --git a/src/org/labkey/snd/SNDSchema.java b/src/org/labkey/snd/SNDSchema.java index d7fa9481a..07a087988 100644 --- a/src/org/labkey/snd/SNDSchema.java +++ b/src/org/labkey/snd/SNDSchema.java @@ -18,6 +18,7 @@ import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbSchemaType; +import org.labkey.api.data.TableInfo; import org.labkey.api.data.dialect.SqlDialect; public class SNDSchema @@ -46,4 +47,17 @@ public SqlDialect getSqlDialect() { return getSchema().getSqlDialect(); } + + public TableInfo getTableInfoPkgs() + { + return getSchema().getTable("Pkgs"); + } + public TableInfo getTableInfoPkgCategoryJunction() + { + return getSchema().getTable("PkgCategoryJunction"); + } + public TableInfo getTableInfoPkgCategories() + { + return getSchema().getTable("PkgCategories"); + } } diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index c71050e22..bb23ea251 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -1,10 +1,24 @@ package org.labkey.snd; +import org.labkey.api.data.Container; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.DuplicateKeyException; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; import org.labkey.api.snd.SNDPackage; import org.labkey.api.snd.SNDService; +import org.labkey.api.util.UnexpectedException; -import java.util.Collections; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Created by marty on 8/4/2017. @@ -15,10 +29,58 @@ public class SNDServiceImpl implements SNDService private SNDServiceImpl() {} - public List savePackage(SNDPackage pkg) + @Override + public void savePackage(Container c, User u, SNDPackage pkg) { - List errors = Collections.emptyList(); + TableInfo pkgsTable = SNDSchema.getInstance().getTableInfoPkgs(); + QueryUpdateService qus = pkgsTable.getUpdateService(); + if (qus == null) + throw new IllegalStateException(); - return errors; + BatchValidationException errors = new BatchValidationException(); + Map pkgValues = new HashMap<>(); + pkgValues.put("Description", pkg.getDescription()); + pkgValues.put("Active", pkg.isActive()); + pkgValues.put("Repeatable", pkg.isRepeatable()); + pkgValues.put("Container", c); + + List> rows = new ArrayList<>(); + rows.add(pkgValues); + + // Create/update package in pkgs table + if(null != pkg.getPkgId() && pkg.getPkgId() > 0) + { + pkgValues.put("PkgId", pkg.getPkgId()); + try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) + { + qus.updateRows(u, c, rows, null, null, null); + tx.commit(); + } + catch (QueryUpdateServiceException | BatchValidationException | InvalidKeyException | SQLException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } + + if (errors.hasErrors()) + throw new UnexpectedException(errors); + } + else + { + pkg.setPkgId(SNDManager.get().generatePackageId(c)); + pkgValues.put("PkgId", pkg.getPkgId()); + + try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) + { + qus.insertRows(u, c, rows, errors, null, null); + tx.commit(); + } + catch (QueryUpdateServiceException | BatchValidationException | DuplicateKeyException | SQLException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } + + if (errors.hasErrors()) + throw new UnexpectedException(errors); + } } } From 8c80786ccc25ff7267a83741ea5d4ba7cdb1b564 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 9 Aug 2017 16:47:16 +0000 Subject: [PATCH 011/686] Spec 30686: Add proper container cleanup --- src/org/labkey/snd/SNDContainerListener.java | 14 ++++++++++++++ test/src/org/labkey/test/tests/snd/SNDTest.java | 12 ------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/org/labkey/snd/SNDContainerListener.java b/src/org/labkey/snd/SNDContainerListener.java index 1ca1008d8..be471620d 100644 --- a/src/org/labkey/snd/SNDContainerListener.java +++ b/src/org/labkey/snd/SNDContainerListener.java @@ -19,6 +19,9 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager.ContainerListener; +import org.labkey.api.data.DbScope; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.Table; import org.labkey.api.security.User; import java.util.Collections; import java.util.Collection; @@ -35,6 +38,17 @@ public void containerCreated(Container c, User user) @Override public void containerDeleted(Container c, User user) { + // This will clean up the SND schema. We will rely on the exp module to clean up related exp data. + DbScope scope = SNDSchema.getInstance().getSchema().getScope(); + SimpleFilter containerFilter = SimpleFilter.createContainerFilter(c); + try (DbScope.Transaction transaction = scope.ensureTransaction()) + { + Table.delete(SNDSchema.getInstance().getTableInfoPkgCategoryJunction(), containerFilter); + Table.delete(SNDSchema.getInstance().getTableInfoPkgCategories(), containerFilter); + Table.delete(SNDSchema.getInstance().getTableInfoPkgs(), containerFilter); + + transaction.commit(); + } } @Override diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 2fc1e5ece..bec6b41cb 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -78,18 +78,6 @@ public class SNDTest extends BaseWebDriverTest implements SqlserverOnlyTest @Override protected void doCleanup(boolean afterTest) throws TestTimeoutException { - openProjectMenu(); - if(isTextPresent(PROJECTNAME)) - { - try - { - deleteTest1Data(); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } _containerHelper.deleteProject(getProjectName(), afterTest); } From a77b96c16eecff38fdc4f5f45194979d780db75e Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 9 Aug 2017 22:25:43 +0000 Subject: [PATCH 012/686] snd.xsd --- schemas/snd.xsd | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 schemas/snd.xsd diff --git a/schemas/snd.xsd b/schemas/snd.xsd new file mode 100644 index 000000000..461a911bb --- /dev/null +++ b/schemas/snd.xsd @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b1f363d60570d9e3ee090c92a39dbf71c8dfdaf3 Mon Sep 17 00:00:00 2001 From: Trey Chadick Date: Fri, 11 Aug 2017 22:35:06 +0000 Subject: [PATCH 013/686] Code pre-review fixes --- test/src/org/labkey/test/tests/snd/SNDTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index bec6b41cb..3b4323f0e 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -36,7 +36,6 @@ import org.labkey.test.pages.snd.BeginPage; import org.labkey.test.util.LogMethod; import org.labkey.test.util.Maps; -import org.labkey.test.util.PasswordUtil; import org.labkey.test.util.SqlserverOnlyTest; import java.io.IOException; @@ -126,7 +125,6 @@ private void doSetup() _containerHelper.enableModules(Arrays.asList("SND")); _containerHelper.createSubfolder(getProjectName(), getProjectName(), TEST1SUBFOLDER, "Collaboration", new String[]{"SND"}); setupTest1Project(); - clickFolder(getProjectName()); } private void setupTest1Project() @@ -149,7 +147,6 @@ public void preTest() @Test public void testSNDModule() { - _containerHelper.enableModule("SND"); BeginPage beginPage = BeginPage.beginAt(this, getProjectName()); assertEquals(200, getResponseCode()); final String expectedHello = "Hello, and welcome to the SND module."; @@ -165,7 +162,7 @@ protected BrowserType bestBrowser() @Override protected String getProjectName() { - return "SNDTest Project"; + return PROJECTNAME; } @Override @@ -175,11 +172,11 @@ public List getAssociatedModules() } @Test - public void testExtensibleColumns() throws IOException, CommandException + public void testExtensibleColumns() throws Exception { clickFolder(TEST1SUBFOLDER); - Connection cn = new Connection(getBaseURL(), PasswordUtil.getUsername(), PasswordUtil.getPassword()); + Connection cn = createDefaultConnection(false); InsertRowsCommand insertRowsCommand = new InsertRowsCommand("snd", "Pkgs"); insertRowsCommand.addRow(TEST1ROW1MAP); From ce4070f0cf5caea14686a83a99c19d54ac526684 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Sun, 13 Aug 2017 21:05:48 +0000 Subject: [PATCH 014/686] Spec 30686: Code review feedback. --- .../dbscripts/sqlserver/snd-17.21-17.22.sql | 12 +++++++++ resources/schemas/snd.xml | 1 + src/org/labkey/snd/view/hello.jsp | 25 ------------------- 3 files changed, 13 insertions(+), 25 deletions(-) create mode 100644 resources/schemas/dbscripts/sqlserver/snd-17.21-17.22.sql delete mode 100644 src/org/labkey/snd/view/hello.jsp diff --git a/resources/schemas/dbscripts/sqlserver/snd-17.21-17.22.sql b/resources/schemas/dbscripts/sqlserver/snd-17.21-17.22.sql new file mode 100644 index 000000000..e6d0d27ea --- /dev/null +++ b/resources/schemas/dbscripts/sqlserver/snd-17.21-17.22.sql @@ -0,0 +1,12 @@ + +CREATE INDEX IDX_SND_PKGS_LSID ON snd.Pkgs(Lsid); +CREATE INDEX IDX_SND_SUPERPKGS_LSID ON snd.SuperPkgs(Lsid); +CREATE INDEX IDX_SND_PKGCATEGORIES_LSID ON snd.PkgCategories(Lsid); +CREATE INDEX IDX_SND_PKGCATEGORYJUNCTION_LSID ON snd.PkgCategoryJunction(Lsid); +CREATE INDEX IDX_SND_PROJECTS_LSID ON snd.Projects(Lsid); +CREATE INDEX IDX_SND_PROJECTITEMS_LSID ON snd.ProjectItems(Lsid); +CREATE INDEX IDX_SND_EVENTS_LSID ON snd.Events(Lsid); +CREATE INDEX IDX_SND_CODEDEVENTS_LSID ON snd.CodedEvents(Lsid); +CREATE INDEX IDX_SND_EVENTNOTES_LSID ON snd.EventNotes(Lsid); + +ALTER TABLE snd.Pkgs ADD Narrative NVARCHAR(MAX) NULL; diff --git a/resources/schemas/snd.xml b/resources/schemas/snd.xml index f739003d5..a02fb9ac3 100644 --- a/resources/schemas/snd.xml +++ b/resources/schemas/snd.xml @@ -22,6 +22,7 @@ + diff --git a/src/org/labkey/snd/view/hello.jsp b/src/org/labkey/snd/view/hello.jsp deleted file mode 100644 index fbc53d256..000000000 --- a/src/org/labkey/snd/view/hello.jsp +++ /dev/null @@ -1,25 +0,0 @@ -<% -/* - * Copyright (c) 2017 LabKey Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -%> -<%@ page import="org.labkey.api.data.Container" %> -<%@ page import="org.labkey.api.security.User" %> -<%@ page extends="org.labkey.api.jsp.JspBase" %> -<% - Container c = getContainer(); - User user = getUser(); -%> -
Hello, and welcome to the SND module.
\ No newline at end of file From aaaab740719968663611d95e8a1f0113958b4616 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Sun, 13 Aug 2017 21:08:12 +0000 Subject: [PATCH 015/686] Spec 30686: Flip module version --- src/org/labkey/snd/SNDModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java index 1b33b96b3..466ed018e 100644 --- a/src/org/labkey/snd/SNDModule.java +++ b/src/org/labkey/snd/SNDModule.java @@ -47,7 +47,7 @@ public String getName() @Override public double getVersion() { - return 17.21; + return 17.22; } @Override From 0729d15fbad886ef43544faa1f91a76947639e80 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 14 Aug 2017 20:13:54 +0000 Subject: [PATCH 016/686] Spec 30931: Infrastructure to Create Packages --- api-src/org/labkey/api/snd/SNDPackage.java | 61 ++++++++- src/org/labkey/snd/SNDController.java | 73 +++-------- src/org/labkey/snd/SNDManager.java | 76 ++++++++++++ src/org/labkey/snd/SNDSchema.java | 47 ++++++- src/org/labkey/snd/SNDServiceImpl.java | 64 ++-------- src/org/labkey/snd/SNDUserSchema.java | 138 +++++++++++++++++++++ 6 files changed, 336 insertions(+), 123 deletions(-) diff --git a/api-src/org/labkey/api/snd/SNDPackage.java b/api-src/org/labkey/api/snd/SNDPackage.java index 56c3535f1..943d54eb1 100644 --- a/api-src/org/labkey/api/snd/SNDPackage.java +++ b/api-src/org/labkey/api/snd/SNDPackage.java @@ -1,8 +1,15 @@ package org.labkey.api.snd; +import org.labkey.api.collections.ArrayListMap; +import org.labkey.api.data.Container; import org.labkey.api.exp.PropertyDescriptor; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Created by marty on 8/4/2017. @@ -11,12 +18,13 @@ public class SNDPackage { private Integer _pkgId; private String _description; + private String _narrative; private boolean _repeatable; private boolean _active; - private Collection _categories; + private List _categories = Collections.EMPTY_LIST; private Collection _attributes; private Collection _subpackages; - private Collection _extraFields; + private Map _extraFields = new HashMap<>(); public Integer getPkgId() { @@ -38,6 +46,16 @@ public void setDescription(String description) this._description = description; } + public String getNarrative() + { + return _narrative; + } + + public void setNarrative(String narrative) + { + _narrative = narrative; + } + public boolean isRepeatable() { return _repeatable; @@ -58,12 +76,12 @@ public void setActive(boolean active) this._active = active; } - public Collection getCategories() + public List getCategories() { return _categories; } - public void setCategories(Collection categories) + public void setCategories(List categories) { this._categories = categories; } @@ -88,13 +106,44 @@ public void setSubpackages(Collection subpackages) this._subpackages = subpackages; } - public Collection getExtraFields() + public Map getExtraFields() { return _extraFields; } - public void setExtraFields(Collection extraFields) + public void setExtraFields(Map extraFields) { this._extraFields = extraFields; } + + public Map getPackageRow(Container c) + { + Map pkgValues = new ArrayListMap<>(); + pkgValues.put("PkgId", getPkgId()); + pkgValues.put("Narrative", getNarrative()); + pkgValues.put("Description", getDescription()); + pkgValues.put("Active", isActive()); + pkgValues.put("Repeatable", isRepeatable()); + pkgValues.put("Container", c); + pkgValues.putAll(getExtraFields()); + + return pkgValues; + } + + public List> getCategoryRows(Container c) + { + List> rows = new ArrayList<>(); + Map row; + + for (Integer categoryId : getCategories()) + { + row = new ArrayListMap<>(); + row.put("PkgId", getPkgId()); + row.put("CategoryId", categoryId); + row.put("Container", c); + rows.add(row); + } + + return rows; + } } diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index 8a6ec9f2d..10d958122 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -16,9 +16,11 @@ package org.labkey.snd; +import org.json.JSONObject; import org.labkey.api.action.ApiAction; import org.labkey.api.action.ApiResponse; import org.labkey.api.action.ApiSimpleResponse; +import org.labkey.api.action.SimpleApiJsonForm; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.action.SpringActionController; import org.labkey.api.security.RequiresPermission; @@ -26,7 +28,6 @@ import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.snd.SNDPackage; import org.labkey.api.snd.SNDService; -import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.springframework.validation.BindException; import org.springframework.web.servlet.ModelAndView; @@ -46,7 +47,7 @@ public class BeginAction extends SimpleViewAction { public ModelAndView getView(Object o, BindException errors) throws Exception { - return new JspView("/org/labkey/snd/view/hello.jsp"); + return null; } public NavTree appendNavTrail(NavTree root) @@ -55,69 +56,23 @@ public NavTree appendNavTrail(NavTree root) } } - public static class SavePackageForm - { - private int pkgId; - private String description; - private boolean active; - private boolean repeatable; - - public int getPkgId() - { - return pkgId; - } - - public void setPkgId(int pkgId) - { - this.pkgId = pkgId; - } - - public String getDescription() - { - return description; - } - - public void setDescription(String description) - { - this.description = description; - } - - public boolean isActive() - { - return active; - } - - public void setActive(boolean active) - { - this.active = active; - } - - public boolean isRepeatable() - { - return repeatable; - } - - public void setRepeatable(boolean repeatable) - { - this.repeatable = repeatable; - } - } - @RequiresPermission(AdminPermission.class) - public class SavePackageAction extends ApiAction + public class SavePackageAction extends ApiAction { @Override - public ApiResponse execute(SavePackageForm form, BindException errors) throws Exception + public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws Exception { + JSONObject json = form.getJsonObject(); SNDPackage pkg = new SNDPackage(); - pkg.setPkgId(form.getPkgId()); - pkg.setDescription(form.getDescription()); - pkg.setActive(form.isActive()); - pkg.setRepeatable(form.isRepeatable()); - SNDService.get().savePackage(getViewContext().getContainer(), getUser(), pkg); + if (json.get("PkgId") != null) + pkg.setPkgId(json.getInt("PkgId")); + + pkg.setDescription(json.getString("Description")); + pkg.setActive(json.getBoolean("Active")); + pkg.setRepeatable(json.getBoolean("Repeatable")); + pkg.setNarrative(json.getString("Narrative")); -// for (String msg : errMsgs) -// errors.reject(ERROR_MSG, msg); + SNDService.get().savePackage(getViewContext().getContainer(), getUser(), pkg); return new ApiSimpleResponse(); } diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 022b310a9..ece203e43 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -17,8 +17,25 @@ package org.labkey.snd; import org.labkey.api.data.Container; +import org.labkey.api.data.DbScope; import org.labkey.api.data.DbSequence; import org.labkey.api.data.DbSequenceManager; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.DuplicateKeyException; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.QueryService; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.UserSchema; +import org.labkey.api.query.ValidationException; +import org.labkey.api.security.User; +import org.labkey.api.snd.SNDPackage; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; public class SNDManager { @@ -42,4 +59,63 @@ public Integer generatePackageId(Container c) sequence.ensureMinimum(_minPkgId); return sequence.next(); } + + public void updatePackage(User u, Container c, SNDPackage pkg, BatchValidationException errors) + { + TableInfo pkgsTable = SNDSchema.getInstance().getTableInfoPkgs(); + QueryUpdateService pkgQus = pkgsTable.getUpdateService(); + if (pkgQus == null) + throw new IllegalStateException(); + + TableInfo pkgCategJuncTable = SNDSchema.getInstance().getTableInfoPkgCategoryJunction(); + QueryUpdateService pkgCategoryQus = pkgCategJuncTable.getUpdateService(); + if (pkgCategoryQus == null) + throw new IllegalStateException(); + + List> pkgRows = new ArrayList<>(); + pkgRows.add(pkg.getPackageRow(c)); + + try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) + { + pkgQus.updateRows(u, c, pkgRows, null, null, null); + pkgCategoryQus.updateRows(u, c, pkg.getCategoryRows(c), null, null, null); + tx.commit(); + } + catch (QueryUpdateServiceException | BatchValidationException | InvalidKeyException | SQLException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } + } + + public void createNewPackage(User u, Container c, SNDPackage pkg, BatchValidationException errors) + { + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + TableInfo pkgsTable = schema.getTable(SNDSchema.PKGS_TABLE_NAME); + QueryUpdateService pkgQus = pkgsTable.getUpdateService(); + if (pkgQus == null) + throw new IllegalStateException(); + + TableInfo pkgCategJuncTable = schema.getTable(SNDSchema.PKGCATEGORYJUNCTION_TABLE_NAME); + QueryUpdateService pkgCategoryQus = pkgCategJuncTable.getUpdateService(); + if (pkgCategoryQus == null) + throw new IllegalStateException(); + + List> pkgRows = new ArrayList<>(); + pkgRows.add(pkg.getPackageRow(c)); + + try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) + { + pkgQus.insertRows(u, c, pkgRows, errors, null, null); + pkgCategoryQus.insertRows(u, c, pkg.getCategoryRows(c), errors, null, null); + tx.commit(); + } + catch (QueryUpdateServiceException | BatchValidationException | DuplicateKeyException | SQLException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } + + //Domain domain = DomainUtil.createDomain(kindName, newDomain, options, getContainer(), getUser(), domainName, null); + + } } \ No newline at end of file diff --git a/src/org/labkey/snd/SNDSchema.java b/src/org/labkey/snd/SNDSchema.java index 07a087988..c80cc13c9 100644 --- a/src/org/labkey/snd/SNDSchema.java +++ b/src/org/labkey/snd/SNDSchema.java @@ -25,6 +25,15 @@ public class SNDSchema { private static final SNDSchema _instance = new SNDSchema(); public static final String NAME = "snd"; + public static final String PKGS_TABLE_NAME = "Pkgs"; + public static final String PKGCATEGORYJUNCTION_TABLE_NAME = "PkgCategoryJunction"; + public static final String PKGCATEGORIES_NAME = "PkgCategories"; + public static final String SUPERPKGS_TABLE_NAME = "SuperPkgs"; + public static final String EVENTS_TABLE_NAME = "Events"; + public static final String CODEDEVENTS_TABLE_NAME = "CodedEvents"; + public static final String EVENTNOTES_TABLE_NAME = "EventNotes"; + public static final String PROJECTS_TABLE_NAME = "Projects"; + public static final String PROJECTITEMS_TABLE_NAME = "ProjectItems"; public static SNDSchema getInstance() { @@ -50,14 +59,46 @@ public SqlDialect getSqlDialect() public TableInfo getTableInfoPkgs() { - return getSchema().getTable("Pkgs"); + return getSchema().getTable(PKGS_TABLE_NAME); } + public TableInfo getTableInfoPkgCategoryJunction() { - return getSchema().getTable("PkgCategoryJunction"); + return getSchema().getTable(PKGCATEGORYJUNCTION_TABLE_NAME); } + public TableInfo getTableInfoPkgCategories() { - return getSchema().getTable("PkgCategories"); + return getSchema().getTable(PKGCATEGORIES_NAME); + } + + public TableInfo getTableInfoSuperPkgs() + { + return getSchema().getTable(SUPERPKGS_TABLE_NAME); + } + + public TableInfo getTableInfoEvents() + { + return getSchema().getTable(EVENTS_TABLE_NAME); + } + + public TableInfo getTableInfoCodedEvents() + { + return getSchema().getTable(CODEDEVENTS_TABLE_NAME); + } + + public TableInfo getTableInfoEventNotes() + { + return getSchema().getTable(EVENTNOTES_TABLE_NAME); + } + + public TableInfo getTableInfoProjects() + { + return getSchema().getTable(PROJECTS_TABLE_NAME); + } + + public TableInfo getTableInfoProjectItems() + { + return getSchema().getTable(PROJECTITEMS_TABLE_NAME); } } diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index bb23ea251..a2425a8d5 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -1,25 +1,12 @@ package org.labkey.snd; import org.labkey.api.data.Container; -import org.labkey.api.data.DbScope; -import org.labkey.api.data.TableInfo; import org.labkey.api.query.BatchValidationException; -import org.labkey.api.query.DuplicateKeyException; -import org.labkey.api.query.InvalidKeyException; -import org.labkey.api.query.QueryUpdateService; -import org.labkey.api.query.QueryUpdateServiceException; -import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.snd.SNDPackage; import org.labkey.api.snd.SNDService; import org.labkey.api.util.UnexpectedException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * Created by marty on 8/4/2017. */ @@ -27,60 +14,27 @@ public class SNDServiceImpl implements SNDService { public static final SNDServiceImpl INSTANCE = new SNDServiceImpl(); - private SNDServiceImpl() {} + private SNDServiceImpl() + { + } @Override public void savePackage(Container c, User u, SNDPackage pkg) { - TableInfo pkgsTable = SNDSchema.getInstance().getTableInfoPkgs(); - QueryUpdateService qus = pkgsTable.getUpdateService(); - if (qus == null) - throw new IllegalStateException(); - BatchValidationException errors = new BatchValidationException(); - Map pkgValues = new HashMap<>(); - pkgValues.put("Description", pkg.getDescription()); - pkgValues.put("Active", pkg.isActive()); - pkgValues.put("Repeatable", pkg.isRepeatable()); - pkgValues.put("Container", c); - - List> rows = new ArrayList<>(); - rows.add(pkgValues); // Create/update package in pkgs table - if(null != pkg.getPkgId() && pkg.getPkgId() > 0) + if (null != pkg.getPkgId() && pkg.getPkgId() > 0) { - pkgValues.put("PkgId", pkg.getPkgId()); - try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) - { - qus.updateRows(u, c, rows, null, null, null); - tx.commit(); - } - catch (QueryUpdateServiceException | BatchValidationException | InvalidKeyException | SQLException e) - { - errors.addRowError(new ValidationException(e.getMessage())); - } - - if (errors.hasErrors()) - throw new UnexpectedException(errors); + SNDManager.get().updatePackage(u, c, pkg, errors); } else { pkg.setPkgId(SNDManager.get().generatePackageId(c)); - pkgValues.put("PkgId", pkg.getPkgId()); - - try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) - { - qus.insertRows(u, c, rows, errors, null, null); - tx.commit(); - } - catch (QueryUpdateServiceException | BatchValidationException | DuplicateKeyException | SQLException e) - { - errors.addRowError(new ValidationException(e.getMessage())); - } - - if (errors.hasErrors()) - throw new UnexpectedException(errors); + SNDManager.get().createNewPackage(u, c, pkg, errors); } + + if (errors.hasErrors()) + throw new UnexpectedException(errors); } } diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index 307853890..17c136487 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; +import org.labkey.api.data.TableInfo; import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.security.User; @@ -29,5 +30,142 @@ public SNDUserSchema(String name, @Nullable String description, User user, Conta super(name, description, user, container, dbschema); } + public enum TableType + { + SuperPkgs + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoSuperPkgs()).init(); + + return table; + } + }, + Pkgs + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); + + return table; + } + }, + PkgCategories + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoPkgCategories()).init(); + + return table; + } + }, + PkgCategoryJunction + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoPkgCategoryJunction()).init(); + + return table; + } + }, + ProjectItems + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoProjectItems()).init(); + + return table; + } + }, + Projects + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoProjects()).init(); + + return table; + } + }, + Events + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoEvents()).init(); + + return table; + } + }, + EventNotes + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoEventNotes()).init(); + return table; + } + }, + CodedEvents + { + @Override + public TableInfo createTable(SNDUserSchema schema) + { + SimpleUserSchema.SimpleTable table = + new SimpleUserSchema.SimpleTable<>( + schema, SNDSchema.getInstance().getTableInfoCodedEvents()).init(); + + return table; + } + }; + + + public abstract TableInfo createTable(SNDUserSchema schema); + } + + @Override + @Nullable + public TableInfo createTable(String name) + { + if (name != null) + { + TableType tableType = null; + for (TableType t : TableType.values()) + { + // Make the enum name lookup case insensitive + if (t.name().equalsIgnoreCase(name.toLowerCase())) + { + tableType = t; + break; + } + } + if (tableType != null) + { + return tableType.createTable(this); + } + } + return null; + } } \ No newline at end of file From ec83270a4880fa0e5225d90e42756703a23b17ee Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 14 Aug 2017 20:39:24 +0000 Subject: [PATCH 017/686] Spec 30931: Infrastructure to create packages. Rename Package, add superpackage. --- .../api/snd/{SNDPackage.java => Package.java} | 2 +- api-src/org/labkey/api/snd/SNDService.java | 2 +- api-src/org/labkey/api/snd/SuperPackage.java | 41 +++++++++++++++++++ src/org/labkey/snd/SNDController.java | 4 +- src/org/labkey/snd/SNDManager.java | 8 ++-- src/org/labkey/snd/SNDServiceImpl.java | 4 +- 6 files changed, 51 insertions(+), 10 deletions(-) rename api-src/org/labkey/api/snd/{SNDPackage.java => Package.java} (95%) create mode 100644 api-src/org/labkey/api/snd/SuperPackage.java diff --git a/api-src/org/labkey/api/snd/SNDPackage.java b/api-src/org/labkey/api/snd/Package.java similarity index 95% rename from api-src/org/labkey/api/snd/SNDPackage.java rename to api-src/org/labkey/api/snd/Package.java index 943d54eb1..e2395066b 100644 --- a/api-src/org/labkey/api/snd/SNDPackage.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -14,7 +14,7 @@ /** * Created by marty on 8/4/2017. */ -public class SNDPackage +public class Package { private Integer _pkgId; private String _description; diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java index 7c7c79929..0644ecc8d 100644 --- a/api-src/org/labkey/api/snd/SNDService.java +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -16,5 +16,5 @@ static SNDService get() return ServiceRegistry.get(SNDService.class); } - void savePackage(Container c, User u, SNDPackage pkg); + void savePackage(Container c, User u, Package pkg); } diff --git a/api-src/org/labkey/api/snd/SuperPackage.java b/api-src/org/labkey/api/snd/SuperPackage.java new file mode 100644 index 000000000..d2278a1a5 --- /dev/null +++ b/api-src/org/labkey/api/snd/SuperPackage.java @@ -0,0 +1,41 @@ +package org.labkey.api.snd; + +/** + * Created by marty on 8/14/2017. + */ +public class SuperPackage +{ + private Integer _superPkgId; + private Integer _parentSuperPkgId; + private Integer _pkgId; + + public Integer getSuperPkgId() + { + return _superPkgId; + } + + public void setSuperPkgId(Integer superPkgId) + { + _superPkgId = superPkgId; + } + + public Integer getParentSuperPkgId() + { + return _parentSuperPkgId; + } + + public void setParentSuperPkgId(Integer parentSuperPkgId) + { + _parentSuperPkgId = parentSuperPkgId; + } + + public Integer getPkgId() + { + return _pkgId; + } + + public void setPkgId(Integer pkgId) + { + _pkgId = pkgId; + } +} diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index 10d958122..bc3779a71 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -26,7 +26,7 @@ import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; -import org.labkey.api.snd.SNDPackage; +import org.labkey.api.snd.Package; import org.labkey.api.snd.SNDService; import org.labkey.api.view.NavTree; import org.springframework.validation.BindException; @@ -63,7 +63,7 @@ public class SavePackageAction extends ApiAction public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws Exception { JSONObject json = form.getJsonObject(); - SNDPackage pkg = new SNDPackage(); + Package pkg = new Package(); if (json.get("PkgId") != null) pkg.setPkgId(json.getInt("PkgId")); diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index ece203e43..7e4c5e3c0 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -30,7 +30,7 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; -import org.labkey.api.snd.SNDPackage; +import org.labkey.api.snd.Package; import java.sql.SQLException; import java.util.ArrayList; @@ -41,7 +41,7 @@ public class SNDManager { private static final SNDManager _instance = new SNDManager(); private static final int _minPkgId = 10000; - private static final String SND_DBSEQUENCE_NAME = "org.labkey.snd.api.SNDPackage"; + private static final String SND_DBSEQUENCE_NAME = "org.labkey.snd.api.Package"; private SNDManager() { @@ -60,7 +60,7 @@ public Integer generatePackageId(Container c) return sequence.next(); } - public void updatePackage(User u, Container c, SNDPackage pkg, BatchValidationException errors) + public void updatePackage(User u, Container c, Package pkg, BatchValidationException errors) { TableInfo pkgsTable = SNDSchema.getInstance().getTableInfoPkgs(); QueryUpdateService pkgQus = pkgsTable.getUpdateService(); @@ -87,7 +87,7 @@ public void updatePackage(User u, Container c, SNDPackage pkg, BatchValidationEx } } - public void createNewPackage(User u, Container c, SNDPackage pkg, BatchValidationException errors) + public void createNewPackage(User u, Container c, Package pkg, BatchValidationException errors) { UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index a2425a8d5..5e04bd2b6 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -3,7 +3,7 @@ import org.labkey.api.data.Container; import org.labkey.api.query.BatchValidationException; import org.labkey.api.security.User; -import org.labkey.api.snd.SNDPackage; +import org.labkey.api.snd.Package; import org.labkey.api.snd.SNDService; import org.labkey.api.util.UnexpectedException; @@ -19,7 +19,7 @@ private SNDServiceImpl() } @Override - public void savePackage(Container c, User u, SNDPackage pkg) + public void savePackage(Container c, User u, Package pkg) { BatchValidationException errors = new BatchValidationException(); From d139d1b8e32d1eb5b6cbc06313aa1c997443659a Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 14 Aug 2017 22:08:14 +0000 Subject: [PATCH 018/686] Spec 30931: Use GWTPropertyDescriptor instead of PropertyDescriptor --- api-src/org/labkey/api/snd/Package.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index e2395066b..5f0d0942a 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -2,7 +2,7 @@ import org.labkey.api.collections.ArrayListMap; import org.labkey.api.data.Container; -import org.labkey.api.exp.PropertyDescriptor; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import java.util.ArrayList; import java.util.Collection; @@ -22,7 +22,7 @@ public class Package private boolean _repeatable; private boolean _active; private List _categories = Collections.EMPTY_LIST; - private Collection _attributes; + private Collection _attributes; private Collection _subpackages; private Map _extraFields = new HashMap<>(); @@ -86,12 +86,12 @@ public void setCategories(List categories) this._categories = categories; } - public Collection getAttributes() + public Collection getAttributes() { return _attributes; } - public void setAttributes(Collection attributes) + public void setAttributes(Collection attributes) { this._attributes = attributes; } From 417e13a9681658d08c017c134bf6605e0870ad37 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 15 Aug 2017 02:58:46 +0000 Subject: [PATCH 019/686] Spec 30686: Extensible columns - test code review. --- .../org/labkey/test/tests/snd/SNDTest.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 3b4323f0e..9fbd9ae83 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -33,7 +33,6 @@ import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.CustomModules; -import org.labkey.test.pages.snd.BeginPage; import org.labkey.test.util.LogMethod; import org.labkey.test.util.Maps; import org.labkey.test.util.SqlserverOnlyTest; @@ -59,9 +58,6 @@ public class SNDTest extends BaseWebDriverTest implements SqlserverOnlyTest private static final String EXTCOLTESTDATA3A = "updated testString 3"; private static final String CREATEDOMAINSAPI = "LABKEY.Domain.create({\n" + - " failure: function (e) {\n" + - " LABKEY.Utils.alert(\"Error\", e.exception);\n" + - " },\n" + " domainGroup: \"test\",\n" + " domainKind: \"SND\",\n" + " module: \"snd\",\n" + @@ -147,10 +143,11 @@ public void preTest() @Test public void testSNDModule() { - BeginPage beginPage = BeginPage.beginAt(this, getProjectName()); - assertEquals(200, getResponseCode()); - final String expectedHello = "Hello, and welcome to the SND module."; - assertEquals("Wrong hello message", expectedHello, beginPage.getHelloMessage()); + //TODO: Implement this once we get a UI +// BeginPage beginPage = BeginPage.beginAt(this, getProjectName()); +// assertEquals(200, getResponseCode()); +// final String expectedHello = "Hello, and welcome to the SND module."; +// assertEquals("Wrong hello message", expectedHello, beginPage.getHelloMessage()); } @Override @@ -183,7 +180,7 @@ public void testExtensibleColumns() throws Exception insertRowsCommand.addRow(TEST1ROW2MAP); insertRowsCommand.addRow(TEST1ROW3MAP); SaveRowsResponse resp = insertRowsCommand.execute(cn, getProjectName() + "/" + TEST1SUBFOLDER); - assert resp.getRowsAffected().intValue() == 3; + assertEquals(resp.getRowsAffected().intValue(), 3); goToSchemaBrowser(); selectQuery("snd", "Pkgs"); @@ -197,7 +194,7 @@ public void testExtensibleColumns() throws Exception UpdateRowsCommand updateRowsCommand = new UpdateRowsCommand("snd", "Pkgs"); updateRowsCommand.addRow(TEST1ROW3AMAP); resp = updateRowsCommand.execute(cn, getProjectName() + "/" + TEST1SUBFOLDER); - assert resp.getRowsAffected().intValue() == 1; + assertEquals(resp.getRowsAffected().intValue(), 1); goToSchemaBrowser(); selectQuery("snd", "Pkgs"); @@ -209,7 +206,7 @@ public void testExtensibleColumns() throws Exception DeleteRowsCommand deleteRowsCommand = new DeleteRowsCommand("snd", "Pkgs"); deleteRowsCommand.addRow(TEST1ROW2MAP); resp = deleteRowsCommand.execute(cn, getProjectName() + "/" + TEST1SUBFOLDER); - assert resp.getRowsAffected().intValue() == 1; + assertEquals(resp.getRowsAffected().intValue(), 1); goToSchemaBrowser(); selectQuery("snd", "Pkgs"); From bbaf2a87973af566e5695852da82622bd12d20a1 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 15 Aug 2017 15:41:36 +0000 Subject: [PATCH 020/686] Spec 30931: Create package infrastructure. --- api-src/org/labkey/api/snd/Package.java | 10 ++--- .../org/labkey/api/snd/PackageDomainKind.java | 40 +++++++++++++++++++ api-src/org/labkey/api/snd/SuperPackage.java | 12 +++--- src/org/labkey/snd/SNDManager.java | 29 ++++++++++++-- src/org/labkey/snd/SNDModule.java | 2 + 5 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 api-src/org/labkey/api/snd/PackageDomainKind.java diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 5f0d0942a..2dbb3a840 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -22,8 +22,8 @@ public class Package private boolean _repeatable; private boolean _active; private List _categories = Collections.EMPTY_LIST; - private Collection _attributes; - private Collection _subpackages; + private List _attributes; + private List _subpackages; private Map _extraFields = new HashMap<>(); public Integer getPkgId() @@ -86,12 +86,12 @@ public void setCategories(List categories) this._categories = categories; } - public Collection getAttributes() + public List getAttributes() { return _attributes; } - public void setAttributes(Collection attributes) + public void setAttributes(List attributes) { this._attributes = attributes; } @@ -101,7 +101,7 @@ public Collection getSubpackages() return _subpackages; } - public void setSubpackages(Collection subpackages) + public void setSubpackages(List subpackages) { this._subpackages = subpackages; } diff --git a/api-src/org/labkey/api/snd/PackageDomainKind.java b/api-src/org/labkey/api/snd/PackageDomainKind.java new file mode 100644 index 000000000..628c0ab74 --- /dev/null +++ b/api-src/org/labkey/api/snd/PackageDomainKind.java @@ -0,0 +1,40 @@ +package org.labkey.api.snd; + +import org.labkey.api.data.Container; +import org.labkey.api.query.ExtendedTableDomainKind; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.AdminPermission; + +/** + * Created by marty on 8/14/2017. + */ +public class PackageDomainKind extends ExtendedTableDomainKind +{ + private final String NAMESPACE_PREFIX = "package"; + private final String SCHEMA_NAME = "snd"; + private final String KIND_NAME = "Package"; + + @Override + public boolean canCreateDefinition(User user, Container container) + { + return container.hasPermission("PackageDomainKind.canCreateDefinition", user, AdminPermission.class); + } + + @Override + protected String getSchemaName() + { + return SCHEMA_NAME; + } + + @Override + protected String getNamespacePrefix() + { + return NAMESPACE_PREFIX; + } + + @Override + public String getKindName() + { + return KIND_NAME; + } +} diff --git a/api-src/org/labkey/api/snd/SuperPackage.java b/api-src/org/labkey/api/snd/SuperPackage.java index d2278a1a5..a95deccd5 100644 --- a/api-src/org/labkey/api/snd/SuperPackage.java +++ b/api-src/org/labkey/api/snd/SuperPackage.java @@ -1,12 +1,14 @@ package org.labkey.api.snd; +import java.util.List; + /** * Created by marty on 8/14/2017. */ public class SuperPackage { private Integer _superPkgId; - private Integer _parentSuperPkgId; + private List _childPackages; private Integer _pkgId; public Integer getSuperPkgId() @@ -19,14 +21,14 @@ public void setSuperPkgId(Integer superPkgId) _superPkgId = superPkgId; } - public Integer getParentSuperPkgId() + public List getParentSuperPkgId() { - return _parentSuperPkgId; + return _childPackages; } - public void setParentSuperPkgId(Integer parentSuperPkgId) + public void setParentSuperPkgId(List childPackages) { - _parentSuperPkgId = parentSuperPkgId; + _childPackages = childPackages; } public Integer getPkgId() diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 7e4c5e3c0..4068ee2e3 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -21,6 +21,11 @@ import org.labkey.api.data.DbSequence; import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainKind; +import org.labkey.api.exp.property.DomainUtil; +import org.labkey.api.gwt.client.model.GWTDomain; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.DuplicateKeyException; import org.labkey.api.query.InvalidKeyException; @@ -31,6 +36,7 @@ import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.snd.Package; +import org.labkey.api.snd.PackageDomainKind; import java.sql.SQLException; import java.util.ArrayList; @@ -60,14 +66,21 @@ public Integer generatePackageId(Container c) return sequence.next(); } + private String getPackageName(int id) + { + return "Package-" + id; + } + public void updatePackage(User u, Container c, Package pkg, BatchValidationException errors) { - TableInfo pkgsTable = SNDSchema.getInstance().getTableInfoPkgs(); + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + TableInfo pkgsTable = schema.getTable(SNDSchema.PKGS_TABLE_NAME); QueryUpdateService pkgQus = pkgsTable.getUpdateService(); if (pkgQus == null) throw new IllegalStateException(); - TableInfo pkgCategJuncTable = SNDSchema.getInstance().getTableInfoPkgCategoryJunction(); + TableInfo pkgCategJuncTable = schema.getTable(SNDSchema.PKGCATEGORYJUNCTION_TABLE_NAME); QueryUpdateService pkgCategoryQus = pkgCategJuncTable.getUpdateService(); if (pkgCategoryQus == null) throw new IllegalStateException(); @@ -85,6 +98,9 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep { errors.addRowError(new ValidationException(e.getMessage())); } + +// String domainURI = +// GWTDomain existingDomain = DomainUtil.getDomainDescriptor(u, domainURI, c); } public void createNewPackage(User u, Container c, Package pkg, BatchValidationException errors) @@ -115,7 +131,14 @@ public void createNewPackage(User u, Container c, Package pkg, BatchValidationEx errors.addRowError(new ValidationException(e.getMessage())); } - //Domain domain = DomainUtil.createDomain(kindName, newDomain, options, getContainer(), getUser(), domainName, null); + GWTDomain newDomain = new GWTDomain<>(); + newDomain.setName(getPackageName(pkg.getPkgId())); + newDomain.setContainer(c.getId()); + newDomain.setDescription(pkg.getDescription()); + newDomain.setFields(pkg.getAttributes()); + + DomainKind kind = new PackageDomainKind(); + Domain domain = DomainUtil.createDomain(kind.getKindName(), newDomain, null, c, u, null, null); } } \ No newline at end of file diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java index 466ed018e..85767c2dc 100644 --- a/src/org/labkey/snd/SNDModule.java +++ b/src/org/labkey/snd/SNDModule.java @@ -26,6 +26,7 @@ import org.labkey.api.query.DefaultSchema; import org.labkey.api.query.QuerySchema; import org.labkey.api.services.ServiceRegistry; +import org.labkey.api.snd.PackageDomainKind; import org.labkey.api.snd.SNDDomainKind; import org.labkey.api.snd.SNDService; import org.labkey.api.view.WebPartFactory; @@ -68,6 +69,7 @@ protected void init() { addController(SNDController.NAME, SNDController.class); PropertyService.get().registerDomainKind(new SNDDomainKind()); + PropertyService.get().registerDomainKind(new PackageDomainKind()); ServiceRegistry.get().registerService(SNDService.class, SNDServiceImpl.INSTANCE); } From 5163d4e4a5f35c62c22d36b9fce55cbd007dd706 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 15 Aug 2017 17:15:33 +0000 Subject: [PATCH 021/686] Update executeScript to executeAsyncScript in test --- .../src/org/labkey/test/tests/snd/SNDTest.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 9fbd9ae83..3c78f889a 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -61,8 +61,19 @@ public class SNDTest extends BaseWebDriverTest implements SqlserverOnlyTest " domainGroup: \"test\",\n" + " domainKind: \"SND\",\n" + " module: \"snd\",\n" + - " importData: false\n" + - "});\n"; + " importData: false,\n" + + " success: onSuccess,\n" + + " failure: onFailure\n" + + "});\n" + + "function onFailure(e)\n" + + "{\n" + + " callback(e.exception);\n" + + "}\n" + + "\n" + + "function onSuccess()\n" + + "{\n" + + " callback('Success!');\n" + + "}\n"; private final Map TEST1ROW1MAP = Maps.of("PkgId", 1001, "Description", "Description 1", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223451b901", "testPkgs", EXTCOLTESTDATA1); private final Map TEST1ROW2MAP = Maps.of("PkgId", 1002, "Description", "Description 2", "ObjectId", "dbe961b9-b7ba-102d-8c2a-99223751b901", "testPkgs", EXTCOLTESTDATA2); @@ -131,7 +142,8 @@ private void setupTest1Project() private void addTestColumns() { - executeScript(CREATEDOMAINSAPI); + String result = (String)executeAsyncScript(CREATEDOMAINSAPI); + assertEquals(result,"Success!", result); } @Before From 6f653a24ec8700986c0ab36602013317aa01fa89 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 15 Aug 2017 18:06:20 +0000 Subject: [PATCH 022/686] Spec 30931: Save super packages stub. Implementation will come next sprint. --- api-src/org/labkey/api/snd/SNDService.java | 1 + src/org/labkey/snd/SNDServiceImpl.java | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java index 0644ecc8d..3ddc28136 100644 --- a/api-src/org/labkey/api/snd/SNDService.java +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -17,4 +17,5 @@ static SNDService get() } void savePackage(Container c, User u, Package pkg); + void saveSuperPackage(Container c, User u, SuperPackage superPkg); } diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 5e04bd2b6..5cb5b0aa6 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -5,6 +5,7 @@ import org.labkey.api.security.User; import org.labkey.api.snd.Package; import org.labkey.api.snd.SNDService; +import org.labkey.api.snd.SuperPackage; import org.labkey.api.util.UnexpectedException; /** @@ -37,4 +38,10 @@ public void savePackage(Container c, User u, Package pkg) if (errors.hasErrors()) throw new UnexpectedException(errors); } + + @Override + public void saveSuperPackage(Container c, User u, SuperPackage superPkg) + { + + } } From 3e9ca394bb36097c01e28d77c7cd42a838ed27ec Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 16 Aug 2017 00:08:55 +0000 Subject: [PATCH 023/686] Spec 30931: Save package attributes. --- .../org/labkey/api/snd/PackageDomainKind.java | 40 +++++++++++++++++-- src/org/labkey/snd/SNDController.java | 29 +++++++++++--- src/org/labkey/snd/SNDManager.java | 18 +++++---- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/api-src/org/labkey/api/snd/PackageDomainKind.java b/api-src/org/labkey/api/snd/PackageDomainKind.java index 628c0ab74..be8d3617b 100644 --- a/api-src/org/labkey/api/snd/PackageDomainKind.java +++ b/api-src/org/labkey/api/snd/PackageDomainKind.java @@ -1,6 +1,10 @@ package org.labkey.api.snd; import org.labkey.api.data.Container; +import org.labkey.api.exp.XarContext; +import org.labkey.api.exp.XarFormatException; +import org.labkey.api.exp.xar.LsidUtils; +import org.labkey.api.module.SimpleModule; import org.labkey.api.query.ExtendedTableDomainKind; import org.labkey.api.security.User; import org.labkey.api.security.permissions.AdminPermission; @@ -10,9 +14,11 @@ */ public class PackageDomainKind extends ExtendedTableDomainKind { - private final String NAMESPACE_PREFIX = "package"; - private final String SCHEMA_NAME = "snd"; - private final String KIND_NAME = "Package"; + private static final String NAMESPACE_PREFIX = "package"; + private static final String SCHEMA_NAME = "snd"; + private static final String KIND_NAME = "Package"; + + private static final String DOMAIN_NAMESPACE_PREFIX_TEMPLATE = NAMESPACE_PREFIX + "-${SchemaName}"; @Override public boolean canCreateDefinition(User user, Container container) @@ -20,8 +26,31 @@ public boolean canCreateDefinition(User user, Container container) return container.hasPermission("PackageDomainKind.canCreateDefinition", user, AdminPermission.class); } + public String generateDomainURI(String schemaName, String tableName, Container c, User u) + { + return getDomainURI(schemaName, tableName, c, u); + } + + public static String getDomainURI(String schemaName, String tableName, Container c, User u) + { + try + { + XarContext xc = getXarContext(schemaName, tableName, getDomainContainer(c), u); + return LsidUtils.resolveLsidFromTemplate(SimpleModule.DOMAIN_LSID_TEMPLATE, xc, DOMAIN_NAMESPACE_PREFIX_TEMPLATE); + } + catch (XarFormatException xfe) + { + return null; + } + } + @Override protected String getSchemaName() + { + return getPackageSchemaName(); + } + + public static String getPackageSchemaName() { return SCHEMA_NAME; } @@ -34,6 +63,11 @@ protected String getNamespacePrefix() @Override public String getKindName() + { + return getPackageKindName(); + } + + public static String getPackageKindName() { return KIND_NAME; } diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index bc3779a71..74c278d6e 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -16,6 +16,7 @@ package org.labkey.snd; +import org.json.JSONArray; import org.json.JSONObject; import org.labkey.api.action.ApiAction; import org.labkey.api.action.ApiResponse; @@ -23,6 +24,8 @@ import org.labkey.api.action.SimpleApiJsonForm; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.action.SpringActionController; +import org.labkey.api.exp.api.ExperimentService; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.ReadPermission; @@ -32,6 +35,9 @@ import org.springframework.validation.BindException; import org.springframework.web.servlet.ModelAndView; +import java.util.ArrayList; +import java.util.List; + public class SNDController extends SpringActionController { private static final DefaultActionResolver _actionResolver = new DefaultActionResolver(SNDController.class); @@ -64,13 +70,24 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws { JSONObject json = form.getJsonObject(); Package pkg = new Package(); - if (json.get("PkgId") != null) - pkg.setPkgId(json.getInt("PkgId")); + pkg.setPkgId(json.optInt("id", 0)); + + pkg.setDescription(json.getString("description")); + pkg.setActive(json.getBoolean("active")); + pkg.setRepeatable(json.getBoolean("repeatable")); + pkg.setNarrative(json.getString("narrative")); + + JSONArray attribs = json.optJSONArray("attributes"); + List pds = new ArrayList<>(); - pkg.setDescription(json.getString("Description")); - pkg.setActive(json.getBoolean("Active")); - pkg.setRepeatable(json.getBoolean("Repeatable")); - pkg.setNarrative(json.getString("Narrative")); + if (null != attribs) + { + for (int i = 0; i < attribs.length(); i++) + { + pds.add(ExperimentService.get().convertJsonToPropertyDescriptor(attribs.getJSONObject(i))); + } + pkg.setAttributes(pds); + } SNDService.get().savePackage(getViewContext().getContainer(), getUser(), pkg); diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 4068ee2e3..f1e30f1aa 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -21,8 +21,6 @@ import org.labkey.api.data.DbSequence; import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.TableInfo; -import org.labkey.api.exp.property.Domain; -import org.labkey.api.exp.property.DomainKind; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; @@ -68,7 +66,7 @@ public Integer generatePackageId(Container c) private String getPackageName(int id) { - return "Package-" + id; + return PackageDomainKind.getPackageKindName() + "-" + id; } public void updatePackage(User u, Container c, Package pkg, BatchValidationException errors) @@ -99,8 +97,15 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep errors.addRowError(new ValidationException(e.getMessage())); } -// String domainURI = -// GWTDomain existingDomain = DomainUtil.getDomainDescriptor(u, domainURI, c); + String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), getPackageName(pkg.getPkgId()), c, u); + + GWTDomain updateDomain = new GWTDomain<>(); + updateDomain.setName(getPackageName(pkg.getPkgId())); + updateDomain.setFields(pkg.getAttributes()); + updateDomain.setDomainURI(domainURI); + + PackageDomainKind kind = new PackageDomainKind(); + kind.updateDomain(c, u, updateDomain); } public void createNewPackage(User u, Container c, Package pkg, BatchValidationException errors) @@ -137,8 +142,7 @@ public void createNewPackage(User u, Container c, Package pkg, BatchValidationEx newDomain.setDescription(pkg.getDescription()); newDomain.setFields(pkg.getAttributes()); - DomainKind kind = new PackageDomainKind(); - Domain domain = DomainUtil.createDomain(kind.getKindName(), newDomain, null, c, u, null, null); + DomainUtil.createDomain(PackageDomainKind.getPackageKindName(), newDomain, null, c, u, null, null); } } \ No newline at end of file From 44baf3ed2c63340d2957df9e9ef91dc5e562ce32 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 16 Aug 2017 15:15:07 +0000 Subject: [PATCH 024/686] Spec 30931: Categories in package --- api-src/org/labkey/api/snd/Package.java | 3 +-- src/org/labkey/snd/SNDController.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 2dbb3a840..3dd61086c 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,7 +20,7 @@ public class Package private String _narrative; private boolean _repeatable; private boolean _active; - private List _categories = Collections.EMPTY_LIST; + private List _categories = new ArrayList<>(); private List _attributes; private List _subpackages; private Map _extraFields = new HashMap<>(); diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index 74c278d6e..3a3b07c6f 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -77,6 +77,17 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws pkg.setRepeatable(json.getBoolean("repeatable")); pkg.setNarrative(json.getString("narrative")); + List categories = new ArrayList<>(); + JSONArray jsonCategories = json.getJSONArray("categories"); + if (null != jsonCategories) + { + for (int j = 0; j < jsonCategories.length(); j++) + { + categories.add(jsonCategories.getInt(j)); + } + } + pkg.setCategories(categories); + JSONArray attribs = json.optJSONArray("attributes"); List pds = new ArrayList<>(); From f677e71dcc4dcf27f433b418d4c0fc5dc2c26ce0 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 16 Aug 2017 16:29:12 +0000 Subject: [PATCH 025/686] Spec 30931: Save package extra fields. Update package categories. --- src/org/labkey/snd/SNDController.java | 23 +++++++++++++++++++---- src/org/labkey/snd/SNDManager.java | 11 +++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index 3a3b07c6f..809645ec1 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -36,7 +36,9 @@ import org.springframework.web.servlet.ModelAndView; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class SNDController extends SpringActionController { @@ -77,22 +79,35 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws pkg.setRepeatable(json.getBoolean("repeatable")); pkg.setNarrative(json.getString("narrative")); - List categories = new ArrayList<>(); + // Get extra fields + JSONObject jsonExtras = json.getJSONObject("extraFields"); + if (null != jsonExtras) + { + Map extras = new HashMap<>(); + for (String s : jsonExtras.keySet()) + { + extras.put(s, jsonExtras.get(s)); + } + pkg.setExtraFields(extras); + } + + // Get categories JSONArray jsonCategories = json.getJSONArray("categories"); if (null != jsonCategories) { + List categories = new ArrayList<>(); for (int j = 0; j < jsonCategories.length(); j++) { categories.add(jsonCategories.getInt(j)); } + pkg.setCategories(categories); } - pkg.setCategories(categories); + // Get attributes JSONArray attribs = json.optJSONArray("attributes"); - List pds = new ArrayList<>(); - if (null != attribs) { + List pds = new ArrayList<>(); for (int i = 0; i < attribs.length(); i++) { pds.add(ExperimentService.get().convertJsonToPropertyDescriptor(attribs.getJSONObject(i))); diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index f1e30f1aa..b8ce97768 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -20,6 +20,8 @@ import org.labkey.api.data.DbScope; import org.labkey.api.data.DbSequence; import org.labkey.api.data.DbSequenceManager; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; @@ -89,10 +91,15 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep try (DbScope.Transaction tx = pkgsTable.getSchema().getScope().ensureTransaction()) { pkgQus.updateRows(u, c, pkgRows, null, null, null); - pkgCategoryQus.updateRows(u, c, pkg.getCategoryRows(c), null, null, null); + + // For categories delete existing junction relations and add new ones + SQLFragment sql = new SQLFragment("DELETE FROM snd.PkgCategoryJunction WHERE PkgId = " + pkg.getPkgId()); + SqlExecutor sqlex = new SqlExecutor(SNDSchema.getInstance().getSchema()); + sqlex.execute(sql); + pkgCategoryQus.insertRows(u, c, pkg.getCategoryRows(c), errors, null, null); tx.commit(); } - catch (QueryUpdateServiceException | BatchValidationException | InvalidKeyException | SQLException e) + catch (QueryUpdateServiceException | BatchValidationException | DuplicateKeyException | SQLException | InvalidKeyException e) { errors.addRowError(new ValidationException(e.getMessage())); } From c4ddf60a3f7468da82010c5ae725b940babb1f97 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 16 Aug 2017 21:52:12 +0000 Subject: [PATCH 026/686] Spec 30931: Check if domain exists. --- src/org/labkey/snd/SNDManager.java | 2 +- src/org/labkey/snd/SNDServiceImpl.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index b8ce97768..c077f29da 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -66,7 +66,7 @@ public Integer generatePackageId(Container c) return sequence.next(); } - private String getPackageName(int id) + public static String getPackageName(int id) { return PackageDomainKind.getPackageKindName() + "-" + id; } diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 5cb5b0aa6..9d569906c 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -1,9 +1,12 @@ package org.labkey.snd; import org.labkey.api.data.Container; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.PropertyService; import org.labkey.api.query.BatchValidationException; import org.labkey.api.security.User; import org.labkey.api.snd.Package; +import org.labkey.api.snd.PackageDomainKind; import org.labkey.api.snd.SNDService; import org.labkey.api.snd.SuperPackage; import org.labkey.api.util.UnexpectedException; @@ -23,9 +26,16 @@ private SNDServiceImpl() public void savePackage(Container c, User u, Package pkg) { BatchValidationException errors = new BatchValidationException(); + Domain domain = null; - // Create/update package in pkgs table if (null != pkg.getPkgId() && pkg.getPkgId() > 0) + { + String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), SNDManager.getPackageName(pkg.getPkgId()), c, u); + domain = PropertyService.get().getDomain(c, domainURI); + } + + // Create/update package in pkgs table + if (null != domain) { SNDManager.get().updatePackage(u, c, pkg, errors); } From 01705563f4658813fc92524af9ed49224303665e Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Thu, 17 Aug 2017 00:23:01 +0000 Subject: [PATCH 027/686] Spec 30931: Add in use column to package. --- src/org/labkey/snd/SNDUserSchema.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index 17c136487..ef1941944 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -18,7 +18,10 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.SQLFragment; import org.labkey.api.data.TableInfo; +import org.labkey.api.query.ExprColumn; import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.security.User; @@ -53,6 +56,19 @@ public TableInfo createTable(SNDUserSchema schema) new SimpleUserSchema.SimpleTable<>( schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); + + SQLFragment inUseSql = new SQLFragment(); + inUseSql.append("(CASE WHEN EXISTS (SELECT sp.PkgId FROM "); + inUseSql.append(SNDSchema.getInstance().getTableInfoSuperPkgs(), "sp"); + inUseSql.append(" JOIN "); + inUseSql.append(SNDSchema.getInstance().getTableInfoCodedEvents(), "ce"); + inUseSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); + inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); + inUseSql.append(" THEN 'true' ELSE 'false' END)"); + ExprColumn inUseCol = new ExprColumn(table, "inUse", inUseSql, JdbcType.BOOLEAN); + inUseCol.setHidden(false); + table.addColumn(inUseCol); + return table; } }, From c2126f64d7ece833742347a95d56d42623b2be1b Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Thu, 17 Aug 2017 00:50:27 +0000 Subject: [PATCH 028/686] Spec 30931: Infrastructure to Create Packages : generate package id if its not found in the xml. --- src/org/labkey/snd/SNDServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 9d569906c..510ebd312 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -41,7 +41,9 @@ public void savePackage(Container c, User u, Package pkg) } else { - pkg.setPkgId(SNDManager.get().generatePackageId(c)); + if(null == pkg.getPkgId()) + pkg.setPkgId(SNDManager.get().generatePackageId(c)); + SNDManager.get().createNewPackage(u, c, pkg, errors); } From 29cdfd8e0132df852ad1ab95a69733a82e23177f Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Thu, 17 Aug 2017 00:51:30 +0000 Subject: [PATCH 029/686] Spec 30931: Infrastructure to Create Packages : modify namespace with 'snd' --- schemas/snd.xsd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/schemas/snd.xsd b/schemas/snd.xsd index 461a911bb..fe6bc6701 100644 --- a/schemas/snd.xsd +++ b/schemas/snd.xsd @@ -2,9 +2,9 @@ + xmlns="http://txbiomed.org/snd"> @@ -51,7 +51,7 @@ - + From fe92c99c3d32cdc5a728ab0f448e841b4f333b44 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Thu, 17 Aug 2017 20:28:50 +0000 Subject: [PATCH 030/686] Spec 30932 : SNPRC SND - XML Package Integration - initial commit. --- src/org/labkey/snd/SNDModule.java | 10 +- .../labkey/snd/pipeline/SNDDataHandler.java | 278 ++++++++++++++++++ webapp/WEB-INF/snd/sndContext.xml | 49 +++ 3 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 src/org/labkey/snd/pipeline/SNDDataHandler.java create mode 100644 webapp/WEB-INF/snd/sndContext.xml diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java index 85767c2dc..e9e5f7523 100644 --- a/src/org/labkey/snd/SNDModule.java +++ b/src/org/labkey/snd/SNDModule.java @@ -19,10 +19,13 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; +import org.labkey.api.exp.api.ExperimentService; import org.labkey.api.exp.property.PropertyService; import org.labkey.api.module.DefaultModule; import org.labkey.api.module.Module; import org.labkey.api.module.ModuleContext; +import org.labkey.api.module.SpringModule; +import org.labkey.api.pipeline.PipelineService; import org.labkey.api.query.DefaultSchema; import org.labkey.api.query.QuerySchema; import org.labkey.api.services.ServiceRegistry; @@ -30,12 +33,13 @@ import org.labkey.api.snd.SNDDomainKind; import org.labkey.api.snd.SNDService; import org.labkey.api.view.WebPartFactory; +import org.labkey.snd.pipeline.SNDDataHandler; import java.util.Collection; import java.util.Collections; import java.util.Set; -public class SNDModule extends DefaultModule +public class SNDModule extends SpringModule { public static final String NAME = "SND"; @@ -74,7 +78,7 @@ protected void init() } @Override - public void doStartup(ModuleContext moduleContext) + protected void startupAfterSpringConfig(ModuleContext moduleContext) { // add a container listener so we'll know when our container is deleted: ContainerManager.addContainerListener(new SNDContainerListener()); @@ -86,6 +90,8 @@ public QuerySchema createSchema(final DefaultSchema schema, Module module) return new SNDUserSchema(SNDSchema.NAME, null, schema.getUser(), schema.getContainer(), SNDSchema.getInstance().getSchema()); } }); + + ExperimentService.get().registerExperimentDataHandler(new SNDDataHandler()); } @Override diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java new file mode 100644 index 000000000..44febcc61 --- /dev/null +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -0,0 +1,278 @@ +package org.labkey.snd.pipeline; + +import org.apache.commons.collections.map.HashedMap; +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.Container; +import org.labkey.api.exp.ExperimentException; +import org.labkey.api.exp.Lsid; +import org.labkey.api.exp.XarContext; +import org.labkey.api.exp.api.AbstractExperimentDataHandler; +import org.labkey.api.exp.api.DataType; +import org.labkey.api.exp.api.ExpData; +import org.labkey.api.exp.api.ExpRun; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; +import org.labkey.api.gwt.client.model.GWTPropertyValidator; +import org.labkey.api.security.User; +import org.labkey.api.snd.Package; +import org.labkey.api.snd.SNDService; +import org.labkey.api.snd.SuperPackage; +import org.labkey.api.util.FileType; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.data.xml.ColumnType; +import org.labkey.data.xml.PropertyValidatorType; +import org.txbiomed.snd.AttributesType; +import org.txbiomed.snd.ExportDocument; +import org.txbiomed.snd.PackageType; +import org.txbiomed.snd.PackagesType; +import org.txbiomed.snd.SuperPackageType; +import org.txbiomed.snd.SuperPackagesType; +import org.txbiomed.snd.USDACategoryType; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Created by Binal on 8/7/2017. + */ +public class SNDDataHandler extends AbstractExperimentDataHandler +{ + + private static final FileType SND_INPUT = new FileType(".snd.xml"); + + @Override + public @Nullable DataType getDataType() + { + return null; + } + + @Override + public void importFile(@NotNull ExpData data, File dataFile, @NotNull ViewBackgroundInfo info, @NotNull Logger log, @NotNull XarContext context) throws ExperimentException + { + ExportDocument exportDocument; + String inputFileName = dataFile.getName(); + + if (SND_INPUT.isType(dataFile)) + { + ExpRun run = data.getRun(); + if (null == run) + { + throw new ExperimentException("Experiment run was null for data file '" + dataFile.getName() +"'"); + } + } + + //read xml data + try(FileInputStream in = FileUtils.openInputStream(dataFile)) + { + XmlOptions options = new XmlOptions(); + options.setDocumentType(ExportDocument.type); + options.setLoadUseXMLReader(SAXParserFactory.newInstance().newSAXParser().getXMLReader()); + + //parse xml tags and get tokens/auto-generated pojos + exportDocument = ExportDocument.Factory.parse(in, options); + + //TODO: validate xml - takes a very long time - do we still want to validate? +// XmlBeansUtil.validateXmlDocument(exportDocument, "Validating " + inputFileName + " against schema."); + } + catch (IOException e) + { + throw new ExperimentException("Error reading input file '" + inputFileName +"'", e); + } + catch (XmlException | SAXException | ParserConfigurationException e) + { + throw new ExperimentException("Could not parse input file '" + inputFileName +"'", e); + } +// catch (XmlValidationException e) +// { +// throw new ExperimentException("Invalid XML file " + inputFileName, e); +// } + + ExportDocument.Export export = exportDocument.getExport(); + + if(null != export) + { + parseAndSavePackages(export, info); + parseAndSaveSuperPackages(export); + } + } + + private void parseAndSavePackages(ExportDocument.Export export, @NotNull ViewBackgroundInfo info) + { + //get Package nodes + PackagesType packages = export.getPackages(); + PackageType[] packageArray = packages.getPackageArray(); + + SNDService sndService = SNDService.get(); + + //convert auto-generated objects/tokens to SND's Package objects + for (PackageType packageType : packageArray) + { + Package pkg = parsePackage(packageType); + sndService.savePackage(info.getContainer(), info.getUser(), pkg); + } + } + + private Package parsePackage(PackageType packageType) + { + Package pkg = new Package(); + + //Id + pkg.setPkgId(packageType.getId()); + + //description + pkg.setDescription(packageType.getDescription()); + + //repeatable + pkg.setRepeatable(packageType.getRepeatable()); + + //displayable //TODO: keep 'active' or change to 'displayable'? + pkg.setActive(packageType.getDisplayable()); + + /* extra field(s)*/ + USDACategoryType.Enum usdaCategoryVal = packageType.getUsdaCategory(); + Map extraFields = new HashedMap(); + + //usda-category + extraFields.put("usda-category", usdaCategoryVal); + pkg.setExtraFields(extraFields); + + //narrative + pkg.setNarrative(packageType.getNarrative()); + + //attributes + pkg.setAttributes(getAttributes(packageType)); + + return pkg; + } + + private List getAttributes(PackageType packageType) + { + AttributesType attributes = packageType.getAttributes(); + ColumnType[] attributeArray = attributes.getAttributeArray(); + + List attributesList = null; + if (attributeArray.length > 1) + { + attributesList = new LinkedList<>(); + for (ColumnType ct : attributeArray) + { + GWTPropertyDescriptor gwtpd = new GWTPropertyDescriptor(); + + //columnName + gwtpd.setName(ct.getColumnName()); + + //rangeURI + String rangeURI = ct.getRangeURI(); + if (null != rangeURI) + gwtpd.setRangeURI(rangeURI); + else + gwtpd.setRangeURI("http://www.w3.org/2001/XMLSchema#" + ct.getDatatype()); + + //nullable + gwtpd.setRequired(ct.getNullable()); + + //columnTitle + gwtpd.setLabel(ct.getColumnTitle()); + + //defaultValue + gwtpd.setDefaultValue(ct.getDefaultValue()); + + //fk + ColumnType.Fk fk = ct.getFk(); + if(null != fk) + { + gwtpd.setLookupQuery(fk.getFkTable()); + gwtpd.setLookupSchema(fk.getFkDbSchema()); + } + + //scale + gwtpd.setScale(ct.getScale()); + + //precision + int precision = ct.getPrecision(); + if (precision >= 1) + { + StringBuilder pr = new StringBuilder("0."); + for (int i = 0; i < precision; i++) + pr.append("#"); + + gwtpd.setFormat(pr.toString()); + } + + //validator + PropertyValidatorType validator = ct.getValidator(); + if(null != validator) + { + GWTPropertyValidator gwtPropertyValidator = new GWTPropertyValidator(); + List gwtPropertyValidatorList = new LinkedList<>(); + gwtPropertyValidator.setName(validator.getName()); //name + gwtPropertyValidator.setExpression(validator.getExpression()); //expression + + Lsid lsid = new Lsid(validator.getTypeURI()); + gwtPropertyValidator.setType(org.labkey.api.gwt.client.model.PropertyValidatorType.getType(lsid.getObjectId()));//typeURI + + gwtPropertyValidatorList.add(gwtPropertyValidator); + } + + attributesList.add(gwtpd); + } + } + return attributesList; + } + + private void parseAndSaveSuperPackages(ExportDocument.Export export) + { + SuperPackagesType superPackagesType = export.getSuperPackages(); + SuperPackageType[] superPackageArray = superPackagesType.getSuperPackageArray(); + for(SuperPackageType superPackageType : superPackageArray) + { + SuperPackage superPackage = parseSuperPackage(superPackageType); + //TODO: Save + } + } + + private SuperPackage parseSuperPackage(SuperPackageType superPackageType) + { + SuperPackage superPackage = new SuperPackage(); + superPackage.setSuperPkgId(superPackageType.getSuperPkgId()); + superPackage.setPkgId(superPackageType.getPkgId()); + + return superPackage; + } + + @Override + public @Nullable ActionURL getContentURL(ExpData data) + { + return null; + } + + @Override + public void deleteData(ExpData data, Container container, User user) + { + + } + + @Override + public void runMoved(ExpData newData, Container container, Container targetContainer, String oldRunLSID, String newRunLSID, User user, int oldDataRowID) throws ExperimentException + { + + } + + @Override + public Priority getPriority(ExpData data) + { + return (null != data && null != data.getDataFileUrl() && SND_INPUT.isType(data.getDataFileUrl()) ? Priority.MEDIUM : null); + } +} diff --git a/webapp/WEB-INF/snd/sndContext.xml b/webapp/WEB-INF/snd/sndContext.xml new file mode 100644 index 000000000..b68450e88 --- /dev/null +++ b/webapp/WEB-INF/snd/sndContext.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.labkey.api.exp.pipeline.XarGeneratorId + + + + + + + + From eda2d2b2710acb10e16a356803009f6c6f5e871d Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Tue, 22 Aug 2017 02:58:42 +0000 Subject: [PATCH 031/686] Spec 30931: Add in use column for packages. --- resources/queries/snd/Pkgs.query.xml | 9 +++++++++ resources/queries/snd/Pkgs/.qview.xml | 17 +++++++++++++++++ src/org/labkey/snd/SNDUserSchema.java | 3 +-- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 resources/queries/snd/Pkgs.query.xml create mode 100644 resources/queries/snd/Pkgs/.qview.xml diff --git a/resources/queries/snd/Pkgs.query.xml b/resources/queries/snd/Pkgs.query.xml new file mode 100644 index 000000000..44baa19ed --- /dev/null +++ b/resources/queries/snd/Pkgs.query.xml @@ -0,0 +1,9 @@ + + + + + Packages +
+
+
+
\ No newline at end of file diff --git a/resources/queries/snd/Pkgs/.qview.xml b/resources/queries/snd/Pkgs/.qview.xml new file mode 100644 index 000000000..a7ae4b134 --- /dev/null +++ b/resources/queries/snd/Pkgs/.qview.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index ef1941944..f086a463b 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -65,8 +65,7 @@ public TableInfo createTable(SNDUserSchema schema) inUseSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); inUseSql.append(" THEN 'true' ELSE 'false' END)"); - ExprColumn inUseCol = new ExprColumn(table, "inUse", inUseSql, JdbcType.BOOLEAN); - inUseCol.setHidden(false); + ExprColumn inUseCol = new ExprColumn(table, "InUse", inUseSql, JdbcType.BOOLEAN); table.addColumn(inUseCol); return table; From 8d54e1c4dbf52eee2c523254c8d524fe81115c2a Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Wed, 23 Aug 2017 05:55:11 +0000 Subject: [PATCH 032/686] Spec 30932 : SNPRC SND - XML Package Integration - Added redactedText property. Added analyzeURL to redirect to pipeline status after Import step. --- .../labkey/snd/pipeline/SNDDataHandler.java | 23 ++++++++++++++----- webapp/WEB-INF/snd/sndContext.xml | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 44febcc61..7177055b4 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -22,6 +22,8 @@ import org.labkey.api.snd.SNDService; import org.labkey.api.snd.SuperPackage; import org.labkey.api.util.FileType; +import org.labkey.api.util.XmlBeansUtil; +import org.labkey.api.util.XmlValidationException; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.data.xml.ColumnType; @@ -50,6 +52,9 @@ public class SNDDataHandler extends AbstractExperimentDataHandler { +// private static final String TABLE_INFO_NS = "dat"; + private static final String TABLE_INFO_NS_VAL = "http://labkey.org/data/xml"; + private static final FileType SND_INPUT = new FileType(".snd.xml"); @Override @@ -63,6 +68,9 @@ public void importFile(@NotNull ExpData data, File dataFile, @NotNull ViewBackgr { ExportDocument exportDocument; String inputFileName = dataFile.getName(); +// Map addnlNameSpaces = new HashedMap(); + +// addnlNameSpaces.put(TABLE_INFO_NS, TABLE_INFO_NS_VAL); if (SND_INPUT.isType(dataFile)) { @@ -78,12 +86,14 @@ public void importFile(@NotNull ExpData data, File dataFile, @NotNull ViewBackgr { XmlOptions options = new XmlOptions(); options.setDocumentType(ExportDocument.type); + options.setValidateStrict();//fails silently if namespace is invalid. However, does throw an error if elements are not prefixed with correct ns. +// options.setLoadAdditionalNamespaces(addnlNameSpaces); options.setLoadUseXMLReader(SAXParserFactory.newInstance().newSAXParser().getXMLReader()); //parse xml tags and get tokens/auto-generated pojos exportDocument = ExportDocument.Factory.parse(in, options); - //TODO: validate xml - takes a very long time - do we still want to validate? + //TODO: validate xml - takes a very long time - do we still want to validate this way? Also, Cancelling doesn't Cancel. Is there a better way? // XmlBeansUtil.validateXmlDocument(exportDocument, "Validating " + inputFileName + " against schema."); } catch (IOException e) @@ -116,11 +126,10 @@ private void parseAndSavePackages(ExportDocument.Export export, @NotNull ViewBac SNDService sndService = SNDService.get(); - //convert auto-generated objects/tokens to SND's Package objects for (PackageType packageType : packageArray) { - Package pkg = parsePackage(packageType); - sndService.savePackage(info.getContainer(), info.getUser(), pkg); + Package pkg = parsePackage(packageType); //convert auto-generated objects/tokens to SND's Package objects + sndService.savePackage(info.getContainer(), info.getUser(), pkg); //save to db } } @@ -162,10 +171,9 @@ private List getAttributes(PackageType packageType) AttributesType attributes = packageType.getAttributes(); ColumnType[] attributeArray = attributes.getAttributeArray(); - List attributesList = null; + List attributesList = new LinkedList<>();; if (attributeArray.length > 1) { - attributesList = new LinkedList<>(); for (ColumnType ct : attributeArray) { GWTPropertyDescriptor gwtpd = new GWTPropertyDescriptor(); @@ -200,6 +208,9 @@ private List getAttributes(PackageType packageType) //scale gwtpd.setScale(ct.getScale()); + //redactedText + gwtpd.setRedactedText(ct.getRedactedText()); + //precision int precision = ct.getPrecision(); if (precision >= 1) diff --git a/webapp/WEB-INF/snd/sndContext.xml b/webapp/WEB-INF/snd/sndContext.xml index b68450e88..3ccf71562 100644 --- a/webapp/WEB-INF/snd/sndContext.xml +++ b/webapp/WEB-INF/snd/sndContext.xml @@ -13,6 +13,7 @@ + From e9ae413d7da339379ce671716685f38388fcfe83 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 23 Aug 2017 14:55:26 +0000 Subject: [PATCH 033/686] Spec 30931: Add in use column for categories --- resources/queries/snd/PkgCategories.query.xml | 9 +++++++++ resources/queries/snd/PkgCategories/.qview.xml | 15 +++++++++++++++ resources/queries/snd/Pkgs/.qview.xml | 6 +++--- src/org/labkey/snd/SNDUserSchema.java | 8 ++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 resources/queries/snd/PkgCategories.query.xml create mode 100644 resources/queries/snd/PkgCategories/.qview.xml diff --git a/resources/queries/snd/PkgCategories.query.xml b/resources/queries/snd/PkgCategories.query.xml new file mode 100644 index 000000000..557c605d4 --- /dev/null +++ b/resources/queries/snd/PkgCategories.query.xml @@ -0,0 +1,9 @@ + + + + + Package Categories +
+
+
+
\ No newline at end of file diff --git a/resources/queries/snd/PkgCategories/.qview.xml b/resources/queries/snd/PkgCategories/.qview.xml new file mode 100644 index 000000000..6804f7d6f --- /dev/null +++ b/resources/queries/snd/PkgCategories/.qview.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/queries/snd/Pkgs/.qview.xml b/resources/queries/snd/Pkgs/.qview.xml index a7ae4b134..95bd842ea 100644 --- a/resources/queries/snd/Pkgs/.qview.xml +++ b/resources/queries/snd/Pkgs/.qview.xml @@ -6,12 +6,12 @@ + + + - - -
\ No newline at end of file diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index f086a463b..587f46823 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -80,6 +80,14 @@ public TableInfo createTable(SNDUserSchema schema) new SimpleUserSchema.SimpleTable<>( schema, SNDSchema.getInstance().getTableInfoPkgCategories()).init(); + SQLFragment inUseSql = new SQLFragment(); + inUseSql.append("(CASE WHEN EXISTS (SELECT PkgId FROM "); + inUseSql.append(SNDSchema.getInstance().getTableInfoPkgCategoryJunction(), "pcj"); + inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".CategoryId = pcj.CategoryId)"); + inUseSql.append(" THEN 'true' ELSE 'false' END)"); + ExprColumn inUseCol = new ExprColumn(table, "InUse", inUseSql, JdbcType.BOOLEAN); + table.addColumn(inUseCol); + return table; } }, From bf88d8b0705d8e1469e9689aa2b18274c05183dd Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Wed, 23 Aug 2017 18:38:23 +0000 Subject: [PATCH 034/686] Spec 30932 : SNPRC SND - XML Package Integration : Saving SuperPackage (wip). --- src/org/labkey/snd/pipeline/SNDDataHandler.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 7177055b4..d8e8760bc 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -114,7 +114,7 @@ public void importFile(@NotNull ExpData data, File dataFile, @NotNull ViewBackgr if(null != export) { parseAndSavePackages(export, info); - parseAndSaveSuperPackages(export); + parseAndSaveSuperPackages(export, info); } } @@ -243,14 +243,15 @@ private List getAttributes(PackageType packageType) return attributesList; } - private void parseAndSaveSuperPackages(ExportDocument.Export export) + private void parseAndSaveSuperPackages(ExportDocument.Export export, @NotNull ViewBackgroundInfo info) { SuperPackagesType superPackagesType = export.getSuperPackages(); SuperPackageType[] superPackageArray = superPackagesType.getSuperPackageArray(); + SNDService sndService = SNDService.get(); for(SuperPackageType superPackageType : superPackageArray) { SuperPackage superPackage = parseSuperPackage(superPackageType); - //TODO: Save + sndService.saveSuperPackage(info.getContainer(), info.getUser(), superPackage); } } From e277e868bf6cfa6097b30b4e96433fc8ca6361c1 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 23 Aug 2017 18:44:18 +0000 Subject: [PATCH 035/686] Spec 30931: Package api updates and super package save. --- api-src/org/labkey/api/snd/SuperPackage.java | 14 +++ src/org/labkey/snd/SNDManager.java | 106 ++++++++++++++++--- src/org/labkey/snd/SNDServiceImpl.java | 7 +- 3 files changed, 112 insertions(+), 15 deletions(-) diff --git a/api-src/org/labkey/api/snd/SuperPackage.java b/api-src/org/labkey/api/snd/SuperPackage.java index a95deccd5..e543a0ef2 100644 --- a/api-src/org/labkey/api/snd/SuperPackage.java +++ b/api-src/org/labkey/api/snd/SuperPackage.java @@ -1,6 +1,10 @@ package org.labkey.api.snd; +import org.labkey.api.collections.ArrayListMap; +import org.labkey.api.data.Container; + import java.util.List; +import java.util.Map; /** * Created by marty on 8/14/2017. @@ -40,4 +44,14 @@ public void setPkgId(Integer pkgId) { _pkgId = pkgId; } + + public Map getSuperPackageRow(Container c) + { + Map superPkgValues = new ArrayListMap<>(); + superPkgValues.put("SuperPkgId", getSuperPkgId()); + superPkgValues.put("ParentSuperPkgId", getParentSuperPkgId()); + superPkgValues.put("PkgId", getPkgId()); + + return superPkgValues; + } } diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index c077f29da..c2c6797c7 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -37,9 +37,11 @@ import org.labkey.api.security.User; import org.labkey.api.snd.Package; import org.labkey.api.snd.PackageDomainKind; +import org.labkey.api.snd.SuperPackage; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -104,18 +106,21 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep errors.addRowError(new ValidationException(e.getMessage())); } - String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), getPackageName(pkg.getPkgId()), c, u); + if(!errors.hasErrors()) + { + String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), getPackageName(pkg.getPkgId()), c, u); - GWTDomain updateDomain = new GWTDomain<>(); - updateDomain.setName(getPackageName(pkg.getPkgId())); - updateDomain.setFields(pkg.getAttributes()); - updateDomain.setDomainURI(domainURI); + GWTDomain updateDomain = new GWTDomain<>(); + updateDomain.setName(getPackageName(pkg.getPkgId())); + updateDomain.setFields(pkg.getAttributes()); + updateDomain.setDomainURI(domainURI); - PackageDomainKind kind = new PackageDomainKind(); - kind.updateDomain(c, u, updateDomain); + PackageDomainKind kind = new PackageDomainKind(); + kind.updateDomain(c, u, updateDomain); + } } - public void createNewPackage(User u, Container c, Package pkg, BatchValidationException errors) + public void createPackage(User u, Container c, Package pkg, BatchValidationException errors) { UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); @@ -143,13 +148,86 @@ public void createNewPackage(User u, Container c, Package pkg, BatchValidationEx errors.addRowError(new ValidationException(e.getMessage())); } - GWTDomain newDomain = new GWTDomain<>(); - newDomain.setName(getPackageName(pkg.getPkgId())); - newDomain.setContainer(c.getId()); - newDomain.setDescription(pkg.getDescription()); - newDomain.setFields(pkg.getAttributes()); + if(!errors.hasErrors()) + { + GWTDomain newDomain = new GWTDomain<>(); + newDomain.setName(getPackageName(pkg.getPkgId())); + newDomain.setContainer(c.getId()); + newDomain.setDescription(pkg.getDescription()); + newDomain.setFields(pkg.getAttributes()); + + DomainUtil.createDomain(PackageDomainKind.getPackageKindName(), newDomain, null, c, u, null, null); + } + } + + public void createSuperPackage(User u, Container c, SuperPackage superPkg, BatchValidationException errors) + { + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + TableInfo superPkgsTable = schema.getTable(SNDSchema.SUPERPKGS_TABLE_NAME); + QueryUpdateService superPkgQus = superPkgsTable.getUpdateService(); + if (superPkgQus == null) + throw new IllegalStateException(); + + List> superPkgRows = new ArrayList<>(); + superPkgRows.add(superPkg.getSuperPackageRow(c)); + + try (DbScope.Transaction tx = superPkgsTable.getSchema().getScope().ensureTransaction()) + { + superPkgQus.insertRows(u, c, superPkgRows, errors, null, null); + tx.commit(); + } + catch (QueryUpdateServiceException | BatchValidationException | DuplicateKeyException | SQLException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } + } + + public List getPackages(Container c, User u, List pkgIds, BatchValidationException errors) + { + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + TableInfo pkgsTable = schema.getTable(SNDSchema.PKGS_TABLE_NAME); + QueryUpdateService pkgQus = pkgsTable.getUpdateService(); + if (pkgQus == null) + throw new IllegalStateException(); + + List> rows = null; + List> keys = new ArrayList<>(); + Map key; + for (Integer pkgId : pkgIds) + { + key = new HashMap<>(); + key.put("PkgId", pkgId); + keys.add(key); + } + + List packages = new ArrayList<>(); + try + { + rows = pkgQus.getRows(u, c, keys); + } + catch (InvalidKeyException | QueryUpdateServiceException | SQLException e) + { + errors.addRowError(new ValidationException(e.getMessage())); + } - DomainUtil.createDomain(PackageDomainKind.getPackageKindName(), newDomain, null, c, u, null, null); + if(!errors.hasErrors() && rows != null && !rows.isEmpty()) + { + Package pkg; + for (Map row : rows) + { + pkg = new Package(); + pkg.setPkgId((Integer)row.get("PkgId")); + pkg.setDescription((String)row.get("Description")); + pkg.setActive((boolean)row.get("Active")); + pkg.setRepeatable((boolean)row.get("Repeatable")); + pkg.setNarrative((String)row.get("Narrative")); + pkg.setQcState((Integer)row.get("QcState")); + packages.add(pkg); + } + } + return packages; } } \ No newline at end of file diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 510ebd312..2d2cfcb5e 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -44,7 +44,7 @@ public void savePackage(Container c, User u, Package pkg) if(null == pkg.getPkgId()) pkg.setPkgId(SNDManager.get().generatePackageId(c)); - SNDManager.get().createNewPackage(u, c, pkg, errors); + SNDManager.get().createPackage(u, c, pkg, errors); } if (errors.hasErrors()) @@ -54,6 +54,11 @@ public void savePackage(Container c, User u, Package pkg) @Override public void saveSuperPackage(Container c, User u, SuperPackage superPkg) { + BatchValidationException errors = new BatchValidationException(); + + //SNDManager.get().createSuperPackage(u, c, superPkg, errors); + if (errors.hasErrors()) + throw new UnexpectedException(errors); } } From 2eef2a1f1723632687bce759db418e976197fe9e Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 23 Aug 2017 19:16:08 +0000 Subject: [PATCH 036/686] Spec 30931: Package --- api-src/org/labkey/api/snd/Package.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 3dd61086c..0822b61f0 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -24,6 +24,7 @@ public class Package private List _attributes; private List _subpackages; private Map _extraFields = new HashMap<>(); + private Integer _qcState; public Integer getPkgId() { @@ -115,6 +116,16 @@ public void setExtraFields(Map extraFields) this._extraFields = extraFields; } + public Integer getQcState() + { + return _qcState; + } + + public void setQcState(Integer qcState) + { + _qcState = qcState; + } + public Map getPackageRow(Container c) { Map pkgValues = new ArrayListMap<>(); @@ -145,4 +156,10 @@ public List> getCategoryRows(Container c) return rows; } + +// public JSONObject toJSON() +// { +// JSONObject json = new JSONObject(); +// json.put() +// } } From 6bd9294a15e33924ef57839043d275cb49092d38 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Fri, 25 Aug 2017 16:52:06 +0000 Subject: [PATCH 037/686] Spec 30931: GetPackages api --- api-src/org/labkey/api/snd/Package.java | 98 ++++++++++++++++++---- api-src/org/labkey/api/snd/SNDService.java | 3 + resources/queries/snd/Pkgs/.qview.xml | 2 +- src/org/labkey/snd/PackageTable.java | 47 +++++++++++ src/org/labkey/snd/SNDController.java | 40 +++++++++ src/org/labkey/snd/SNDServiceImpl.java | 14 ++++ src/org/labkey/snd/SNDUserSchema.java | 18 +--- 7 files changed, 189 insertions(+), 33 deletions(-) create mode 100644 src/org/labkey/snd/PackageTable.java diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 0822b61f0..099e222c2 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -1,8 +1,11 @@ package org.labkey.api.snd; +import org.json.JSONArray; +import org.json.JSONObject; import org.labkey.api.collections.ArrayListMap; import org.labkey.api.data.Container; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; +import org.labkey.api.gwt.client.model.GWTPropertyValidator; import java.util.ArrayList; import java.util.Collection; @@ -21,11 +24,19 @@ public class Package private boolean _repeatable; private boolean _active; private List _categories = new ArrayList<>(); - private List _attributes; + private List _attributes = new ArrayList<>(); private List _subpackages; private Map _extraFields = new HashMap<>(); private Integer _qcState; + public static final String PKGID_COL = "pkgId"; + public static final String DESCRIPTION_COL = "description"; + public static final String ACTIVE_COL = "active"; + public static final String REPEATABLE_COL = "repeatable"; + public static final String QCSTATE_COL = "qcState"; + public static final String NARRATIVE_COL = "narrative"; + public static final String CONTAINER_COL = "container"; + public Integer getPkgId() { return _pkgId; @@ -129,12 +140,13 @@ public void setQcState(Integer qcState) public Map getPackageRow(Container c) { Map pkgValues = new ArrayListMap<>(); - pkgValues.put("PkgId", getPkgId()); - pkgValues.put("Narrative", getNarrative()); - pkgValues.put("Description", getDescription()); - pkgValues.put("Active", isActive()); - pkgValues.put("Repeatable", isRepeatable()); - pkgValues.put("Container", c); + pkgValues.put(PKGID_COL, getPkgId()); + pkgValues.put(NARRATIVE_COL, getNarrative()); + pkgValues.put(DESCRIPTION_COL, getDescription()); + pkgValues.put(ACTIVE_COL, isActive()); + pkgValues.put(REPEATABLE_COL, isRepeatable()); + pkgValues.put(QCSTATE_COL, getQcState()); + pkgValues.put(CONTAINER_COL, c); pkgValues.putAll(getExtraFields()); return pkgValues; @@ -148,18 +160,74 @@ public List> getCategoryRows(Container c) for (Integer categoryId : getCategories()) { row = new ArrayListMap<>(); - row.put("PkgId", getPkgId()); - row.put("CategoryId", categoryId); - row.put("Container", c); + row.put(PKGID_COL, getPkgId()); + row.put("categoryId", categoryId); + row.put(CONTAINER_COL, c); rows.add(row); } return rows; } -// public JSONObject toJSON() -// { -// JSONObject json = new JSONObject(); -// json.put() -// } + public JSONArray convertPropertyValidatorsToJson(GWTPropertyDescriptor pd) + { + JSONArray json = new JSONArray(); + JSONObject obj; + for (GWTPropertyValidator pv : pd.getPropertyValidators()) + { + obj = new JSONObject(); + obj.put("name", pv.getName()); + obj.put("description", pv.getDescription()); + obj.put("type", pv.getType().getTypeName()); + obj.put("expression", pv.getExpression()); + obj.put("errorMessage", pv.getErrorMessage()); + json.put(obj); + } + + return json; + } + + public JSONObject convertPropertyDescriptorToJson(GWTPropertyDescriptor pd) + { + JSONObject json = new JSONObject(); + json.put("name", pd.getName()); + json.put("rangeURI", pd.getRangeURI()); + json.put("required", pd.isRequired()); + json.put("label", pd.getLabel()); + json.put("scale", pd.getScale()); + json.put("format", pd.getFormat()); + json.put("lookupSchema", pd.getLookupSchema()); + json.put("lookupQuery", pd.getLookupQuery()); + json.put("validators", convertPropertyValidatorsToJson(pd)); + + return json; + } + + public JSONObject toJSON(Container c) + { + JSONObject json = new JSONObject(); + json.put(PKGID_COL, getPkgId()); + json.put(DESCRIPTION_COL, getDescription()); + json.put(REPEATABLE_COL, isRepeatable()); + json.put(ACTIVE_COL, isActive()); + json.put(NARRATIVE_COL, getNarrative()); + json.put(QCSTATE_COL, getQcState()); + json.put(CONTAINER_COL, c.getId()); + + JSONArray categories = new JSONArray(); + for (Integer categoryId : getCategories()) + { + categories.put(categoryId); + } + json.put("categories", categories); + + JSONArray attributes = new JSONArray(); + for (GWTPropertyDescriptor pd : getAttributes()) + { + attributes.put(convertPropertyDescriptorToJson(pd)); + } + json.put("attributes", attributes); + + return json; + } } diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java index 3ddc28136..a577cd48d 100644 --- a/api-src/org/labkey/api/snd/SNDService.java +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -5,6 +5,8 @@ import org.labkey.api.security.User; import org.labkey.api.services.ServiceRegistry; +import java.util.List; + /** * Created by marty on 8/4/2017. */ @@ -18,4 +20,5 @@ static SNDService get() void savePackage(Container c, User u, Package pkg); void saveSuperPackage(Container c, User u, SuperPackage superPkg); + List getPackages(Container c, User u, List pkgIds); } diff --git a/resources/queries/snd/Pkgs/.qview.xml b/resources/queries/snd/Pkgs/.qview.xml index 95bd842ea..f6904b3e4 100644 --- a/resources/queries/snd/Pkgs/.qview.xml +++ b/resources/queries/snd/Pkgs/.qview.xml @@ -6,7 +6,7 @@ - + diff --git a/src/org/labkey/snd/PackageTable.java b/src/org/labkey/snd/PackageTable.java new file mode 100644 index 000000000..d399ee68d --- /dev/null +++ b/src/org/labkey/snd/PackageTable.java @@ -0,0 +1,47 @@ +package org.labkey.snd; + +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.SimpleUserSchema; + +/** + * Created by marty on 8/23/2017. + */ +public class PackageTable extends SimpleUserSchema.SimpleTable +{ + + /** + * Create the simple table. + * SimpleTable doesn't add columns until .init() has been called to allow derived classes to fully initialize themselves before adding columns. + * + * @param schema + * @param table + */ + public PackageTable(SNDUserSchema schema, TableInfo table) + { + super(schema, table); + } + + @Override + public SimpleUserSchema.SimpleTable init() + { + super.init(); + + SQLFragment inUseSql = new SQLFragment(); + inUseSql.append("(CASE WHEN EXISTS (SELECT sp.PkgId FROM "); + inUseSql.append(SNDSchema.getInstance().getTableInfoSuperPkgs(), "sp"); + inUseSql.append(" JOIN "); + inUseSql.append(SNDSchema.getInstance().getTableInfoCodedEvents(), "ce"); + inUseSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); + inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); + inUseSql.append(" THEN 'true' ELSE 'false' END)"); + ExprColumn inUseCol = new ExprColumn(this, "hasData", inUseSql, JdbcType.BOOLEAN); + addColumn(inUseCol); + + return this; + } + + +} diff --git a/src/org/labkey/snd/SNDController.java b/src/org/labkey/snd/SNDController.java index 809645ec1..f3a8f10ec 100644 --- a/src/org/labkey/snd/SNDController.java +++ b/src/org/labkey/snd/SNDController.java @@ -33,6 +33,7 @@ import org.labkey.api.snd.SNDService; import org.labkey.api.view.NavTree; import org.springframework.validation.BindException; +import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; import java.util.ArrayList; @@ -120,4 +121,43 @@ public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws return new ApiSimpleResponse(); } } + + @RequiresPermission(AdminPermission.class) + public class GetPackagesAction extends ApiAction + { + @Override + public void validateForm(SimpleApiJsonForm form, Errors errors) + { + JSONObject json = form.getJsonObject(); + JSONArray pkgIds = json.getJSONArray("packages"); + if (pkgIds == null) + errors.reject(ERROR_MSG, "Package IDs not defined."); + } + + @Override + public ApiResponse execute(SimpleApiJsonForm form, BindException errors) throws Exception + { + JSONObject json = form.getJsonObject(); + JSONArray pkgIds = json.getJSONArray("packages"); + ApiSimpleResponse response = new ApiSimpleResponse(); + + List pkgs = null; + List ids = new ArrayList<>(); + for (int j = 0; j < pkgIds.length(); j++) + { + ids.add(pkgIds.getInt(j)); + } + + pkgs = SNDService.get().getPackages(getViewContext().getContainer(), getUser(), ids); + + JSONArray jsonOut = new JSONArray(); + for (Package pkg : pkgs) + { + jsonOut.put(pkg.toJSON(getViewContext().getContainer())); + } + + response.put("json", jsonOut); + return response; + } + } } \ No newline at end of file diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 2d2cfcb5e..34f7367d2 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -11,6 +11,8 @@ import org.labkey.api.snd.SuperPackage; import org.labkey.api.util.UnexpectedException; +import java.util.List; + /** * Created by marty on 8/4/2017. */ @@ -61,4 +63,16 @@ public void saveSuperPackage(Container c, User u, SuperPackage superPkg) if (errors.hasErrors()) throw new UnexpectedException(errors); } + + @Override + public List getPackages(Container c, User u, List pkgIds) + { + BatchValidationException errors = new BatchValidationException(); + + List pkgs = SNDManager.get().getPackages(c, u, pkgIds, errors); + if (errors.hasErrors()) + throw new UnexpectedException(errors); + + return pkgs; + } } diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index 587f46823..7f79da18b 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -52,23 +52,7 @@ public TableInfo createTable(SNDUserSchema schema) @Override public TableInfo createTable(SNDUserSchema schema) { - SimpleUserSchema.SimpleTable table = - new SimpleUserSchema.SimpleTable<>( - schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); - - - SQLFragment inUseSql = new SQLFragment(); - inUseSql.append("(CASE WHEN EXISTS (SELECT sp.PkgId FROM "); - inUseSql.append(SNDSchema.getInstance().getTableInfoSuperPkgs(), "sp"); - inUseSql.append(" JOIN "); - inUseSql.append(SNDSchema.getInstance().getTableInfoCodedEvents(), "ce"); - inUseSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); - inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); - inUseSql.append(" THEN 'true' ELSE 'false' END)"); - ExprColumn inUseCol = new ExprColumn(table, "InUse", inUseSql, JdbcType.BOOLEAN); - table.addColumn(inUseCol); - - return table; + return new PackageTable(schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); } }, PkgCategories From acd00acf7c246f430b7788680ad337900155d6cb Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Fri, 25 Aug 2017 18:24:38 +0000 Subject: [PATCH 038/686] Spec 30931: Get package attributes and categories. --- api-src/org/labkey/api/snd/Package.java | 66 ++++++++++++++----------- src/org/labkey/snd/SNDManager.java | 39 ++++++++++++--- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 099e222c2..37c0856d8 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -29,13 +29,15 @@ public class Package private Map _extraFields = new HashMap<>(); private Integer _qcState; - public static final String PKGID_COL = "pkgId"; - public static final String DESCRIPTION_COL = "description"; - public static final String ACTIVE_COL = "active"; - public static final String REPEATABLE_COL = "repeatable"; - public static final String QCSTATE_COL = "qcState"; - public static final String NARRATIVE_COL = "narrative"; - public static final String CONTAINER_COL = "container"; + public static final String PKG_ID = "pkgId"; + public static final String PKG_DESCRIPTION = "description"; + public static final String PKG_ACTIVE = "active"; + public static final String PKG_REPEATABLE = "repeatable"; + public static final String PKG_QCSTATE = "qcState"; + public static final String PKG_NARRATIVE = "narrative"; + public static final String PKG_CONTAINER = "container"; + public static final String PKG_CATEGORIES = "categories"; + public static final String PKG_ATTRIBUTES = "attributes"; public Integer getPkgId() { @@ -140,13 +142,13 @@ public void setQcState(Integer qcState) public Map getPackageRow(Container c) { Map pkgValues = new ArrayListMap<>(); - pkgValues.put(PKGID_COL, getPkgId()); - pkgValues.put(NARRATIVE_COL, getNarrative()); - pkgValues.put(DESCRIPTION_COL, getDescription()); - pkgValues.put(ACTIVE_COL, isActive()); - pkgValues.put(REPEATABLE_COL, isRepeatable()); - pkgValues.put(QCSTATE_COL, getQcState()); - pkgValues.put(CONTAINER_COL, c); + pkgValues.put(PKG_ID, getPkgId()); + pkgValues.put(PKG_NARRATIVE, getNarrative()); + pkgValues.put(PKG_DESCRIPTION, getDescription()); + pkgValues.put(PKG_ACTIVE, isActive()); + pkgValues.put(PKG_REPEATABLE, isRepeatable()); + pkgValues.put(PKG_QCSTATE, getQcState()); + pkgValues.put(PKG_CONTAINER, c); pkgValues.putAll(getExtraFields()); return pkgValues; @@ -160,9 +162,9 @@ public List> getCategoryRows(Container c) for (Integer categoryId : getCategories()) { row = new ArrayListMap<>(); - row.put(PKGID_COL, getPkgId()); + row.put(PKG_ID, getPkgId()); row.put("categoryId", categoryId); - row.put(CONTAINER_COL, c); + row.put(PKG_CONTAINER, c); rows.add(row); } @@ -206,27 +208,33 @@ public JSONObject convertPropertyDescriptorToJson(GWTPropertyDescriptor pd) public JSONObject toJSON(Container c) { JSONObject json = new JSONObject(); - json.put(PKGID_COL, getPkgId()); - json.put(DESCRIPTION_COL, getDescription()); - json.put(REPEATABLE_COL, isRepeatable()); - json.put(ACTIVE_COL, isActive()); - json.put(NARRATIVE_COL, getNarrative()); - json.put(QCSTATE_COL, getQcState()); - json.put(CONTAINER_COL, c.getId()); + json.put(PKG_ID, getPkgId()); + json.put(PKG_DESCRIPTION, getDescription()); + json.put(PKG_REPEATABLE, isRepeatable()); + json.put(PKG_ACTIVE, isActive()); + json.put(PKG_NARRATIVE, getNarrative()); + json.put(PKG_QCSTATE, getQcState()); + json.put(PKG_CONTAINER, c.getId()); JSONArray categories = new JSONArray(); - for (Integer categoryId : getCategories()) + if(getCategories() != null) { - categories.put(categoryId); + for (Integer categoryId : getCategories()) + { + categories.put(categoryId); + } + json.put(PKG_CATEGORIES, categories); } - json.put("categories", categories); JSONArray attributes = new JSONArray(); - for (GWTPropertyDescriptor pd : getAttributes()) + if(getAttributes() != null) { - attributes.put(convertPropertyDescriptorToJson(pd)); + for (GWTPropertyDescriptor pd : getAttributes()) + { + attributes.put(convertPropertyDescriptorToJson(pd)); + } + json.put(PKG_ATTRIBUTES, attributes); } - json.put("attributes", attributes); return json; } diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index c2c6797c7..df8e0f7ce 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -22,6 +22,7 @@ import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; @@ -41,6 +42,7 @@ import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -183,6 +185,28 @@ public void createSuperPackage(User u, Container c, SuperPackage superPkg, Batch } } + private List getPackageAttributes(Container c, User u, int pkgId) + { + String uri = PackageDomainKind.getDomainURI(SNDSchema.NAME, getPackageName(pkgId), c, u); + GWTDomain domain = DomainUtil.getDomainDescriptor(u, uri, c); + if(domain != null) + return domain.getFields(); + + return Collections.emptyList(); + } + + + private List getPackageCategories(Container c, User u, int pkgId) + { + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + SQLFragment sql = new SQLFragment("SELECT CategoryId FROM "); + sql.append(schema.getTable(SNDSchema.PKGCATEGORYJUNCTION_TABLE_NAME), "c"); + sql.append(" WHERE Container = ? AND PkgId = ?").add(c).add(pkgId); + SqlSelector selector = new SqlSelector(schema.getDbSchema(), sql); + return selector.getArrayList(Integer.class); + } + public List getPackages(Container c, User u, List pkgIds, BatchValidationException errors) { UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); @@ -218,12 +242,15 @@ public List getPackages(Container c, User u, List pkgIds, Batc for (Map row : rows) { pkg = new Package(); - pkg.setPkgId((Integer)row.get("PkgId")); - pkg.setDescription((String)row.get("Description")); - pkg.setActive((boolean)row.get("Active")); - pkg.setRepeatable((boolean)row.get("Repeatable")); - pkg.setNarrative((String)row.get("Narrative")); - pkg.setQcState((Integer)row.get("QcState")); + pkg.setPkgId((Integer)row.get(Package.PKG_ID)); + pkg.setDescription((String)row.get(Package.PKG_DESCRIPTION)); + pkg.setActive((boolean)row.get(Package.PKG_ACTIVE)); + pkg.setRepeatable((boolean)row.get(Package.PKG_REPEATABLE)); + pkg.setNarrative((String)row.get(Package.PKG_NARRATIVE)); + pkg.setQcState((Integer)row.get(Package.PKG_QCSTATE)); + pkg.setCategories(getPackageCategories(c, u, pkg.getPkgId())); + pkg.setAttributes(getPackageAttributes(c, u, pkg.getPkgId())); + packages.add(pkg); } } From 4ece067a6b43e3c48e9f2437fb75af5612e77acb Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Fri, 25 Aug 2017 19:08:36 +0000 Subject: [PATCH 039/686] Spec 30931: Back out changes to see if fixes TC build. --- api-src/org/labkey/api/snd/Package.java | 66 +++++++++++-------------- src/org/labkey/snd/SNDManager.java | 39 +++------------ 2 files changed, 35 insertions(+), 70 deletions(-) diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 37c0856d8..099e222c2 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -29,15 +29,13 @@ public class Package private Map _extraFields = new HashMap<>(); private Integer _qcState; - public static final String PKG_ID = "pkgId"; - public static final String PKG_DESCRIPTION = "description"; - public static final String PKG_ACTIVE = "active"; - public static final String PKG_REPEATABLE = "repeatable"; - public static final String PKG_QCSTATE = "qcState"; - public static final String PKG_NARRATIVE = "narrative"; - public static final String PKG_CONTAINER = "container"; - public static final String PKG_CATEGORIES = "categories"; - public static final String PKG_ATTRIBUTES = "attributes"; + public static final String PKGID_COL = "pkgId"; + public static final String DESCRIPTION_COL = "description"; + public static final String ACTIVE_COL = "active"; + public static final String REPEATABLE_COL = "repeatable"; + public static final String QCSTATE_COL = "qcState"; + public static final String NARRATIVE_COL = "narrative"; + public static final String CONTAINER_COL = "container"; public Integer getPkgId() { @@ -142,13 +140,13 @@ public void setQcState(Integer qcState) public Map getPackageRow(Container c) { Map pkgValues = new ArrayListMap<>(); - pkgValues.put(PKG_ID, getPkgId()); - pkgValues.put(PKG_NARRATIVE, getNarrative()); - pkgValues.put(PKG_DESCRIPTION, getDescription()); - pkgValues.put(PKG_ACTIVE, isActive()); - pkgValues.put(PKG_REPEATABLE, isRepeatable()); - pkgValues.put(PKG_QCSTATE, getQcState()); - pkgValues.put(PKG_CONTAINER, c); + pkgValues.put(PKGID_COL, getPkgId()); + pkgValues.put(NARRATIVE_COL, getNarrative()); + pkgValues.put(DESCRIPTION_COL, getDescription()); + pkgValues.put(ACTIVE_COL, isActive()); + pkgValues.put(REPEATABLE_COL, isRepeatable()); + pkgValues.put(QCSTATE_COL, getQcState()); + pkgValues.put(CONTAINER_COL, c); pkgValues.putAll(getExtraFields()); return pkgValues; @@ -162,9 +160,9 @@ public List> getCategoryRows(Container c) for (Integer categoryId : getCategories()) { row = new ArrayListMap<>(); - row.put(PKG_ID, getPkgId()); + row.put(PKGID_COL, getPkgId()); row.put("categoryId", categoryId); - row.put(PKG_CONTAINER, c); + row.put(CONTAINER_COL, c); rows.add(row); } @@ -208,33 +206,27 @@ public JSONObject convertPropertyDescriptorToJson(GWTPropertyDescriptor pd) public JSONObject toJSON(Container c) { JSONObject json = new JSONObject(); - json.put(PKG_ID, getPkgId()); - json.put(PKG_DESCRIPTION, getDescription()); - json.put(PKG_REPEATABLE, isRepeatable()); - json.put(PKG_ACTIVE, isActive()); - json.put(PKG_NARRATIVE, getNarrative()); - json.put(PKG_QCSTATE, getQcState()); - json.put(PKG_CONTAINER, c.getId()); + json.put(PKGID_COL, getPkgId()); + json.put(DESCRIPTION_COL, getDescription()); + json.put(REPEATABLE_COL, isRepeatable()); + json.put(ACTIVE_COL, isActive()); + json.put(NARRATIVE_COL, getNarrative()); + json.put(QCSTATE_COL, getQcState()); + json.put(CONTAINER_COL, c.getId()); JSONArray categories = new JSONArray(); - if(getCategories() != null) + for (Integer categoryId : getCategories()) { - for (Integer categoryId : getCategories()) - { - categories.put(categoryId); - } - json.put(PKG_CATEGORIES, categories); + categories.put(categoryId); } + json.put("categories", categories); JSONArray attributes = new JSONArray(); - if(getAttributes() != null) + for (GWTPropertyDescriptor pd : getAttributes()) { - for (GWTPropertyDescriptor pd : getAttributes()) - { - attributes.put(convertPropertyDescriptorToJson(pd)); - } - json.put(PKG_ATTRIBUTES, attributes); + attributes.put(convertPropertyDescriptorToJson(pd)); } + json.put("attributes", attributes); return json; } diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index df8e0f7ce..c2c6797c7 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -22,7 +22,6 @@ import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SqlExecutor; -import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; @@ -42,7 +41,6 @@ import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -185,28 +183,6 @@ public void createSuperPackage(User u, Container c, SuperPackage superPkg, Batch } } - private List getPackageAttributes(Container c, User u, int pkgId) - { - String uri = PackageDomainKind.getDomainURI(SNDSchema.NAME, getPackageName(pkgId), c, u); - GWTDomain domain = DomainUtil.getDomainDescriptor(u, uri, c); - if(domain != null) - return domain.getFields(); - - return Collections.emptyList(); - } - - - private List getPackageCategories(Container c, User u, int pkgId) - { - UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); - - SQLFragment sql = new SQLFragment("SELECT CategoryId FROM "); - sql.append(schema.getTable(SNDSchema.PKGCATEGORYJUNCTION_TABLE_NAME), "c"); - sql.append(" WHERE Container = ? AND PkgId = ?").add(c).add(pkgId); - SqlSelector selector = new SqlSelector(schema.getDbSchema(), sql); - return selector.getArrayList(Integer.class); - } - public List getPackages(Container c, User u, List pkgIds, BatchValidationException errors) { UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); @@ -242,15 +218,12 @@ public List getPackages(Container c, User u, List pkgIds, Batc for (Map row : rows) { pkg = new Package(); - pkg.setPkgId((Integer)row.get(Package.PKG_ID)); - pkg.setDescription((String)row.get(Package.PKG_DESCRIPTION)); - pkg.setActive((boolean)row.get(Package.PKG_ACTIVE)); - pkg.setRepeatable((boolean)row.get(Package.PKG_REPEATABLE)); - pkg.setNarrative((String)row.get(Package.PKG_NARRATIVE)); - pkg.setQcState((Integer)row.get(Package.PKG_QCSTATE)); - pkg.setCategories(getPackageCategories(c, u, pkg.getPkgId())); - pkg.setAttributes(getPackageAttributes(c, u, pkg.getPkgId())); - + pkg.setPkgId((Integer)row.get("PkgId")); + pkg.setDescription((String)row.get("Description")); + pkg.setActive((boolean)row.get("Active")); + pkg.setRepeatable((boolean)row.get("Repeatable")); + pkg.setNarrative((String)row.get("Narrative")); + pkg.setQcState((Integer)row.get("QcState")); packages.add(pkg); } } From a3f1fa9160e8b945e7918430a61a4a5cc088ff86 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Fri, 25 Aug 2017 20:55:15 +0000 Subject: [PATCH 040/686] Spec 30931: Get package attributes and categories --- api-src/org/labkey/api/snd/Package.java | 66 ++++++++++++++----------- src/org/labkey/snd/SNDManager.java | 39 ++++++++++++--- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/api-src/org/labkey/api/snd/Package.java b/api-src/org/labkey/api/snd/Package.java index 099e222c2..37c0856d8 100644 --- a/api-src/org/labkey/api/snd/Package.java +++ b/api-src/org/labkey/api/snd/Package.java @@ -29,13 +29,15 @@ public class Package private Map _extraFields = new HashMap<>(); private Integer _qcState; - public static final String PKGID_COL = "pkgId"; - public static final String DESCRIPTION_COL = "description"; - public static final String ACTIVE_COL = "active"; - public static final String REPEATABLE_COL = "repeatable"; - public static final String QCSTATE_COL = "qcState"; - public static final String NARRATIVE_COL = "narrative"; - public static final String CONTAINER_COL = "container"; + public static final String PKG_ID = "pkgId"; + public static final String PKG_DESCRIPTION = "description"; + public static final String PKG_ACTIVE = "active"; + public static final String PKG_REPEATABLE = "repeatable"; + public static final String PKG_QCSTATE = "qcState"; + public static final String PKG_NARRATIVE = "narrative"; + public static final String PKG_CONTAINER = "container"; + public static final String PKG_CATEGORIES = "categories"; + public static final String PKG_ATTRIBUTES = "attributes"; public Integer getPkgId() { @@ -140,13 +142,13 @@ public void setQcState(Integer qcState) public Map getPackageRow(Container c) { Map pkgValues = new ArrayListMap<>(); - pkgValues.put(PKGID_COL, getPkgId()); - pkgValues.put(NARRATIVE_COL, getNarrative()); - pkgValues.put(DESCRIPTION_COL, getDescription()); - pkgValues.put(ACTIVE_COL, isActive()); - pkgValues.put(REPEATABLE_COL, isRepeatable()); - pkgValues.put(QCSTATE_COL, getQcState()); - pkgValues.put(CONTAINER_COL, c); + pkgValues.put(PKG_ID, getPkgId()); + pkgValues.put(PKG_NARRATIVE, getNarrative()); + pkgValues.put(PKG_DESCRIPTION, getDescription()); + pkgValues.put(PKG_ACTIVE, isActive()); + pkgValues.put(PKG_REPEATABLE, isRepeatable()); + pkgValues.put(PKG_QCSTATE, getQcState()); + pkgValues.put(PKG_CONTAINER, c); pkgValues.putAll(getExtraFields()); return pkgValues; @@ -160,9 +162,9 @@ public List> getCategoryRows(Container c) for (Integer categoryId : getCategories()) { row = new ArrayListMap<>(); - row.put(PKGID_COL, getPkgId()); + row.put(PKG_ID, getPkgId()); row.put("categoryId", categoryId); - row.put(CONTAINER_COL, c); + row.put(PKG_CONTAINER, c); rows.add(row); } @@ -206,27 +208,33 @@ public JSONObject convertPropertyDescriptorToJson(GWTPropertyDescriptor pd) public JSONObject toJSON(Container c) { JSONObject json = new JSONObject(); - json.put(PKGID_COL, getPkgId()); - json.put(DESCRIPTION_COL, getDescription()); - json.put(REPEATABLE_COL, isRepeatable()); - json.put(ACTIVE_COL, isActive()); - json.put(NARRATIVE_COL, getNarrative()); - json.put(QCSTATE_COL, getQcState()); - json.put(CONTAINER_COL, c.getId()); + json.put(PKG_ID, getPkgId()); + json.put(PKG_DESCRIPTION, getDescription()); + json.put(PKG_REPEATABLE, isRepeatable()); + json.put(PKG_ACTIVE, isActive()); + json.put(PKG_NARRATIVE, getNarrative()); + json.put(PKG_QCSTATE, getQcState()); + json.put(PKG_CONTAINER, c.getId()); JSONArray categories = new JSONArray(); - for (Integer categoryId : getCategories()) + if(getCategories() != null) { - categories.put(categoryId); + for (Integer categoryId : getCategories()) + { + categories.put(categoryId); + } + json.put(PKG_CATEGORIES, categories); } - json.put("categories", categories); JSONArray attributes = new JSONArray(); - for (GWTPropertyDescriptor pd : getAttributes()) + if(getAttributes() != null) { - attributes.put(convertPropertyDescriptorToJson(pd)); + for (GWTPropertyDescriptor pd : getAttributes()) + { + attributes.put(convertPropertyDescriptorToJson(pd)); + } + json.put(PKG_ATTRIBUTES, attributes); } - json.put("attributes", attributes); return json; } diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index c2c6797c7..df8e0f7ce 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -22,6 +22,7 @@ import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SqlExecutor; +import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; @@ -41,6 +42,7 @@ import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -183,6 +185,28 @@ public void createSuperPackage(User u, Container c, SuperPackage superPkg, Batch } } + private List getPackageAttributes(Container c, User u, int pkgId) + { + String uri = PackageDomainKind.getDomainURI(SNDSchema.NAME, getPackageName(pkgId), c, u); + GWTDomain domain = DomainUtil.getDomainDescriptor(u, uri, c); + if(domain != null) + return domain.getFields(); + + return Collections.emptyList(); + } + + + private List getPackageCategories(Container c, User u, int pkgId) + { + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + SQLFragment sql = new SQLFragment("SELECT CategoryId FROM "); + sql.append(schema.getTable(SNDSchema.PKGCATEGORYJUNCTION_TABLE_NAME), "c"); + sql.append(" WHERE Container = ? AND PkgId = ?").add(c).add(pkgId); + SqlSelector selector = new SqlSelector(schema.getDbSchema(), sql); + return selector.getArrayList(Integer.class); + } + public List getPackages(Container c, User u, List pkgIds, BatchValidationException errors) { UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); @@ -218,12 +242,15 @@ public List getPackages(Container c, User u, List pkgIds, Batc for (Map row : rows) { pkg = new Package(); - pkg.setPkgId((Integer)row.get("PkgId")); - pkg.setDescription((String)row.get("Description")); - pkg.setActive((boolean)row.get("Active")); - pkg.setRepeatable((boolean)row.get("Repeatable")); - pkg.setNarrative((String)row.get("Narrative")); - pkg.setQcState((Integer)row.get("QcState")); + pkg.setPkgId((Integer)row.get(Package.PKG_ID)); + pkg.setDescription((String)row.get(Package.PKG_DESCRIPTION)); + pkg.setActive((boolean)row.get(Package.PKG_ACTIVE)); + pkg.setRepeatable((boolean)row.get(Package.PKG_REPEATABLE)); + pkg.setNarrative((String)row.get(Package.PKG_NARRATIVE)); + pkg.setQcState((Integer)row.get(Package.PKG_QCSTATE)); + pkg.setCategories(getPackageCategories(c, u, pkg.getPkgId())); + pkg.setAttributes(getPackageAttributes(c, u, pkg.getPkgId())); + packages.add(pkg); } } From b8217f59e1f509e4b934ce1f09613390924b94ae Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 28 Aug 2017 03:03:35 +0000 Subject: [PATCH 041/686] Spec 30931: Update package delete query update service. --- resources/queries/snd/Pkgs/.qview.xml | 2 +- src/org/labkey/snd/PackageTable.java | 59 ++++++++++++++++++++++++++- src/org/labkey/snd/SNDManager.java | 24 +++++++++-- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/resources/queries/snd/Pkgs/.qview.xml b/resources/queries/snd/Pkgs/.qview.xml index f6904b3e4..5be687fab 100644 --- a/resources/queries/snd/Pkgs/.qview.xml +++ b/resources/queries/snd/Pkgs/.qview.xml @@ -6,7 +6,7 @@ - + diff --git a/src/org/labkey/snd/PackageTable.java b/src/org/labkey/snd/PackageTable.java index d399ee68d..750ef032a 100644 --- a/src/org/labkey/snd/PackageTable.java +++ b/src/org/labkey/snd/PackageTable.java @@ -1,10 +1,23 @@ package org.labkey.snd; +import org.labkey.api.data.Container; import org.labkey.api.data.JdbcType; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.DomainNotFoundException; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.PropertyService; import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.SimpleQueryUpdateService; import org.labkey.api.query.SimpleUserSchema; +import org.labkey.api.security.User; +import org.labkey.api.snd.PackageDomainKind; + +import java.sql.SQLException; +import java.util.Map; /** * Created by marty on 8/23/2017. @@ -37,11 +50,55 @@ public SimpleUserSchema.SimpleTable init() inUseSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); inUseSql.append(" THEN 'true' ELSE 'false' END)"); - ExprColumn inUseCol = new ExprColumn(this, "hasData", inUseSql, JdbcType.BOOLEAN); + ExprColumn inUseCol = new ExprColumn(this, "HasData", inUseSql, JdbcType.BOOLEAN); addColumn(inUseCol); return this; } + @Override + public QueryUpdateService getUpdateService() + { + return new PackageTable.UpdateService(this); + } + + protected class UpdateService extends SimpleQueryUpdateService + { + public UpdateService(SimpleUserSchema.SimpleTable ti) + { + super(ti, ti.getRealTable()); + } + + public UpdateService(SimpleUserSchema.SimpleTable simpleTable, TableInfo table, DomainUpdateHelper helper) + { + super(simpleTable, table, helper); + } + @Override + protected Map deleteRow(User user, Container container, Map oldRowMap) throws QueryUpdateServiceException, SQLException, InvalidKeyException + { + int pkgId = (Integer)oldRowMap.get("PkgId"); + if(SNDManager.get().isInUse(container, user, pkgId)) + throw new QueryUpdateServiceException("Package in use, cannot delete."); + + SNDManager.get().deletePackageCategories(container, user, pkgId); + + String domainName = SNDManager.getPackageName(pkgId); + Domain domain = PropertyService.get().getDomain(getDomainContainer(container), PackageDomainKind.getDomainURI(SNDSchema.NAME, domainName, container, user)); + if(domain == null) + throw new QueryUpdateServiceException("Package domain not found."); + + try + { + domain.delete(user); + } + catch (DomainNotFoundException e) + { + throw new QueryUpdateServiceException(e); + } + + return super.deleteRow(user, container, oldRowMap); + } + + } } diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index df8e0f7ce..e54dadd0e 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -21,14 +21,17 @@ import org.labkey.api.data.DbSequence; import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.DuplicateKeyException; +import org.labkey.api.query.FieldKey; import org.labkey.api.query.InvalidKeyException; import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; @@ -75,6 +78,23 @@ public static String getPackageName(int id) return PackageDomainKind.getPackageKindName() + "-" + id; } + public boolean isInUse(Container c, User u, int pkgId) + { + UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); + + TableInfo pkgsTable = schema.getTable(SNDSchema.PKGS_TABLE_NAME); + TableSelector ts = new TableSelector(pkgsTable, Collections.singleton("HasData"), new SimpleFilter(FieldKey.fromString("PkgId"), pkgId), null); + Boolean[] ret = ts.getArray(Boolean.class); + return ret[0]; + } + + public void deletePackageCategories(Container c, User u, int pkgId) + { + SQLFragment sql = new SQLFragment("DELETE FROM snd.PkgCategoryJunction WHERE PkgId = " + pkgId); + SqlExecutor sqlex = new SqlExecutor(SNDSchema.getInstance().getSchema()); + sqlex.execute(sql); + } + public void updatePackage(User u, Container c, Package pkg, BatchValidationException errors) { UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); @@ -97,9 +117,7 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep pkgQus.updateRows(u, c, pkgRows, null, null, null); // For categories delete existing junction relations and add new ones - SQLFragment sql = new SQLFragment("DELETE FROM snd.PkgCategoryJunction WHERE PkgId = " + pkg.getPkgId()); - SqlExecutor sqlex = new SqlExecutor(SNDSchema.getInstance().getSchema()); - sqlex.execute(sql); + deletePackageCategories(c, u, pkg.getPkgId()); pkgCategoryQus.insertRows(u, c, pkg.getCategoryRows(c), errors, null, null); tx.commit(); } From 2492bb0b2178fd2eb01218ec6d1b28c46e341c56 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 28 Aug 2017 04:37:31 +0000 Subject: [PATCH 042/686] Spec 30931: Add Has Project calculated column to package table. --- resources/queries/snd/Pkgs/.qview.xml | 3 ++- src/org/labkey/snd/PackageTable.java | 23 +++++++++++++++++------ src/org/labkey/snd/SNDManager.java | 22 +++++++++++----------- src/org/labkey/snd/SNDServiceImpl.java | 2 +- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/resources/queries/snd/Pkgs/.qview.xml b/resources/queries/snd/Pkgs/.qview.xml index 5be687fab..42ef0d5ae 100644 --- a/resources/queries/snd/Pkgs/.qview.xml +++ b/resources/queries/snd/Pkgs/.qview.xml @@ -6,7 +6,8 @@ - + + diff --git a/src/org/labkey/snd/PackageTable.java b/src/org/labkey/snd/PackageTable.java index 750ef032a..d1a29f8d8 100644 --- a/src/org/labkey/snd/PackageTable.java +++ b/src/org/labkey/snd/PackageTable.java @@ -42,15 +42,26 @@ public SimpleUserSchema.SimpleTable init() { super.init(); + SQLFragment hasDataSql = new SQLFragment(); + hasDataSql.append("(CASE WHEN EXISTS (SELECT sp.PkgId FROM "); + hasDataSql.append(SNDSchema.getInstance().getTableInfoSuperPkgs(), "sp"); + hasDataSql.append(" JOIN "); + hasDataSql.append(SNDSchema.getInstance().getTableInfoCodedEvents(), "ce"); + hasDataSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); + hasDataSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); + hasDataSql.append(" THEN 'true' ELSE 'false' END)"); + ExprColumn hasDataCol = new ExprColumn(this, "HasEvent", hasDataSql, JdbcType.BOOLEAN); + addColumn(hasDataCol); + SQLFragment inUseSql = new SQLFragment(); inUseSql.append("(CASE WHEN EXISTS (SELECT sp.PkgId FROM "); inUseSql.append(SNDSchema.getInstance().getTableInfoSuperPkgs(), "sp"); inUseSql.append(" JOIN "); - inUseSql.append(SNDSchema.getInstance().getTableInfoCodedEvents(), "ce"); - inUseSql.append(" ON sp.SuperPkgId = ce.SuperPkgId"); + inUseSql.append(SNDSchema.getInstance().getTableInfoProjectItems(), "pi"); + inUseSql.append(" ON sp.SuperPkgId = pi.SuperPkgId"); inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".PkgId = sp.PkgId)"); inUseSql.append(" THEN 'true' ELSE 'false' END)"); - ExprColumn inUseCol = new ExprColumn(this, "HasData", inUseSql, JdbcType.BOOLEAN); + ExprColumn inUseCol = new ExprColumn(this, "HasProject", inUseSql, JdbcType.BOOLEAN); addColumn(inUseCol); return this; @@ -77,15 +88,15 @@ public UpdateService(SimpleUserSchema.SimpleTable simpleTable, TableInfo table, @Override protected Map deleteRow(User user, Container container, Map oldRowMap) throws QueryUpdateServiceException, SQLException, InvalidKeyException { - int pkgId = (Integer)oldRowMap.get("PkgId"); - if(SNDManager.get().isInUse(container, user, pkgId)) + int pkgId = (Integer) oldRowMap.get("PkgId"); + if (SNDManager.get().isInUse(container, user, pkgId)) throw new QueryUpdateServiceException("Package in use, cannot delete."); SNDManager.get().deletePackageCategories(container, user, pkgId); String domainName = SNDManager.getPackageName(pkgId); Domain domain = PropertyService.get().getDomain(getDomainContainer(container), PackageDomainKind.getDomainURI(SNDSchema.NAME, domainName, container, user)); - if(domain == null) + if (domain == null) throw new QueryUpdateServiceException("Package domain not found."); try diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index e54dadd0e..92670725b 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -126,7 +126,7 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep errors.addRowError(new ValidationException(e.getMessage())); } - if(!errors.hasErrors()) + if (!errors.hasErrors()) { String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), getPackageName(pkg.getPkgId()), c, u); @@ -168,7 +168,7 @@ public void createPackage(User u, Container c, Package pkg, BatchValidationExcep errors.addRowError(new ValidationException(e.getMessage())); } - if(!errors.hasErrors()) + if (!errors.hasErrors()) { GWTDomain newDomain = new GWTDomain<>(); newDomain.setName(getPackageName(pkg.getPkgId())); @@ -207,7 +207,7 @@ private List getPackageAttributes(Container c, User u, in { String uri = PackageDomainKind.getDomainURI(SNDSchema.NAME, getPackageName(pkgId), c, u); GWTDomain domain = DomainUtil.getDomainDescriptor(u, uri, c); - if(domain != null) + if (domain != null) return domain.getFields(); return Collections.emptyList(); @@ -254,18 +254,18 @@ public List getPackages(Container c, User u, List pkgIds, Batc errors.addRowError(new ValidationException(e.getMessage())); } - if(!errors.hasErrors() && rows != null && !rows.isEmpty()) + if (!errors.hasErrors() && rows != null && !rows.isEmpty()) { Package pkg; - for (Map row : rows) + for (Map row : rows) { pkg = new Package(); - pkg.setPkgId((Integer)row.get(Package.PKG_ID)); - pkg.setDescription((String)row.get(Package.PKG_DESCRIPTION)); - pkg.setActive((boolean)row.get(Package.PKG_ACTIVE)); - pkg.setRepeatable((boolean)row.get(Package.PKG_REPEATABLE)); - pkg.setNarrative((String)row.get(Package.PKG_NARRATIVE)); - pkg.setQcState((Integer)row.get(Package.PKG_QCSTATE)); + pkg.setPkgId((Integer) row.get(Package.PKG_ID)); + pkg.setDescription((String) row.get(Package.PKG_DESCRIPTION)); + pkg.setActive((boolean) row.get(Package.PKG_ACTIVE)); + pkg.setRepeatable((boolean) row.get(Package.PKG_REPEATABLE)); + pkg.setNarrative((String) row.get(Package.PKG_NARRATIVE)); + pkg.setQcState((Integer) row.get(Package.PKG_QCSTATE)); pkg.setCategories(getPackageCategories(c, u, pkg.getPkgId())); pkg.setAttributes(getPackageAttributes(c, u, pkg.getPkgId())); diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 34f7367d2..42cad37e9 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -43,7 +43,7 @@ public void savePackage(Container c, User u, Package pkg) } else { - if(null == pkg.getPkgId()) + if (null == pkg.getPkgId()) pkg.setPkgId(SNDManager.get().generatePackageId(c)); SNDManager.get().createPackage(u, c, pkg, errors); From 6a3d6ab014bff579396c0dbe3bed8ca27724c664 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 28 Aug 2017 06:05:04 +0000 Subject: [PATCH 043/686] Spec 30931: Query update service delete for categories --- src/org/labkey/snd/CategoriesTable.java | 93 +++++++++++++++++++ .../{PackageTable.java => PackagesTable.java} | 24 ++++- src/org/labkey/snd/SNDManager.java | 13 --- src/org/labkey/snd/SNDUserSchema.java | 18 +--- 4 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 src/org/labkey/snd/CategoriesTable.java rename src/org/labkey/snd/{PackageTable.java => PackagesTable.java} (79%) diff --git a/src/org/labkey/snd/CategoriesTable.java b/src/org/labkey/snd/CategoriesTable.java new file mode 100644 index 000000000..892ac9096 --- /dev/null +++ b/src/org/labkey/snd/CategoriesTable.java @@ -0,0 +1,93 @@ +package org.labkey.snd; + +import org.labkey.api.data.Container; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; +import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.InvalidKeyException; +import org.labkey.api.query.QueryUpdateService; +import org.labkey.api.query.QueryUpdateServiceException; +import org.labkey.api.query.SimpleQueryUpdateService; +import org.labkey.api.query.SimpleUserSchema; +import org.labkey.api.security.User; + +import java.sql.SQLException; +import java.util.Collections; +import java.util.Map; + +/** + * Created by marty on 8/27/2017. + */ +public class CategoriesTable extends SimpleUserSchema.SimpleTable +{ + + /** + * Create the simple table. + * SimpleTable doesn't add columns until .init() has been called to allow derived classes to fully initialize themselves before adding columns. + * + * @param schema + * @param table + */ + public CategoriesTable(SNDUserSchema schema, TableInfo table) + { + super(schema, table); + } + + @Override + public SimpleUserSchema.SimpleTable init() + { + super.init(); + + SQLFragment inUseSql = new SQLFragment(); + inUseSql.append("(CASE WHEN EXISTS (SELECT PkgId FROM "); + inUseSql.append(SNDSchema.getInstance().getTableInfoPkgCategoryJunction(), "pcj"); + inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".CategoryId = pcj.CategoryId)"); + inUseSql.append(" THEN 'true' ELSE 'false' END)"); + ExprColumn inUseCol = new ExprColumn(this, "InUse", inUseSql, JdbcType.BOOLEAN); + addColumn(inUseCol); + + return this; + } + + protected boolean isCategoryInUse(Container c, User u, int catId) + { + TableSelector ts = new TableSelector(this, Collections.singleton("InUse"), new SimpleFilter(FieldKey.fromString("PkgId"), catId), null); + Boolean[] ret = ts.getArray(Boolean.class); + + return ret[0]; + } + + @Override + public QueryUpdateService getUpdateService() + { + return new CategoriesTable.UpdateService(this); + } + + protected class UpdateService extends SimpleQueryUpdateService + { + public UpdateService(SimpleUserSchema.SimpleTable ti) + { + super(ti, ti.getRealTable()); + } + + public UpdateService(SimpleUserSchema.SimpleTable simpleTable, TableInfo table, DomainUpdateHelper helper) + { + super(simpleTable, table, helper); + } + + @Override + protected Map deleteRow(User user, Container container, Map oldRowMap) throws QueryUpdateServiceException, SQLException, InvalidKeyException + { + int categoryId = (Integer) oldRowMap.get("CategoryId"); + if (isCategoryInUse(container, user, categoryId)) + throw new QueryUpdateServiceException("Category in use, cannot delete."); + + return super.deleteRow(user, container, oldRowMap); + } + + } +} diff --git a/src/org/labkey/snd/PackageTable.java b/src/org/labkey/snd/PackagesTable.java similarity index 79% rename from src/org/labkey/snd/PackageTable.java rename to src/org/labkey/snd/PackagesTable.java index d1a29f8d8..21d62c1b9 100644 --- a/src/org/labkey/snd/PackageTable.java +++ b/src/org/labkey/snd/PackagesTable.java @@ -3,11 +3,14 @@ import org.labkey.api.data.Container; import org.labkey.api.data.JdbcType; import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.TableInfo; +import org.labkey.api.data.TableSelector; import org.labkey.api.exp.DomainNotFoundException; import org.labkey.api.exp.property.Domain; import org.labkey.api.exp.property.PropertyService; import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.FieldKey; import org.labkey.api.query.InvalidKeyException; import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.QueryUpdateServiceException; @@ -17,12 +20,14 @@ import org.labkey.api.snd.PackageDomainKind; import java.sql.SQLException; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * Created by marty on 8/23/2017. */ -public class PackageTable extends SimpleUserSchema.SimpleTable +public class PackagesTable extends SimpleUserSchema.SimpleTable { /** @@ -32,7 +37,7 @@ public class PackageTable extends SimpleUserSchema.SimpleTable * @param schema * @param table */ - public PackageTable(SNDUserSchema schema, TableInfo table) + public PackagesTable(SNDUserSchema schema, TableInfo table) { super(schema, table); } @@ -67,10 +72,21 @@ public SimpleUserSchema.SimpleTable init() return this; } + protected boolean isPackageInUse(Container c, User u, int pkgId) + { + Set cols = new HashSet<>(); + cols.add("HasEvent"); + cols.add("HasProject"); + TableSelector ts = new TableSelector(this, cols, new SimpleFilter(FieldKey.fromString("PkgId"), pkgId), null); + Map ret = ts.getMap(); + + return Boolean.parseBoolean((String) ret.get("HasEvent")) | Boolean.parseBoolean((String) ret.get("HasProject")); + } + @Override public QueryUpdateService getUpdateService() { - return new PackageTable.UpdateService(this); + return new PackagesTable.UpdateService(this); } protected class UpdateService extends SimpleQueryUpdateService @@ -89,7 +105,7 @@ public UpdateService(SimpleUserSchema.SimpleTable simpleTable, TableInfo table, protected Map deleteRow(User user, Container container, Map oldRowMap) throws QueryUpdateServiceException, SQLException, InvalidKeyException { int pkgId = (Integer) oldRowMap.get("PkgId"); - if (SNDManager.get().isInUse(container, user, pkgId)) + if (isPackageInUse(container, user, pkgId)) throw new QueryUpdateServiceException("Package in use, cannot delete."); SNDManager.get().deletePackageCategories(container, user, pkgId); diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 92670725b..676acc4ba 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -21,17 +21,14 @@ import org.labkey.api.data.DbSequence; import org.labkey.api.data.DbSequenceManager; import org.labkey.api.data.SQLFragment; -import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; -import org.labkey.api.data.TableSelector; import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.gwt.client.model.GWTDomain; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.BatchValidationException; import org.labkey.api.query.DuplicateKeyException; -import org.labkey.api.query.FieldKey; import org.labkey.api.query.InvalidKeyException; import org.labkey.api.query.QueryService; import org.labkey.api.query.QueryUpdateService; @@ -78,16 +75,6 @@ public static String getPackageName(int id) return PackageDomainKind.getPackageKindName() + "-" + id; } - public boolean isInUse(Container c, User u, int pkgId) - { - UserSchema schema = QueryService.get().getUserSchema(u, c, SNDSchema.NAME); - - TableInfo pkgsTable = schema.getTable(SNDSchema.PKGS_TABLE_NAME); - TableSelector ts = new TableSelector(pkgsTable, Collections.singleton("HasData"), new SimpleFilter(FieldKey.fromString("PkgId"), pkgId), null); - Boolean[] ret = ts.getArray(Boolean.class); - return ret[0]; - } - public void deletePackageCategories(Container c, User u, int pkgId) { SQLFragment sql = new SQLFragment("DELETE FROM snd.PkgCategoryJunction WHERE PkgId = " + pkgId); diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index 7f79da18b..238b93448 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -18,10 +18,7 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; import org.labkey.api.data.DbSchema; -import org.labkey.api.data.JdbcType; -import org.labkey.api.data.SQLFragment; import org.labkey.api.data.TableInfo; -import org.labkey.api.query.ExprColumn; import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.security.User; @@ -52,7 +49,7 @@ public TableInfo createTable(SNDUserSchema schema) @Override public TableInfo createTable(SNDUserSchema schema) { - return new PackageTable(schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); + return new PackagesTable(schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); } }, PkgCategories @@ -60,19 +57,8 @@ public TableInfo createTable(SNDUserSchema schema) @Override public TableInfo createTable(SNDUserSchema schema) { - SimpleUserSchema.SimpleTable table = - new SimpleUserSchema.SimpleTable<>( - schema, SNDSchema.getInstance().getTableInfoPkgCategories()).init(); + return new CategoriesTable(schema, SNDSchema.getInstance().getTableInfoPkgCategories()).init(); - SQLFragment inUseSql = new SQLFragment(); - inUseSql.append("(CASE WHEN EXISTS (SELECT PkgId FROM "); - inUseSql.append(SNDSchema.getInstance().getTableInfoPkgCategoryJunction(), "pcj"); - inUseSql.append(" WHERE " + ExprColumn.STR_TABLE_ALIAS + ".CategoryId = pcj.CategoryId)"); - inUseSql.append(" THEN 'true' ELSE 'false' END)"); - ExprColumn inUseCol = new ExprColumn(table, "InUse", inUseSql, JdbcType.BOOLEAN); - table.addColumn(inUseCol); - - return table; } }, PkgCategoryJunction From db6f6785536a5649d748eed320b57524abb75df2 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 28 Aug 2017 17:35:04 +0000 Subject: [PATCH 044/686] Spec 30931: Update package id generation --- src/org/labkey/snd/SNDServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 42cad37e9..7b16ebd7d 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -43,7 +43,7 @@ public void savePackage(Container c, User u, Package pkg) } else { - if (null == pkg.getPkgId()) + if (null == pkg.getPkgId() || pkg.getPkgId() == 0) pkg.setPkgId(SNDManager.get().generatePackageId(c)); SNDManager.get().createPackage(u, c, pkg, errors); From aea097b2119a78f055b3ba1aa59c815787cfb1ea Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 28 Aug 2017 18:40:07 +0000 Subject: [PATCH 045/686] Spec 30931: Do not update domain for in use package update --- src/org/labkey/snd/PackagesTable.java | 4 ++-- src/org/labkey/snd/SNDManager.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/snd/PackagesTable.java b/src/org/labkey/snd/PackagesTable.java index 21d62c1b9..6f8b88715 100644 --- a/src/org/labkey/snd/PackagesTable.java +++ b/src/org/labkey/snd/PackagesTable.java @@ -72,7 +72,7 @@ public SimpleUserSchema.SimpleTable init() return this; } - protected boolean isPackageInUse(Container c, User u, int pkgId) + public boolean isPackageInUse(int pkgId) { Set cols = new HashSet<>(); cols.add("HasEvent"); @@ -105,7 +105,7 @@ public UpdateService(SimpleUserSchema.SimpleTable simpleTable, TableInfo table, protected Map deleteRow(User user, Container container, Map oldRowMap) throws QueryUpdateServiceException, SQLException, InvalidKeyException { int pkgId = (Integer) oldRowMap.get("PkgId"); - if (isPackageInUse(container, user, pkgId)) + if (isPackageInUse(pkgId)) throw new QueryUpdateServiceException("Package in use, cannot delete."); SNDManager.get().deletePackageCategories(container, user, pkgId); diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 676acc4ba..57ba7d94f 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -113,7 +113,8 @@ public void updatePackage(User u, Container c, Package pkg, BatchValidationExcep errors.addRowError(new ValidationException(e.getMessage())); } - if (!errors.hasErrors()) + // If package is in use (either assigned to an event or project) then do not update the domain + if (!errors.hasErrors() && !((PackagesTable)pkgsTable).isPackageInUse(pkg.getPkgId())) { String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), getPackageName(pkg.getPkgId()), c, u); From 68bce213001b8f647148914ad7add8ede443c0bb Mon Sep 17 00:00:00 2001 From: Ron Dashwood Date: Tue, 29 Aug 2017 00:08:29 +0000 Subject: [PATCH 046/686] Spec 30931: Fix automated test - Add testPkgs column to grid view. --- test/src/org/labkey/test/tests/snd/SNDTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/src/org/labkey/test/tests/snd/SNDTest.java b/test/src/org/labkey/test/tests/snd/SNDTest.java index 3c78f889a..cde99d001 100644 --- a/test/src/org/labkey/test/tests/snd/SNDTest.java +++ b/test/src/org/labkey/test/tests/snd/SNDTest.java @@ -33,6 +33,7 @@ import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; import org.labkey.test.categories.CustomModules; +import org.labkey.test.components.CustomizeView; import org.labkey.test.util.LogMethod; import org.labkey.test.util.Maps; import org.labkey.test.util.SqlserverOnlyTest; @@ -199,6 +200,13 @@ public void testExtensibleColumns() throws Exception assertTextPresent(PKGSTESTCOL); waitAndClickAndWait(Locator.linkWithText("view data")); + CustomizeView customizeViewHelper = new CustomizeView(this); + + customizeViewHelper.openCustomizeViewPanel(); + customizeViewHelper.addColumn("testPkgs"); + customizeViewHelper.clickViewGrid(); + waitForText("grid view is unsaved."); + assertTextPresent(EXTCOLTESTDATA1, EXTCOLTESTDATA2, EXTCOLTESTDATA3); clickFolder(TEST1SUBFOLDER); From 5350a3214610d59743d6adee4e6776fddad09fb0 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Tue, 29 Aug 2017 08:03:38 +0000 Subject: [PATCH 047/686] Spec 30932 : SNPRC SND - XML Package Integration : Updates around redirect page after the Import step. Enabled xml validation. Updated xsd. Some cleanup. --- resources/views/importPackages.html | 38 +++++++++++++++++++ resources/views/importPackages.view.xml | 8 ++++ schemas/snd.xsd | 14 +++---- .../labkey/snd/pipeline/SNDDataHandler.java | 30 ++++++--------- webapp/WEB-INF/snd/sndContext.xml | 2 +- 5 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 resources/views/importPackages.html create mode 100644 resources/views/importPackages.view.xml diff --git a/resources/views/importPackages.html b/resources/views/importPackages.html new file mode 100644 index 000000000..1280b7fbf --- /dev/null +++ b/resources/views/importPackages.html @@ -0,0 +1,38 @@ +
+
+ + diff --git a/resources/views/importPackages.view.xml b/resources/views/importPackages.view.xml new file mode 100644 index 000000000..c9f1f6c16 --- /dev/null +++ b/resources/views/importPackages.view.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/schemas/snd.xsd b/schemas/snd.xsd index fe6bc6701..d0688f710 100644 --- a/schemas/snd.xsd +++ b/schemas/snd.xsd @@ -22,7 +22,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -56,17 +56,17 @@ - + - + - - - + + + diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index d8e8760bc..7e55ec1b0 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -51,10 +51,7 @@ */ public class SNDDataHandler extends AbstractExperimentDataHandler { - -// private static final String TABLE_INFO_NS = "dat"; - private static final String TABLE_INFO_NS_VAL = "http://labkey.org/data/xml"; - + private static final Logger _log = Logger.getLogger(SNDDataHandler.class); private static final FileType SND_INPUT = new FileType(".snd.xml"); @Override @@ -68,9 +65,6 @@ public void importFile(@NotNull ExpData data, File dataFile, @NotNull ViewBackgr { ExportDocument exportDocument; String inputFileName = dataFile.getName(); -// Map addnlNameSpaces = new HashedMap(); - -// addnlNameSpaces.put(TABLE_INFO_NS, TABLE_INFO_NS_VAL); if (SND_INPUT.isType(dataFile)) { @@ -84,30 +78,28 @@ public void importFile(@NotNull ExpData data, File dataFile, @NotNull ViewBackgr //read xml data try(FileInputStream in = FileUtils.openInputStream(dataFile)) { - XmlOptions options = new XmlOptions(); - options.setDocumentType(ExportDocument.type); - options.setValidateStrict();//fails silently if namespace is invalid. However, does throw an error if elements are not prefixed with correct ns. -// options.setLoadAdditionalNamespaces(addnlNameSpaces); - options.setLoadUseXMLReader(SAXParserFactory.newInstance().newSAXParser().getXMLReader()); + XmlOptions options = XmlBeansUtil.getDefaultParseOptions(); + options.setValidateStrict(); //parse xml tags and get tokens/auto-generated pojos exportDocument = ExportDocument.Factory.parse(in, options); - //TODO: validate xml - takes a very long time - do we still want to validate this way? Also, Cancelling doesn't Cancel. Is there a better way? -// XmlBeansUtil.validateXmlDocument(exportDocument, "Validating " + inputFileName + " against schema."); + _log.info("Starting xml Validation"); + XmlBeansUtil.validateXmlDocument(exportDocument, "Validating " + inputFileName + " against schema."); + _log.info("End xml Validation"); } catch (IOException e) { throw new ExperimentException("Error reading input file '" + inputFileName +"'", e); } - catch (XmlException | SAXException | ParserConfigurationException e) + catch (XmlException e) { throw new ExperimentException("Could not parse input file '" + inputFileName +"'", e); } -// catch (XmlValidationException e) -// { -// throw new ExperimentException("Invalid XML file " + inputFileName, e); -// } + catch (XmlValidationException e) + { + throw new ExperimentException("Invalid XML file " + inputFileName, e); + } ExportDocument.Export export = exportDocument.getExport(); diff --git a/webapp/WEB-INF/snd/sndContext.xml b/webapp/WEB-INF/snd/sndContext.xml index 3ccf71562..b0447aab5 100644 --- a/webapp/WEB-INF/snd/sndContext.xml +++ b/webapp/WEB-INF/snd/sndContext.xml @@ -13,7 +13,7 @@ - + From c9e5d11393fd411ef7ef4ca9708861eceddc7dae Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Tue, 29 Aug 2017 17:53:38 +0000 Subject: [PATCH 048/686] Spec 30932 : Allow inserting of at least 1 attribute. --- src/org/labkey/snd/pipeline/SNDDataHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 7e55ec1b0..451e1ba03 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -163,8 +163,8 @@ private List getAttributes(PackageType packageType) AttributesType attributes = packageType.getAttributes(); ColumnType[] attributeArray = attributes.getAttributeArray(); - List attributesList = new LinkedList<>();; - if (attributeArray.length > 1) + List attributesList = new LinkedList<>(); + if (attributeArray.length > 0) { for (ColumnType ct : attributeArray) { From 399c17dd466ab83459c803f2a4bd3e520fa6666e Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Tue, 29 Aug 2017 19:07:23 +0000 Subject: [PATCH 049/686] Spec 30932 : Minor refactoring around saving super packages. --- api-src/org/labkey/api/snd/SNDService.java | 2 +- src/org/labkey/snd/SNDServiceImpl.java | 4 ++-- src/org/labkey/snd/pipeline/SNDDataHandler.java | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java index a577cd48d..481d33e4d 100644 --- a/api-src/org/labkey/api/snd/SNDService.java +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -19,6 +19,6 @@ static SNDService get() } void savePackage(Container c, User u, Package pkg); - void saveSuperPackage(Container c, User u, SuperPackage superPkg); + void saveSuperPackage(Container c, User u, List superPkgs); List getPackages(Container c, User u, List pkgIds); } diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index 7b16ebd7d..c10922109 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -54,11 +54,11 @@ public void savePackage(Container c, User u, Package pkg) } @Override - public void saveSuperPackage(Container c, User u, SuperPackage superPkg) + public void saveSuperPackage(Container c, User u, List superPkgs) { BatchValidationException errors = new BatchValidationException(); - //SNDManager.get().createSuperPackage(u, c, superPkg, errors); + //SNDManager.get().createSuperPackage(u, c, superPkgs, errors); if (errors.hasErrors()) throw new UnexpectedException(errors); diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 451e1ba03..2c70b9666 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -240,11 +240,13 @@ private void parseAndSaveSuperPackages(ExportDocument.Export export, @NotNull Vi SuperPackagesType superPackagesType = export.getSuperPackages(); SuperPackageType[] superPackageArray = superPackagesType.getSuperPackageArray(); SNDService sndService = SNDService.get(); + List superPackages = new LinkedList<>(); for(SuperPackageType superPackageType : superPackageArray) { SuperPackage superPackage = parseSuperPackage(superPackageType); - sndService.saveSuperPackage(info.getContainer(), info.getUser(), superPackage); + superPackages.add(superPackage); } + sndService.saveSuperPackage(info.getContainer(), info.getUser(), superPackages); } private SuperPackage parseSuperPackage(SuperPackageType superPackageType) From dc915c88463a8eb34c9f2f92232b3fbabae4e2c1 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Tue, 29 Aug 2017 19:41:08 +0000 Subject: [PATCH 050/686] Spec 30932 : Sample data. --- test/sampledata/1_initial.snd.xml | 40 ++++++++++++++ test/sampledata/2_changeNarrative.snd.xml | 40 ++++++++++++++ test/sampledata/3_insertPackage.snd.xml | 47 ++++++++++++++++ test/sampledata/4_addAttribute.snd.xml | 54 +++++++++++++++++++ test/sampledata/5_removeAttribute.snd.xml | 43 +++++++++++++++ test/sampledata/6_removeAllAttributes.snd.xml | 28 ++++++++++ 6 files changed, 252 insertions(+) create mode 100644 test/sampledata/1_initial.snd.xml create mode 100644 test/sampledata/2_changeNarrative.snd.xml create mode 100644 test/sampledata/3_insertPackage.snd.xml create mode 100644 test/sampledata/4_addAttribute.snd.xml create mode 100644 test/sampledata/5_removeAttribute.snd.xml create mode 100644 test/sampledata/6_removeAllAttributes.snd.xml diff --git a/test/sampledata/1_initial.snd.xml b/test/sampledata/1_initial.snd.xml new file mode 100644 index 000000000..9a8013aba --- /dev/null +++ b/test/sampledata/1_initial.snd.xml @@ -0,0 +1,40 @@ + + + + + Check Vitals Test + + + string + false + Vital 1 + Default 1 + select + veterinarian + + table1 + title + snd + + + + double + true + Vital 2 + 2 + text + + Range + urn:lsid:labkey.com:PropertyValidator:range + ~lte=109&~gte=80.0000 + + + + + + + + + + + \ No newline at end of file diff --git a/test/sampledata/2_changeNarrative.snd.xml b/test/sampledata/2_changeNarrative.snd.xml new file mode 100644 index 000000000..765174998 --- /dev/null +++ b/test/sampledata/2_changeNarrative.snd.xml @@ -0,0 +1,40 @@ + + + + + Check Vitals + + + string + false + Vital 1 + Default 1 + select + veterinarian + + table1 + title + snd + + + + double + true + Vital 2 + 2 + text + + Range + urn:lsid:labkey.com:PropertyValidator:range + ~lte=109&~gte=80.0000 + + + + + + + + + + + \ No newline at end of file diff --git a/test/sampledata/3_insertPackage.snd.xml b/test/sampledata/3_insertPackage.snd.xml new file mode 100644 index 000000000..fd3294040 --- /dev/null +++ b/test/sampledata/3_insertPackage.snd.xml @@ -0,0 +1,47 @@ + + + + + Therapy started + + + + Check Vitals + + + string + false + Vital 1 + Default 1 + select + veterinarian + + table1 + title + snd + + + + double + true + Vital 2 + 2 + text + + Range + urn:lsid:labkey.com:PropertyValidator:range + ~lte=109&~gte=80.0000 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/sampledata/4_addAttribute.snd.xml b/test/sampledata/4_addAttribute.snd.xml new file mode 100644 index 000000000..a84ea7703 --- /dev/null +++ b/test/sampledata/4_addAttribute.snd.xml @@ -0,0 +1,54 @@ + + + + + Therapy started + + + string + false + Therapy A + Drug A + + + + + Check Vitals + + + string + false + Vital 1 + Default 1 + select + veterinarian + + table1 + title + snd + + + + double + true + Vital 2 + 2 + text + + Range + urn:lsid:labkey.com:PropertyValidator:range + ~lte=109&~gte=80.0000 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/sampledata/5_removeAttribute.snd.xml b/test/sampledata/5_removeAttribute.snd.xml new file mode 100644 index 000000000..edcb13b64 --- /dev/null +++ b/test/sampledata/5_removeAttribute.snd.xml @@ -0,0 +1,43 @@ + + + + + Therapy started + + + string + false + Therapy A + Drug A + + + + + Check Vitals + + + string + false + Vital 1 + Default 1 + select + veterinarian + + table1 + title + snd + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/sampledata/6_removeAllAttributes.snd.xml b/test/sampledata/6_removeAllAttributes.snd.xml new file mode 100644 index 000000000..bf6683475 --- /dev/null +++ b/test/sampledata/6_removeAllAttributes.snd.xml @@ -0,0 +1,28 @@ + + + + + Therapy started + + + string + false + Therapy A + Drug A + + + + + Check Vitals + + + + + + + + + + + + \ No newline at end of file From 81e2d9fa794fa64aa905cc8dbee046451db28105 Mon Sep 17 00:00:00 2001 From: Josh Date Date: Wed, 30 Aug 2017 06:46:41 +0000 Subject: [PATCH 051/686] SND - add package search/viewer component and top level design for package edit/create pages - initial redux setup --- src/client/app.tsx | 2 +- src/client/components/NotFound/NotFound.tsx | 18 ++ src/client/components/Packages/PackageRow.tsx | 51 +++++ src/client/containers/App/App.tsx | 21 +- src/client/containers/App/actions.ts | 1 + .../containers/Categories/Forms/Edit.tsx | 15 ++ .../containers/LandingPage/LandingPage.tsx | 19 ++ .../containers/Packages/Forms/NewPackage.tsx | 15 ++ .../containers/Packages/Forms/PackageForm.tsx | 81 +++++++ .../Packages/Forms/PackageViewer.tsx | 196 +++++++++++++++++ src/client/containers/Packages/actions.ts | 126 +++++++++++ src/client/containers/Packages/constants.ts | 16 ++ src/client/containers/Packages/model.ts | 203 ++++++++++++++++++ src/client/containers/Packages/reducer.ts | 135 ++++++++++++ src/client/reducers/index.ts | 14 ++ src/client/routing/Routes.tsx | 74 +++++++ src/client/utils/constants.ts | 14 ++ src/client/utils/query.ts | 32 +++ 18 files changed, 1029 insertions(+), 4 deletions(-) create mode 100644 src/client/components/NotFound/NotFound.tsx create mode 100644 src/client/components/Packages/PackageRow.tsx create mode 100644 src/client/containers/App/actions.ts create mode 100644 src/client/containers/Categories/Forms/Edit.tsx create mode 100644 src/client/containers/LandingPage/LandingPage.tsx create mode 100644 src/client/containers/Packages/Forms/NewPackage.tsx create mode 100644 src/client/containers/Packages/Forms/PackageForm.tsx create mode 100644 src/client/containers/Packages/Forms/PackageViewer.tsx create mode 100644 src/client/containers/Packages/actions.ts create mode 100644 src/client/containers/Packages/constants.ts create mode 100644 src/client/containers/Packages/model.ts create mode 100644 src/client/containers/Packages/reducer.ts create mode 100644 src/client/utils/constants.ts create mode 100644 src/client/utils/query.ts diff --git a/src/client/app.tsx b/src/client/app.tsx index 8baf75acd..b6869cba3 100644 --- a/src/client/app.tsx +++ b/src/client/app.tsx @@ -58,7 +58,7 @@ const store = storeCreator( jQuery(() => { ReactDom.render( -
+
{ /* ConnectedRouter will use the store from Provider automatically */ }
diff --git a/src/client/components/NotFound/NotFound.tsx b/src/client/components/NotFound/NotFound.tsx new file mode 100644 index 000000000..c1be26bcd --- /dev/null +++ b/src/client/components/NotFound/NotFound.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { Panel } from 'react-bootstrap'; +import { RouteComponentProps } from 'react-router-dom'; + +export class NotFound extends React.Component, any> { + render() { + return ( +
+ +
No page found at:
+
+ {this.props.location.pathname} +
+
+
+ ); + } +} \ No newline at end of file diff --git a/src/client/components/Packages/PackageRow.tsx b/src/client/components/Packages/PackageRow.tsx new file mode 100644 index 000000000..2d87faa94 --- /dev/null +++ b/src/client/components/Packages/PackageRow.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; + + +import { QueryPackageModel } from '../../containers/Packages/model' + +interface PackageRowProps { + pkg?: QueryPackageModel +} + + +interface PackageViewerStateProps { + showDrafts: boolean +} + + +export class PackageRow extends React.Component { + + constructor(props?: PackageRowProps) { + super(props); + + this.state = { + showDrafts: false + } + } + + render() { + const resultStyle = { + padding: '3px 10px' + }; + + const resultIconStyle = { + marginRight: '10px', + cursor: 'pointer' + }; + + const { Description, PkgId } = this.props.pkg; + + return ( +
+
+ + + +
+
+ {[PkgId.value, Description.value].join(' - ')} +
+
+ ) + } +} diff --git a/src/client/containers/App/App.tsx b/src/client/containers/App/App.tsx index 5352809af..8062a2f30 100644 --- a/src/client/containers/App/App.tsx +++ b/src/client/containers/App/App.tsx @@ -2,20 +2,35 @@ import * as React from 'react'; import { connect } from 'react-redux'; import { Route, RouteComponentProps, RouteProps, Switch } from 'react-router-dom'; +import { Routes } from '../../routing/Routes' + +interface AppOwnProps extends RouteComponentProps<{}> {} +interface AppStateProps {} + +type AppProps = AppOwnProps & AppStateProps; + function mapStateToProps(state: any) { return { } } -export class AppImpl extends React.Component { +export class AppImpl extends React.Component { render() { return (
- Hi, I'm your home page....eventually + + {Routes.map((route: RouteProps, index: number) => { + return ; + })} +
) } } -export const App = connect(mapStateToProps)(AppImpl); \ No newline at end of file +export const App = connect(mapStateToProps)(AppImpl); \ No newline at end of file diff --git a/src/client/containers/App/actions.ts b/src/client/containers/App/actions.ts new file mode 100644 index 000000000..c55ffd4a5 --- /dev/null +++ b/src/client/containers/App/actions.ts @@ -0,0 +1 @@ +// setAppError \ No newline at end of file diff --git a/src/client/containers/Categories/Forms/Edit.tsx b/src/client/containers/Categories/Forms/Edit.tsx new file mode 100644 index 000000000..44d0ef306 --- /dev/null +++ b/src/client/containers/Categories/Forms/Edit.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { Panel } from 'react-bootstrap'; +import { RouteComponentProps } from 'react-router-dom'; + +export class EditCategories extends React.Component, any> { + render() { + return ( +
+ + Hi I'm your new Edit Categories Page + +
+ ); + } +} \ No newline at end of file diff --git a/src/client/containers/LandingPage/LandingPage.tsx b/src/client/containers/LandingPage/LandingPage.tsx new file mode 100644 index 000000000..692d61d05 --- /dev/null +++ b/src/client/containers/LandingPage/LandingPage.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { Panel } from 'react-bootstrap'; + +import { RouteComponentProps } from 'react-router-dom'; + + + +export class LandingPageImpl extends React.Component, any> { + + render() { + return ( + + Hi, I'm your landing page + + ) + } +} + +export const LandingPage = LandingPageImpl; \ No newline at end of file diff --git a/src/client/containers/Packages/Forms/NewPackage.tsx b/src/client/containers/Packages/Forms/NewPackage.tsx new file mode 100644 index 000000000..3dbac771b --- /dev/null +++ b/src/client/containers/Packages/Forms/NewPackage.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { Panel } from 'react-bootstrap'; +import { RouteComponentProps } from 'react-router-dom'; + +export class NewPackage extends React.Component, any> { + render() { + return ( +
+ + Hi I'm your new package page + +
+ ); + } +} \ No newline at end of file diff --git a/src/client/containers/Packages/Forms/PackageForm.tsx b/src/client/containers/Packages/Forms/PackageForm.tsx new file mode 100644 index 000000000..9fc618cf1 --- /dev/null +++ b/src/client/containers/Packages/Forms/PackageForm.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; + +import { Link, RouteComponentProps } from 'react-router-dom' + +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { APP_STATE_PROPS } from '../../../reducers/index' +import * as actions from '../actions' +import { PackageModel } from '../model' + + +interface PackageFormOwnProps extends RouteComponentProps<{id: string}> {} + +interface PackageFormState { + dispatch?: Dispatch + + packageModel?: PackageModel +} + +interface PackageFormStateProps { + +} + +type PackageFormProps = PackageFormOwnProps & PackageFormState; + + + +function mapStateToProps(state: APP_STATE_PROPS, ownProps: PackageFormOwnProps) { + const { id } = ownProps.match.params; + + return { + packageModel: state.packages.packageData[id] + }; +} + +export class PackageFormImpl extends React.Component { + + private timer: number = 0; + + constructor(props?: PackageFormProps) { + super(props); + + this.state = { + showDrafts: false + } + } + + componentDidMount() { + this.getPackage(this.props); + } + + componentWillUnmount() { + clearTimeout(this.timer); + } + + componentWillReceiveProps(nextProps?: PackageFormProps) { + this.getPackage(nextProps); + } + + getPackage(props?: PackageFormProps) { + const { dispatch, packageModel } = props; + const { id } = props.match.params; + + if (id && !packageModel) { + dispatch(actions.getPackage(id)); + } + } + + render() { + + console.log(this.props.packageModel) + return ( +
+ +
+ ) + } +} + +export const PackageForm = connect(mapStateToProps)(PackageFormImpl); \ No newline at end of file diff --git a/src/client/containers/Packages/Forms/PackageViewer.tsx b/src/client/containers/Packages/Forms/PackageViewer.tsx new file mode 100644 index 000000000..c19956b72 --- /dev/null +++ b/src/client/containers/Packages/Forms/PackageViewer.tsx @@ -0,0 +1,196 @@ +import * as React from 'react'; +import { Button, DropdownButton, SplitButton, MenuItem, Panel } from 'react-bootstrap'; + +import { Link, RouteComponentProps } from 'react-router-dom' + +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { CSSProperties } from "react"; + + +import { APP_STATE_PROPS } from '../../../reducers/index' +import * as actions from '../actions' +import { QueryPackageModel, PackagesModel } from '../model' +import { PackageRow } from '../../../components/Packages/PackageRow' + +interface PackageViewerOwnProps extends RouteComponentProps<{}> {} + +interface PackageViewerState { + dispatch?: Dispatch + + packagesModel?: PackagesModel +} + +interface PackageViewerStateProps { + showDrafts: boolean +} + +type PackageViewerProps = PackageViewerOwnProps & PackageViewerState; + + + +function mapStateToProps(state: APP_STATE_PROPS, ownProps: PackageViewerOwnProps) { + + return { + packagesModel: state.packages + }; +} + +export class PackageViewerImpl extends React.Component { + + private timer: number = 0; + + constructor(props?: PackageViewerProps) { + super(props); + + this.state = { + showDrafts: false + } + } + + componentDidMount() { + const { dispatch } = this.props; + // investigate mapDispatchToProps + dispatch(actions.getPackages()); + } + + componentWillUnmount() { + clearTimeout(this.timer); + } + + componentWillReceiveProps(nextProps?: PackageViewerProps) { + const { dispatch } = nextProps; + const { isLoaded, isLoading, data } = nextProps.packagesModel; + if (!data && !isLoading && !isLoaded) { + dispatch(actions.getPackages()); + } + } + + + toggleDrafts(evt: React.MouseEvent) { + const { showDrafts } = this.state; + + evt.preventDefault(); + + this.setState({ + showDrafts: !showDrafts + }) + } + + handleClear() { + const { dispatch, packagesModel } = this.props; + const { hasInput } = packagesModel + + dispatch(actions.filterPackages('')); + } + + handleInputChange(evt: React.ChangeEvent) { + const { dispatch } = this.props; + const input = evt.currentTarget.value; + + clearTimeout(this.timer); + + this.timer = setTimeout(() => { + this.timer = null; + + dispatch(actions.filterPackages(input)) + }, 50); + + } + + render() { + + // todo: add css style sheet and webpack support + const buttonStyle = { + padding: '2.5px 15px', + }; + + const searchStyle = { + borderRadius: '4px', + padding: '5px 5px 5px 25px', + fontSize: '1.1em', + width: '100%' + }; + + const iconStyle: CSSProperties = { + position: 'absolute', + left: '21px', + fontSize: '1.1em', + top: '8px' + }; + + const clearStyle: CSSProperties = { + position: 'absolute', + cursor: 'pointer', + right: '21px', + fontSize: '1.1em', + top: '8px', + color: 'lightgray' + }; + + const { showDrafts } = this.state; + const { data, filteredActive, filteredDrafts, isLoaded } = this.props.packagesModel; + + // input should be own component + return ( + +
+
+
+ + + +
+
+ + this.handleClear()}/> + this.handleInputChange(evt)}/> +
+
+
+ + Edit Categories + Edit Projects + +
+
+
+
+
+ {isLoaded && data ? +
+
this.toggleDrafts(evt)}> + this.toggleDrafts(evt)}/> Show drafts +
+
+ {showDrafts ? +
+

Drafts

+
+ {filteredDrafts.map((id) => { + const pkg: QueryPackageModel = data[id]; + return ; + })} +
+
+ : null} + +
+

Active

+
+ {filteredActive.map((id) => { + const pkg: QueryPackageModel = data[id]; + return ; + })} +
+
+
+
+ + :
Loading...
} + + ) + } +} + +export const PackageViewer = connect(mapStateToProps)(PackageViewerImpl); \ No newline at end of file diff --git a/src/client/containers/Packages/actions.ts b/src/client/containers/Packages/actions.ts new file mode 100644 index 000000000..551fa33f7 --- /dev/null +++ b/src/client/containers/Packages/actions.ts @@ -0,0 +1,126 @@ +import { PKG_TYPES } from './constants' +import { PackageModel, PackagesModel } from './model' + +import { labkeyAjax, selectRows } from '../../utils/query' + +export function getPackage(id: string | number) { + return (dispatch, getState) => { + + const packagesModel: PackagesModel = getState().packages; + + if (!packagesModel.packageLoaded && !packagesModel.packageLoading) { + dispatch(packageLoading()); + + return labkeyAjax( + 'snd', + 'getPackages', + null, + {"packages":[id]} + ).then((response: PackageQueryResponse) => { + // cannot set loaded as there could be more packages + dispatch(packageLoaded()); + dispatch(packageSuccess(response)); + }).catch((error) => { + // set error + console.log('error', error) + }); + } + } +} + +export function getPackages() { + return (dispatch, getState) => { + dispatch(packagesLoading()); + + return selectRows('snd', 'pkgs').then((response: LabKeyQueryResponse) => { + dispatch(packagesLoaded()); + dispatch(packagesSuccess(response)) + + }).catch((error) => { + // set error + console.log('error', error) + }); + } +} + +function packageLoaded() { + return { + type: PKG_TYPES.PACKAGE_LOADED + } +} + +function packageLoading() { + return { + type: PKG_TYPES.PACKAGE_LOADING + }; +} + +function packagesLoaded() { + return { + type: PKG_TYPES.PACKAGES_LOADED + } +} + +function packagesLoading() { + return { + type: PKG_TYPES.PACKAGES_LOADING + }; +} + +function packageSuccess(response: PackageQueryResponse) { + return { + type: PKG_TYPES.PACKAGE_SUCCESS, + response + }; +} + +function packagesSuccess(response: LabKeyQueryResponse) { + return { + type: PKG_TYPES.PACKAGES_SUCCESS, + response + }; +} + +export function filterPackages(input: string) { + return { + type: PKG_TYPES.PACKAGES_SEARCH_FILTER, + input + }; +} + + + + + + + +export interface LabKeyQueryRowPropertyProps { + displayValue?: string + url?: string + value: any +} + +interface LabKeyQueryResponse { + columnModel?: Array<{ + align: string, + dataIndex: string, + editable: boolean, + header: string, + hidden: boolean, + required: boolean, + scale: number, + sortable: boolean, + width: number + }> + metaData?: {[key: string]: any} + queryName: string + rowCount: number + rows: Array<{data: {[key: string]: any}}> + schemaKey: {name: string, parent: any} + schemaName: Array | string +} + +interface PackageQueryResponse { + json: Array +} + diff --git a/src/client/containers/Packages/constants.ts b/src/client/containers/Packages/constants.ts new file mode 100644 index 000000000..7c59eb25c --- /dev/null +++ b/src/client/containers/Packages/constants.ts @@ -0,0 +1,16 @@ + + +const PKG_PREFIX = 'packages/'; + +export const PKG_TYPES = { + PACKAGE_LOADED: PKG_PREFIX + 'PACKAGE_LOADED', + PACKAGE_LOADING: PKG_PREFIX + 'PACKAGE_LOADING', + PACKAGES_LOADED: PKG_PREFIX + 'PACKAGES_LOADED', + PACKAGES_LOADING: PKG_PREFIX + 'PACKAGES_LOADING', + + + PACKAGE_SUCCESS: PKG_PREFIX + 'PACKAGE_SUCCESS', + PACKAGES_SUCCESS: PKG_PREFIX + 'PACKAGES_SUCCESS', + SET_ACTIVE_PACKAGE: PKG_PREFIX + 'SET_ACTIVE_PACKAGE', + PACKAGES_SEARCH_FILTER: PKG_PREFIX + 'PACKAGES_SEARCH_FILTER', +}; \ No newline at end of file diff --git a/src/client/containers/Packages/model.ts b/src/client/containers/Packages/model.ts new file mode 100644 index 000000000..fdef09303 --- /dev/null +++ b/src/client/containers/Packages/model.ts @@ -0,0 +1,203 @@ +import { LabKeyQueryRowPropertyProps } from '../../utils/constants' + + +interface PackagesModelProps { + active: Array + data: {[key: string]: any} + dataIds: Array + drafts: Array + filteredActive?: Array + filteredDrafts?: Array + hasInput?: boolean + + isError: boolean + isLoaded: boolean + isLoading: boolean + + message: string + packageCount: number + packageData: {[key: string]: PackageModel} + packageLoaded: boolean + packageLoading: boolean +} + +export const defaultPackagesModel: PackagesModelProps = { + active: [], + data: {}, + dataIds: [], + drafts: [], + filteredActive: [], + filteredDrafts: [], + hasInput: false, + isError: false, + isLoaded: false, + isLoading: false, + message: undefined, + packageCount: 0, + packageData: {}, + packageLoaded: false, + packageLoading: false +}; + +export class PackagesModel implements PackagesModelProps { + active: Array; + data: {[key: string]: any}; + dataIds: Array; + drafts: Array; + filteredActive?: Array; + filteredDrafts?: Array; + hasInput?: boolean; + isError: boolean; + isLoaded: boolean; + isLoading: boolean; + message: string; + packageCount: number; + packageData: {[key: string]: PackageModel}; + + packageLoaded: boolean + packageLoading: boolean + + constructor(values: PackagesModelProps = defaultPackagesModel) { + Object.keys(values).forEach(key => { + this[key] = values[key]; + }); + } +} + + + +interface QueryPackageModelProps { + Active: LabKeyQueryRowPropertyProps + Container: LabKeyQueryRowPropertyProps + Created: LabKeyQueryRowPropertyProps + CreatedBy: LabKeyQueryRowPropertyProps + Description: LabKeyQueryRowPropertyProps + HasEvent: LabKeyQueryRowPropertyProps + HasProject: LabKeyQueryRowPropertyProps + ModifiedBy: LabKeyQueryRowPropertyProps + Narrative: LabKeyQueryRowPropertyProps + ObjectId: LabKeyQueryRowPropertyProps + PkgId: LabKeyQueryRowPropertyProps + QcState: LabKeyQueryRowPropertyProps + Repeatable: LabKeyQueryRowPropertyProps + links: any +} + +export const defaultQueryPackageModel: QueryPackageModelProps = { + Active: undefined, + Container: undefined, + Created: undefined, + CreatedBy: undefined, + Description: undefined, + HasEvent: undefined, + HasProject: undefined, + ModifiedBy: undefined, + Narrative: undefined, + ObjectId: undefined, + PkgId: undefined, + QcState: undefined, + Repeatable: undefined, + links: undefined +}; + +export class QueryPackageModel implements QueryPackageModelProps { + Active: LabKeyQueryRowPropertyProps; + Container: LabKeyQueryRowPropertyProps; + Created: LabKeyQueryRowPropertyProps; + CreatedBy: LabKeyQueryRowPropertyProps; + Description: LabKeyQueryRowPropertyProps; + HasEvent: LabKeyQueryRowPropertyProps; + HasProject: LabKeyQueryRowPropertyProps; + ModifiedBy: LabKeyQueryRowPropertyProps; + Narrative: LabKeyQueryRowPropertyProps; + ObjectId: LabKeyQueryRowPropertyProps; + PkgId: LabKeyQueryRowPropertyProps; + QcState: LabKeyQueryRowPropertyProps; + Repeatable: LabKeyQueryRowPropertyProps; + links: any; + + constructor(values: QueryPackageModelProps = defaultQueryPackageModel) { + Object.keys(values).forEach(key => { + this[key] = values[key]; + }); + } +} + +interface PackageModelValidatorProps { + description: string + errorMessage: string + expression: string + name: string + type: string +} + +interface PackageModelAttributeProps { + format: string + label: string + lookupQuery: string + lookupSchema: string + name: string + rangeURI: string + required: boolean + scale: number + validators: Array +} + +interface PackageModelProps { + active: boolean + attributes: Array + categories: Array + container: string + description: string + narrative: string + pkgId: number + qcState: any + repeatable: boolean +} + +export const defaultPackageModel = { + active: false, + attributes: [], + categories: [], + container: undefined, + description: undefined, + narrative: undefined, + pkgId: 0, + qcState: null, + repeatable: false +}; + +export class PackageModel implements PackageModelProps { + + active: boolean; + attributes: Array<{ + format: string; + label: string; + lookupQuery: string; + lookupSchema: string; + name: string; + rangeURI: string; + required: boolean; + scale: number; + validators: Array<{ + description: string; + errorMessage: string; + expression: string; + name: string; + type: string; + }>; + }>; + categories: Array; + container: string; + description: string; + narrative: string; + pkgId: number; + qcState: any; + repeatable: boolean; + + constructor(values: PackageModelProps = defaultPackageModel) { + Object.keys(values).forEach(key => { + this[key] = values[key]; + }); + } +} \ No newline at end of file diff --git a/src/client/containers/Packages/reducer.ts b/src/client/containers/Packages/reducer.ts new file mode 100644 index 000000000..6b3340660 --- /dev/null +++ b/src/client/containers/Packages/reducer.ts @@ -0,0 +1,135 @@ +import { handleActions } from 'redux-actions'; + +import { PKG_TYPES } from './constants' +import { QueryPackageModel, PackageModel, PackagesModel } from './model' + + +export const packages = handleActions({ + + [PKG_TYPES.PACKAGE_LOADED]: (state: PackagesModel) => { + + return new PackagesModel(Object.assign({}, state, { + packageLoaded: true, + packageLoading: false + })); + }, + + [PKG_TYPES.PACKAGE_LOADING]: (state: PackagesModel) => { + return new PackagesModel(Object.assign({}, state, { + packageLoaded: false, + packageLoading: true + })); + }, + + [PKG_TYPES.PACKAGES_LOADED]: (state: PackagesModel) => { + + return new PackagesModel(Object.assign({}, state, { + isLoaded: true, + isLoading: false + })); + }, + + [PKG_TYPES.PACKAGES_LOADING]: (state: PackagesModel) => { + return new PackagesModel(Object.assign({}, state, { + isLoaded: false, + isLoading: true + })); + }, + + [PKG_TYPES.PACKAGES_SEARCH_FILTER]: (state: PackagesModel, action: any) => { + const { active, data, dataIds, drafts } = state; + const { input } = action; + + let filteredActive = active, + filteredDrafts = drafts; + + if (input && input !== '') { + const filtered = filterPackages(input, dataIds, data); + if (filtered.length) { + filteredActive = filtered.filter((id) => { + return active.indexOf(id) !== -1; + }); + + filteredDrafts = filtered.filter((id) => { + return drafts.indexOf(id) !== -1; + }); + } + } + + return new PackagesModel(Object.assign({}, state, { + filteredActive, + filteredDrafts + })); + }, + + [PKG_TYPES.PACKAGES_SUCCESS]: (state: PackagesModel, action: any) => { + const { response } = action; + + let active = [], + dataIds = [], + drafts = []; + const data = response.rows.reduce((prev, next: QueryPackageModel) => { + + const id = next.PkgId.value; + dataIds.push(id); + prev[id] = new QueryPackageModel(next); + + // should filter on hasEvent or Active? + if (next.Active.value === true) { + active.push(id); + } + else { + drafts.push(id); + } + + return prev; + }, {}); + + return new PackagesModel(Object.assign({}, state, { + active, + data, + dataIds, + drafts, + filteredActive: active, + filteredDrafts: drafts, + packageCount: response.rowCount + })); + }, + + [PKG_TYPES.PACKAGE_SUCCESS]: (state: PackagesModel, action: any) => { + const { response } = action; + const { json } = response; + + const packageData = json.reduce((prev, next: PackageModel) => { + const id = next.pkgId; + prev[id] = new PackageModel(next); + + return prev; + }, {}); + + return new PackagesModel(Object.assign({}, state, { + packageData: Object.assign({}, state.packageData, packageData) + })); + }, + +}, new PackagesModel()); + +function filterPackages(input: string, dataIds: Array , data: {[key: string]: any}) { + + return dataIds.filter((id: number) => { + const pkg: QueryPackageModel = data[id]; + + if (pkg) { + return ( + pkg.Description && + pkg.Description.value.indexOf(input) !== -1 + ) || ( + pkg.PkgId && + pkg.PkgId.value.toString().indexOf(input) !== -1 + ) + } + + return false; + }); + +} \ No newline at end of file diff --git a/src/client/reducers/index.ts b/src/client/reducers/index.ts index 01ac9ff41..472b993bc 100644 --- a/src/client/reducers/index.ts +++ b/src/client/reducers/index.ts @@ -17,15 +17,29 @@ import { routerReducer } from 'react-router-redux'; import { combineReducers } from 'redux'; import { reducer as formReducer } from 'redux-form'; +import { packages } from '../containers/Packages/reducer' +import { PackagesModel } from '../containers/Packages/model' + export interface APP_STATE_PROPS { + packages: PackagesModel + + form: any + router: any } export const reducers = combineReducers({ + packages, + form: formReducer, router: routerReducer, }); +// packages +// categories +// app +// user +// wizards \ No newline at end of file diff --git a/src/client/routing/Routes.tsx b/src/client/routing/Routes.tsx index e69de29bb..da85c01c6 100644 --- a/src/client/routing/Routes.tsx +++ b/src/client/routing/Routes.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { RouteComponentProps } from 'react-router-dom'; + + +import { EditCategories } from '../containers/Categories/Forms/Edit' +import { LandingPage } from '../containers/LandingPage/LandingPage' +import { PackageViewer } from '../containers/Packages/Forms/PackageViewer' +import { NewPackage } from '../containers/Packages/Forms/NewPackage' + +import { PackageForm } from '../containers/Packages/Forms/PackageForm' + +import { NotFound } from '../components/NotFound/NotFound' + + +export interface RouteProps { + component: React.ComponentClass>; + exact?: boolean; + path?: string; +} + + +export const Routes: Array = [ + { + component: LandingPage, + exact: true, + path: '/', + }, + + { + component: PackageViewer, + exact: true, + path: '/packages', + }, + + // { + // component: PackageForm, + // exact: true, + // path: '/packages/new', + // }, + + { + component: PackageForm, + exact: true, + path: '/packages/edit/:id', + }, + + // { + // component: PackageViewer, + // exact: true, + // path: '/packages/view/:id', + // }, + + // { + // component: PackageViewer, + // exact: true, + // path: '/packages/clone/:id', + // }, + + { + component: EditCategories, + exact: true, + path: '/categories', + }, + + { + component: EditCategories, + exact: true, + path: '/categories/edit', + }, + + { + component: NotFound + } +]; diff --git a/src/client/utils/constants.ts b/src/client/utils/constants.ts new file mode 100644 index 000000000..075601c14 --- /dev/null +++ b/src/client/utils/constants.ts @@ -0,0 +1,14 @@ + +// PACKAGES +const SND_SCHEMA = 'snd'; +export const SND_TABLES = { + PKGS: '' +}; + + + +export interface LabKeyQueryRowPropertyProps { + displayValue?: string + url?: string + value: any +} \ No newline at end of file diff --git a/src/client/utils/query.ts b/src/client/utils/query.ts new file mode 100644 index 000000000..1b4d89431 --- /dev/null +++ b/src/client/utils/query.ts @@ -0,0 +1,32 @@ +export function labkeyAjax(controller: string, action: string, params?: any, jsonData?: any, container?: string): Promise { + return new Promise((resolve, reject) => { + return LABKEY.Ajax.request({ + url: LABKEY.ActionURL.buildURL(controller, [action, 'api'].join('.'), container), + jsonData, + params, + success: LABKEY.Utils.getCallbackWrapper((data) => { + resolve(data); + }), + failure: LABKEY.Utils.getCallbackWrapper((data) => { + reject(data); + }) + }); + }); +} + +export function selectRows(schemaName: string, queryName: string, params?: {[key: string]: any}): Promise { + return new Promise((resolve, reject) => { + return LABKEY.Query.selectRows({ + schemaName, + queryName, + ...params, + requiredVersion: 17.1, // newer? + success: (data) => { + resolve(data); + }, + failure: (data) => { + reject(data); + } + }); + }); +} \ No newline at end of file From d79c677bf747149d6f1a1cd099689b13c31f1364 Mon Sep 17 00:00:00 2001 From: Josh Date Date: Wed, 30 Aug 2017 07:04:14 +0000 Subject: [PATCH 052/686] SND - remove console logs - update routes - add landing page links --- src/client/components/Packages/PackageRow.tsx | 14 +++++--- .../containers/Packages/Forms/PackageForm.tsx | 1 - src/client/routing/Routes.tsx | 32 +++++++++---------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/client/components/Packages/PackageRow.tsx b/src/client/components/Packages/PackageRow.tsx index 2d87faa94..e34d51c71 100644 --- a/src/client/components/Packages/PackageRow.tsx +++ b/src/client/components/Packages/PackageRow.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; - +import { Link } from 'react-router-dom' import { QueryPackageModel } from '../../containers/Packages/model' @@ -38,9 +38,15 @@ export class PackageRow extends React.Component
- - - + + + + + + + + +
{[PkgId.value, Description.value].join(' - ')} diff --git a/src/client/containers/Packages/Forms/PackageForm.tsx b/src/client/containers/Packages/Forms/PackageForm.tsx index 9fc618cf1..3a57d111e 100644 --- a/src/client/containers/Packages/Forms/PackageForm.tsx +++ b/src/client/containers/Packages/Forms/PackageForm.tsx @@ -69,7 +69,6 @@ export class PackageFormImpl extends React.Component diff --git a/src/client/routing/Routes.tsx b/src/client/routing/Routes.tsx index da85c01c6..06a880e86 100644 --- a/src/client/routing/Routes.tsx +++ b/src/client/routing/Routes.tsx @@ -32,11 +32,11 @@ export const Routes: Array = [ path: '/packages', }, - // { - // component: PackageForm, - // exact: true, - // path: '/packages/new', - // }, + { + component: PackageForm, + exact: true, + path: '/packages/new', + }, { component: PackageForm, @@ -44,17 +44,17 @@ export const Routes: Array = [ path: '/packages/edit/:id', }, - // { - // component: PackageViewer, - // exact: true, - // path: '/packages/view/:id', - // }, - - // { - // component: PackageViewer, - // exact: true, - // path: '/packages/clone/:id', - // }, + { + component: PackageForm, + exact: true, + path: '/packages/view/:id', + }, + + { + component: PackageForm, + exact: true, + path: '/packages/clone/:id', + }, { component: EditCategories, From 614d46a4f3e7a42deacae9ff1d1b29e7fda0c2fa Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 30 Aug 2017 15:16:31 +0000 Subject: [PATCH 053/686] Spec 30931: Pluralize name --- api-src/org/labkey/api/snd/SNDService.java | 2 +- src/org/labkey/snd/SNDServiceImpl.java | 2 +- src/org/labkey/snd/pipeline/SNDDataHandler.java | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/api-src/org/labkey/api/snd/SNDService.java b/api-src/org/labkey/api/snd/SNDService.java index 481d33e4d..3ae48916c 100644 --- a/api-src/org/labkey/api/snd/SNDService.java +++ b/api-src/org/labkey/api/snd/SNDService.java @@ -19,6 +19,6 @@ static SNDService get() } void savePackage(Container c, User u, Package pkg); - void saveSuperPackage(Container c, User u, List superPkgs); + void saveSuperPackages(Container c, User u, List superPkgs); List getPackages(Container c, User u, List pkgIds); } diff --git a/src/org/labkey/snd/SNDServiceImpl.java b/src/org/labkey/snd/SNDServiceImpl.java index c10922109..7cb9e6b27 100644 --- a/src/org/labkey/snd/SNDServiceImpl.java +++ b/src/org/labkey/snd/SNDServiceImpl.java @@ -54,7 +54,7 @@ public void savePackage(Container c, User u, Package pkg) } @Override - public void saveSuperPackage(Container c, User u, List superPkgs) + public void saveSuperPackages(Container c, User u, List superPkgs) { BatchValidationException errors = new BatchValidationException(); diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 2c70b9666..c56550937 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -35,10 +35,7 @@ import org.txbiomed.snd.SuperPackageType; import org.txbiomed.snd.SuperPackagesType; import org.txbiomed.snd.USDACategoryType; -import org.xml.sax.SAXException; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParserFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -246,7 +243,7 @@ private void parseAndSaveSuperPackages(ExportDocument.Export export, @NotNull Vi SuperPackage superPackage = parseSuperPackage(superPackageType); superPackages.add(superPackage); } - sndService.saveSuperPackage(info.getContainer(), info.getUser(), superPackages); + sndService.saveSuperPackages(info.getContainer(), info.getUser(), superPackages); } private SuperPackage parseSuperPackage(SuperPackageType superPackageType) From 3fbff67f7896854efb17ad78ac6c00cce1ab6f2c Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Wed, 30 Aug 2017 16:27:16 +0000 Subject: [PATCH 054/686] Spec 30931: Move package domain creation for QUS. --- src/org/labkey/snd/PackagesTable.java | 33 +++++++++++++++++++++++++++ src/org/labkey/snd/SNDManager.java | 14 +++++++----- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/snd/PackagesTable.java b/src/org/labkey/snd/PackagesTable.java index 6f8b88715..b9e9dcdc9 100644 --- a/src/org/labkey/snd/PackagesTable.java +++ b/src/org/labkey/snd/PackagesTable.java @@ -1,5 +1,6 @@ package org.labkey.snd; +import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; import org.labkey.api.data.JdbcType; import org.labkey.api.data.SQLFragment; @@ -8,7 +9,12 @@ import org.labkey.api.data.TableSelector; import org.labkey.api.exp.DomainNotFoundException; import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainUtil; import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.gwt.client.model.GWTDomain; +import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; +import org.labkey.api.query.BatchValidationException; +import org.labkey.api.query.DuplicateKeyException; import org.labkey.api.query.ExprColumn; import org.labkey.api.query.FieldKey; import org.labkey.api.query.InvalidKeyException; @@ -21,9 +27,11 @@ import java.sql.SQLException; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; + /** * Created by marty on 8/23/2017. */ @@ -101,6 +109,31 @@ public UpdateService(SimpleUserSchema.SimpleTable simpleTable, TableInfo table, super(simpleTable, table, helper); } + @Override + public List> insertRows(User user, Container container, List> rows, BatchValidationException errors, @Nullable Map configParameters, Map extraScriptContext) throws DuplicateKeyException, QueryUpdateServiceException, SQLException + { + int pkgId; + String description; + GWTDomain newDomain; + + // Create domain for each package + for (Map row : rows) + { + pkgId = (Integer) row.get("pkgId"); + description = (String) row.get("description"); + + newDomain = new GWTDomain<>(); + newDomain.setName(SNDManager.getPackageName(pkgId)); + newDomain.setContainer(container.getId()); + newDomain.setDescription(description); + + DomainUtil.createDomain(PackageDomainKind.getPackageKindName(), newDomain, null, container, user, null, null); + + } + + return super.insertRows(user, container, rows, errors, configParameters, extraScriptContext); + } + @Override protected Map deleteRow(User user, Container container, Map oldRowMap) throws QueryUpdateServiceException, SQLException, InvalidKeyException { diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index 57ba7d94f..6a77a8388 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -158,13 +158,15 @@ public void createPackage(User u, Container c, Package pkg, BatchValidationExcep if (!errors.hasErrors()) { - GWTDomain newDomain = new GWTDomain<>(); - newDomain.setName(getPackageName(pkg.getPkgId())); - newDomain.setContainer(c.getId()); - newDomain.setDescription(pkg.getDescription()); - newDomain.setFields(pkg.getAttributes()); + String domainURI = PackageDomainKind.getDomainURI(PackageDomainKind.getPackageSchemaName(), getPackageName(pkg.getPkgId()), c, u); + GWTDomain domain = DomainUtil.getDomainDescriptor(u, domainURI, c); + + if (domain == null) + throw new IllegalStateException("Cannot save package attributes. Domain not found."); - DomainUtil.createDomain(PackageDomainKind.getPackageKindName(), newDomain, null, c, u, null, null); + domain.setFields(pkg.getAttributes()); + PackageDomainKind kind = new PackageDomainKind(); + kind.updateDomain(c, u, domain); } } From cf659ab4d7746feccad8a31f287cdba00ec96b4b Mon Sep 17 00:00:00 2001 From: Josh Date Date: Wed, 30 Aug 2017 19:40:39 +0000 Subject: [PATCH 055/686] SND - initial work to update redux store using query instead of everything in packages key --- .../Packages/Forms/PackageViewer.tsx | 4 +- src/client/containers/Packages/actions.ts | 34 +---- src/client/containers/Packages/constants.ts | 2 - src/client/containers/Packages/model.ts | 2 +- src/client/query/actions.ts | 122 ++++++++++++++++++ src/client/query/constants.ts | 10 ++ src/client/query/model.ts | 114 ++++++++++++++++ src/client/query/reducer.ts | 96 ++++++++++++++ src/client/reducers/index.ts | 5 +- src/client/utils/constants.ts | 14 -- src/client/utils/query.ts | 32 ----- 11 files changed, 352 insertions(+), 83 deletions(-) create mode 100644 src/client/query/actions.ts create mode 100644 src/client/query/constants.ts create mode 100644 src/client/query/model.ts create mode 100644 src/client/query/reducer.ts delete mode 100644 src/client/utils/query.ts diff --git a/src/client/containers/Packages/Forms/PackageViewer.tsx b/src/client/containers/Packages/Forms/PackageViewer.tsx index c19956b72..27d56ac7c 100644 --- a/src/client/containers/Packages/Forms/PackageViewer.tsx +++ b/src/client/containers/Packages/Forms/PackageViewer.tsx @@ -12,6 +12,7 @@ import { APP_STATE_PROPS } from '../../../reducers/index' import * as actions from '../actions' import { QueryPackageModel, PackagesModel } from '../model' import { PackageRow } from '../../../components/Packages/PackageRow' +import { querySelectRows } from '../../../query/actions' interface PackageViewerOwnProps extends RouteComponentProps<{}> {} @@ -51,6 +52,7 @@ export class PackageViewerImpl extends React.Component
- +
diff --git a/src/client/containers/Packages/actions.ts b/src/client/containers/Packages/actions.ts index 551fa33f7..03d269130 100644 --- a/src/client/containers/Packages/actions.ts +++ b/src/client/containers/Packages/actions.ts @@ -1,7 +1,8 @@ import { PKG_TYPES } from './constants' import { PackageModel, PackagesModel } from './model' -import { labkeyAjax, selectRows } from '../../utils/query' +import { labkeyAjax, selectRows } from '../../query/actions' +import { LabKeyQueryResponse } from '../../query/model' export function getPackage(id: string | number) { return (dispatch, getState) => { @@ -89,37 +90,6 @@ export function filterPackages(input: string) { } - - - - - -export interface LabKeyQueryRowPropertyProps { - displayValue?: string - url?: string - value: any -} - -interface LabKeyQueryResponse { - columnModel?: Array<{ - align: string, - dataIndex: string, - editable: boolean, - header: string, - hidden: boolean, - required: boolean, - scale: number, - sortable: boolean, - width: number - }> - metaData?: {[key: string]: any} - queryName: string - rowCount: number - rows: Array<{data: {[key: string]: any}}> - schemaKey: {name: string, parent: any} - schemaName: Array | string -} - interface PackageQueryResponse { json: Array } diff --git a/src/client/containers/Packages/constants.ts b/src/client/containers/Packages/constants.ts index 7c59eb25c..a19be406f 100644 --- a/src/client/containers/Packages/constants.ts +++ b/src/client/containers/Packages/constants.ts @@ -1,5 +1,3 @@ - - const PKG_PREFIX = 'packages/'; export const PKG_TYPES = { diff --git a/src/client/containers/Packages/model.ts b/src/client/containers/Packages/model.ts index fdef09303..e1d63bb7b 100644 --- a/src/client/containers/Packages/model.ts +++ b/src/client/containers/Packages/model.ts @@ -1,4 +1,4 @@ -import { LabKeyQueryRowPropertyProps } from '../../utils/constants' +import { LabKeyQueryRowPropertyProps } from '../../query/model' interface PackagesModelProps { diff --git a/src/client/query/actions.ts b/src/client/query/actions.ts new file mode 100644 index 000000000..c417a661c --- /dev/null +++ b/src/client/query/actions.ts @@ -0,0 +1,122 @@ +import { QUERY_TYPES } from './constants' +import { LabKeyQueryResponse } from './model' + + +export function resolveKey(schema: string, query: string): string { + return [schema, query].join('|').toLowerCase(); +} + +export function getSchemaName(schemaQuery: string) { + if (schemaQuery) { + const splitSchemaQuery = schemaQuery.split('|'); + if (splitSchemaQuery && splitSchemaQuery.length === 2) { + return splitSchemaQuery[0]; + } + } + + return undefined; +} + +export function getQueryName(schemaQuery: string) { + if (schemaQuery) { + const splitSchemaQuery = schemaQuery.split('|'); + if (splitSchemaQuery && splitSchemaQuery.length === 2) { + return splitSchemaQuery[1]; + } + } + + return undefined; +} + +export function queryError(schemaQuery: string, error: any) { + return { + type: QUERY_TYPES.QUERY_ERROR, + error, + schemaQuery + }; +} + +export function queryInvalidate(schemaQuery: string) { + return { + type: QUERY_TYPES.QUERY_INVALIDATE, + schemaQuery + }; +} + +export function queryLoaded(schemaQuery: string) { + return { + type: QUERY_TYPES.QUERY_LOADED, + schemaQuery + }; +} + +export function queryLoading(schemaQuery: string) { + return { + type: QUERY_TYPES.QUERY_LOADING, + schemaQuery + }; +} + +export function querySuccess(schemaQuery: string, response) { + return { + type: QUERY_TYPES.QUERY_SUCCESS, + response, + schemaQuery + }; +} + +export function querySelectRows(schemaName: string, queryName: string, params?: {[key: string]: any}) { + return (dispatch) => { + const schemaQuery: string = resolveKey(schemaName, queryName); + dispatch(queryLoading(schemaQuery)); + + return selectRows(schemaName, queryName, params).then((response: LabKeyQueryResponse) => { + dispatch(queryLoaded(schemaQuery)); + + const { id } = response.metaData; + if (id) { + dispatch(querySuccess(schemaQuery, response)); + } + else { + throw new Error([schemaName, queryName].join(' ') + 'Response does not include id column'); + } + + }).catch((error) => { + dispatch(queryError(schemaQuery, error)); + }); + } +} + + +export function labkeyAjax(controller: string, action: string, params?: any, jsonData?: any, container?: string): Promise { + return new Promise((resolve, reject) => { + return LABKEY.Ajax.request({ + url: LABKEY.ActionURL.buildURL(controller, [action, 'api'].join('.'), container), + jsonData, + params, + success: LABKEY.Utils.getCallbackWrapper((data) => { + resolve(data); + }), + failure: LABKEY.Utils.getCallbackWrapper((data) => { + reject(data); + }) + }); + }); +} + +export function selectRows(schemaName: string, queryName: string, params?: {[key: string]: any}): Promise { + return new Promise((resolve, reject) => { + return LABKEY.Query.selectRows({ + schemaName, + queryName, + ...params, + requiredVersion: 17.1, // newer? + success: (data: LabKeyQueryResponse) => { + resolve(data); + }, + failure: (data) => { + reject(data); + } + }); + }); +} \ No newline at end of file diff --git a/src/client/query/constants.ts b/src/client/query/constants.ts new file mode 100644 index 000000000..836c3aa8e --- /dev/null +++ b/src/client/query/constants.ts @@ -0,0 +1,10 @@ +const QUERY_PREFIX = 'queries/'; + +export const QUERY_TYPES = { + QUERY_ERROR: QUERY_PREFIX + 'QUERY_ERROR', + QUERY_INVALIDATE: QUERY_PREFIX + 'QUERY_INVALIDATE', + QUERY_LOADED: QUERY_PREFIX + 'QUERY_LOADED', + QUERY_LOADING: QUERY_PREFIX + 'QUERY_LOADING', + QUERY_SUCCESS: QUERY_PREFIX + 'QUERY_SUCCESS', + +}; \ No newline at end of file diff --git a/src/client/query/model.ts b/src/client/query/model.ts new file mode 100644 index 000000000..637d3ebce --- /dev/null +++ b/src/client/query/model.ts @@ -0,0 +1,114 @@ + + +export interface LabKeyQueryRowPropertyProps { + displayValue?: string + url?: string + value: any +} + +interface LabKeyQueryColumnModelProps { + align: string + dataIndex: string + editable: boolean + header: string + hidden: boolean + required: boolean + scale: number + sortable: boolean + width: number +} + +interface LabKeyQueryMetaDataProps { + description: string + fields: Array + id: string + importMessage: string + importTemplates: Array<{label: string, url: string}> + root: string + title: string + totalProperty: string +} + +export interface LabKeyQueryResponse { + columnModel?: Array + metaData?: LabKeyQueryMetaDataProps + queryName: string + rowCount: number + rows: Array<{[key: string]: LabKeyQueryRowPropertyProps}> + schemaKey: {name: string, parent: any} + schemaName: Array | string +} + + +export interface QueryModelProps { + schema: string + query: string + + data: {[key: string]: LabKeyQueryRowPropertyProps} + dataCount: number + dataIds: Array + filters?: Array // define filter type + isError?: boolean + isLoaded?: boolean + isLoading?: boolean + message?: string +} + +const defaultQueryModel: QueryModelProps = { + schema: undefined, + query: undefined, + + data: {}, + dataCount: 0, + dataIds: [], + filters: [], // define filter type + isError: false, + isLoaded: false, + isLoading: false, + message: undefined, +}; + +export class QueryModel implements QueryModelProps { + schema: string; + query: string; + + data: {[key: string]: LabKeyQueryRowPropertyProps}; + dataCount: number; + dataIds: Array; + filters?: Array; // define filter type + isError?: boolean; + isLoaded?: boolean; + isLoading?: boolean; + message?: string; + + constructor(values: QueryModelProps = defaultQueryModel) { + const data: QueryModelProps = Object.assign({}, defaultQueryModel, values); + Object.keys(data).forEach(key => { + this[key] = values[key]; + }); + } +} + +export interface QueryModelsProps { + data: {[key: string]: QueryModel} + loadedQueries: Array + loadingQueries: Array +} + +const defaultQueryModelsContainer: QueryModelsProps = { + data: {}, + loadedQueries: [], + loadingQueries: [] +}; + +export class QueryModelsContainer implements QueryModelsProps { + data: {[key: string]: QueryModel}; + loadedQueries: Array; + loadingQueries: Array; + + constructor(values: QueryModelsProps = defaultQueryModelsContainer) { + Object.keys(values).forEach(key => { + this[key] = values[key]; + }); + } +} \ No newline at end of file diff --git a/src/client/query/reducer.ts b/src/client/query/reducer.ts new file mode 100644 index 000000000..8ed328d18 --- /dev/null +++ b/src/client/query/reducer.ts @@ -0,0 +1,96 @@ +import { QUERY_TYPES } from './constants' +import { handleActions } from 'redux-actions'; + +import { QueryModel, QueryModelsContainer } from './model' +import * as actions from './actions' + + + +export const queries = handleActions({ + + [QUERY_TYPES.QUERY_ERROR]: (state: QueryModelsContainer, action: any) => { + const { error, schemaQuery } = action; + + + return state; + }, + + [QUERY_TYPES.QUERY_INVALIDATE]: (state: QueryModelsContainer, action: any) => { + const { schemaQuery } = action; + const { loadedQueries, loadingQueries } = state; + + let loaded = removeQuery(schemaQuery, loadedQueries), + loading = removeQuery(schemaQuery, loadingQueries); + + const updatedState = Object.assign({}, state, {loadedQueries: loaded, loadingQueries: loading}); + return new QueryModelsContainer(updatedState); + }, + + [QUERY_TYPES.QUERY_LOADED]: (state: QueryModelsContainer, action: any) => { + const { schemaQuery } = action; + const { loadedQueries, loadingQueries } = state; + + let loaded = addQuery(schemaQuery, loadedQueries), + loading = removeQuery(schemaQuery, loadingQueries); + + const updatedState = Object.assign({}, state, {loadedQueries: loaded, loadingQueries: loading}); + return new QueryModelsContainer(updatedState); + }, + + [QUERY_TYPES.QUERY_LOADING]: (state: QueryModelsContainer, action: any) => { + const { schemaQuery } = action; + const { loadedQueries, loadingQueries } = state; + + let loaded = removeQuery(schemaQuery, loadedQueries), + loading = addQuery(schemaQuery, loadingQueries); + + const updatedState = Object.assign({}, state, {loadedQueries: loaded, loadingQueries: loading}); + return new QueryModelsContainer(updatedState); + }, + + [QUERY_TYPES.QUERY_SUCCESS]: (state: QueryModelsContainer, action: any) => { + const { response, schemaQuery } = action; + const pkCol = response.metaData.id; + + let dataIds = []; + const data = response.rows.reduce((prev, next) => { + + const id = next[pkCol].value; + dataIds.push(id); + prev[id] = next; + + return prev; + + }, {}); + + const queryModel = new QueryModel({ + schema: actions.getSchemaName(schemaQuery), + query: actions.getQueryName(schemaQuery), + + data, + dataIds, + dataCount: response.rowCount + }); + + const updatedState = Object.assign({}, state, {data: {[schemaQuery]: queryModel}}); + return new QueryModelsContainer(updatedState); + }, + +}, new QueryModelsContainer()); + + +function addQuery(query, queries: Array): Array { + if (queries && Array.isArray(queries) && queries.indexOf(query) === -1) { + return queries.concat(query); + } + + return []; +} + +function removeQuery(query, queries: Array): Array { + if (queries && Array.isArray(queries) && queries.indexOf(query) !== -1) { + return queries.filter((q) => q !== query); + } + + return queries; +} \ No newline at end of file diff --git a/src/client/reducers/index.ts b/src/client/reducers/index.ts index 472b993bc..6d4c3e629 100644 --- a/src/client/reducers/index.ts +++ b/src/client/reducers/index.ts @@ -17,13 +17,15 @@ import { routerReducer } from 'react-router-redux'; import { combineReducers } from 'redux'; import { reducer as formReducer } from 'redux-form'; +import { queries } from '../query/reducer' +import { QueryModelsContainer } from '../query/model' import { packages } from '../containers/Packages/reducer' import { PackagesModel } from '../containers/Packages/model' export interface APP_STATE_PROPS { packages: PackagesModel - + queries: QueryModelsContainer form: any router: any @@ -31,6 +33,7 @@ export interface APP_STATE_PROPS { export const reducers = combineReducers({ packages, + queries, form: formReducer, router: routerReducer, diff --git a/src/client/utils/constants.ts b/src/client/utils/constants.ts index 075601c14..e69de29bb 100644 --- a/src/client/utils/constants.ts +++ b/src/client/utils/constants.ts @@ -1,14 +0,0 @@ - -// PACKAGES -const SND_SCHEMA = 'snd'; -export const SND_TABLES = { - PKGS: '' -}; - - - -export interface LabKeyQueryRowPropertyProps { - displayValue?: string - url?: string - value: any -} \ No newline at end of file diff --git a/src/client/utils/query.ts b/src/client/utils/query.ts deleted file mode 100644 index 1b4d89431..000000000 --- a/src/client/utils/query.ts +++ /dev/null @@ -1,32 +0,0 @@ -export function labkeyAjax(controller: string, action: string, params?: any, jsonData?: any, container?: string): Promise { - return new Promise((resolve, reject) => { - return LABKEY.Ajax.request({ - url: LABKEY.ActionURL.buildURL(controller, [action, 'api'].join('.'), container), - jsonData, - params, - success: LABKEY.Utils.getCallbackWrapper((data) => { - resolve(data); - }), - failure: LABKEY.Utils.getCallbackWrapper((data) => { - reject(data); - }) - }); - }); -} - -export function selectRows(schemaName: string, queryName: string, params?: {[key: string]: any}): Promise { - return new Promise((resolve, reject) => { - return LABKEY.Query.selectRows({ - schemaName, - queryName, - ...params, - requiredVersion: 17.1, // newer? - success: (data) => { - resolve(data); - }, - failure: (data) => { - reject(data); - } - }); - }); -} \ No newline at end of file From fe52f590705b614acbbf33b8466094f9e77567ef Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Thu, 31 Aug 2017 01:46:28 +0000 Subject: [PATCH 056/686] Spec 30932 : Extra field usda-category should be usdaCode. --- src/org/labkey/snd/pipeline/SNDDataHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index c56550937..6941fb018 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -143,7 +143,7 @@ private Package parsePackage(PackageType packageType) Map extraFields = new HashedMap(); //usda-category - extraFields.put("usda-category", usdaCategoryVal); + extraFields.put("usdaCode", usdaCategoryVal); pkg.setExtraFields(extraFields); //narrative From c3eba14c62416bde8d043d3a2ac9dd2414717c56 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Thu, 31 Aug 2017 01:47:47 +0000 Subject: [PATCH 057/686] Spec 30932 : Removed TODO. --- src/org/labkey/snd/pipeline/SNDDataHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 6941fb018..77639237a 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -135,7 +135,7 @@ private Package parsePackage(PackageType packageType) //repeatable pkg.setRepeatable(packageType.getRepeatable()); - //displayable //TODO: keep 'active' or change to 'displayable'? + //displayable pkg.setActive(packageType.getDisplayable()); /* extra field(s)*/ From bc96cb0b52a91ab4bafa1e230b88fa4629fc3d6a Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Thu, 31 Aug 2017 02:27:18 +0000 Subject: [PATCH 058/686] Spec 30932 : Setting Property Validators. --- src/org/labkey/snd/pipeline/SNDDataHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/snd/pipeline/SNDDataHandler.java b/src/org/labkey/snd/pipeline/SNDDataHandler.java index 77639237a..7e5ea93a8 100644 --- a/src/org/labkey/snd/pipeline/SNDDataHandler.java +++ b/src/org/labkey/snd/pipeline/SNDDataHandler.java @@ -224,8 +224,8 @@ private List getAttributes(PackageType packageType) gwtPropertyValidator.setType(org.labkey.api.gwt.client.model.PropertyValidatorType.getType(lsid.getObjectId()));//typeURI gwtPropertyValidatorList.add(gwtPropertyValidator); + gwtpd.setPropertyValidators(gwtPropertyValidatorList); } - attributesList.add(gwtpd); } } From 96b8a25dcbe61c32c537ae2584491c22b7593dbe Mon Sep 17 00:00:00 2001 From: Josh Date Date: Thu, 31 Aug 2017 03:27:46 +0000 Subject: [PATCH 059/686] SND - store data more generically - make package search more generic --- src/client/components/Packages/PackageRow.tsx | 24 +- .../Packages/Forms/PackageSearch.tsx | 116 +++++++++ .../Packages/Forms/PackageViewer.tsx | 221 ++++++++++-------- src/client/containers/Packages/actions.ts | 44 +--- src/client/containers/Packages/constants.ts | 6 +- src/client/containers/Packages/model.ts | 23 +- src/client/containers/Packages/reducer.ts | 38 ++- src/client/query/actions.ts | 17 +- src/client/query/constants.ts | 1 + src/client/query/model.ts | 23 +- src/client/query/reducer.ts | 48 +++- src/client/utils/actions.tsx | 7 + 12 files changed, 382 insertions(+), 186 deletions(-) create mode 100644 src/client/containers/Packages/Forms/PackageSearch.tsx create mode 100644 src/client/utils/actions.tsx diff --git a/src/client/components/Packages/PackageRow.tsx b/src/client/components/Packages/PackageRow.tsx index e34d51c71..a11c5ad91 100644 --- a/src/client/components/Packages/PackageRow.tsx +++ b/src/client/components/Packages/PackageRow.tsx @@ -1,27 +1,14 @@ import * as React from 'react'; import { Link } from 'react-router-dom' -import { QueryPackageModel } from '../../containers/Packages/model' +import { LabKeyQueryRowPropertyProps } from '../../query/model' interface PackageRowProps { - pkg?: QueryPackageModel + data: {[key: string]: LabKeyQueryRowPropertyProps} + dataId: number } - -interface PackageViewerStateProps { - showDrafts: boolean -} - - -export class PackageRow extends React.Component { - - constructor(props?: PackageRowProps) { - super(props); - - this.state = { - showDrafts: false - } - } +export class PackageRow extends React.Component { render() { const resultStyle = { @@ -33,8 +20,9 @@ export class PackageRow extends React.Component
diff --git a/src/client/containers/Packages/Forms/PackageSearch.tsx b/src/client/containers/Packages/Forms/PackageSearch.tsx new file mode 100644 index 000000000..0a628696d --- /dev/null +++ b/src/client/containers/Packages/Forms/PackageSearch.tsx @@ -0,0 +1,116 @@ +import * as React from 'react'; +import { LabKeyQueryRowPropertyProps } from '../../../query/model' + +interface PackageSearchInputProps { + inputRenderer?: React.ComponentClass<{}> + showInput?: boolean +} + +export class PackageSearchInput extends React.Component { + + private inputRenderer: React.ComponentClass<{}> = null; + + static defaultProps = { + showInput: true + }; + + constructor(props?: PackageSearchInputProps) { + super(props); + + const { inputRenderer } = props; + + if (inputRenderer) { + this.inputRenderer = inputRenderer; + } + else { + this.inputRenderer = DefaultInput + } + } + + render() { + const { showInput } = this.props; + + if (showInput) { + return React.createElement(this.inputRenderer); + } + + return null; + } +} + +export interface PackageSearchRowProps { + data: {[key: string]: LabKeyQueryRowPropertyProps} + dataId: number +} + +interface PackageSearchResultsProps { + data: {[key: string]: LabKeyQueryRowPropertyProps} + dataIds: Array + rowRenderer?: React.ComponentClass // todo: fix renderer typing +} + +export class PackageSearchResults extends React.Component { + + private rowRenderer: React.ComponentClass = null; + + + constructor(props?: PackageSearchResultsProps) { + super(props); + + const { rowRenderer } = props; + + if (rowRenderer) { + this.rowRenderer = rowRenderer; + } + else { + this.rowRenderer = DefaultRowRenderer + } + } + + render() { + const { data, dataIds } = this.props; + + if (data && dataIds.length) { + return ( +
+ {dataIds.map((d, i) => { + const rowData = data[d]; + return ( +
+ {React.createElement(this.rowRenderer, {data: rowData, dataId: d})} +
+ ) + })} +
+ ) + } + + return null; + } +} + + +export class DefaultInput extends React.Component { + render() { + console.log('default input', this.props) + return( +
+ +
+ ) + } +} + + + + +export class DefaultRowRenderer extends React.Component { + render() { + console.log('default row', this.props) + return( +
+ +
+ ) + } +} diff --git a/src/client/containers/Packages/Forms/PackageViewer.tsx b/src/client/containers/Packages/Forms/PackageViewer.tsx index 27d56ac7c..b87cb3b79 100644 --- a/src/client/containers/Packages/Forms/PackageViewer.tsx +++ b/src/client/containers/Packages/Forms/PackageViewer.tsx @@ -11,78 +11,128 @@ import { CSSProperties } from "react"; import { APP_STATE_PROPS } from '../../../reducers/index' import * as actions from '../actions' import { QueryPackageModel, PackagesModel } from '../model' +import { SND_PKG_QUERY, SND_PKG_SCHEMA } from '../constants' import { PackageRow } from '../../../components/Packages/PackageRow' -import { querySelectRows } from '../../../query/actions' +import { querySelectRows, resolveKey } from '../../../query/actions' +import { QueryModel } from '../../../query/model' + +import { PackageSearchInput, PackageSearchResults } from './PackageSearch' interface PackageViewerOwnProps extends RouteComponentProps<{}> {} interface PackageViewerState { dispatch?: Dispatch + packagesData?: QueryModel packagesModel?: PackagesModel } -interface PackageViewerStateProps { - showDrafts: boolean -} type PackageViewerProps = PackageViewerOwnProps & PackageViewerState; +const resolvedSNDKey = resolveKey(SND_PKG_SCHEMA, SND_PKG_QUERY); - -function mapStateToProps(state: APP_STATE_PROPS, ownProps: PackageViewerOwnProps) { +function mapStateToProps(state: APP_STATE_PROPS) { return { + packagesData: state.queries.data[resolvedSNDKey], packagesModel: state.packages }; } -export class PackageViewerImpl extends React.Component { - - private timer: number = 0; +export class PackageViewerImpl extends React.Component { constructor(props?: PackageViewerProps) { super(props); - - this.state = { - showDrafts: false - } } componentDidMount() { - const { dispatch } = this.props; + const { dispatch, packagesModel } = this.props; + if (!packagesModel || (packagesModel && !packagesModel.isInit)) { + dispatch(querySelectRows(SND_PKG_SCHEMA, SND_PKG_QUERY)); + } // investigate mapDispatchToProps - dispatch(querySelectRows('snd', 'pkgs')); - dispatch(actions.getPackages()); - } - - componentWillUnmount() { - clearTimeout(this.timer); } componentWillReceiveProps(nextProps?: PackageViewerProps) { - const { dispatch } = nextProps; - const { isLoaded, isLoading, data } = nextProps.packagesModel; - if (!data && !isLoading && !isLoaded) { - dispatch(actions.getPackages()); + const { dispatch, packagesData, packagesModel } = nextProps; + const pDataExists = (packagesData && packagesData.isLoaded); + const pModelExists = (packagesModel && packagesModel.isInit); + + if (pDataExists && !pModelExists) { + dispatch(packagesModel.init(packagesData)); } } + render() { + const { data, filteredActive, filteredDrafts } = this.props.packagesModel; + // todo: add css style sheet and webpack support - toggleDrafts(evt: React.MouseEvent) { - const { showDrafts } = this.state; + return ( + +
+ - evt.preventDefault(); +
- this.setState({ - showDrafts: !showDrafts - }) +
+
+

Drafts

+
+ +
+
+ +
+

Active

+
+ +
+
+
+
+ + ) + } +} + +export const PackageViewer = connect(mapStateToProps)(PackageViewerImpl); + + +interface PackageViewerInputProps { + dispatch?: Dispatch<{}> +} + +interface PackageViewerInputStateProps { + input?: string + showDrafts?: boolean +} + +export class PackageViewerInputImpl extends React.Component { + + private timer: number = 0; + private inputRef: HTMLInputElement; + + constructor(props?: any) { + super(props); + + this.state = { + input: '', + showDrafts: false + } } handleClear() { - const { dispatch, packagesModel } = this.props; - const { hasInput } = packagesModel + const { dispatch } = this.props; + this.setInput(''); + this.inputRef.focus(); dispatch(actions.filterPackages('')); } @@ -90,6 +140,8 @@ export class PackageViewerImpl extends React.Component { @@ -97,12 +149,23 @@ export class PackageViewerImpl extends React.Component) { + const { showDrafts } = this.state; - // todo: add css style sheet and webpack support + evt.preventDefault(); + + this.setState({ + showDrafts: !showDrafts + }); + } + + render() { const buttonStyle = { padding: '2.5px 15px', }; @@ -130,69 +193,41 @@ export class PackageViewerImpl extends React.Component -
-
-
- - - -
-
- - this.handleClear()}/> - this.handleInputChange(evt)}/> -
-
-
- - Edit Categories - Edit Projects - -
-
-
-
+ return( +
+
+ + +
- {isLoaded && data ? -
-
this.toggleDrafts(evt)}> - this.toggleDrafts(evt)}/> Show drafts -
-
- {showDrafts ? -
-

Drafts

-
- {filteredDrafts.map((id) => { - const pkg: QueryPackageModel = data[id]; - return ; - })} -
-
- : null} - -
-

Active

-
- {filteredActive.map((id) => { - const pkg: QueryPackageModel = data[id]; - return ; - })} -
-
-
+
+ + this.handleClear()}/> + this.handleInputChange(evt)} + ref={(el) => this.inputRef = el} + style={searchStyle} + type="text" + value={input}/> +
+
+
+ + Edit Categories + Edit Projects +
- - :
Loading...
} - +
+
this.toggleDrafts(evt)}> + this.toggleDrafts(evt)}/> Show drafts +
+
) } } -export const PackageViewer = connect(mapStateToProps)(PackageViewerImpl); \ No newline at end of file +const PackageViewerInputWrap = (props: PackageViewerInputProps & Dispatch<{}>) => ; +export const PackageViewerInput = connect()(PackageViewerInputWrap); \ No newline at end of file diff --git a/src/client/containers/Packages/actions.ts b/src/client/containers/Packages/actions.ts index 03d269130..dcbb21047 100644 --- a/src/client/containers/Packages/actions.ts +++ b/src/client/containers/Packages/actions.ts @@ -1,8 +1,8 @@ import { PKG_TYPES } from './constants' import { PackageModel, PackagesModel } from './model' -import { labkeyAjax, selectRows } from '../../query/actions' -import { LabKeyQueryResponse } from '../../query/model' +import { labkeyAjax } from '../../query/actions' +import { QueryModel } from '../../query/model' export function getPackage(id: string | number) { return (dispatch, getState) => { @@ -29,21 +29,6 @@ export function getPackage(id: string | number) { } } -export function getPackages() { - return (dispatch, getState) => { - dispatch(packagesLoading()); - - return selectRows('snd', 'pkgs').then((response: LabKeyQueryResponse) => { - dispatch(packagesLoaded()); - dispatch(packagesSuccess(response)) - - }).catch((error) => { - // set error - console.log('error', error) - }); - } -} - function packageLoaded() { return { type: PKG_TYPES.PACKAGE_LOADED @@ -56,18 +41,6 @@ function packageLoading() { }; } -function packagesLoaded() { - return { - type: PKG_TYPES.PACKAGES_LOADED - } -} - -function packagesLoading() { - return { - type: PKG_TYPES.PACKAGES_LOADING - }; -} - function packageSuccess(response: PackageQueryResponse) { return { type: PKG_TYPES.PACKAGE_SUCCESS, @@ -75,17 +48,18 @@ function packageSuccess(response: PackageQueryResponse) { }; } -function packagesSuccess(response: LabKeyQueryResponse) { +export function filterPackages(input: string) { return { - type: PKG_TYPES.PACKAGES_SUCCESS, - response + type: PKG_TYPES.PACKAGES_SEARCH_FILTER, + input }; } -export function filterPackages(input: string) { +export function packagesInit(model: PackagesModel, dataResponse: QueryModel) { return { - type: PKG_TYPES.PACKAGES_SEARCH_FILTER, - input + type: PKG_TYPES.PACKAGE_INIT, + dataResponse, + model }; } diff --git a/src/client/containers/Packages/constants.ts b/src/client/containers/Packages/constants.ts index a19be406f..79706ff28 100644 --- a/src/client/containers/Packages/constants.ts +++ b/src/client/containers/Packages/constants.ts @@ -1,6 +1,7 @@ const PKG_PREFIX = 'packages/'; export const PKG_TYPES = { + PACKAGE_INIT: PKG_PREFIX + 'PACKAGE_INIT', PACKAGE_LOADED: PKG_PREFIX + 'PACKAGE_LOADED', PACKAGE_LOADING: PKG_PREFIX + 'PACKAGE_LOADING', PACKAGES_LOADED: PKG_PREFIX + 'PACKAGES_LOADED', @@ -11,4 +12,7 @@ export const PKG_TYPES = { PACKAGES_SUCCESS: PKG_PREFIX + 'PACKAGES_SUCCESS', SET_ACTIVE_PACKAGE: PKG_PREFIX + 'SET_ACTIVE_PACKAGE', PACKAGES_SEARCH_FILTER: PKG_PREFIX + 'PACKAGES_SEARCH_FILTER', -}; \ No newline at end of file +}; + +export const SND_PKG_SCHEMA: string = 'snd'; +export const SND_PKG_QUERY: string = 'pkgs'; \ No newline at end of file diff --git a/src/client/containers/Packages/model.ts b/src/client/containers/Packages/model.ts index e1d63bb7b..e0bd052f0 100644 --- a/src/client/containers/Packages/model.ts +++ b/src/client/containers/Packages/model.ts @@ -1,4 +1,6 @@ -import { LabKeyQueryRowPropertyProps } from '../../query/model' +import { LabKeyQueryRowPropertyProps, QueryModel } from '../../query/model' + +import * as actions from './actions' interface PackagesModelProps { @@ -11,9 +13,7 @@ interface PackagesModelProps { hasInput?: boolean isError: boolean - isLoaded: boolean - isLoading: boolean - + isInit?: boolean message: string packageCount: number packageData: {[key: string]: PackageModel} @@ -30,8 +30,7 @@ export const defaultPackagesModel: PackagesModelProps = { filteredDrafts: [], hasInput: false, isError: false, - isLoaded: false, - isLoading: false, + isInit: false, message: undefined, packageCount: 0, packageData: {}, @@ -39,6 +38,7 @@ export const defaultPackagesModel: PackagesModelProps = { packageLoading: false }; +// may make more sense for this to be in wizards/packages/packageSearch? export class PackagesModel implements PackagesModelProps { active: Array; data: {[key: string]: any}; @@ -48,20 +48,23 @@ export class PackagesModel implements PackagesModelProps { filteredDrafts?: Array; hasInput?: boolean; isError: boolean; - isLoaded: boolean; - isLoading: boolean; + isInit?: boolean; message: string; packageCount: number; packageData: {[key: string]: PackageModel}; - packageLoaded: boolean - packageLoading: boolean + packageLoaded: boolean; + packageLoading: boolean; constructor(values: PackagesModelProps = defaultPackagesModel) { Object.keys(values).forEach(key => { this[key] = values[key]; }); } + + init(data: QueryModel) { + return actions.packagesInit(this, data); + } } diff --git a/src/client/containers/Packages/reducer.ts b/src/client/containers/Packages/reducer.ts index 6b3340660..3d46ed954 100644 --- a/src/client/containers/Packages/reducer.ts +++ b/src/client/containers/Packages/reducer.ts @@ -21,18 +21,38 @@ export const packages = handleActions({ })); }, - [PKG_TYPES.PACKAGES_LOADED]: (state: PackagesModel) => { + [PKG_TYPES.PACKAGE_INIT]: (state: PackagesModel, action: any) => { + const { dataResponse, model } = action; + const { data, dataCount, dataIds } = dataResponse; - return new PackagesModel(Object.assign({}, state, { - isLoaded: true, - isLoading: false - })); - }, + let active = [], + ids = [], + drafts = []; + const packagesData = dataIds.reduce((prev, next: number) => { + const packageData = data[next]; + const id = packageData.PkgId.value; + ids.push(id); + prev[id] = new QueryPackageModel(packageData); + // // should filter on hasEvent or Active? + if (packageData.Active.value === true) { + active.push(id); + } + else { + drafts.push(id); + } + + return prev; + }, {}); - [PKG_TYPES.PACKAGES_LOADING]: (state: PackagesModel) => { return new PackagesModel(Object.assign({}, state, { - isLoaded: false, - isLoading: true + active, + data: packagesData, + dataIds: ids, + drafts, + isInit: true, + filteredActive: active, + filteredDrafts: drafts, + packageCount: dataCount })); }, diff --git a/src/client/query/actions.ts b/src/client/query/actions.ts index c417a661c..ef7645841 100644 --- a/src/client/query/actions.ts +++ b/src/client/query/actions.ts @@ -36,6 +36,13 @@ export function queryError(schemaQuery: string, error: any) { }; } +export function queryInitialize(schemaQuery: string) { + return { + type: QUERY_TYPES.QUERY_INIT, + schemaQuery + }; +} + export function queryInvalidate(schemaQuery: string) { return { type: QUERY_TYPES.QUERY_INVALIDATE, @@ -66,16 +73,22 @@ export function querySuccess(schemaQuery: string, response) { } export function querySelectRows(schemaName: string, queryName: string, params?: {[key: string]: any}) { - return (dispatch) => { + return (dispatch, getState) => { const schemaQuery: string = resolveKey(schemaName, queryName); + const model = getState().queries.data[schemaQuery]; + + if (!model) { + dispatch(queryInitialize(schemaQuery)); + } + dispatch(queryLoading(schemaQuery)); return selectRows(schemaName, queryName, params).then((response: LabKeyQueryResponse) => { - dispatch(queryLoaded(schemaQuery)); const { id } = response.metaData; if (id) { dispatch(querySuccess(schemaQuery, response)); + dispatch(queryLoaded(schemaQuery)); } else { throw new Error([schemaName, queryName].join(' ') + 'Response does not include id column'); diff --git a/src/client/query/constants.ts b/src/client/query/constants.ts index 836c3aa8e..6812c9db8 100644 --- a/src/client/query/constants.ts +++ b/src/client/query/constants.ts @@ -2,6 +2,7 @@ const QUERY_PREFIX = 'queries/'; export const QUERY_TYPES = { QUERY_ERROR: QUERY_PREFIX + 'QUERY_ERROR', + QUERY_INIT: QUERY_PREFIX + 'QUERY_INIT', QUERY_INVALIDATE: QUERY_PREFIX + 'QUERY_INVALIDATE', QUERY_LOADED: QUERY_PREFIX + 'QUERY_LOADED', QUERY_LOADING: QUERY_PREFIX + 'QUERY_LOADING', diff --git a/src/client/query/model.ts b/src/client/query/model.ts index 637d3ebce..d11d519f5 100644 --- a/src/client/query/model.ts +++ b/src/client/query/model.ts @@ -41,17 +41,18 @@ export interface LabKeyQueryResponse { export interface QueryModelProps { - schema: string - query: string + schema?: string + query?: string - data: {[key: string]: LabKeyQueryRowPropertyProps} - dataCount: number - dataIds: Array + data?: {[key: string]: LabKeyQueryRowPropertyProps} + dataCount?: number + dataIds?: Array filters?: Array // define filter type isError?: boolean isLoaded?: boolean isLoading?: boolean message?: string + metaData?: LabKeyQueryMetaDataProps } const defaultQueryModel: QueryModelProps = { @@ -66,20 +67,22 @@ const defaultQueryModel: QueryModelProps = { isLoaded: false, isLoading: false, message: undefined, + metaData: {} as LabKeyQueryMetaDataProps }; export class QueryModel implements QueryModelProps { - schema: string; - query: string; + schema?: string; + query?: string; - data: {[key: string]: LabKeyQueryRowPropertyProps}; - dataCount: number; - dataIds: Array; + data?: {[key: string]: LabKeyQueryRowPropertyProps}; + dataCount?: number; + dataIds?: Array; filters?: Array; // define filter type isError?: boolean; isLoaded?: boolean; isLoading?: boolean; message?: string; + metaData?: LabKeyQueryMetaDataProps; constructor(values: QueryModelProps = defaultQueryModel) { const data: QueryModelProps = Object.assign({}, defaultQueryModel, values); diff --git a/src/client/query/reducer.ts b/src/client/query/reducer.ts index 8ed328d18..0fa03b43f 100644 --- a/src/client/query/reducer.ts +++ b/src/client/query/reducer.ts @@ -15,6 +15,18 @@ export const queries = handleActions({ return state; }, + [QUERY_TYPES.QUERY_INIT]: (state: QueryModelsContainer, action: any) => { + const { schemaQuery } = action; + + const model = new QueryModel({ + schema: actions.getSchemaName(schemaQuery), + query: actions.getQueryName(schemaQuery) + }); + + const updatedState = Object.assign({}, state, {data: {[schemaQuery]: model}}); + return new QueryModelsContainer(updatedState); + }, + [QUERY_TYPES.QUERY_INVALIDATE]: (state: QueryModelsContainer, action: any) => { const { schemaQuery } = action; const { loadedQueries, loadingQueries } = state; @@ -33,7 +45,18 @@ export const queries = handleActions({ let loaded = addQuery(schemaQuery, loadedQueries), loading = removeQuery(schemaQuery, loadingQueries); - const updatedState = Object.assign({}, state, {loadedQueries: loaded, loadingQueries: loading}); + const model = new QueryModel(Object.assign({}, state.data[schemaQuery], { + isLoaded: true, + isLoading: false + })); + + const updatedData = Object.assign({}, state.data, {[schemaQuery]: model}); + + const updatedState = Object.assign({}, state, { + data: updatedData, + loadedQueries: loaded, + loadingQueries: loading + }); return new QueryModelsContainer(updatedState); }, @@ -44,7 +67,18 @@ export const queries = handleActions({ let loaded = removeQuery(schemaQuery, loadedQueries), loading = addQuery(schemaQuery, loadingQueries); - const updatedState = Object.assign({}, state, {loadedQueries: loaded, loadingQueries: loading}); + const model = new QueryModel(Object.assign({}, state.data[schemaQuery], { + isLoaded: false, + isLoading: true + })); + + const updatedData = Object.assign({}, state.data, {[schemaQuery]: model}); + + const updatedState = Object.assign({}, state, { + data: updatedData, + loadedQueries: loaded, + loadingQueries: loading + }); return new QueryModelsContainer(updatedState); }, @@ -63,14 +97,12 @@ export const queries = handleActions({ }, {}); - const queryModel = new QueryModel({ - schema: actions.getSchemaName(schemaQuery), - query: actions.getQueryName(schemaQuery), - + const queryModel = new QueryModel(Object.assign({}, state.data[schemaQuery], { data, dataIds, - dataCount: response.rowCount - }); + dataCount: response.rowCount, + metaData: response.metaData + })); const updatedState = Object.assign({}, state, {data: {[schemaQuery]: queryModel}}); return new QueryModelsContainer(updatedState); diff --git a/src/client/utils/actions.tsx b/src/client/utils/actions.tsx new file mode 100644 index 000000000..09044cabc --- /dev/null +++ b/src/client/utils/actions.tsx @@ -0,0 +1,7 @@ + + +const If = ({ cond, children }) => ( + cond ? children() : null +); + +// create permissions wrapper/s \ No newline at end of file From 69ee8aca456b6b7344ddec8a8403e1732dd6a734 Mon Sep 17 00:00:00 2001 From: Josh Date Date: Thu, 31 Aug 2017 06:12:21 +0000 Subject: [PATCH 060/686] SND - initial package form inputs --- .../containers/Packages/Forms/PackageForm.tsx | 103 +++++++++++-- .../Packages/Forms/PackageViewer.tsx | 20 +-- src/client/containers/Packages/actions.ts | 54 +------ src/client/containers/Packages/constants.ts | 8 +- src/client/containers/Packages/model.ts | 95 +----------- src/client/containers/Packages/reducer.ts | 71 +-------- .../containers/Wizards/Packages/actions.ts | 79 ++++++++++ .../containers/Wizards/Packages/constants.ts | 11 ++ .../containers/Wizards/Packages/model.ts | 140 ++++++++++++++++++ .../containers/Wizards/Packages/reducer.ts | 72 +++++++++ src/client/containers/Wizards/reducer.ts | 12 ++ src/client/reducers/index.ts | 18 +-- 12 files changed, 431 insertions(+), 252 deletions(-) create mode 100644 src/client/containers/Wizards/Packages/actions.ts create mode 100644 src/client/containers/Wizards/Packages/constants.ts create mode 100644 src/client/containers/Wizards/Packages/model.ts create mode 100644 src/client/containers/Wizards/Packages/reducer.ts create mode 100644 src/client/containers/Wizards/reducer.ts diff --git a/src/client/containers/Packages/Forms/PackageForm.tsx b/src/client/containers/Packages/Forms/PackageForm.tsx index 3a57d111e..ea4cd48d9 100644 --- a/src/client/containers/Packages/Forms/PackageForm.tsx +++ b/src/client/containers/Packages/Forms/PackageForm.tsx @@ -1,13 +1,13 @@ import * as React from 'react'; - -import { Link, RouteComponentProps } from 'react-router-dom' +import { Panel } from 'react-bootstrap' +import { RouteComponentProps } from 'react-router-dom' import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { APP_STATE_PROPS } from '../../../reducers/index' -import * as actions from '../actions' -import { PackageModel } from '../model' +import * as actions from '../../Wizards/Packages/actions' +import { PackageModel } from '../../Wizards/Packages/model' interface PackageFormOwnProps extends RouteComponentProps<{id: string}> {} @@ -30,17 +30,28 @@ function mapStateToProps(state: APP_STATE_PROPS, ownProps: PackageFormOwnProps) const { id } = ownProps.match.params; return { - packageModel: state.packages.packageData[id] + packageModel: state.wizards.packages.packageData[id] }; } export class PackageFormImpl extends React.Component { private timer: number = 0; + private panelHeader: React.ReactNode = null; + private view: string = undefined; constructor(props?: PackageFormProps) { super(props); + const { path } = props.match; + const { id } = props.match.params; + + const parts = path.split('/'); + if (parts && parts[2]) { + this.view = parts[2]; + this.panelHeader = parsePackageHeader(parts[2], id); + } + this.state = { showDrafts: false } @@ -63,18 +74,90 @@ export class PackageFormImpl extends React.Component - -
+ +
+
+
+
+ Package Id +
+
+ Description +
+
+
+
+ +
+
+ +
+
+
+
+ Narrative +
+
+
+
+